ast: Break compatibility

- Turned `static` and `const` into scoped modifiers (like `pub`
- Added anonymous record patterns
This commit is contained in:
2025-12-20 05:22:14 -05:00
parent 64ce18d576
commit f551e3aef3
8 changed files with 43 additions and 36 deletions

View File

@@ -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!(" ");

View File

@@ -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 { <Let(Match, ..)>,* }
Match, // match Expr { <Bind(Match, ..)>,* }
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<A: Annotation = Span>(
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<Pat>),
NamedRecord(Path, Box<Pat>),
/// Matches a Tuple Struct Expression `Ident ( Pat )`
NamedTuple(Path, Box<Pat>),
/// 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,

View File

@@ -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<A: Annotation> Display for Bind<A> {
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 => "[;]",

View File

@@ -149,8 +149,8 @@ impl<A: Annotation> Foldable<A> 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)?)

View File

@@ -174,8 +174,8 @@ impl<A: Annotation> Match<A> 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<A: Annotation> Match<A> 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),
}

View File

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

View File

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

View File

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