//! The Abstract Syntax Tree defines an interface between the parser and type checker pub mod macro_matcher; /// A value with an annotation. #[derive(Clone, PartialEq, Eq)] pub struct Anno(pub T, pub A); impl std::fmt::Debug for Anno { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ::fmt(&self.1, f)?; f.write_str(": ")?; ::fmt(&self.0, f) } } /// An annotation: extra data added on to important AST nodes. pub trait Annotation: Clone + std::fmt::Display + std::fmt::Debug + PartialEq + Eq {} impl Annotation for T {} /// A qualified identifier #[derive(Clone, Debug, PartialEq, Eq)] pub struct FqPath { // TODO: Identifier interning pub parts: Vec, // TODO: generic parameters } impl From<&str> for FqPath { fn from(value: &str) -> Self { Self { parts: vec![value.to_owned()] } } } /// A literal value (boolean, character, integer, string) #[derive(Clone, Debug, PartialEq, Eq)] pub enum Literal { /// A boolean literal: true | false Bool(bool), /// A character literal: 'a', '\u{1f988}' Char(char), /// An integer literal: 0, 123, 0x10 Int(u128, u32), /// A string 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. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Pat { /// Matches anything without binding Ignore, /// Matches nothing, ever Never, /// Matches nothing; used for macro substitution MetId(String), /// Matches anything, and binds it to a name Name(String), /// Matches against a named const value Path(FqPath), /// Matches a Struct Expression `Ident { Pat }` NamedStruct(FqPath, Box), /// Matches a Tuple Struct Expression `Ident ( Pat )` NamedTuple(FqPath, Box), /// Matches a literal value by equality comparison Lit(Literal), /// Matches a compound pattern Op(PatOp, Vec), } /// Operators on lists of patterns #[derive(Clone, Debug, PartialEq, Eq)] pub enum PatOp { /// Changes the visibility mode to "public" Pub, /// Changes the binding mode to "mutable" Mut, /// Matches the dereference of a pointer (`&pat`) Ref, /// Matches the dereference of a raw pointer (`*pat`) Ptr, /// 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 Slice, /// Matches a constant-size slice with repeating elements Arrep, /// Matches a type annotation or struct member Typed, /// Matches a function signature Fn, /// Matches one of a list of alternatives Alt, } /// A pattern binding /// ```ignore /// 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 /// Pat => Expr // in match /// `````` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Bind(pub BindKind, pub Pat, pub Vec, A>>); #[derive(Clone, Debug, PartialEq, Eq)] pub enum BindKind { /// 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 Fn, /// A `mod Pat Expr` binding Mod, /// An `impl Pat Expr` binding Impl, /// A `Pat => Expr` binding Match, } /// A make (constructor) expression /// ```ignore /// Expr { (Ident (: Expr)?),* } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Make(pub Anno, A>, pub Vec>); /// A single "arm" of a make expression /// ```text /// Identifier (':' Expr)? /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct MakeArm(pub String, pub Option, A>>); /// A record-type definition #[derive(Clone, Debug, PartialEq, Eq)] pub struct Typedef(pub TypedefKind, pub Pat); /// The type of type being defined #[derive(Clone, Debug, PartialEq, Eq)] pub enum TypedefKind { Struct, Enum, } /// Expressions: The beating heart of Dough #[derive(Clone, Debug, PartialEq, Eq)] pub enum Expr { /// An identifier Id(FqPath), /// An escaped token for macro binding 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>), /// (struct | enum | type) Pat::NoTopAlt Struct(Box), /// Expr { (Ident (: Expr)?),* } Make(Box>), /// Op Expr | Expr Op | Expr (Op Expr)+ | Op Expr Expr else Expr Op(Op, Vec>), } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Op { // -- true operators Do, // Expr ; Expr As, // Expr as Expr Macro, // macro { (Pat => Expr)* } Block, // { Expr } Array, // [ Expr,* ] ArRep, // [ Expr ; Expr ] Group, // ( Expr ,?) Tuple, // Expr (, Expr)* Meta, // #[ Expr ] Try, // Expr '?' Index, // Expr [ Expr,* ] Call, // Expr ( Expr,* ) Pub, // pub Expr Loop, // loop Expr Match, // match Expr { ,* } If, // if Expr Expr (else Expr)? While, // while Expr Expr (else Expr)? Break, // break Expr Return, // return Expr Dot, // Expr . Expr RangeEx, // Expr? ..Expr RangeIn, // Expr? ..=Expr Neg, // -Expr Not, // !Expr Identity, // !!Expr Refer, // &Expr Deref, // *Expr Mul, // Expr * Expr Div, // Expr / Expr Rem, // Expr % Expr Add, // Expr + Expr Sub, // Expr - Expr Shl, // Expr << Expr Shr, // Expr >> Expr And, // Expr & Expr Xor, // Expr ^ Expr Or, // Expr | Expr Lt, // Expr < Expr Leq, // Expr <= Expr Eq, // Expr == Expr Neq, // Expr != Expr Geq, // Expr >= Expr Gt, // Expr > Expr LogAnd, // Expr && Expr LogXor, // Expr ^^ Expr LogOr, // Expr || Expr Set, // Expr = Expr MulSet, // Expr *= Expr DivSet, // Expr /= Expr RemSet, // Expr %= Expr AddSet, // Expr += Expr SubSet, // Expr -= Expr ShlSet, // Expr <<= Expr ShrSet, // Expr >>= Expr AndSet, // Expr &= Expr XorSet, // Expr ^= Expr OrSet, // Expr |= Expr } impl Default for Expr { fn default() -> Self { Self::Op(Op::Tuple, vec![]) } } impl Expr { pub fn anno(self, annotation: A) -> Anno, A> { Anno(self, annotation) } pub fn and_do(self, annotation: A, other: Anno, A>) -> Self { let Self::Op(Op::Do, mut exprs) = self else { return Self::Op(Op::Do, vec![self.anno(annotation), other]); }; let Anno(Self::Op(Op::Do, mut other), _) = other else { exprs.push(other); return Self::Op(Op::Do, exprs); }; exprs.append(&mut other); Self::Op(Op::Do, exprs) } pub fn is_place(&self) -> bool { matches!( self, Self::Id(_) | Self::Op(Op::Index, _) | Self::Op(Op::Dot, _) | Self::Op(Op::Deref, _) ) } #[allow(clippy::type_complexity)] pub fn as_slice(&self) -> Option<(Op, &[Anno, A>])> { match self { Expr::Op(op, args) => Some((*op, args.as_slice())), _ => None, } } } use crate::{fmt::FmtAdapter, span::Span}; use std::{fmt::Display, format_args as fmt}; impl Display for Literal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Bool(v) => v.fmt(f), Self::Char(c) => write!(f, "'{}'", c.escape_debug()), Self::Int(i, 2) => write!(f, "0b{i:b}"), Self::Int(i, 8) => write!(f, "0o{i:o}"), Self::Int(i, 16) => write!(f, "0x{i:x}"), Self::Int(i, _) => i.fmt(f), Self::Str(s) => write!(f, "\"{}\"", s.escape_debug()), } } } impl Display for FqPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { parts } = self; f.list(parts, "::") } } impl Display for Anno { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } 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; match op { BindKind::Match => f.delimit(fmt!("{pat} => "), "").list(exprs, ",!? "), BindKind::Fn | BindKind::Mod | BindKind::Impl => { f.delimit(fmt!("{op}{pat} "), "").list(exprs, ",!? ") } _ => match exprs.as_slice() { [] => write!(f, "{op}{pat}"), [value] => write!(f, "{op}{pat} = {value}"), [value, fail] => write!(f, "{op} {pat} = {value} else {fail}"), other => f.delimit(fmt!("{op}{pat} ("), ")").list(other, ", "), }, } } } impl Display for BindKind { 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::Fn => "fn ", Self::Mod => "mod ", Self::Impl => "impl ", Self::Match => "| ", }) } } impl Display for Make { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(expr, make_arms) = self; f.delimit(fmt!("({expr} {{"), "})").list(make_arms, ", ") } } 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::Struct => "struct ", TypedefKind::Enum => "enum ", })?; match pat { Pat::NamedStruct(name, bind) => match bind.as_ref() { Pat::Op(PatOp::Tuple, parts) => f .delimit_indented(fmt!("{name} {{"), "}") .list_wrap("\n", parts, ",\n", ",\n"), other => write!(f, "{name} {{ {other} }}"), }, _ => pat.fmt(f), } } } impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { 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), Self::Op(op @ (Op::If | Op::While), exprs) => match exprs.as_slice() { [cond, pass, Anno(Expr::Op(Op::Tuple, e), _)] if e.is_empty() => { write!(f, "{op}{cond} {pass}") } [cond, pass, fail] => write!(f, "{op}{cond} {pass} else {fail}"), other => f.delimit(fmt!("({op}, "), ")").list(other, ", "), }, Self::Op(op @ Op::Match, exprs) => match exprs.as_slice() { [scrutinee, arms @ ..] => f .delimit_indented(fmt!("{op}{scrutinee} {{"), "}") .list_wrap("\n", arms, ",\n", ",\n"), [] => write!(f, "{op} () {{}}"), // invalid, but whatever. }, Self::Op(Op::Array, exprs) => f.delimit("[", "]").list(exprs, ", "), Self::Op(Op::ArRep, exprs) => f.delimit("[", "]").list(exprs, "; "), Self::Op(Op::Block, exprs) => f .delimit_indented("{", "}") .list_wrap("\n", exprs, "\n", "\n"), Self::Op(Op::Tuple, exprs) => f.delimit("(", ")").list(exprs, ", "), Self::Op(Op::Group, exprs) => f.list(exprs, ", "), Self::Op(Op::Meta, exprs) => match exprs.as_slice() { [meta, expr @ ..] => f.delimit(fmt!("#[{meta}] "), "").list(expr, ","), [] => write!(f, "#[]"), }, Self::Op(op @ Op::Call, exprs) => match exprs.as_slice() { [callee, args @ ..] => f.delimit(fmt!("{callee}("), ")").list(args, ", "), [] => write!(f, "{op}"), }, Self::Op(op @ Op::Index, exprs) => match exprs.as_slice() { [callee, args @ ..] => f.delimit(fmt!("{callee}["), "]").list(args, ", "), [] => write!(f, "{op}"), }, Self::Op(op @ Op::Do, exprs) => f.list(exprs, op), Self::Op(op @ Op::Macro, exprs) => f.delimit(op, "").list(exprs, " => "), Self::Op(op @ Op::Try, exprs) => f.delimit("(", fmt!("){op}")).list(exprs, ", "), Self::Op(op, exprs) => match exprs.as_slice() { [one] => write!(f, "{op}{one}"), many => f.delimit("(", ")").list(many, op), }, } } } impl Display for Op { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { Op::Do => ";\n", Op::As => " as ", Op::Macro => "macro ", Op::Block => "{}", Op::Array => "[]", Op::ArRep => "[;]", Op::Group => "()", Op::Tuple => "()", Op::Meta => "#[]", Op::Try => "?", Op::Index => "", Op::Call => "", Op::Pub => "pub ", Op::Loop => "loop ", Op::Match => "match ", Op::If => "if ", Op::While => "while ", Op::Break => "break ", Op::Return => "return ", Op::Dot => ".", Op::RangeEx => "..", Op::RangeIn => "..=", Op::Neg => "-", Op::Not => "!", Op::Identity => "!!", Op::Refer => "&", Op::Deref => "*", Op::Mul => " * ", Op::Div => " / ", Op::Rem => " % ", Op::Add => " + ", Op::Sub => " - ", Op::Shl => " << ", Op::Shr => " >> ", Op::And => " & ", Op::Xor => " ^ ", Op::Or => " | ", Op::Lt => " < ", Op::Leq => " <= ", Op::Eq => " == ", Op::Neq => " != ", Op::Geq => " >= ", Op::Gt => " > ", Op::LogAnd => " && ", Op::LogXor => " ^^ ", Op::LogOr => " || ", Op::Set => " = ", Op::MulSet => " *= ", Op::DivSet => " /= ", Op::RemSet => " %= ", Op::AddSet => " += ", Op::SubSet => " -= ", Op::ShlSet => " <<= ", Op::ShrSet => " >>= ", Op::AndSet => " &= ", Op::XorSet => " ^= ", Op::OrSet => " |= ", }) } } impl Display for MakeArm { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self(name, Some(body)) => write!(f, "{name}: {body}"), Self(name, None) => write!(f, "{name}"), } } } impl Display for Pat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Ignore => "_".fmt(f), Self::Never => "!".fmt(f), Self::Lit(literal) => literal.fmt(f), 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() { 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::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() { [] => 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::Pub => "pub ", Self::Mut => "mut ", Self::Ref => "&", Self::Ptr => "*", Self::Rest => "..", Self::RangeEx => "..", Self::RangeIn => "..=", Self::Tuple => ", ", Self::Slice => ", ", Self::Arrep => "; ", Self::Typed => ": ", Self::Fn => " -> ", Self::Alt => " | ", }) } } impl TryFrom> for Pat { type Error = Expr; fn try_from(value: Expr) -> Result { Ok(match value { Expr::Id(FqPath { mut parts }) if parts.len() == 1 => { match parts.pop().expect("parts should have len 1") { ig if ig == "_" => Self::Ignore, name => Self::Name(name), } } Expr::Id(path) => Self::Path(path), Expr::MetId(name) => Self::MetId(name), Expr::Lit(literal) => Self::Lit(literal), Expr::Op(Op::RangeEx, exprs) => Self::Op( if exprs.len() > 1 { PatOp::RangeEx } else { PatOp::Rest }, exprs .into_iter() .map(Self::try_from) .collect::, Expr>>()?, ), Expr::Op(Op::Tuple, exprs) => Self::Op( PatOp::Tuple, exprs .into_iter() .map(Self::try_from) .collect::>()?, ), Expr::Op(Op::Array, exprs) => Self::Op( PatOp::Slice, exprs .into_iter() .map(Self::try_from) .collect::>()?, ), other => Err(other)?, }) } } impl TryFrom, A>> for Pat { type Error = Expr; fn try_from(value: Anno, A>) -> Result { Self::try_from(value.0) } }