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:
John 2024-04-20 14:51:54 -05:00
parent 9dc0cc7841
commit efd442bbfa
11 changed files with 192 additions and 11 deletions

View File

@ -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 {

View File

@ -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,

View File

@ -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 {

View File

@ -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 => {}

View File

@ -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"),
}
}
}

View File

@ -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",

View File

@ -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) {

View File

@ -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;

View File

@ -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(())?,
})

View File

@ -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>;

View File

@ -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 ;