Initial Commit

This commit is contained in:
2025-08-28 02:26:06 -04:00
committed by Val
commit c83218d750
17 changed files with 2276 additions and 0 deletions

384
src/ast.rs Normal file
View File

@@ -0,0 +1,384 @@
//! The Abstract Syntax Tree defines an interface between the parser and type checker
pub mod 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 {}
#[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 {
Ignore,
MetId(String),
Name(String),
Rest(Option<Box<Pat>>),
Lit(Literal),
Tuple(Vec<Pat>),
Slice(Vec<Pat>),
}
/// The arms 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>>);
/// The arms of a match expression
/// ```ignore
/// (Pat |)* Pat? => Expr
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MatchArm<A: Annotation = Span>(pub Vec<Pat>, pub Anno<Expr<A>, A>);
/// In-universe types
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Ty {
/// `_`
Infer,
/// `(..Tys)`
Tuple(Vec<Ty>),
/// `[Ty]`
Slice(Box<Ty>),
/// `[Ty; _]`
Array(Box<Ty>, usize),
/// `[Rety, ..Args]`
Fn(Vec<Ty>),
}
/// 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 pattern = expr
Let(Pat, Option<Box<Anno<Self, A>>>),
/// `const Pat (= Expr)?` (Basically let rec)
Const(Pat, Box<Anno<Self, A>>),
/// `| Pat,* | Expr` | `|| Expr` | `fn (Pat,*) Expr`
Fn(Vec<Pat>, Box<Anno<Self, A>>),
/// Expr { (Ident (: Expr)?),* }
Make(Box<Anno<Self, A>>, Vec<MakeArm<A>>),
/// match Expr { MatchArm,* }
Match(Box<Anno<Self, A>>, Vec<MatchArm<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, _)
)
}
// pub fn is_assignee(&self) -> bool {
// match self {
// Self::Id(_) => todo!(),
// Self::MetId(_) => todo!(),
// Self::Lit(literal) => todo!(),
// Self::Let(pat, anno) => todo!(),
// Self::Const(pat, anno) => todo!(),
// Self::Fn(pats, anno) => todo!(),
// Self::Make(anno, make_arms) => todo!(),
// Self::Match(anno, match_arms) => todo!(),
// Self::Op(Op::Add, annos) => todo!(),
// Self::Op(Op::And, _) => false,
// }
// }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Op {
// -- fake operators used to assign precedences to special forms
Id, // Identifier
Mid, // MetaIdentifier
Lit, // Literal
Let, // let Pat = Expr
Const, // const Pat = Expr
Fn, // fn ( Pat,* ) Expr
Make, // Expr{ Expr,* }
Macro, // macro Expr => Expr
Match, // match Expr { MatchArm,* }
End, // Produces an empty value.
// -- true operators
Do, // Expr ; Expr
Block, // { Expr }
Array, // [ Expr,* ]
Group, // ( Expr ,?)
Tuple, // ( Expr,* )
Try, // Expr '?'
Index, // Expr [ Expr,* ]
Call, // Expr ( Expr,* )
Lambda, // |Pat?| 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 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(pat, Some(expr)) => write!(f, "let {pat} = {expr}"),
Self::Let(pat, None) => write!(f, "let {pat}"),
Self::Const(pat, expr) => write!(f, "const {pat} = {expr}"),
Self::Make(expr, make_arms) => {
f.delimit(fmt!("make {expr} {{"), "}").list(make_arms, ", ")
}
Self::Match(expr, match_arms) => f
.delimit_indented(fmt!("match {expr} {{\n"), "\n}")
.list_end(match_arms, ",\n", ","),
Self::Fn(pats, expr) => f.delimit("fn (", fmt!(") {expr}")).list(pats, ", "),
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::Block, exprs) => f.delimit_indented("{\n", "\n}").list(exprs, ", "),
Self::Op(Op::Tuple, exprs) => f.delimit("(", ")").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("", op).list(exprs, ", "),
Self::Op(op, exprs) => match exprs.as_slice() {
[_] => f.delimit(op, "").list(exprs, ", "),
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::Id => "_".fmt(f),
Op::Mid => "`".fmt(f),
Op::Lit => "##".fmt(f),
Op::Let => "let ".fmt(f),
Op::Const => "const ".fmt(f),
Op::Fn => "fn ".fmt(f),
Op::Macro => "macro ".fmt(f),
Op::Match => "match ".fmt(f),
Op::End => "()".fmt(f),
Op::Block => "{}".fmt(f),
Op::Array => "[]".fmt(f),
Op::Group => "()".fmt(f),
Op::Tuple => "()".fmt(f),
Op::Try => "?".fmt(f),
Op::Index => "".fmt(f),
Op::Call => "".fmt(f),
Op::Make => "".fmt(f),
Op::Lambda => "".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(pats, expr) = self;
f.delimit("", fmt!(" => {expr}")).list(pats, " | ")
}
}
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) => name.fmt(f),
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, ", "),
}
}
}
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)
}
}