486 lines
15 KiB
Rust
486 lines
15 KiB
Rust
//! 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<T: Annotation, A: Annotation = Span>(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<T: Clone + std::fmt::Debug + std::fmt::Display + PartialEq + Eq> 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<Box<Pat>>),
|
|
/// Matches a literal value by equality comparison
|
|
Lit(Literal),
|
|
/// Matches the elements of a tuple
|
|
Tuple(Vec<Pat>),
|
|
/// Matches the elements
|
|
Slice(Vec<Pat>),
|
|
/// Matches one of the provided alternates
|
|
Alt(Vec<Pat>),
|
|
/// Matches a typed pattern
|
|
Typed(Box<Pat>, Ty),
|
|
}
|
|
|
|
/// In-universe types
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum Ty {
|
|
/// `_`
|
|
Infer,
|
|
/// `(Identifier :: )* Identifier`
|
|
Named(String),
|
|
/// `(..Tys)`
|
|
Tuple(Vec<Ty>),
|
|
/// `[Ty]`
|
|
Slice(Box<Ty>),
|
|
/// `[Ty; _]`
|
|
Array(Box<Ty>, Box<Expr>),
|
|
/// `[..Args, Rety]`
|
|
Fn(Vec<Ty>),
|
|
}
|
|
|
|
/// A `let` binding
|
|
/// ```ignore
|
|
/// let Pat (= Expr)?
|
|
/// ``````
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Let<A: Annotation = Span>(pub Pat, pub Option<Anno<Expr<A>, A>>);
|
|
|
|
/// A `const` binding (which defines its name before executing)
|
|
/// ```ignore
|
|
/// const Pat = Expr
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Const<A: Annotation = Span>(pub Pat, pub Anno<Expr<A>, A>);
|
|
|
|
/// A function definition
|
|
/// ```ignore
|
|
/// fn Ident? (Pat) Expr
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Fn<A: Annotation = Span>(pub Option<String>, pub Pat, pub Anno<Expr<A>, A>);
|
|
|
|
/// A match expression
|
|
/// ```ignore
|
|
/// match Expr { MatchArm,* }
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Match<A: Annotation = Span>(pub Anno<Expr<A>, A>, pub Vec<MatchArm<A>>);
|
|
|
|
/// The "arms" of a match expression
|
|
/// ```ignore
|
|
/// Pat => Expr
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct MatchArm<A: Annotation = Span>(pub Pat, pub Anno<Expr<A>, A>);
|
|
|
|
/// A make (constructor) expression
|
|
/// ```ignore
|
|
/// Expr { (Ident (: Expr)?),* }
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Make<A: Annotation = Span>(pub Box<Anno<Expr<A>, A>>, pub Vec<MakeArm<A>>);
|
|
|
|
/// A single "arm" of a make expression
|
|
/// ```ignore
|
|
/// Identifier (':' Expr)?
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct MakeArm<A: Annotation = Span>(pub String, pub Option<Anno<Expr<A>, A>>);
|
|
|
|
/// An inline namespace
|
|
/// ```ignore
|
|
/// mod Ty Expr
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Mod<A: Annotation = Span>(pub Ty, pub Anno<Expr<A>, A>);
|
|
|
|
/// Expressions: The beating heart of Dough
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum Expr<A: Annotation = Span> {
|
|
/// An identifier
|
|
Id(String),
|
|
/// A meta-identifier
|
|
MetId(String),
|
|
/// A literal bool, string, char, or int
|
|
Lit(Literal),
|
|
/// let Pat<NoTopAlt> = expr
|
|
Let(Box<Let<A>>),
|
|
/// `const Pat<NoTopAlt> (= Expr)?` (Basically let rec)
|
|
Const(Box<Const<A>>),
|
|
/// `| Pat<Tuple> | Expr` | `|| Expr` | `fn Ident? (Pat,*) Expr`
|
|
Fn(Box<Fn<A>>),
|
|
/// Expr { (Ident (: Expr)?),* }
|
|
Make(Box<Make<A>>),
|
|
/// match Expr { MatchArm,* }
|
|
Match(Box<Match<A>>),
|
|
/// mod Ty Expr
|
|
Mod(Box<Mod<A>>),
|
|
/// Op Expr | Expr Op | Expr (Op Expr)+ | Op Expr Expr else Expr
|
|
Op(Op, Vec<Anno<Self, A>>),
|
|
}
|
|
|
|
impl<A: Annotation> Expr<A> {
|
|
pub fn anno(self, annotation: A) -> Anno<Expr<A>, 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<Expr<A>, 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<T: Display + Annotation, A: Annotation> Display for Anno<T, A> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl<A: Annotation> Display for Const<A> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let Self(pat, expr) = self;
|
|
write!(f, "const {pat} = {expr}")
|
|
}
|
|
}
|
|
|
|
impl<A: Annotation> Display for Fn<A> {
|
|
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<A: Annotation> Display for Let<A> {
|
|
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<A: Annotation> Display for Match<A> {
|
|
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<A: Annotation> Display for Make<A> {
|
|
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<A: Annotation> Display for Mod<A> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let Self(ty, expr) = self;
|
|
write!(f, "mod {ty} {expr}")
|
|
}
|
|
}
|
|
|
|
impl<A: Annotation> Display for Expr<A> {
|
|
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<A: Annotation> Display for MakeArm<A> {
|
|
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<A: Annotation> Display for MatchArm<A> {
|
|
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<A: Annotation> TryFrom<Expr<A>> for Pat {
|
|
type Error = Expr<A>;
|
|
|
|
fn try_from(value: Expr<A>) -> Result<Self, Self::Error> {
|
|
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::<Result<_, _>>()?,
|
|
),
|
|
Expr::Op(Op::Array, exprs) => Self::Slice(
|
|
exprs
|
|
.into_iter()
|
|
.map(Self::try_from)
|
|
.collect::<Result<_, _>>()?,
|
|
),
|
|
other => Err(other)?,
|
|
})
|
|
}
|
|
}
|
|
impl<A: Annotation> TryFrom<Anno<Expr<A>, A>> for Pat {
|
|
type Error = Expr<A>;
|
|
|
|
fn try_from(value: Anno<Expr<A>, A>) -> Result<Self, Self::Error> {
|
|
Self::try_from(value.0)
|
|
}
|
|
}
|