197 lines
5.9 KiB
Rust
197 lines
5.9 KiB
Rust
|
|
/// This is the parser and interpreter for the 'Foo' language. See `tutorial.md` in the repository's root to learn
|
||
|
|
/// about it.
|
||
|
|
use chumsky::prelude::*;
|
||
|
|
|
||
|
|
#[derive(Debug)]
|
||
|
|
enum Expr {
|
||
|
|
Num(f64),
|
||
|
|
Var(String),
|
||
|
|
|
||
|
|
Neg(Box<Expr>),
|
||
|
|
Add(Box<Expr>, Box<Expr>),
|
||
|
|
Sub(Box<Expr>, Box<Expr>),
|
||
|
|
Mul(Box<Expr>, Box<Expr>),
|
||
|
|
Div(Box<Expr>, Box<Expr>),
|
||
|
|
|
||
|
|
Call(String, Vec<Expr>),
|
||
|
|
Let {
|
||
|
|
name: String,
|
||
|
|
rhs: Box<Expr>,
|
||
|
|
then: Box<Expr>,
|
||
|
|
},
|
||
|
|
Fn {
|
||
|
|
name: String,
|
||
|
|
args: Vec<String>,
|
||
|
|
body: Box<Expr>,
|
||
|
|
then: Box<Expr>,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
fn parser() -> impl Parser<char, Expr, Error = Simple<char>> {
|
||
|
|
let ident = text::ident().padded();
|
||
|
|
|
||
|
|
let expr = recursive(|expr| {
|
||
|
|
let int = text::int(10)
|
||
|
|
.map(|s: String| Expr::Num(s.parse().unwrap()))
|
||
|
|
.padded();
|
||
|
|
|
||
|
|
let call = ident
|
||
|
|
.then(
|
||
|
|
expr.clone()
|
||
|
|
.separated_by(just(','))
|
||
|
|
.allow_trailing()
|
||
|
|
.delimited_by(just('('), just(')')),
|
||
|
|
)
|
||
|
|
.map(|(f, args)| Expr::Call(f, args));
|
||
|
|
|
||
|
|
let atom = int
|
||
|
|
.or(expr.delimited_by(just('('), just(')')))
|
||
|
|
.or(call)
|
||
|
|
.or(ident.map(Expr::Var));
|
||
|
|
|
||
|
|
let op = |c| just(c).padded();
|
||
|
|
|
||
|
|
let unary = op('-')
|
||
|
|
.repeated()
|
||
|
|
.then(atom)
|
||
|
|
.foldr(|_op, rhs| Expr::Neg(Box::new(rhs)));
|
||
|
|
|
||
|
|
let product = unary
|
||
|
|
.clone()
|
||
|
|
.then(
|
||
|
|
op('*')
|
||
|
|
.to(Expr::Mul as fn(_, _) -> _)
|
||
|
|
.or(op('/').to(Expr::Div as fn(_, _) -> _))
|
||
|
|
.then(unary)
|
||
|
|
.repeated(),
|
||
|
|
)
|
||
|
|
.foldl(|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)));
|
||
|
|
|
||
|
|
let sum = product
|
||
|
|
.clone()
|
||
|
|
.then(
|
||
|
|
op('+')
|
||
|
|
.to(Expr::Add as fn(_, _) -> _)
|
||
|
|
.or(op('-').to(Expr::Sub as fn(_, _) -> _))
|
||
|
|
.then(product)
|
||
|
|
.repeated(),
|
||
|
|
)
|
||
|
|
.foldl(|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)));
|
||
|
|
|
||
|
|
sum
|
||
|
|
});
|
||
|
|
|
||
|
|
let decl = recursive(|decl| {
|
||
|
|
let r#let = text::keyword("let")
|
||
|
|
.ignore_then(ident)
|
||
|
|
.then_ignore(just('='))
|
||
|
|
.then(expr.clone())
|
||
|
|
.then_ignore(just(';'))
|
||
|
|
.then(decl.clone())
|
||
|
|
.map(|((name, rhs), then)| Expr::Let {
|
||
|
|
name,
|
||
|
|
rhs: Box::new(rhs),
|
||
|
|
then: Box::new(then),
|
||
|
|
});
|
||
|
|
|
||
|
|
let r#fn = text::keyword("fn")
|
||
|
|
.ignore_then(ident)
|
||
|
|
.then(ident.repeated())
|
||
|
|
.then_ignore(just('='))
|
||
|
|
.then(expr.clone())
|
||
|
|
.then_ignore(just(';'))
|
||
|
|
.then(decl)
|
||
|
|
.map(|(((name, args), body), then)| Expr::Fn {
|
||
|
|
name,
|
||
|
|
args,
|
||
|
|
body: Box::new(body),
|
||
|
|
then: Box::new(then),
|
||
|
|
});
|
||
|
|
|
||
|
|
r#let.or(r#fn).or(expr).padded()
|
||
|
|
});
|
||
|
|
|
||
|
|
decl.then_ignore(end())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn eval<'a>(
|
||
|
|
expr: &'a Expr,
|
||
|
|
vars: &mut Vec<(&'a String, f64)>,
|
||
|
|
funcs: &mut Vec<(&'a String, &'a [String], &'a Expr)>,
|
||
|
|
) -> Result<f64, String> {
|
||
|
|
match expr {
|
||
|
|
Expr::Num(x) => Ok(*x),
|
||
|
|
Expr::Neg(a) => Ok(-eval(a, vars, funcs)?),
|
||
|
|
Expr::Add(a, b) => Ok(eval(a, vars, funcs)? + eval(b, vars, funcs)?),
|
||
|
|
Expr::Sub(a, b) => Ok(eval(a, vars, funcs)? - eval(b, vars, funcs)?),
|
||
|
|
Expr::Mul(a, b) => Ok(eval(a, vars, funcs)? * eval(b, vars, funcs)?),
|
||
|
|
Expr::Div(a, b) => Ok(eval(a, vars, funcs)? / eval(b, vars, funcs)?),
|
||
|
|
Expr::Var(name) => {
|
||
|
|
if let Some((_, val)) = vars.iter().rev().find(|(var, _)| *var == name) {
|
||
|
|
Ok(*val)
|
||
|
|
} else {
|
||
|
|
Err(format!("Cannot find variable `{}` in scope", name))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Expr::Let { name, rhs, then } => {
|
||
|
|
let rhs = eval(rhs, vars, funcs)?;
|
||
|
|
vars.push((name, rhs));
|
||
|
|
let output = eval(then, vars, funcs);
|
||
|
|
vars.pop();
|
||
|
|
output
|
||
|
|
}
|
||
|
|
Expr::Call(name, args) => {
|
||
|
|
if let Some((_, arg_names, body)) =
|
||
|
|
funcs.iter().rev().find(|(var, _, _)| *var == name).copied()
|
||
|
|
{
|
||
|
|
if arg_names.len() == args.len() {
|
||
|
|
let mut args = args
|
||
|
|
.iter()
|
||
|
|
.map(|arg| eval(arg, vars, funcs))
|
||
|
|
.zip(arg_names.iter())
|
||
|
|
.map(|(val, name)| Ok((name, val?)))
|
||
|
|
.collect::<Result<_, String>>()?;
|
||
|
|
vars.append(&mut args);
|
||
|
|
let output = eval(body, vars, funcs);
|
||
|
|
vars.truncate(vars.len() - args.len());
|
||
|
|
output
|
||
|
|
} else {
|
||
|
|
Err(format!(
|
||
|
|
"Wrong number of arguments for function `{}`: expected {}, found {}",
|
||
|
|
name,
|
||
|
|
arg_names.len(),
|
||
|
|
args.len(),
|
||
|
|
))
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
Err(format!("Cannot find function `{}` in scope", name))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Expr::Fn {
|
||
|
|
name,
|
||
|
|
args,
|
||
|
|
body,
|
||
|
|
then,
|
||
|
|
} => {
|
||
|
|
funcs.push((name, args, body));
|
||
|
|
let output = eval(then, vars, funcs);
|
||
|
|
funcs.pop();
|
||
|
|
output
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn main() {
|
||
|
|
let src = std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap();
|
||
|
|
|
||
|
|
match parser().parse(src) {
|
||
|
|
Ok(ast) => match eval(&ast, &mut Vec::new(), &mut Vec::new()) {
|
||
|
|
Ok(output) => println!("{}", output),
|
||
|
|
Err(eval_err) => println!("Evaluation error: {}", eval_err),
|
||
|
|
},
|
||
|
|
Err(parse_errs) => parse_errs
|
||
|
|
.into_iter()
|
||
|
|
.for_each(|e| println!("Parse error: {}", e)),
|
||
|
|
}
|
||
|
|
}
|