Files
Doughlang/examples/7fold.rs
John 2be73d2660 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)
2026-01-06 04:57:15 -05:00

186 lines
4.2 KiB
Rust

//! Demonstrates the visitor pattern
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#"
let x: [i32; 4] = [1, 2, 3, 4];
use foo::bar::baz::qux::{a, b, c, d, e};
struct<T> Point {x: T, y: T}
let v = Point {
x, y: 20 + x
}
fn main() -> (&str, bool, i128) {
// An if expression is like the ternary conditional operator in C
let y = if 10 < 50 {
"\u{1f988}"
} else {
"x"
}
// A `while` expression is like the while-else construct in Python,
// but it returns a value via the `break` keyword
let z = while false {
// do a thing repeatedly
break true
} else {
// If `while` does not `break`, fall through to the `else` expression
false
}
// The same is true of `for` expressions!
let w = for idx in 0..100 {
if idx > 2 * 2 {
break idx
}
} else 12345; // semicolon operator required here
// desugars to
let w = match ((0..100).into_iter()) {
__iter => loop match (__iter.next()) {
None => break 12345,
Some (idx) => {
if (idx > (2 * 2)) {
break idx
}
},
},
};
// A block evaluates to its last expression,
// or Empty if there is none
// (🦈, false, 5)
(y, z, w)
}
"#;
fn main() -> Result<(), Box<dyn Error>> {
let ast: Anno<Expr> = Parser::new(Lexer::new(CODE)).parse(Prec::MIN)?;
let mut counters = CountNodes::new();
counters.visit(&ast);
println!("{counters:#?}");
let Ok(ast) = ast.fold_in(&mut Sevenfold) else {
return Ok(());
};
println!("\nSevenfold:\n{ast}");
Ok(())
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct CountNodes {
exprs: usize,
macro_ids: usize,
idents: usize,
paths: usize,
literals: usize,
uses: usize,
patterns: usize,
binds: usize,
makes: usize,
makearms: usize,
}
impl CountNodes {
fn new() -> Self {
Self::default()
}
}
impl<'a, A: AstTypes> Visit<'a, A> for CountNodes {
type Error = Infallible;
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_pat(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
self.patterns += 1;
item.children(self)
}
fn visit_bind(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
self.binds += 1;
item.children(self)
}
fn visit_make(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
self.makes += 1;
item.children(self)
}
fn visit_makearm(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
self.makearms += 1;
item.children(self)
}
}
struct 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)
}
}