/// 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), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), Call(String, Vec), Let { name: String, rhs: Box, then: Box, }, Fn { name: String, args: Vec, body: Box, then: Box, }, } fn parser() -> impl Parser> { 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 { 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::>()?; 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)), } }