diff --git a/src/ast.rs b/src/ast.rs index 695a05e..030e874 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -45,6 +45,19 @@ pub enum Literal { Str(String), } +/// A compound import declaration +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Use { + /// "*" + Glob, + /// Identifier + Name(String), + /// Identifier :: Use + Path(String, Box), + /// { Use, * } + Tree(Vec), +} + /// Binding patterns for each kind of matchable value. /// /// This covers both patterns in Match expressions, and type annotations. @@ -71,6 +84,8 @@ pub enum Pat { /// Operators on lists of patterns #[derive(Clone, Debug, PartialEq, Eq)] pub enum PatOp { + /// Changes the binding mode to "mutable" + Mut, /// Matches the dereference of a pointer (`&pat`) Ref, /// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`) @@ -83,6 +98,8 @@ pub enum PatOp { Tuple, /// Matches the elements of a slice or array Slice, + /// Matches a constant-size slice with repeating elements + Arrep, /// Matches a type annotation or struct member Typed, /// Matches a function signature @@ -96,6 +113,7 @@ pub enum PatOp { /// let Pat (= Expr (else Expr)?)? /// const Pat (= Expr (else Expr)?)? /// static Pat (= Expr (else Expr)?)? +/// type Pat (= Expr)? /// fn Pat Expr /// mod Pat Expr /// impl Pat Expr @@ -112,6 +130,8 @@ pub enum BindKind { Const, /// A `static Pat = Expr` binding Static, + /// A type-alias binding + Type, /// A `fn Pat Expr` binding Fn, /// A `mod Pat Expr` binding @@ -143,7 +163,6 @@ pub struct Typedef(pub TypedefKind, pub Pat); /// The type of type being defined #[derive(Clone, Debug, PartialEq, Eq)] pub enum TypedefKind { - Alias, Struct, Enum, } @@ -157,6 +176,8 @@ pub enum Expr { MetId(String), /// A literal bool, string, char, or int Lit(Literal), + /// use Use + Use(Use), /// `(let | const | static) Pat::NoTopAlt (= expr (else expr)?)?` | /// `(fn | mod | impl) Pat::Fn Expr` Bind(Box>), @@ -310,6 +331,23 @@ impl Display for Anno { } } +impl Display for Use { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Glob => "*".fmt(f), + Self::Name(name) => name.fmt(f), + Self::Path(segment, rest) => write!(f, "{segment}::{rest}"), + Self::Tree(items) => match items.len() { + 0 => "{}".fmt(f), + 1..=3 => f.delimit("{ ", " }").list(items, ", "), + _ => f + .delimit_indented("{", "}") + .list_wrap("\n", items, ",\n", ",\n"), + }, + } + } +} + impl Display for Bind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(op, pat, exprs) = self; @@ -335,6 +373,7 @@ impl Display for BindKind { Self::Let => "let ", Self::Const => "const ", Self::Static => "static ", + Self::Type => "type ", Self::Fn => "fn ", Self::Mod => "mod ", Self::Impl => "impl ", @@ -354,7 +393,6 @@ impl Display for Typedef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(kind, pat) = self; f.write_str(match kind { - TypedefKind::Alias => "type ", TypedefKind::Struct => "struct ", TypedefKind::Enum => "enum ", })?; @@ -376,6 +414,7 @@ impl Display for Expr { Self::Id(id) => id.fmt(f), Self::MetId(id) => write!(f, "`{id}"), Self::Lit(literal) => literal.fmt(f), + Self::Use(v) => write!(f, "use {v}"), Self::Bind(v) => v.fmt(f), Self::Struct(v) => v.fmt(f), Self::Make(v) => v.fmt(f), @@ -515,6 +554,7 @@ impl Display for Pat { Self::NamedTuple(name, bind) => write!(f, "{name} {bind}"), Self::Op(PatOp::Tuple, pats) => f.delimit("(", ")").list(pats, ", "), Self::Op(PatOp::Slice, pats) => f.delimit("[", "]").list(pats, ", "), + Self::Op(op @ PatOp::Arrep, pats) => f.delimit("[", "]").list(pats, op), Self::Op(op @ (PatOp::Typed | PatOp::Fn), pats) => f.list(pats, op), Self::Op(op @ PatOp::Alt, pats) => f.delimit("<", ">").list(pats, op), Self::Op(op, pats) => match pats.as_slice() { @@ -529,12 +569,14 @@ impl Display for Pat { impl Display for PatOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { + Self::Mut => "mut ", Self::Ref => "&", Self::Rest => "..", Self::RangeEx => "..", Self::RangeIn => "..=", Self::Tuple => ", ", Self::Slice => ", ", + Self::Arrep => "; ", Self::Typed => ": ", Self::Fn => " -> ", Self::Alt => " | ", diff --git a/src/ast/macro_matcher.rs b/src/ast/macro_matcher.rs index 453f3a5..ae94b22 100644 --- a/src/ast/macro_matcher.rs +++ b/src/ast/macro_matcher.rs @@ -127,6 +127,8 @@ impl Match for Expr { (Expr::Id(_), _) => false, (Expr::Lit(pat), Expr::Lit(expr)) => pat == expr, (Expr::Lit(_), _) => false, + (Expr::Use(_), Expr::Use(_)) => true, + (Expr::Use(_), _) => false, (Expr::Bind(pat), Expr::Bind(expr)) => Match::recurse(sub, pat, expr), (Expr::Bind(..), _) => false, (Expr::Struct(pat), Expr::Struct(expr)) => Match::recurse(sub, pat, expr), @@ -147,7 +149,7 @@ impl Match for Expr { *self = expr.clone() } } - Expr::Id(_) | Expr::Lit(_) => {} + Expr::Id(_) | Expr::Lit(_) | Expr::Use(_) => {} Expr::Bind(expr) => expr.apply(sub), Expr::Struct(expr) => expr.apply(sub), Expr::Make(expr) => expr.apply(sub), diff --git a/src/lexer.rs b/src/lexer.rs index 2222ac8..e7f3e23 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -307,13 +307,16 @@ impl<'t> Lexer<'t> { "macro" => TKind::Macro, "match" => TKind::Match, "mod" => TKind::Module, + "mut" => TKind::Mut, "or" => TKind::Or, "pub" => TKind::Public, "return" => TKind::Return, - "static" => TKind::Const, // TODO: Static + "static" => TKind::Static, "struct" => TKind::Struct, "then" => TKind::Do, "true" => TKind::True, + "type" => TKind::Type, + "use" => TKind::Use, "while" => TKind::While, _ => token.kind, }, diff --git a/src/parser.rs b/src/parser.rs index 936b8d8..0a04a47 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -16,6 +16,7 @@ pub enum ParseError { FromLexer(LexError), Expected(TKind, TKind, Span), NotLiteral(TKind, Span), + NotUse(TKind, Span), NotPattern(TKind, Span), NotType(TKind, Span), NotPrefix(TKind, Span), @@ -34,6 +35,7 @@ impl Display for ParseError { Self::FromLexer(e) => e.fmt(f), Self::Expected(e, tk, loc) => write!(f, "{loc}: Expected {e:?}, got {tk:?}."), Self::NotLiteral(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a literal."), + Self::NotUse(tk, loc) => write!(f, "{loc}: {tk:?} is no use!"), Self::NotPattern(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a pattern."), Self::NotType(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a type."), Self::NotPrefix(tk, loc) => write!(f, "{loc}: {tk:?} is not a prefix operator."), @@ -283,6 +285,26 @@ impl<'t> Parse<'t> for Literal { } } +impl<'t> Parse<'t> for Use { + type Prec = (); + + fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult { + let tok = p.next()?; + Ok(match tok.kind { + TKind::Star => p.then(Use::Glob), + TKind::Identifier => { + let name = tok.lexeme.string().expect("should have String"); + match p.next_if(TKind::ColonColon).allow_eof()? { + Some(Ok(_)) => Use::Path(name, p.parse(())?), + _ => Use::Name(name), + } + } + TKind::LCurly => Use::Tree(p.list(vec![], (), TKind::Comma, TKind::RCurly)?), + _ => Err(ParseError::NotUse(tok.kind, tok.span))?, + }) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum PPrec { Min, @@ -338,6 +360,7 @@ impl<'t> Parse<'t> for Pat { } TKind::Bar => p.consume().parse(level)?, TKind::Amp => Pat::Op(PatOp::Ref, vec![p.consume().parse(PPrec::Max)?]), + TKind::Mut => Pat::Op(PatOp::Mut, vec![p.consume().parse(level)?]), TKind::AmpAmp => Pat::Op( PatOp::Ref, vec![Pat::Op(PatOp::Ref, vec![p.consume().parse(PPrec::Max)?])], @@ -349,10 +372,10 @@ impl<'t> Parse<'t> for Pat { // TODO: make these postfix. match p.peek().map(|t| t.kind) { Ok(TKind::LParen) => Pat::NamedTuple(path, p.parse(PPrec::Typed)?), - Ok(TKind::LCurly) if level <= PPrec::Tuple => Pat::NamedStruct( + Ok(TKind::LCurly) if level <= PPrec::Tuple.next() => Pat::NamedStruct( path, p.consume() - .opt(PPrec::Alt, TKind::RCurly)? + .opt(PPrec::Tuple, TKind::RCurly)? .unwrap_or_else(|| Box::new(Pat::Op(PatOp::Tuple, vec![]))), ), Ok(_) | Err(ParseError::EOF(_)) => match path.parts.len() { @@ -390,11 +413,7 @@ impl<'t> Parse<'t> for Pat { p.consume() .list(vec![], PPrec::Typed, TKind::Comma, TKind::RParen)?, ), - TKind::LBrack => Pat::Op( - PatOp::Slice, - p.consume() - .list(vec![], PPrec::Typed, TKind::Comma, TKind::RBrack)?, - ), + TKind::LBrack => parse_array_pat(p)?, _ => Err(ParseError::NotPattern(tok.kind, tok.span))?, }; @@ -426,59 +445,22 @@ impl<'t> Parse<'t> for Pat { } } -// impl<'t> Parse<'t> for Ty { -// type Prec = (); +fn parse_array_pat<'t>(p: &mut Parser<'t>) -> PResult { + if p.consume().peek()?.kind == TKind::RBrack { + p.consume(); + return Ok(Pat::Op(PatOp::Slice, vec![])); + } -// fn parse(p: &mut Parser<'t>, level: Self::Prec) -> PResult -// where Self: Sized { -// let &Token { kind, span, .. } = p.peek()?; + let item = p.parse(PPrec::Tuple)?; + let repeat = p.opt_if(PPrec::Tuple, TKind::Semi)?; + p.expect(TKind::RBrack)?; -// // TODO: this is a kinda jank way of error reporting -// let head = match kind { -// TKind::Identifier => match p.peek()?.lexeme.str() { -// Some("_") => p.consume().then(Ty::Infer), -// _ => Ty::Named(p.parse(())?), -// }, -// TKind::Amp => Ty::Ref(p.consume().parse(())?), -// TKind::AmpAmp => Ty::Ref(Box::new(Ty::Ref(p.consume().parse(())?))), -// TKind::LBrack => { -// let ty = p.consume().parse(level)?; -// match p.next()? { -// Token { kind: TKind::Semi, .. } => { -// let ty = Ty::Array(ty, p.parse(Prec::Binary.next())?); -// p.expect(TKind::RBrack)?; -// ty -// } -// Token { kind: TKind::RBrack, .. } => Ty::Slice(ty), -// tok => Err(ParseError::NotType(tok.kind, tok.span))?, -// } -// } -// TKind::Fn => { -// p.consume(); -// match p.parse(())? { -// Ty::Fn(args) => Ty::Fn(args), -// other @ Ty::Tuple(_) => Ty::Fn(vec![other, Ty::Tuple(vec![])]), -// other => Ty::Fn(vec![other, Ty::Tuple(vec![])]), -// } -// } -// TKind::LParen => { -// Ty::Tuple(p.consume().list(vec![], (), TKind::Comma, TKind::RParen)?) -// } -// _ => Err(ParseError::NotType(kind, span))?, -// }; - -// Ok(match p.next_if(TKind::Arrow).allow_eof()? { -// Some(Ok(_)) => Ty::Fn(vec![ -// match head { -// args @ Ty::Tuple(_) => args, -// arg => Ty::Tuple(vec![arg]), -// }, -// p.parse(())?, -// ]), -// _ => head, -// }) -// } -// } + Ok(match (repeat, item) { + (Some(repeat), item) => Pat::Op(PatOp::Arrep, vec![item, repeat]), + (None, Pat::Op(PatOp::Tuple, items)) => Pat::Op(PatOp::Slice, items), + (None, item) => Pat::Op(PatOp::Slice, vec![item]), + }) +} /// Organizes the precedence hierarchy for syntactic elements #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -549,9 +531,11 @@ pub enum Ps { Id, // Identifier Mid, // MetaIdentifier Lit, // Literal + Use, // use Use Let, // let Pat = Expr (else Expr)? Const, // const Pat = Expr Static, // static Pat = Expr + Type, // type Pat = Expr Typedef, // struct { Pat } | struct ( Pat ) For, // for Pat in Expr Expr else Expr Fn, // fn ( Pat,* ) Expr @@ -559,7 +543,8 @@ pub enum Ps { Lambda, // | Pat,* | Expr DoubleRef, // && Expr Make, // Expr{ Expr,* } - Mod, // mod Ty Expr + Mod, // mod Pat Expr + Impl, // impl Pat Expr ImplicitDo, // An implicit semicolon ExplicitDo, // An explicit leading semicolon End, // Produces an empty value. @@ -576,6 +561,7 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => { (Ps::Lit, Prec::Max) } + TKind::Use => (Ps::Use, Prec::Max), TKind::Public => (Ps::Op(Op::Pub), Prec::Body), TKind::For => (Ps::For, Prec::Body), @@ -583,8 +569,11 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { TKind::Match => (Ps::Op(Op::Match), Prec::Body), TKind::Macro => (Ps::Op(Op::Macro), Prec::Assign), TKind::Module => (Ps::Mod, Prec::Body), + TKind::Impl => (Ps::Impl, Prec::Body), TKind::Let => (Ps::Let, Prec::Tuple), TKind::Const => (Ps::Const, Prec::Body), + TKind::Static => (Ps::Static, Prec::Body), + TKind::Type => (Ps::Type, Prec::Body), TKind::Struct | TKind::Enum => (Ps::Typedef, Prec::Body), TKind::Loop => (Ps::Op(Op::Loop), Prec::Body), TKind::If => (Ps::Op(Op::If), Prec::Body), @@ -693,7 +682,7 @@ impl<'t> Parse<'t> for Bind { vec![p.expect(TKind::FatArrow)?.parse(Prec::Body.next())?], )) } - BindKind::Mod => Ok(Self( + BindKind::Mod | BindKind::Impl => Ok(Self( level, p.consume().parse(PPrec::Max)?, vec![p.parse(Prec::Body.next())?], @@ -853,11 +842,14 @@ impl<'t> Parse<'t> for Expr { Ps::Id => Expr::Id(p.parse(())?), Ps::Mid => Expr::MetId(p.consume().next()?.lexeme.to_string()), Ps::Lit => Expr::Lit(p.parse(())?), + Ps::Use => Expr::Use(p.consume().parse(())?), Ps::Typedef => Expr::Struct(p.parse(())?), Ps::Let => Expr::Bind(p.parse(BindKind::Let)?), Ps::Const => Expr::Bind(p.parse(BindKind::Const)?), Ps::Static => Expr::Bind(p.parse(BindKind::Static)?), + Ps::Type => Expr::Bind(p.parse(BindKind::Type)?), Ps::Mod => Expr::Bind(p.parse(BindKind::Mod)?), + Ps::Impl => Expr::Bind(p.parse(BindKind::Impl)?), Ps::Fn => Expr::Bind(p.parse(BindKind::Fn)?), Ps::Lambda | Ps::Lambda0 => { p.consume(); diff --git a/src/token.rs b/src/token.rs index 34e495a..03e38c6 100644 --- a/src/token.rs +++ b/src/token.rs @@ -82,12 +82,15 @@ pub enum TKind { Macro, Match, Module, + Mut, Or, Public, Return, Static, Struct, True, + Type, + Use, While, Identifier, // or Keyword