diff --git a/src/ast.rs b/src/ast.rs index 1602c87..6756c1e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -32,6 +32,10 @@ pub enum Pat { MetId(String), /// Matches anything, and binds it to a name Name(String), + /// Matches a Struct Expression `Ident { Pat }` + Struct(String, Box), + /// Matches a Tuple Struct Expression `Ident ( Pat )` + TupStruct(String, Box), /// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`). Rest(Option>), /// Matches a literal value by equality comparison @@ -119,6 +123,11 @@ pub struct MakeArm(pub String, pub Option, A> #[derive(Clone, Debug, PartialEq, Eq)] pub struct Mod(pub Ty, pub Anno, A>); +/// A record-type definition +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Struct(pub Pat); + + /// Expressions: The beating heart of Dough #[derive(Clone, Debug, PartialEq, Eq)] pub enum Expr { @@ -132,6 +141,8 @@ pub enum Expr { Let(Box>), /// `const Pat (= Expr)?` (Basically let rec) Const(Box>), + /// struct Pat + Struct(Box), /// `| Pat | Expr` | `|| Expr` | `fn Ident? (Pat,*) Expr` Fn(Box>), /// Expr { (Ident (: Expr)?),* } @@ -298,6 +309,13 @@ impl Display for Mod { } } +impl Display for Struct { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self(pat) = self; + write!(f, "struct {pat}") + } +} + impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -306,6 +324,7 @@ impl Display for Expr { Self::Lit(literal) => literal.fmt(f), Self::Let(v) => v.fmt(f), Self::Const(v) => v.fmt(f), + Self::Struct(v) => v.fmt(f), Self::Make(v) => v.fmt(f), Self::Match(v) => v.fmt(f), Self::Mod(v) => v.fmt(f), @@ -419,6 +438,8 @@ impl Display for Pat { Self::Lit(literal) => literal.fmt(f), Self::MetId(name) => write!(f, "`{name}"), Self::Name(name) => name.fmt(f), + Self::Struct(name, bind) => write!(f, "{name} {{{bind}}}"), + Self::TupStruct(name, bind) => write!(f, "{name} {bind}"), Self::Rest(Some(rest)) => write!(f, "..{rest}"), Self::Rest(None) => write!(f, ".."), Self::Tuple(pats) => f.delimit("(", ")").list(pats, ", "), diff --git a/src/ast/macro_matcher.rs b/src/ast/macro_matcher.rs index 49ad50e..93cbd7e 100644 --- a/src/ast/macro_matcher.rs +++ b/src/ast/macro_matcher.rs @@ -159,6 +159,18 @@ impl Match for Mod { } } +impl Match for Struct { + fn recurse(sub: &mut Subst, pat: &Self, expr: &Self) -> bool { + let (Self(pat_pat), Self(expr_pat)) = (pat, expr); + Match::recurse(sub, pat_pat, expr_pat) + } + + fn apply(&mut self, sub: &Subst) { + let Self(pat) = self; + pat.apply(sub); + } +} + impl Match for Expr { fn recurse(sub: &mut Subst, pat: &Self, expr: &Self) -> bool { match (pat, expr) { @@ -172,6 +184,8 @@ impl Match for Expr { (Expr::Let(..), _) => false, (Expr::Const(pat), Expr::Const(expr)) => Match::recurse(sub, pat, expr), (Expr::Const(..), _) => false, + (Expr::Struct(pat), Expr::Struct(expr)) => Match::recurse(sub, pat, expr), + (Expr::Struct(_), _) => false, (Expr::Make(pat), Expr::Make(expr)) => Match::recurse(sub, pat, expr), (Expr::Make(..), _) => false, (Expr::Match(pat), Expr::Match(expr)) => Match::recurse(sub, pat, expr), @@ -197,6 +211,7 @@ impl Match for Expr { Expr::Id(_) | Expr::Lit(_) => {} Expr::Let(expr) => expr.apply(sub), Expr::Const(expr) => expr.apply(sub), + Expr::Struct(expr) => expr.apply(sub), Expr::Make(expr) => expr.apply(sub), Expr::Match(expr) => expr.apply(sub), Expr::Mod(expr) => expr.apply(sub), @@ -242,6 +257,10 @@ impl Match for Pat { (Pat::Ignore, _) => false, (Pat::Name(pat), Pat::Name(expr)) => pat == expr, (Pat::Name(_), _) => false, + (Pat::Struct(_, pat), Pat::Struct(_, expr)) => Match::recurse(sub, pat, expr), + (Pat::Struct(..), _) => false, + (Pat::TupStruct(_, pat), Pat::TupStruct(_, expr)) => Match::recurse(sub, pat, expr), + (Pat::TupStruct(..), _) => false, (Pat::Rest(pat), Pat::Rest(expr)) => Match::recurse(sub, pat, expr), (Pat::Rest(_), _) => false, (Pat::Lit(pat), Pat::Lit(expr)) => pat == expr, @@ -265,6 +284,8 @@ impl Match for Pat { *self = expr.clone() } } + Pat::Struct(_, expr) => expr.apply(sub), + Pat::TupStruct(_, expr) => expr.apply(sub), Pat::Rest(pat) => pat.apply(sub), Pat::Tuple(pats) => pats.apply(sub), Pat::Slice(pats) => pats.apply(sub), diff --git a/src/lexer.rs b/src/lexer.rs index 26ec567..0b30e10 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -235,6 +235,7 @@ impl<'t> Lexer<'t> { "mod" => TKind::Module, "pub" => TKind::Public, "return" => TKind::Return, + "struct" => TKind::Struct, "then" => TKind::Do, "true" => TKind::True, "while" => TKind::While, diff --git a/src/main.rs b/src/main.rs index cc80380..597b353 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,7 +100,7 @@ fn exprs() -> Result<(), Box> { println!("\x1b[31m{e}\x1b[0m"); break Ok(Response::Deny); } - Ok(v) => println!("{v}\n{v:#?}"), + Ok(v) => println!("{v}\n{v:?}"), } } })?; diff --git a/src/parser.rs b/src/parser.rs index 3d00bbd..7b9ec92 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -248,7 +248,19 @@ impl<'t> Parse<'t> for Pat { } TKind::Identifier => match tok.lexeme.as_str() { "_" => p.consume().then(Pat::Ignore), - _ => Pat::Name(p.take_lexeme().expect("should have Token")), + _ => { + let name = p.take_lexeme().expect("should have Token"); + match p.peek().map(|t| t.kind)? { + TKind::LParen => Pat::TupStruct(name, p.parse(PPrec::Tuple)?), + TKind::LCurly => Pat::Struct( + name, + p.consume() + .opt(PPrec::Tuple, TKind::RCurly)? + .unwrap_or_else(|| Box::new(Pat::Tuple(vec![]))), + ), + _ => Pat::Name(name), + } + } }, TKind::Grave => Pat::MetId(p.consume().next()?.lexeme), TKind::DotDot => Pat::Rest(match p.consume().peek_if(TKind::Identifier) { @@ -258,13 +270,13 @@ impl<'t> Parse<'t> for Pat { TKind::LParen => { Pat::Tuple( p.consume() - .list(vec![], PPrec::Max, TKind::Comma, TKind::RParen)?, + .list(vec![], PPrec::Typed, TKind::Comma, TKind::RParen)?, ) } TKind::LBrack => { Pat::Slice( p.consume() - .list(vec![], PPrec::Max, TKind::Comma, TKind::RBrack)?, + .list(vec![], PPrec::Typed, TKind::Comma, TKind::RBrack)?, ) } _ => Err(ParseError::NotPattern(tok.kind, tok.span))?, @@ -415,6 +427,7 @@ pub enum Ps { Lit, // Literal Let, // let Pat = Expr Const, // const Pat = Expr + Struct, // struct { Pat } | struct ( Pat ) Fn, // fn ( Pat,* ) Expr Lambda0, // || Expr Lambda, // | Pat,* | Expr @@ -444,6 +457,7 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { TKind::Module => (Ps::Mod, Prec::Body), TKind::Let => (Ps::Let, Prec::Tuple), TKind::Const => (Ps::Const, Prec::Body), + TKind::Struct => (Ps::Struct, Prec::Body), TKind::Loop => (Ps::Op(Op::Loop), Prec::Body), TKind::If => (Ps::Op(Op::If), Prec::Body), TKind::While => (Ps::Op(Op::While), Prec::Body), @@ -521,23 +535,36 @@ impl<'t> Parse<'t> for Const { } } +impl<'t> Parse<'t> for Struct { + type Prec = (); + + fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult { + let value = p.consume().parse(PPrec::Tuple)?; + Ok(Self(value)) + } +} + impl<'t> Parse<'t> for Fn { type Prec = (); fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult { - Ok(Self( - p.consume() - .next_if(TKind::Identifier) - .map(|id| id.lexeme) - .ok(), - Pat::Tuple(p.consume_if(TKind::LParen)?.list( - vec![], - PPrec::Tuple, - TKind::Comma, - TKind::RParen, - )?), - p.parse(Prec::Body.next())?, - )) + match p.consume().next_if(TKind::Identifier) { + Ok(Token { lexeme, .. }) => Ok(Self( + Some(lexeme), + p.parse(PPrec::Typed)?, + p.parse(Prec::Body.next())?, + )), + _ => Ok(Self( + None, + Pat::Tuple(p.consume_if(TKind::LParen)?.list( + vec![], + PPrec::Tuple, + TKind::Comma, + TKind::RParen, + )?), + p.parse(Prec::Body.next())?, + )), + } } } @@ -617,6 +644,7 @@ impl<'t> Parse<'t> for Expr { Ps::Lit => Expr::Lit(p.parse(())?), Ps::Let => Expr::Let(p.parse(())?), Ps::Const => Expr::Const(p.parse(())?), + Ps::Struct => Expr::Struct(p.parse(())?), Ps::Match => Expr::Match(p.parse(())?), Ps::Mod => Expr::Mod(p.parse(())?), Ps::Op(Op::Block) => Expr::Op( diff --git a/src/token.rs b/src/token.rs index 1ca67f2..93e444c 100644 --- a/src/token.rs +++ b/src/token.rs @@ -27,6 +27,7 @@ pub enum TKind { Module, Public, Return, + Struct, True, While,