//! The Abstract Syntax Tree defines an interface between the parser and type checker pub mod macro_matcher; /// A value with an annotation. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Anno(pub T, pub A); /// 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 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(i128), /// A string literal: Str(String), } /// Binding patterns for each kind of matchable [Ty] #[derive(Clone, Debug, PartialEq, Eq)] pub enum Pat { /// Matches anything without binding Ignore, /// Matches nothing; used for macro substitution. MetId(String), /// Matches anything, and binds it to a name Name(String), /// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`). Rest(Option>), /// Matches a literal value by equality comparison Lit(Literal), /// Matches the elements of a tuple Tuple(Vec), /// Matches the elements Slice(Vec), /// Matches one of the provided alternates Alt(Vec), /// Matches a typed pattern Typed(Box, Ty), } /// In-universe types #[derive(Clone, Debug, PartialEq, Eq)] pub enum Ty { /// `_` Infer, /// `(Identifier :: )* Identifier` Named(String), /// `(..Tys)` Tuple(Vec), /// `[Ty]` Slice(Box), /// `[Ty; _]` Array(Box, Box), /// `[..Args, Rety]` Fn(Vec), } /// A `let` binding /// ```ignore /// let Pat (= Expr)? /// `````` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Let(pub Pat, pub Option, A>>); /// A `const` binding (which defines its name before executing) /// ```ignore /// const Pat = Expr /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Const(pub Pat, pub Anno, A>); /// A function definition /// ```ignore /// fn Ident? (Pat) Expr /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Fn(pub Option, pub Pat, pub Anno, A>); /// A match expression /// ```ignore /// match Expr { MatchArm,* } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match(pub Anno, A>, pub Vec>); /// The "arms" of a match expression /// ```ignore /// Pat => Expr /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct MatchArm(pub Pat, pub Anno, A>); /// A make (constructor) expression /// ```ignore /// Expr { (Ident (: Expr)?),* } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Make(pub Box, A>>, pub Vec>); /// A single "arm" of a make expression /// ```ignore /// Identifier (':' Expr)? /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct MakeArm(pub String, pub Option, A>>); /// An inline namespace /// ```ignore /// mod Ty Expr /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Mod(pub Ty, pub Anno, A>); /// Expressions: The beating heart of Dough #[derive(Clone, Debug, PartialEq, Eq)] pub enum Expr { /// An identifier Id(String), /// A meta-identifier MetId(String), /// A literal bool, string, char, or int Lit(Literal), /// let Pat = expr Let(Box>), /// `const Pat (= Expr)?` (Basically let rec) Const(Box>), /// `| Pat | Expr` | `|| Expr` | `fn Ident? (Pat,*) Expr` Fn(Box>), /// Expr { (Ident (: Expr)?),* } Make(Box>), /// match Expr { MatchArm,* } Match(Box>), /// mod Ty Expr Mod(Box>), /// Op Expr | Expr Op | Expr (Op Expr)+ | Op Expr Expr else Expr Op(Op, Vec>), } impl Expr { pub fn anno(self, annotation: A) -> Anno, A> { Anno(self, annotation) } pub fn is_place(&self) -> bool { matches!( self, Self::Id(_) | Self::Op(Op::Index, _) | Self::Op(Op::Dot, _) | Self::Op(Op::Path, _) | 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, } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Op { // -- true operators Do, // Expr ; Expr As, // Expr as Expr Macro, // macro Expr => Expr Block, // { Expr } Array, // [ Expr,* ] ArRep, // [ Expr ; Expr ] Group, // ( Expr ,?) Tuple, // Expr (, Expr)* Try, // Expr '?' Index, // Expr [ Expr,* ] Call, // Expr ( Expr,* ) Pub, // pub Expr Loop, // loop Expr If, // if Expr Expr (else Expr)? While, // while Expr Expr (else Expr)? Break, // break Expr Return, // return Expr Dot, // Expr . Expr Path, // 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 } 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) => i.fmt(f), Self::Str(s) => write!(f, "\"{}\"", s.escape_debug()), } } } impl Display for Anno { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Display for Const { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(pat, expr) = self; write!(f, "const {pat} = {expr}") } } impl Display for Fn { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self(Some(name), pat, expr) => write!(f, "fn {name} {pat} {expr}"), Self(None, pat, expr) => write!(f, "|{pat}| {expr}"), } } } impl Display for Let { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self(pat, Some(expr)) => write!(f, "let {pat} = {expr}"), Self(pat, None) => write!(f, "let ({pat})"), } } } impl Display for Match { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(expr, match_arms) = self; f.delimit_indented(fmt!("match {expr} {{"), "}") .list_wrap("\n", match_arms, ",\n", ",\n") } } 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 Mod { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(ty, expr) = self; write!(f, "mod {ty} {expr}") } } 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::Let(v) => v.fmt(f), Self::Const(v) => v.fmt(f), Self::Make(v) => v.fmt(f), Self::Match(v) => v.fmt(f), Self::Mod(v) => v.fmt(f), Self::Fn(v) => v.fmt(f), Self::Op(op @ (Op::If | Op::While), exprs) => match exprs.as_slice() { [cond, pass, fail] => write!(f, "{op}{cond} {pass} else {fail}"), other => f.delimit(fmt!("({op}, "), ")").list(other, ", "), }, 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 @ 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::Do, exprs) => f.list(exprs, ";\n"), 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 { match self { Op::Do => "; ".fmt(f), Op::As => " as ".fmt(f), Op::Macro => "macro ".fmt(f), Op::Block => "{}".fmt(f), Op::Array => "[]".fmt(f), Op::ArRep => "[;]".fmt(f), Op::Group => "()".fmt(f), Op::Tuple => "()".fmt(f), Op::Try => "?".fmt(f), Op::Index => "".fmt(f), Op::Call => "".fmt(f), Op::Pub => "pub ".fmt(f), Op::Loop => "loop ".fmt(f), Op::If => "if ".fmt(f), Op::While => "while ".fmt(f), Op::Break => "break ".fmt(f), Op::Return => "return ".fmt(f), Op::Dot => ".".fmt(f), Op::Path => "::".fmt(f), Op::RangeEx => "..".fmt(f), Op::RangeIn => "..=".fmt(f), Op::Neg => "-".fmt(f), Op::Not => "!".fmt(f), Op::Identity => "!!".fmt(f), Op::Refer => "&".fmt(f), Op::Deref => "*".fmt(f), Op::Mul => " * ".fmt(f), Op::Div => " / ".fmt(f), Op::Rem => " % ".fmt(f), Op::Add => " + ".fmt(f), Op::Sub => " - ".fmt(f), Op::Shl => " << ".fmt(f), Op::Shr => " >> ".fmt(f), Op::And => " & ".fmt(f), Op::Xor => " ^ ".fmt(f), Op::Or => " | ".fmt(f), Op::Lt => " < ".fmt(f), Op::Leq => " <= ".fmt(f), Op::Eq => " == ".fmt(f), Op::Neq => " != ".fmt(f), Op::Geq => " >= ".fmt(f), Op::Gt => " > ".fmt(f), Op::LogAnd => " && ".fmt(f), Op::LogXor => " ^^ ".fmt(f), Op::LogOr => " || ".fmt(f), Op::Set => " = ".fmt(f), } } } 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 MatchArm { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(pat, expr) = self; write!(f, "{pat} => {expr}") } } impl Display for Pat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Ignore => "_".fmt(f), Self::Lit(literal) => literal.fmt(f), Self::MetId(name) => write!(f, "`{name}"), Self::Name(name) => name.fmt(f), Self::Rest(Some(rest)) => write!(f, "..{rest}"), Self::Rest(None) => write!(f, ".."), Self::Tuple(pats) => f.delimit("(", ")").list(pats, ", "), Self::Slice(pats) => f.delimit("[", "]").list(pats, ", "), Self::Alt(pats) => f.delimit("<", ">").list(pats, " | "), Self::Typed(pat, ty) => write!(f, "{pat}: {ty}"), } } } 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::Tuple(items) => f.delimit('(', ')').list(items, ", "), Self::Slice(ty) => write!(f, "[{ty}]"), Self::Array(ty, n) => write!(f, "[{ty}; {n}]"), Self::Fn(items) => match items.as_slice() { [] => write!(f, "fn ()"), [args @ .., rety] => f .delimit(fmt!("fn ("), fmt!(") -> {rety}")) .list(args, ", "), }, } } } impl TryFrom> for Pat { type Error = Expr; fn try_from(value: Expr) -> Result { Ok(match value { Expr::Id(name) if name == "_" => Self::Ignore, Expr::Id(name) => Self::Name(name), Expr::MetId(name) => Self::MetId(name), Expr::Lit(literal) => Self::Lit(literal), Expr::Op(Op::RangeEx, exprs) if exprs.is_empty() => Self::Rest(None), Expr::Op(Op::RangeEx, mut exprs) if exprs.len() == 1 => { Self::Rest(Some(Box::new(Self::try_from(exprs.remove(0))?))) } Expr::Op(Op::Tuple, exprs) => Self::Tuple( exprs .into_iter() .map(Self::try_from) .collect::>()?, ), Expr::Op(Op::Array, exprs) => Self::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) } }