doughlang: symbol interning and AST reparameterization

- intern: Add interners from cl-intern

- ast:
- Break AST into ternimals (AstTypes) and nonterminals (Expr, Pat, Bind, Make, Use)
- Make AST generic over terminals (except operators, for now)
This commit is contained in:
2026-01-06 04:57:15 -05:00
parent e4c008bd4b
commit 2be73d2660
17 changed files with 952 additions and 592 deletions

View File

@@ -5,11 +5,13 @@ use std::{convert::Infallible, error::Error};
use doughlang::{
ast::{
fold::{Fold, Foldable},
types::{Literal, Path, Symbol},
visit::{Visit, Walk},
*,
},
lexer::Lexer,
parser::{Parser, expr::Prec},
span::Span,
};
const CODE: &str = r#"
@@ -86,6 +88,7 @@ fn main() -> Result<(), Box<dyn Error>> {
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct CountNodes {
exprs: usize,
macro_ids: usize,
idents: usize,
paths: usize,
literals: usize,
@@ -102,50 +105,54 @@ impl CountNodes {
}
}
impl<'a> Visit<'a> for CountNodes {
impl<'a, A: AstTypes> Visit<'a, A> for CountNodes {
type Error = Infallible;
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
fn visit_macro_id(&mut self, _name: &'a A::MacroId) -> Result<(), Self::Error> {
self.macro_ids += 1;
Ok(())
}
fn visit_symbol(&mut self, _name: &'a A::Symbol) -> Result<(), Self::Error> {
self.idents += 1;
Ok(())
}
fn visit_path(&mut self, _path: &'a A::Path) -> Result<(), Self::Error> {
self.paths += 1;
Ok(())
}
fn visit_literal(&mut self, _lit: &'a A::Literal) -> Result<(), Self::Error> {
self.literals += 1;
Ok(())
}
fn visit_use(&mut self, item: &'a Use<A>) -> Result<(), Self::Error> {
self.uses += 1;
item.children(self)
}
fn visit_expr(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
self.exprs += 1;
expr.children(self)
}
fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> {
self.idents += 1;
name.children(self)
}
fn visit_path(&mut self, path: &'a Path) -> Result<(), Self::Error> {
self.paths += 1;
path.children(self)
}
fn visit_literal(&mut self, lit: &'a Literal) -> Result<(), Self::Error> {
self.literals += 1;
lit.children(self)
}
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
self.uses += 1;
item.children(self)
}
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
fn visit_pat(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
self.patterns += 1;
item.children(self)
}
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
fn visit_bind(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
self.binds += 1;
item.children(self)
}
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
fn visit_make(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
self.makes += 1;
item.children(self)
}
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
fn visit_makearm(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
self.makearms += 1;
item.children(self)
}
@@ -153,10 +160,26 @@ impl<'a> Visit<'a> for CountNodes {
struct Sevenfold;
impl<A: Annotation> Fold<A> for Sevenfold {
impl Fold<DefaultTypes> for Sevenfold {
type Error = ();
fn fold_annotation(&mut self, anno: Span) -> Result<Span, Self::Error> {
Ok(anno)
}
fn fold_literal(&mut self, _lit: Literal) -> Result<Literal, Self::Error> {
Ok(Literal::Int(7, 10))
}
fn fold_macro_id(&mut self, name: Symbol) -> Result<Symbol, Self::Error> {
Ok(name)
}
fn fold_symbol(&mut self, name: Symbol) -> Result<Symbol, Self::Error> {
Ok(name)
}
fn fold_path(&mut self, path: Path) -> Result<Path, Self::Error> {
Ok(path)
}
}

View File

@@ -2,9 +2,9 @@ use std::error::Error;
use doughlang::{
ast::{
visit::{Visit, Walk},
*,
types::{Literal, Path}, visit::{Visit, Walk}, *
},
intern::interned::Interned,
lexer::Lexer,
parser::{Parser, expr::Prec},
};
@@ -126,19 +126,15 @@ impl ToLisp {
}
}
impl<'a> Visit<'a> for ToLisp {
impl<'a> Visit<'a, DefaultTypes> for ToLisp {
type Error = ();
fn visit(&mut self, walk: &'a impl Walk<'a>) -> Result<(), Self::Error> {
walk.visit_in(self)
}
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
fn visit_expr(&mut self, expr: &'a Expr) -> Result<(), Self::Error> {
match expr {
Expr::Omitted => print!("()"),
Expr::Id(path) => path.visit_in(self)?,
Expr::Id(path) => self.visit_path(path)?,
Expr::MetId(id) => print!("`{id}"),
Expr::Lit(literal) => literal.visit_in(self)?,
Expr::Lit(literal) => self.visit_literal(literal)?,
Expr::Use(import) => import.visit_in(self)?,
Expr::Bind(bind) => bind.visit_in(self)?,
Expr::Make(make) => make.visit_in(self)?,
@@ -155,7 +151,12 @@ impl<'a> Visit<'a> for ToLisp {
Ok(())
}
fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> {
fn visit_macro_id(&mut self, name: &'a Interned<'static, str>) -> Result<(), Self::Error> {
print!("{name}");
Ok(())
}
fn visit_symbol(&mut self, name: &'a Interned<'static, str>) -> Result<(), Self::Error> {
print!("{name}");
Ok(())
}
@@ -165,7 +166,7 @@ impl<'a> Visit<'a> for ToLisp {
print!("(at");
for part in parts {
print!(" ");
part.visit_in(self)?;
self.visit_symbol(part)?;
}
print!(")");
Ok(())
@@ -179,7 +180,7 @@ impl<'a> Visit<'a> for ToLisp {
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
match item {
Use::Glob => print!("(use-glob)"),
Use::Name(name) => name.visit_in(self)?,
Use::Name(name) => self.visit_symbol(name)?,
Use::Alias(name, alias) => print!("(use-alias {name} {alias})"),
Use::Path(name, tree) => {
print!("(use-path {name} ");
@@ -198,7 +199,7 @@ impl<'a> Visit<'a> for ToLisp {
Ok(())
}
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
fn visit_pat(&mut self, item: &'a Pat) -> Result<(), Self::Error> {
match item {
Pat::Ignore => print!("(ignore)"),
Pat::Never => print!("(never)"),
@@ -217,7 +218,7 @@ impl<'a> Visit<'a> for ToLisp {
Ok(())
}
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
fn visit_bind(&mut self, item: &'a Bind) -> Result<(), Self::Error> {
let Bind(op, generics, pattern, exprs) = item;
print!("({} ", self.bind_op(*op));
if !generics.is_empty() {
@@ -236,11 +237,11 @@ impl<'a> Visit<'a> for ToLisp {
Ok(())
}
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
fn visit_make(&mut self, item: &'a Make) -> Result<(), Self::Error> {
item.children(self)
}
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
fn visit_makearm(&mut self, item: &'a MakeArm) -> Result<(), Self::Error> {
item.children(self)
}
}

View File

@@ -90,50 +90,50 @@ impl Weight {
}
}
impl<'a> Visit<'a> for Weight {
impl<'a, A: AstTypes> Visit<'a, A> for Weight {
type Error = Infallible;
fn visit_expr<A: Annotation>(&mut self, item: &'a Expr<A>) -> Result<(), Self::Error> {
fn visit_expr(&mut self, item: &'a Expr<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_ident(&mut self, item: &'a str) -> Result<(), Self::Error> {
fn visit_symbol(&mut self, item: &'a A::Symbol) -> Result<(), Self::Error> {
self.size += size_of_val(item);
Ok(())
}
fn visit_path(&mut self, item: &'a A::Path) -> Result<(), Self::Error> {
self.size += size_of_val(item);
Ok(())
}
fn visit_literal(&mut self, item: &'a A::Literal) -> Result<(), Self::Error> {
self.size += size_of_val(item);
Ok(())
}
fn visit_use(&mut self, item: &'a Use<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_path(&mut self, item: &'a Path) -> Result<(), Self::Error> {
self.size += size_of_val(item) + size_of_val(item.parts.as_slice());
item.children(self)
}
fn visit_literal(&mut self, item: &'a Literal) -> Result<(), Self::Error> {
fn visit_pat(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
fn visit_bind(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
fn visit_make(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
fn visit_makearm(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
self.size += size_of_val(item);
item.children(self)
}