diff --git a/src/ast.rs b/src/ast.rs index 20ea482..103e637 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -71,10 +71,14 @@ pub enum Pat { /// Operators on lists of patterns #[derive(Clone, Debug, PartialEq, Eq)] pub enum PatOp { + /// Matches the dereference of a pointer (`&pat`) + Ref, /// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`) Rest, /// Matches an exclusive bounded range (`0..100`) RangeEx, + /// Matches an inclusive bounded range (`0..=100`) + RangeIn, /// Matches the elements of a tuple Tuple, /// Matches the elements of a slice or array @@ -90,6 +94,8 @@ pub enum Ty { Infer, /// `(Identifier :: )* Identifier` Named(FqPath), + /// `&Ty` + Ref(Box), /// `(..Tys)` Tuple(Vec), /// `[Ty]` @@ -156,9 +162,17 @@ pub struct MakeArm(pub String, pub Option, A> #[derive(Clone, Debug, PartialEq, Eq)] pub struct Mod(pub Ty, pub Anno, A>); +/// The type of type being defined +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TypedefKind { + Alias, + Struct, + Enum, +} + /// A record-type definition #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Struct(pub Pat); +pub struct Typedef(pub TypedefKind, pub Pat); /// Expressions: The beating heart of Dough #[derive(Clone, Debug, PartialEq, Eq)] @@ -173,8 +187,8 @@ pub enum Expr { Let(Box>), /// `const Pat (= Expr)?` (Basically let rec) Const(Box>), - /// struct Pat - Struct(Box), + /// (struct | enum | type) Pat + Struct(Box), /// `| Pat | Expr` | `|| Expr` | `fn Ident? (Pat,*) Expr` Fn(Box>), /// Expr { (Ident (: Expr)?),* } @@ -364,17 +378,22 @@ impl Display for Mod { } } -impl Display for Struct { +impl Display for Typedef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self(pat) = self; + let Self(kind, pat) = self; + let kind = match kind { + TypedefKind::Alias => "type", + TypedefKind::Struct => "struct", + TypedefKind::Enum => "enum", + }; match pat { Pat::Struct(name, bind) => match bind.as_ref() { Pat::Op(PatOp::Tuple, parts) => f - .delimit_indented(fmt!("struct {name} {{"), "}") + .delimit_indented(fmt!("{kind} {name} {{"), "}") .list_wrap("\n", parts, ",\n", ",\n"), other => write!(f, "{name} {{ {other} }}"), }, - _ => write!(f, "struct {pat}"), + _ => write!(f, "{kind} {pat}"), } } } @@ -510,25 +529,38 @@ impl Display for Pat { }, Self::TupStruct(name, bind) => write!(f, "{name} {bind}"), Self::Typed(pat, ty) => write!(f, "{pat}: {ty}"), - Self::Op(PatOp::Rest, pats) => match pats.as_slice() { - [] => write!(f, ".."), - [rest] => write!(f, "..{rest}"), - [from, to] => write!(f, "{from}..{to}"), - _ => f.list(pats, "<..>"), - }, - Self::Op(PatOp::RangeEx, 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(PatOp::Alt, pats) => f.delimit("<", ">").list(pats, " | "), + Self::Op(op @ PatOp::Alt, pats) => f.delimit("<", ">").list(pats, op), + Self::Op(op, pats) => match pats.as_slice() { + [] => op.fmt(f), + [rest] => write!(f, "{op}{rest}"), + _ => f.delimit("(", ")").list(pats, op), + }, } } } +impl Display for PatOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Rest => "..", + Self::RangeEx => "..", + Self::RangeIn => "..=", + Self::Ref => "&", + Self::Tuple => ", ", + Self::Slice => ", ", + Self::Alt => " | ", + }) + } +} + impl Display for Ty { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Infer => "_".fmt(f), Self::Named(name) => name.fmt(f), + Self::Ref(ty) => write!(f, "&{ty}"), Self::Tuple(items) => f.delimit('(', ')').list(items, ", "), Self::Slice(ty) => write!(f, "[{ty}]"), Self::Array(ty, n) => write!(f, "[{ty}; {n}]"), diff --git a/src/ast/macro_matcher.rs b/src/ast/macro_matcher.rs index cef648c..cb728a1 100644 --- a/src/ast/macro_matcher.rs +++ b/src/ast/macro_matcher.rs @@ -162,14 +162,14 @@ impl Match for Mod { } } -impl Match for Struct { +impl Match for Typedef { 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) + let (Self(pat_kind, pat_pat), Self(expr_kind, expr_pat)) = (pat, expr); + pat_kind == expr_kind && Match::recurse(sub, pat_pat, expr_pat) } fn apply(&mut self, sub: &Subst) { - let Self(pat) = self; + let Self(_, pat) = self; pat.apply(sub); } } diff --git a/src/lexer.rs b/src/lexer.rs index f788496..ee99f92 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -241,6 +241,7 @@ impl<'t> Lexer<'t> { "const" => TKind::Const, "do" => TKind::Do, "else" => TKind::Else, + "enum" => TKind::Enum, "false" => TKind::False, "fn" => TKind::Fn, "for" => TKind::For, diff --git a/src/parser.rs b/src/parser.rs index 30a4fb3..b7212ab 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -279,6 +279,7 @@ enum PatPs { fn pat_from_infix(token: &Token) -> Option<(PatPs, PPrec)> { Some(match token.kind { TKind::DotDot => (PatPs::Op(PatOp::RangeEx), PPrec::Range), + TKind::DotDotEq => (PatPs::Op(PatOp::RangeIn), PPrec::Range), TKind::Colon => (PatPs::Typed, PPrec::Typed), TKind::Comma => (PatPs::Op(PatOp::Tuple), PPrec::Tuple), TKind::Bar => (PatPs::Op(PatOp::Alt), PPrec::Alt), @@ -297,6 +298,11 @@ impl<'t> Parse<'t> for Pat { Pat::Lit(p.parse(())?) } TKind::Bar => p.consume().parse(level)?, + TKind::Amp => Pat::Op(PatOp::Ref, vec![p.consume().parse(PPrec::Max)?]), + TKind::AmpAmp => Pat::Op( + PatOp::Ref, + vec![Pat::Op(PatOp::Ref, vec![p.consume().parse(PPrec::Max)?])], + ), TKind::Identifier => match tok.lexeme.str() { Some("_") => p.consume().then(Pat::Ignore), _ => { @@ -335,6 +341,13 @@ impl<'t> Parse<'t> for Pat { _ => vec![], }, ), + TKind::DotDotEq => Pat::Op( + PatOp::RangeIn, + match p.consume().peek()?.kind { + TKind::Grave | TKind::Integer | TKind::Character => vec![p.parse(level)?], + _ => vec![], + }, + ), TKind::LParen => Pat::Op( PatOp::Tuple, p.consume() @@ -384,6 +397,8 @@ impl<'t> Parse<'t> for Ty { 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()? { @@ -494,7 +509,7 @@ pub enum Ps { Lit, // Literal Let, // let Pat = Expr Const, // const Pat = Expr - Struct, // struct { Pat } | struct ( Pat ) + Typedef, // struct { Pat } | struct ( Pat ) For, // for Pat in Expr Expr else Expr Fn, // fn ( Pat,* ) Expr Lambda0, // || Expr @@ -528,7 +543,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::Struct | TKind::Enum => (Ps::Typedef, 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), @@ -560,7 +575,7 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { fn from_infix(token: &Token) -> PResult<(Ps, Prec)> { Ok(match token.kind { TKind::Semi => (Ps::Op(Op::Do), Prec::Do), // the inspiration - TKind::As => (Ps::Op(Op::As), Prec::Body), + TKind::As => (Ps::Op(Op::As), Prec::Max), TKind::Comma => (Ps::Op(Op::Tuple), Prec::Tuple), TKind::Dot => (Ps::Op(Op::Dot), Prec::Project), TKind::AmpAmp => (Ps::Op(Op::LogAnd), Prec::LogAnd), @@ -606,12 +621,16 @@ impl<'t> Parse<'t> for Const { } } -impl<'t> Parse<'t> for Struct { +impl<'t> Parse<'t> for Typedef { type Prec = (); fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult { - let value = p.consume().parse(PPrec::Min)?; - Ok(Self(value)) + let tok = p.next()?; + match tok.kind { + TKind::Enum => Ok(Self(TypedefKind::Enum, p.parse(PPrec::Alt)?)), + TKind::Struct => Ok(Self(TypedefKind::Struct, p.parse(PPrec::Tuple)?)), + _ => Err(ParseError::NotType(tok.kind, tok.span)), + } } } @@ -837,7 +856,7 @@ impl<'t> Parse<'t> for Expr { Ps::Let => Expr::Let(p.parse(())?), Ps::For => parse_for(p, ())?, Ps::Const => Expr::Const(p.parse(())?), - Ps::Struct => Expr::Struct(p.parse(())?), + Ps::Typedef => 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 f5e274e..00eed68 100644 --- a/src/token.rs +++ b/src/token.rs @@ -62,6 +62,7 @@ pub enum TKind { Const, Do, Else, + Enum, False, Fn, For,