conlang: Import items into scope with use
!
grammar: - Improve specification of `Path` - Add `Use` and `UseTree` rules - Add `Use` as a variant of ItemKind cl-token: - Add new keywords `use` and `as` cl-ast: - Add nodes for Use and UseTree - Add new ItemKind for Use - Implement traversal in Visit and Fold cl-interpret: - Mark ItemKind::Use with a todo cl-parser: - Update to match grammar cl-typeck: - Update to match changes in AST - Mark UseTrees as NameCollectable and TypeResolvable, but leave as todos
This commit is contained in:
parent
9dc0cc7841
commit
efd442bbfa
@ -99,6 +99,8 @@ pub enum ItemKind {
|
||||
Function(Function),
|
||||
/// An [implementation](Impl)
|
||||
Impl(Impl),
|
||||
/// An [import](Use)
|
||||
Use(Use),
|
||||
}
|
||||
|
||||
/// An alias to another [Ty]
|
||||
@ -223,6 +225,21 @@ pub enum ImplKind {
|
||||
Trait { impl_trait: Path, for_type: Box<Ty> },
|
||||
}
|
||||
|
||||
/// An import of nonlocal [Item]s
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Use {
|
||||
pub tree: UseTree,
|
||||
}
|
||||
|
||||
/// A tree of [Item] imports
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum UseTree {
|
||||
Tree(Path, Vec<UseTree>),
|
||||
Alias(Path, Identifier),
|
||||
Path(Path),
|
||||
Glob,
|
||||
}
|
||||
|
||||
/// A type expression
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Ty {
|
||||
|
@ -116,6 +116,7 @@ mod display {
|
||||
ItemKind::Struct(v) => v.fmt(f),
|
||||
ItemKind::Enum(v) => v.fmt(f),
|
||||
ItemKind::Impl(v) => v.fmt(f),
|
||||
ItemKind::Use(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,6 +281,27 @@ mod display {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Use {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { tree } = self;
|
||||
write!(f, "use {tree}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UseTree {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UseTree::Tree(path, tree) => {
|
||||
write!(f, "{path}")?;
|
||||
separate(tree, ", ")(f.delimit(INLINE_BRACES))
|
||||
}
|
||||
UseTree::Alias(path, name) => write!(f, "{path} as {name}"),
|
||||
UseTree::Path(path) => write!(f, "{path}"),
|
||||
UseTree::Glob => write!(f, "*"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ty {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.kind.fmt(f)
|
||||
@ -665,6 +687,7 @@ mod convert {
|
||||
Struct => ItemKind::Struct,
|
||||
Enum => ItemKind::Enum,
|
||||
Impl => ItemKind::Impl,
|
||||
Use => ItemKind::Use,
|
||||
}
|
||||
impl From for StructKind {
|
||||
Vec<Ty> => StructKind::Tuple,
|
||||
|
@ -158,6 +158,13 @@ pub trait Fold {
|
||||
fn fold_impl_kind(&mut self, kind: ImplKind) -> ImplKind {
|
||||
or_fold_impl_kind(self, kind)
|
||||
}
|
||||
fn fold_use(&mut self, u: Use) -> Use {
|
||||
let Use { tree } = u;
|
||||
Use { tree: self.fold_use_tree(tree) }
|
||||
}
|
||||
fn fold_use_tree(&mut self, tree: UseTree) -> UseTree {
|
||||
or_fold_use_tree(self, tree)
|
||||
}
|
||||
fn fold_ty(&mut self, t: Ty) -> Ty {
|
||||
let Ty { extents, kind } = t;
|
||||
Ty { extents: self.fold_span(extents), kind: self.fold_ty_kind(kind) }
|
||||
@ -375,6 +382,7 @@ pub fn or_fold_item_kind<F: Fold + ?Sized>(folder: &mut F, kind: ItemKind) -> It
|
||||
ItemKind::Static(s) => ItemKind::Static(folder.fold_static(s)),
|
||||
ItemKind::Function(f) => ItemKind::Function(folder.fold_function(f)),
|
||||
ItemKind::Impl(i) => ItemKind::Impl(folder.fold_impl(i)),
|
||||
ItemKind::Use(u) => ItemKind::Use(folder.fold_use(u)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,6 +449,23 @@ pub fn or_fold_impl_kind<F: Fold + ?Sized>(folder: &mut F, kind: ImplKind) -> Im
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn or_fold_use_tree<F: Fold + ?Sized>(folder: &mut F, tree: UseTree) -> UseTree {
|
||||
match tree {
|
||||
UseTree::Tree(path, tree) => UseTree::Tree(
|
||||
folder.fold_path(path),
|
||||
tree.into_iter()
|
||||
.map(|tree| folder.fold_use_tree(tree))
|
||||
.collect(),
|
||||
),
|
||||
UseTree::Alias(path, name) => {
|
||||
UseTree::Alias(folder.fold_path(path), folder.fold_identifier(name))
|
||||
}
|
||||
UseTree::Path(path) => UseTree::Path(folder.fold_path(path)),
|
||||
UseTree::Glob => UseTree::Glob,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Folds a [TyKind] in the default way
|
||||
pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind {
|
||||
|
@ -125,11 +125,18 @@ pub trait Visit<'a>: Sized {
|
||||
fn visit_impl(&mut self, i: &'a Impl) {
|
||||
let Impl { target, body } = i;
|
||||
self.visit_impl_kind(target);
|
||||
self.visit_file(body)
|
||||
self.visit_file(body);
|
||||
}
|
||||
fn visit_impl_kind(&mut self, target: &'a ImplKind) {
|
||||
or_visit_impl_kind(self, target)
|
||||
}
|
||||
fn visit_use(&mut self, u: &'a Use) {
|
||||
let Use { tree } = u;
|
||||
self.visit_use_tree(tree);
|
||||
}
|
||||
fn visit_use_tree(&mut self, tree: &'a UseTree) {
|
||||
or_visit_use_tree(self, tree)
|
||||
}
|
||||
fn visit_ty(&mut self, t: &'a Ty) {
|
||||
let Ty { extents, kind } = t;
|
||||
self.visit_span(extents);
|
||||
@ -320,6 +327,7 @@ pub fn or_visit_item_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ItemKind)
|
||||
ItemKind::Static(s) => visitor.visit_static(s),
|
||||
ItemKind::Function(f) => visitor.visit_function(f),
|
||||
ItemKind::Impl(i) => visitor.visit_impl(i),
|
||||
ItemKind::Use(u) => visitor.visit_use(u),
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,6 +372,21 @@ pub fn or_visit_impl_kind<'a, V: Visit<'a>>(visitor: &mut V, target: &'a ImplKin
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_visit_use_tree<'a, V: Visit<'a>>(visitor: &mut V, tree: &'a UseTree) {
|
||||
match tree {
|
||||
UseTree::Tree(path, tree) => {
|
||||
visitor.visit_path(path);
|
||||
tree.iter().for_each(|tree| visitor.visit_use_tree(tree));
|
||||
}
|
||||
UseTree::Alias(path, name) => {
|
||||
visitor.visit_path(path);
|
||||
visitor.visit_identifier(name);
|
||||
}
|
||||
UseTree::Path(path) => visitor.visit_path(path),
|
||||
UseTree::Glob => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) {
|
||||
match kind {
|
||||
TyKind::Never => {}
|
||||
|
@ -36,6 +36,7 @@ impl Interpret for Item {
|
||||
ItemKind::Struct(item) => item.interpret(env),
|
||||
ItemKind::Enum(item) => item.interpret(env),
|
||||
ItemKind::Impl(item) => item.interpret(env),
|
||||
ItemKind::Use(_) => todo!("namespaces and imports in the interpreter"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub enum ErrorKind {
|
||||
want: Parsing,
|
||||
},
|
||||
/// Indicates unfinished code
|
||||
Todo,
|
||||
Todo(&'static str),
|
||||
}
|
||||
impl From<LexError> for ErrorKind {
|
||||
fn from(value: LexError) -> Self {
|
||||
@ -69,6 +69,8 @@ pub enum Parsing {
|
||||
VariantKind,
|
||||
Impl,
|
||||
ImplKind,
|
||||
Use,
|
||||
UseTree,
|
||||
|
||||
Ty,
|
||||
TyKind,
|
||||
@ -116,7 +118,7 @@ impl Display for Error {
|
||||
let Self { reason, while_parsing, loc } = self;
|
||||
match reason {
|
||||
// TODO entries are debug-printed
|
||||
ErrorKind::Todo => write!(f, "{loc} {reason} {while_parsing:?}"),
|
||||
ErrorKind::Todo(_) => write!(f, "{loc} {reason} {while_parsing:?}"),
|
||||
// lexical errors print their own higher-resolution loc info
|
||||
ErrorKind::Lexical(e) => write!(f, "{e} (while parsing {while_parsing})"),
|
||||
_ => write!(f, "{loc} {reason} while parsing {while_parsing}"),
|
||||
@ -134,7 +136,7 @@ impl Display for ErrorKind {
|
||||
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
|
||||
ErrorKind::ExpectedToken { want: e, got: g } => write!(f, "Expected `{e}`, got `{g}`"),
|
||||
ErrorKind::ExpectedParsing { want } => write!(f, "Expected {want}"),
|
||||
ErrorKind::Todo => write!(f, "TODO:"),
|
||||
ErrorKind::Todo(unfinished) => write!(f, "TODO: {unfinished}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,6 +168,8 @@ impl Display for Parsing {
|
||||
Parsing::VariantKind => "an enum variant",
|
||||
Parsing::Impl => "an impl block",
|
||||
Parsing::ImplKind => "the target of an impl block",
|
||||
Parsing::Use => "a use item",
|
||||
Parsing::UseTree => "a use-tree",
|
||||
|
||||
Parsing::Ty => "a type",
|
||||
Parsing::TyKind => "a type",
|
||||
|
@ -162,6 +162,7 @@ macro item_like() {
|
||||
| TokenKind::Struct
|
||||
| TokenKind::Enum
|
||||
| TokenKind::Impl
|
||||
| TokenKind::Use
|
||||
}
|
||||
|
||||
/// Top level parsing
|
||||
@ -204,17 +205,24 @@ impl<'t> Parser<'t> {
|
||||
///
|
||||
/// See also: [Parser::path_part], [Parser::identifier]
|
||||
///
|
||||
/// [Path] = `::`? ([PathPart] `::`)* [PathPart]?
|
||||
/// [Path] = `::` *RelativePath*? | *RelativePath* \
|
||||
/// *RelativePath* = [PathPart] (`::` [PathPart])*
|
||||
pub fn path(&mut self) -> PResult<Path> {
|
||||
const PARSING: Parsing = Parsing::PathExpr;
|
||||
let absolute = self.match_op(Punct::ColonColon, PARSING).is_ok();
|
||||
let mut parts = vec![];
|
||||
|
||||
while let Ok(path_part) = self.path_part() {
|
||||
parts.push(path_part);
|
||||
if self.match_op(Punct::ColonColon, PARSING).is_err() {
|
||||
break;
|
||||
if absolute {
|
||||
match self.path_part() {
|
||||
Ok(part) => parts.push(part),
|
||||
Err(_) => return Ok(Path { absolute, parts }),
|
||||
}
|
||||
} else {
|
||||
parts.push(self.path_part()?)
|
||||
};
|
||||
|
||||
while self.match_op(Punct::ColonColon, Parsing::PathExpr).is_ok() {
|
||||
parts.push(self.path_part()?)
|
||||
}
|
||||
|
||||
Ok(Path { absolute, parts })
|
||||
@ -295,6 +303,7 @@ impl<'t> Parser<'t> {
|
||||
TokenKind::Struct => self.parse_struct()?.into(),
|
||||
TokenKind::Enum => self.parse_enum()?.into(),
|
||||
TokenKind::Impl => self.parse_impl()?.into(),
|
||||
TokenKind::Use => self.parse_use()?.into(),
|
||||
t => Err(self.error(Unexpected(t), Parsing::Item))?,
|
||||
})
|
||||
}
|
||||
@ -596,6 +605,35 @@ impl<'t> Parser<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_use(&mut self) -> PResult<Use> {
|
||||
self.consume_peeked();
|
||||
Ok(Use { tree: self.parse_use_tree()? })
|
||||
}
|
||||
|
||||
pub fn parse_use_tree(&mut self) -> PResult<UseTree> {
|
||||
const PARSING: Parsing = Parsing::UseTree;
|
||||
// glob import
|
||||
if self.match_op(Punct::Star, PARSING).is_ok() {
|
||||
return Ok(UseTree::Glob);
|
||||
}
|
||||
let path = self.path()?;
|
||||
Ok(match self.peek_kind(PARSING) {
|
||||
Ok(TokenKind::As) => {
|
||||
self.consume_peeked();
|
||||
UseTree::Alias(path, self.identifier()?)
|
||||
}
|
||||
Ok(TokenKind::Punct(Punct::LCurly)) => UseTree::Tree(
|
||||
path,
|
||||
delim(
|
||||
sep(Self::parse_use_tree, Punct::Comma, CURLIES.1, PARSING),
|
||||
CURLIES,
|
||||
PARSING,
|
||||
)(self)?,
|
||||
),
|
||||
_ => UseTree::Path(path),
|
||||
})
|
||||
}
|
||||
|
||||
/// [Visibility] = `pub`?
|
||||
pub fn visibility(&mut self) -> Visibility {
|
||||
match self.match_type(TokenKind::Pub, Parsing::Visibility) {
|
||||
|
@ -207,6 +207,7 @@ pub mod yamlify {
|
||||
ItemKind::Struct(f) => y.yaml(f),
|
||||
ItemKind::Enum(f) => y.yaml(f),
|
||||
ItemKind::Impl(f) => y.yaml(f),
|
||||
ItemKind::Use(f) => y.yaml(f),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -322,6 +323,22 @@ pub mod yamlify {
|
||||
};
|
||||
}
|
||||
}
|
||||
impl Yamlify for Use {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { tree } = self;
|
||||
y.key("Use").yaml(tree);
|
||||
}
|
||||
}
|
||||
impl Yamlify for UseTree {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
match self {
|
||||
UseTree::Tree(path, tree) => y.pair("path", path).pair("tree", tree),
|
||||
UseTree::Alias(path, name) => y.pair("path", path).pair("name", name),
|
||||
UseTree::Path(path) => y.pair("path", path),
|
||||
UseTree::Glob => y.value("Glob"),
|
||||
};
|
||||
}
|
||||
}
|
||||
impl Yamlify for Block {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { stmts } = self;
|
||||
|
@ -13,6 +13,7 @@ pub enum TokenKind {
|
||||
/// A non-keyword identifier
|
||||
Identifier,
|
||||
// A keyword
|
||||
As,
|
||||
Break,
|
||||
Cl,
|
||||
Const,
|
||||
@ -38,6 +39,7 @@ pub enum TokenKind {
|
||||
Super,
|
||||
True,
|
||||
Type,
|
||||
Use,
|
||||
While,
|
||||
/// Delimiter or punctuation
|
||||
Punct(Punct),
|
||||
@ -110,6 +112,7 @@ impl Display for TokenKind {
|
||||
TokenKind::Literal => "literal".fmt(f),
|
||||
TokenKind::Identifier => "identifier".fmt(f),
|
||||
|
||||
TokenKind::As => "as".fmt(f),
|
||||
TokenKind::Break => "break".fmt(f),
|
||||
TokenKind::Cl => "cl".fmt(f),
|
||||
TokenKind::Const => "const".fmt(f),
|
||||
@ -135,6 +138,7 @@ impl Display for TokenKind {
|
||||
TokenKind::Super => "super".fmt(f),
|
||||
TokenKind::True => "true".fmt(f),
|
||||
TokenKind::Type => "type".fmt(f),
|
||||
TokenKind::Use => "use".fmt(f),
|
||||
TokenKind::While => "while".fmt(f),
|
||||
|
||||
TokenKind::Punct(op) => op.fmt(f),
|
||||
@ -147,6 +151,7 @@ impl FromStr for TokenKind {
|
||||
/// Parses a string s to return a Keyword
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"as" => Self::As,
|
||||
"break" => Self::Break,
|
||||
"cl" => Self::Cl,
|
||||
"const" => Self::Const,
|
||||
@ -172,6 +177,7 @@ impl FromStr for TokenKind {
|
||||
"super" => Self::Super,
|
||||
"true" => Self::True,
|
||||
"type" => Self::Type,
|
||||
"use" => Self::Use,
|
||||
"while" => Self::While,
|
||||
_ => Err(())?,
|
||||
})
|
||||
|
@ -697,6 +697,7 @@ pub mod name_collector {
|
||||
ItemKind::Const(i) => i.collect(c, parent),
|
||||
ItemKind::Static(i) => i.collect(c, parent),
|
||||
ItemKind::Function(i) => i.collect(c, parent),
|
||||
ItemKind::Use(i) => i.collect(c, parent),
|
||||
}?;
|
||||
c[id].set_meta(meta).set_vis(*vis).set_source(self);
|
||||
|
||||
@ -808,6 +809,17 @@ pub mod name_collector {
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for Use {
|
||||
fn collect(&'a self, _c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
let Self { tree } = self;
|
||||
todo!("Use {tree} in {parent}")
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for UseTree {
|
||||
fn collect(&'a self, _c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
todo!("Use {self} in {parent}")
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for Block {
|
||||
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
self.stmts.as_slice().collect(c, parent)
|
||||
@ -917,6 +929,7 @@ pub mod type_resolver {
|
||||
ItemKind::Static(_) => "static",
|
||||
ItemKind::Function(_) => "fn",
|
||||
ItemKind::Impl(_) => "impl",
|
||||
ItemKind::Use(_) => "use",
|
||||
};
|
||||
eprintln!(
|
||||
"Resolver: \x1b[32mEvaluating\x1b[0m \"\x1b[36m{kind} {}\x1b[0m\" (`{id:?}`)",
|
||||
@ -984,6 +997,7 @@ pub mod type_resolver {
|
||||
ItemKind::Const(i) => i.resolve_type(prj, id),
|
||||
ItemKind::Static(i) => i.resolve_type(prj, id),
|
||||
ItemKind::Function(i) => i.resolve_type(prj, id),
|
||||
ItemKind::Use(i) => i.resolve_type(prj, id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1014,6 +1028,14 @@ pub mod type_resolver {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeResolvable<'a> for Use {
|
||||
type Out = DefKind<'a>;
|
||||
|
||||
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
|
||||
todo!("Resolve types for {self} with ID {id} in {prj:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeResolvable<'a> for Alias {
|
||||
type Out = DefKind<'a>;
|
||||
|
||||
|
@ -15,7 +15,7 @@ Meta = Identifier ('=' Literal | '(' (Literal ',')* Literal? ')')? ;
|
||||
Item = Attrs Visibility ItemKind ;
|
||||
ItemKind = Const | Static | Module
|
||||
| Function | Struct | Enum
|
||||
| Alias | Impl ;
|
||||
| Alias | Impl | Use ;
|
||||
|
||||
|
||||
(* item *)
|
||||
@ -45,6 +45,10 @@ Alias = "type" Identifier ('=' Ty)? ';' ;
|
||||
Impl = "impl" Path '{' Item* '}' ;
|
||||
(* TODO: Impl Trait for Target*)
|
||||
|
||||
Use = "use" UseTree ;
|
||||
UseTree = Path '{' (UseTree ',')* UseTree? '}'
|
||||
| Path "as" Identifier
|
||||
| Path | '*' ;
|
||||
|
||||
(* type *)
|
||||
Ty = Never | Empty | Path | TyTuple | TyRef | TyFn ;
|
||||
@ -57,7 +61,8 @@ TyFn = "fn" TyTuple ('->' Ty)? ;
|
||||
|
||||
|
||||
(* path *)
|
||||
Path = '::'? PathPart ('::' PathPart)* ;
|
||||
Path = PathPart ('::' PathPart)*
|
||||
| '::' (PathPart ('::' PathPart)*)? ;
|
||||
PathPart = "super" | "self" | Identifier ;
|
||||
Identifier = IDENTIFIER ;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user