use bittern::{Arena, Strong, Weak, SecondaryMap, Ref, Identity};
use core::hash::Hash;
use core::ops::{Add, Sub, Mul, Div};
use std::cell::Cell;
fn main() {
let input = r#"
do
let a 3000
let b 6000
let c sqrt (+ (pow a 2) (pow b 2))
(c)
"#;
let parser = Parser::new();
let expr = parser.parse(input).strong();
assert_eq!(parser.expr_table.len(), 14);
let result = Eval::new(&parser).eval(expr);
assert_eq!(result, Some(6708));
}
type Int = i64;
type Name = str;
#[derive(Identity, Hash, PartialEq, Eq, Debug)]
enum Token {
ParenOpen,
ParenClose,
Int(Int),
Name(Strong<Name>),
Do,
Let,
Add,
Sub,
Mul,
Div,
Pow,
Sqrt,
}
#[derive(Identity, Hash, PartialEq, Eq, Debug)]
enum Expr {
Empty,
Int(Int),
Name(Strong<Name>),
Block(Vec<Weak<Expr>>),
Let(Weak<Expr>, Weak<Expr>),
Add(Weak<Expr>, Weak<Expr>),
Sub(Weak<Expr>, Weak<Expr>),
Mul(Weak<Expr>, Weak<Expr>),
Div(Weak<Expr>, Weak<Expr>),
Pow(Weak<Expr>, Weak<Expr>),
Sqrt(Weak<Expr>),
}
struct Eval {
var_table: SecondaryMap<Name, Option<Int>>,
}
impl Eval {
fn new(parser: &Parser) -> Self {
Self {
var_table: SecondaryMap::new(parser.name_table.clone()),
}
}
fn eval(&mut self, expr: Strong<Expr>) -> Option<Int> {
fn pow(lhs: Int, rhs: Int) -> Int {
lhs.pow(rhs.clamp(u32::MIN as i64, u32::MAX as i64) as u32)
}
match &*expr {
Expr::Empty => None,
Expr::Int(i) => Some(*i),
Expr::Name(name) => self.var(name),
Expr::Block(exprs) => self.eval_block(exprs),
Expr::Let(lhs, rhs) => self.eval_let(lhs, rhs),
Expr::Add(lhs, rhs) => self.eval_binary(Int::add, lhs, rhs),
Expr::Sub(lhs, rhs) => self.eval_binary(Int::sub, lhs, rhs),
Expr::Mul(lhs, rhs) => self.eval_binary(Int::mul, lhs, rhs),
Expr::Div(lhs, rhs) => self.eval_binary(Int::div, lhs, rhs),
Expr::Pow(lhs, rhs) => self.eval_binary(pow, lhs, rhs),
Expr::Sqrt(rel) => self.eval_unary(Int::isqrt, rel),
}
}
fn var(&self, name: &Strong<Name>) -> Option<Int> {
match self.var_table.get(name) {
None => None,
Some(val) => *val,
}
}
fn eval_block(&mut self, exprs: &[Weak<Expr>]) -> Option<Int> {
if exprs.is_empty() {
return None;
}
for i in 0..(exprs.len() - 1) {
let expr = (&exprs[i]).strong().expect("invalid relation");
self.eval(expr);
}
let expr = (&exprs[exprs.len() - 1]).strong().expect("invalid relation");
self.eval(expr)
}
fn eval_let(&mut self, lhs_rel: &Weak<Expr>, rhs_rel: &Weak<Expr>) -> Option<Int> {
let lhs = lhs_rel.strong().expect("invalid relation");
let Expr::Name(name) = &*lhs else {
panic!("lhs of let statement must be a name");
};
if self.var_table.contains(name) {
panic!("name `{}` is assigned more than once", name);
}
let rhs = rhs_rel.strong().expect("invalid relation");
let rhs_val = self.eval(rhs);
self.var_table.insert(name.clone(), rhs_val);
rhs_val
}
fn eval_unary(&mut self, op: fn(Int) -> Int, rel: &Weak<Expr>) -> Option<Int> {
let expr = rel.strong().expect("invalid relation");
let Some(val) = self.eval(expr) else {
return None;
};
Some(op(val))
}
fn eval_binary(&mut self, op: fn(Int, Int) -> Int, lhs_rel: &Weak<Expr>, rhs_rel: &Weak<Expr>) -> Option<Int> {
let lhs = lhs_rel.strong().expect("invalid relation");
let rhs = rhs_rel.strong().expect("invalid relation");
let Some(lhs_val) = self.eval(lhs) else {
return None;
};
let Some(rhs_val) = self.eval(rhs) else {
return None;
};
Some(op(lhs_val, rhs_val))
}
}
struct Parser<'src> {
input: Cell<&'src str>,
name_table: Arena<Name>,
expr_table: Arena<Expr>,
}
impl<'src> Parser<'src> {
fn new() -> Self {
Self {
input: Cell::new(""),
name_table: Arena::new(),
expr_table: Arena::new(),
}
}
fn parse(&'_ self, input: &'src str) -> Ref<'_, Expr> {
self.input.set(input);
let expr = self.expr();
if !self.input.get().is_empty() {
panic!("expected EOF");
}
expr
}
fn expr(&'_ self) -> Ref<'_, Expr> {
let Some(token) = self.next_token() else {
panic!("unexpected EOF");
};
self.expr_start(token)
}
fn expr_start(&'_ self, token: Token) -> Ref<'_, Expr> {
match token {
Token::ParenClose => panic!("unexpected closing paren"),
Token::ParenOpen => match self.next_token() {
None => panic!("unexpected EOF"),
Some(Token::ParenClose) => self.empty(),
Some(token) => {
let expr = self.expr_start(token);
let Some(Token::ParenClose) = self.next_token() else {
panic!("expected closing paren");
};
expr
},
},
Token::Int(i) => self.int(i),
Token::Name(s) => self.name(s),
Token::Do => self.op_many(Expr::Block),
Token::Let => self.op_binary(Expr::Let),
Token::Add => self.op_binary(Expr::Add),
Token::Sub => self.op_binary(Expr::Sub),
Token::Mul => self.op_binary(Expr::Mul),
Token::Div => self.op_binary(Expr::Div),
Token::Pow => self.op_binary(Expr::Pow),
Token::Sqrt => self.op_unary(Expr::Sqrt),
}
}
fn empty(&'_ self) -> Ref<'_, Expr> {
self.expr_table.intern_owned(Expr::Empty)
}
fn int(&'_ self, i: Int) -> Ref<'_, Expr> {
self.expr_table.intern_owned(Expr::Int(i))
}
fn name(&'_ self, s: Strong<Name>) -> Ref<'_, Expr> {
self.expr_table.intern_owned(Expr::Name(s))
}
fn op_unary(&'_ self, op: fn(Weak<Expr>) -> Expr) -> Ref<'_, Expr> {
let val = self.expr().weak();
self.expr_table.intern_owned(op(val))
}
fn op_binary(&'_ self, op: fn(Weak<Expr>, Weak<Expr>) -> Expr) -> Ref<'_, Expr> {
let lhs = self.expr().weak();
let rhs = self.expr().weak();
self.expr_table.intern_owned(op(lhs, rhs))
}
fn op_many(&'_ self, op: fn(Vec<Weak<Expr>>) -> Expr) -> Ref<'_, Expr> {
let mut exprs: Vec<Weak<Expr>> = Vec::new();
loop {
if matches!(self.peek_char(), None | Some(')')) {
break;
}
let expr = self.expr();
exprs.push(expr.weak());
}
self.expr_table.intern_owned(op(exprs))
}
fn read_until<P>(&self, pat: P) -> &'src str
where P: Fn(char) -> bool
{
let input = self.input.get();
match input.find(pat) {
None => {
self.input.set("");
input
}
Some(end) => {
let s = &input[..end];
self.input.set(&input[end..]);
s
}
}
}
fn read_while<P>(&self, pat: P) -> &'src str
where P: Fn(char) -> bool
{
self.read_until(|c| !pat(c))
}
fn peek_char(&self) -> Option<char> {
self.input.get().chars().next()
}
fn ignore_whitespace(&self) {
self.read_while(char::is_whitespace);
}
fn next_token(&self) -> Option<Token> {
self.ignore_whitespace();
let input = self.input.get();
let token = match self.peek_char()? {
'(' => {
self.input.set(&input[1..]);
Token::ParenOpen
},
')' => {
self.input.set(&input[1..]);
Token::ParenClose
},
c => {
let word = self.read_until(|c| c == '(' || c == ')' || c.is_whitespace());
if c.is_digit(10) {
let i = word.parse::<Int>().expect("invalid int literal");
Token::Int(i)
} else {
match word {
"do" => Token::Do,
"let" => Token::Let,
"add" | "+" => Token::Add,
"sub" | "-" => Token::Sub,
"mul" | "*" => Token::Mul,
"div" | "/" => Token::Div,
"pow" | "**" => Token::Pow,
"sqrt" => Token::Sqrt,
_ => Token::Name(self.name_table.intern(word).strong())
}
}
}
};
self.ignore_whitespace();
Some(token)
}
}