diff --git a/examples/to-lisp.rs b/examples/to-lisp.rs index 41c4a22..742b16c 100644 --- a/examples/to-lisp.rs +++ b/examples/to-lisp.rs @@ -30,8 +30,6 @@ impl ToLisp { fn bind_op(&self, op: BindOp) -> &'static str { match op { BindOp::Let => "let", - BindOp::Const => "const", - BindOp::Static => "static", BindOp::Type => "type", BindOp::Fn => "fn", BindOp::Mod => "mod", @@ -51,6 +49,7 @@ impl ToLisp { PatOp::Rest => "rest", PatOp::RangeEx => "range-ex", PatOp::RangeIn => "range-in", + PatOp::Record => "record", PatOp::Tuple => "tuple", PatOp::Slice => "slice", PatOp::ArRep => "ar-rep", @@ -75,6 +74,8 @@ impl ToLisp { Op::Call => "call", Op::Pub => "pub", Op::Loop => "loop", + Op::Const => "const", + Op::Static => "static", Op::Match => "match", Op::If => "if", Op::While => "while", @@ -201,7 +202,7 @@ impl<'a> Visit<'a> for ToLisp { Pat::MetId(id) => print!("(replace {id})"), Pat::Name(name) => print!("{name}"), Pat::Path(path) => path.visit_in(self)?, - Pat::NamedStruct(path, pat) => { + Pat::NamedRecord(path, pat) => { print!("(struct-pat "); path.visit_in(self)?; print!(" "); diff --git a/src/ast.rs b/src/ast.rs index 484725a..074afbb 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -81,8 +81,10 @@ pub enum Op { Call, // Expr ( Expr,* ) Pub, // pub Expr + Const, // const Expr + Static, // static Expr Loop, // loop Expr - Match, // match Expr { ,* } + Match, // match Expr { ,* } If, // if Expr Expr (else Expr)? While, // while Expr Expr (else Expr)? Break, // break Expr @@ -176,8 +178,6 @@ pub enum Use { /// A pattern binding /// ```ignore /// let Pat (= Expr (else Expr)?)? -/// const Pat (= Expr (else Expr)?)? -/// static Pat (= Expr (else Expr)?)? /// type Pat (= Expr)? /// struct Pat /// enum Pat @@ -199,10 +199,6 @@ pub struct Bind( pub enum BindOp { /// A `let Pat (= Expr (else Expr)?)?` binding Let, - /// A `const Pat = Expr` binding - Const, - /// A `static Pat = Expr` binding - Static, /// A type-alias binding Type, /// A `fn Pat Expr` binding @@ -251,7 +247,7 @@ pub enum Pat { /// Matches against a named const value Path(Path), /// Matches a Struct Expression `Ident { Pat }` - NamedStruct(Path, Box), + NamedRecord(Path, Box), /// Matches a Tuple Struct Expression `Ident ( Pat )` NamedTuple(Path, Box), /// Matches a literal value by equality comparison @@ -277,15 +273,17 @@ pub enum PatOp { RangeEx, /// Matches an inclusive bounded range (`0..=100`) RangeIn, - /// Matches the elements of a tuple + /// Matches the elements of a record or struct { a, b, c } + Record, + /// Matches the elements of a tuple ( a, b, c ) Tuple, - /// Matches the elements of a slice or array + /// Matches the elements of a slice or array [ a, b, c ] Slice, /// Matches a constant-size slice with repeating elements ArRep, /// Matches a type annotation or struct member Typed, - /// Matches a function signature + /// Changes the binding mode to "function-body" Fn, /// Matches one of a list of alternatives Alt, diff --git a/src/ast/display.rs b/src/ast/display.rs index f87c868..3cd3364 100644 --- a/src/ast/display.rs +++ b/src/ast/display.rs @@ -81,6 +81,8 @@ impl Display for Op { Op::Index => "", Op::Call => "", Op::Pub => "pub ", + Op::Const => "const ", + Op::Static => "static ", Op::Loop => "loop ", Op::Match => "match ", Op::If => "if ", @@ -182,7 +184,7 @@ impl Display for Bind { f.delimit(fmt!("{pat} "), "").list(exprs, ",!? ") } BindOp::Struct | BindOp::Enum => match pat { - Pat::NamedStruct(name, bind) => match bind.as_ref() { + Pat::NamedRecord(name, bind) => match bind.as_ref() { Pat::Op(PatOp::Tuple, parts) => f .delimit_indented(fmt!("{name} {{"), "}") .list_wrap("\n", parts, ",\n", ",\n"), @@ -211,8 +213,6 @@ impl Display for BindOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { Self::Let => "let ", - Self::Const => "const ", - Self::Static => "static ", Self::Type => "type ", Self::Struct => "struct ", Self::Enum => "enum ", @@ -250,18 +250,22 @@ impl Display for Pat { Self::MetId(name) => write!(f, "`{name}"), Self::Name(name) => name.fmt(f), Self::Path(path) => path.fmt(f), - Self::NamedStruct(name, bind) => match bind.as_ref() { + Self::NamedRecord(name, bind) => match bind.as_ref() { Pat::Op(PatOp::Tuple, parts) => { f.delimit(fmt!("{name} {{ "), " }").list(parts, ", ") } other => write!(f, "{name} {{ {other} }}"), }, Self::NamedTuple(name, bind) => write!(f, "{name} {bind}"), + Self::Op(PatOp::Record, pats) => f.delimit("{ ", " }").list(pats, ", "), 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 @ (PatOp::Typed | PatOp::Fn), pats) => match pats.as_slice() { + [fun] => write!(f, "fn {fun}"), // TODO: reconsider this + pats => f.list(pats, op), + }, + Self::Op(op @ PatOp::Alt, pats) => f.list(pats, op), Self::Op(op, pats) => match pats.as_slice() { [] => op.fmt(f), [rest] => write!(f, "{op}{rest}"), @@ -281,6 +285,7 @@ impl Display for PatOp { Self::Rest => "..", Self::RangeEx => "..", Self::RangeIn => "..=", + Self::Record => ", ", Self::Tuple => ", ", Self::Slice => ", ", Self::ArRep => "[;]", diff --git a/src/ast/fold.rs b/src/ast/fold.rs index e61e28d..2391823 100644 --- a/src/ast/fold.rs +++ b/src/ast/fold.rs @@ -149,8 +149,8 @@ impl Foldable for Pat { Self::MetId(name) => Self::MetId(name.fold_in(folder)?), Self::Name(name) => Self::Name(name.fold_in(folder)?), Self::Path(path) => Self::Path(path.fold_in(folder)?), - Self::NamedStruct(path, pat) => { - Self::NamedStruct(path.fold_in(folder)?, pat.fold_in(folder)?) + Self::NamedRecord(path, pat) => { + Self::NamedRecord(path.fold_in(folder)?, pat.fold_in(folder)?) } Self::NamedTuple(path, pat) => { Self::NamedTuple(path.fold_in(folder)?, pat.fold_in(folder)?) diff --git a/src/ast/macro_matcher.rs b/src/ast/macro_matcher.rs index 8970942..ef63e4c 100644 --- a/src/ast/macro_matcher.rs +++ b/src/ast/macro_matcher.rs @@ -174,8 +174,8 @@ impl Match for Pat { (Pat::Name(_), _) => false, (Pat::Path(_), Pat::Path(_)) => true, (Pat::Path(_), _) => false, - (Pat::NamedStruct(_, pat), Pat::NamedStruct(_, expr)) => Match::recurse(sub, pat, expr), - (Pat::NamedStruct(..), _) => false, + (Pat::NamedRecord(_, pat), Pat::NamedRecord(_, expr)) => Match::recurse(sub, pat, expr), + (Pat::NamedRecord(..), _) => false, (Pat::NamedTuple(_, pat), Pat::NamedTuple(_, expr)) => Match::recurse(sub, pat, expr), (Pat::NamedTuple(..), _) => false, (Pat::Lit(pat), Pat::Lit(expr)) => pat == expr, @@ -193,7 +193,7 @@ impl Match for Pat { *self = expr.clone(); } } - Pat::NamedStruct(_, expr) => expr.apply(sub), + Pat::NamedRecord(_, expr) => expr.apply(sub), Pat::NamedTuple(_, expr) => expr.apply(sub), Pat::Op(_, pats) => pats.apply(sub), } diff --git a/src/ast/visit.rs b/src/ast/visit.rs index f1d5f75..016df6b 100644 --- a/src/ast/visit.rs +++ b/src/ast/visit.rs @@ -110,7 +110,7 @@ impl<'a> Walk<'a> for Pat { Self::MetId(id) => id.visit_in(v), Self::Name(name) => name.visit_in(v), Self::Path(path) => path.visit_in(v), - Self::NamedStruct(path, pat) | Self::NamedTuple(path, pat) => { + Self::NamedRecord(path, pat) | Self::NamedTuple(path, pat) => { path.visit_in(v)?; pat.visit_in(v) } diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 485ef59..526ffb8 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -101,6 +101,8 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { TKind::Use => (Ps::Use, Prec::Max), TKind::Pub => (Ps::Op(Op::Pub), Prec::Body), + TKind::Const => (Ps::Op(Op::Const), Prec::Body), + TKind::Static => (Ps::Op(Op::Static), Prec::Body), TKind::For => (Ps::For, Prec::Body), TKind::Match => (Ps::Op(Op::Match), Prec::Body), TKind::Macro => (Ps::Op(Op::Macro), Prec::Assign), @@ -109,8 +111,6 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { | TKind::Mod | TKind::Impl | TKind::Let - | TKind::Const - | TKind::Static | TKind::Type | TKind::Struct | TKind::Enum => (Ps::Def, Prec::Body), @@ -440,8 +440,6 @@ fn from_bind(p: &mut Parser<'_>) -> PResult<(BindOp, PPrec, Option, Optio let bk = match p.peek()?.kind { // Token Operator Pat prec Body Token Body prec Else prec TKind::Let => (BindOp::Let, PPrec::Tuple, Some(TKind::Eq), Some(Prec::Tuple), Some(Prec::Body)), - TKind::Const => (BindOp::Const, PPrec::Tuple, Some(TKind::Eq), Some(Prec::Tuple), None), - TKind::Static => (BindOp::Static, PPrec::Tuple, Some(TKind::Eq), Some(Prec::Tuple), None), TKind::Type => (BindOp::Type, PPrec::Tuple, Some(TKind::Eq), Some(Prec::Extend), None), TKind::Struct => (BindOp::Struct, PPrec::Tuple, None, None, None), TKind::Enum => (BindOp::Enum, PPrec::Tuple, None, None, None), diff --git a/src/parser/pat.rs b/src/parser/pat.rs index ea26a3d..137852a 100644 --- a/src/parser/pat.rs +++ b/src/parser/pat.rs @@ -73,12 +73,12 @@ impl<'t> Parse<'t> for Pat { // Prefix let mut head = match tok.kind { - TKind::Fn => return p.consume().parse(Prec::Fn), TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => { Pat::Lit(p.parse(())?) } TKind::Bar => p.consume().parse(level)?, TKind::Bang => p.consume().then(Pat::Never), + TKind::Fn => Pat::Op(PatOp::Fn, vec![p.consume().parse(Prec::Typed)?]), TKind::Amp => Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?]), TKind::Star => Pat::Op(PatOp::Ptr, vec![p.consume().parse(Prec::Max)?]), TKind::Mut => Pat::Op(PatOp::Mut, vec![p.consume().parse(Prec::Max)?]), @@ -94,7 +94,7 @@ impl<'t> Parse<'t> for Pat { // TODO: make these postfix. match p.peek().map(Token::kind) { Ok(TKind::LParen) => Pat::NamedTuple(path, p.parse(Prec::Typed)?), - Ok(TKind::LCurly) if level <= Prec::Tuple.next() => Pat::NamedStruct( + Ok(TKind::LCurly) if level <= Prec::Tuple.next() => Pat::NamedRecord( path, p.consume() .opt(Prec::Tuple, TKind::RCurly)? @@ -130,6 +130,11 @@ impl<'t> Parse<'t> for Pat { _ => vec![], }, ), + TKind::LCurly => Pat::Op( + PatOp::Record, + p.consume() + .list(vec![], Prec::Typed, TKind::Comma, TKind::RCurly)?, + ), TKind::LParen => Pat::Op( PatOp::Tuple, p.consume() @@ -145,9 +150,9 @@ impl<'t> Parse<'t> for Pat { { let kind = tok.kind; head = match op { - PatOp::Typed => Pat::Op(PatOp::Typed, vec![head, p.consume().parse(prec.next())?]), - PatOp::Fn => Pat::Op(PatOp::Fn, vec![head, p.consume().parse(Prec::Fn.next())?]), - op @ (PatOp::RangeEx | PatOp::RangeIn) => Pat::Op( + PatOp::Typed => Pat::Op(op, vec![head, p.consume().parse(prec.next())?]), + PatOp::Fn => Pat::Op(op, vec![head, p.consume().parse(Prec::Fn.next())?]), + PatOp::RangeEx | PatOp::RangeIn => Pat::Op( op, match p.consume().peek().map(Token::kind) { Ok(TKind::Integer | TKind::Character | TKind::Identifier) => { @@ -156,7 +161,7 @@ impl<'t> Parse<'t> for Pat { _ => vec![head], }, ), - op => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?), + _ => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?), } } Ok(head)