274 lines
7.7 KiB
Rust
274 lines
7.7 KiB
Rust
use core::fmt;
|
|
use std::{
|
|
cell::RefCell,
|
|
collections::{HashMap, hash_map::Entry},
|
|
io::{self, Write},
|
|
ops, process,
|
|
rc::Rc,
|
|
};
|
|
|
|
use crate::inst::Inst;
|
|
|
|
type Key<'a> = &'a str;
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Val<'a> {
|
|
Num(f64),
|
|
Fun {
|
|
insts: &'a [Inst<'a>],
|
|
env: Rc<RefCell<Env<'a>>>,
|
|
},
|
|
}
|
|
|
|
impl Default for Val<'_> {
|
|
fn default() -> Self {
|
|
Self::Num(0.)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Val<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Val::Num(n) => write!(f, "{n}"),
|
|
Val::Fun { insts, env } => {
|
|
write!(f, "{} {}", Inst::Block(insts.to_vec()), env.borrow())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Val<'a> {
|
|
fn to_num(&self) -> f64 {
|
|
match self {
|
|
Val::Num(n) => *n,
|
|
Val::Fun { insts: _, env: _ } => 0.,
|
|
}
|
|
}
|
|
|
|
fn call(&self, stack: &mut Stack<'a>) {
|
|
match self {
|
|
Val::Num(n) => stack.push(Val::Num(*n)),
|
|
Val::Fun { insts, env } => {
|
|
eval(insts, stack, &env.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type EnvMap<'a> = HashMap<Key<'a>, Val<'a>>;
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Env<'a> {
|
|
Local(EnvMap<'a>),
|
|
Closure {
|
|
local: EnvMap<'a>,
|
|
above: Rc<RefCell<Env<'a>>>,
|
|
},
|
|
}
|
|
|
|
impl fmt::Display for Env<'_> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Env::Local(map) => {
|
|
for key in map.keys() {
|
|
write!(f, "{key} ")?;
|
|
}
|
|
Ok(())
|
|
}
|
|
Env::Closure { local, above } => {
|
|
for key in local.keys() {
|
|
write!(f, "{key} ")?;
|
|
}
|
|
write!(f, "| ")?;
|
|
write!(f, "{}", above.borrow())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for Env<'_> {
|
|
fn default() -> Self {
|
|
Env::Local(HashMap::new())
|
|
}
|
|
}
|
|
|
|
impl<'a> Env<'a> {
|
|
fn set(&mut self, key: Key<'a>, val: Val<'a>) {
|
|
match self {
|
|
Env::Local(map) => {
|
|
map.insert(key, val);
|
|
}
|
|
Env::Closure { local, above } => match local.entry(key) {
|
|
Entry::Occupied(mut e) => {
|
|
e.insert(val);
|
|
}
|
|
Entry::Vacant(e) => {
|
|
if !above.borrow_mut().set_if_exists(key, val.clone()) {
|
|
e.insert(val);
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Returns whether the key was set
|
|
fn set_if_exists(&mut self, key: Key<'a>, val: Val<'a>) -> bool {
|
|
match self {
|
|
Env::Local(map) => {
|
|
if let Entry::Occupied(mut e) = map.entry(key) {
|
|
e.insert(val);
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
Env::Closure { local, above } => {
|
|
if let Entry::Occupied(mut e) = local.entry(key) {
|
|
e.insert(val);
|
|
true
|
|
} else {
|
|
above.borrow_mut().set_if_exists(key, val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get(&self, key: Key<'a>) -> Option<Val<'a>> {
|
|
match self {
|
|
Env::Local(map) => map.get(key).cloned(),
|
|
Env::Closure { local, above } => local
|
|
.get(key)
|
|
.cloned()
|
|
.or_else(|| above.borrow().get(key).clone()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Stack<'a>(Vec<Val<'a>>);
|
|
|
|
impl<'a> Stack<'a> {
|
|
fn new() -> Self {
|
|
Self(Vec::new())
|
|
}
|
|
|
|
fn pop(&mut self) -> Option<Val<'a>> {
|
|
self.0.pop()
|
|
}
|
|
|
|
fn pop_num(&mut self) -> f64 {
|
|
self.pop_default().to_num()
|
|
}
|
|
|
|
fn push(&mut self, val: Val<'a>) {
|
|
self.0.push(val);
|
|
}
|
|
|
|
fn push_num(&mut self, n: f64) {
|
|
self.push(Val::Num(n));
|
|
}
|
|
|
|
fn pop_default(&mut self) -> Val<'a> {
|
|
self.pop().unwrap_or_default()
|
|
}
|
|
|
|
fn run_dyadic(&mut self, f: impl Fn(f64, f64) -> f64) {
|
|
let b = self.pop_default();
|
|
let a = self.pop_default();
|
|
self.push_num(f(a.to_num(), b.to_num()));
|
|
}
|
|
|
|
fn run_monadic(&mut self, f: impl Fn(f64) -> f64) {
|
|
let n = self.pop_num();
|
|
self.push_num(f(n));
|
|
}
|
|
}
|
|
|
|
fn eval<'a>(insts: &'a [Inst<'a>], stack: &mut Stack<'a>, env: &Rc<RefCell<Env<'a>>>) {
|
|
for i in insts {
|
|
match i {
|
|
Inst::Set(var) => env.borrow_mut().set(var, stack.pop_default()),
|
|
Inst::Var(var) => match *var {
|
|
"+" => stack.run_dyadic(ops::Add::add),
|
|
"-" => stack.run_dyadic(ops::Sub::sub),
|
|
"*" => stack.run_dyadic(ops::Mul::mul),
|
|
"/" => stack.run_dyadic(ops::Div::div),
|
|
"%" | "rem" => stack.run_dyadic(ops::Rem::rem),
|
|
"sin" => stack.run_monadic(f64::sin),
|
|
"cos" => stack.run_monadic(f64::cos),
|
|
"tan" => stack.run_monadic(f64::tan),
|
|
"abs" => stack.run_monadic(f64::abs),
|
|
"ceil" => stack.run_monadic(f64::ceil),
|
|
"floor" => stack.run_monadic(f64::floor),
|
|
"round" => stack.run_monadic(f64::round),
|
|
"expt" => stack.run_dyadic(f64::powf),
|
|
"=" => stack.run_dyadic(|a, b| ((a - b).abs() < 1e-10).into()),
|
|
"<" => stack.run_dyadic(|a, b| (a < b).into()),
|
|
"dup" => {
|
|
let top = stack.pop_default();
|
|
stack.push(top.clone());
|
|
stack.push(top);
|
|
}
|
|
"swap" => {
|
|
let a = stack.pop_default();
|
|
let b = stack.pop_default();
|
|
stack.push(a);
|
|
stack.push(b);
|
|
}
|
|
"print" => {
|
|
print!("{}", stack.pop_default());
|
|
}
|
|
"println" => {
|
|
println!("{}", stack.pop_default());
|
|
}
|
|
"put" => {
|
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
|
let byte = stack.pop_num() as u8;
|
|
io::stdout().write_all(&[byte]).unwrap();
|
|
}
|
|
"explode" => {
|
|
eprintln!("I ESPLODED");
|
|
for (i, val) in stack.0.iter().rev().enumerate() {
|
|
eprintln!("{}. {val}", i + 1);
|
|
}
|
|
io::stdout().flush().unwrap();
|
|
io::stderr().flush().unwrap();
|
|
process::exit(3);
|
|
}
|
|
"ite" => {
|
|
let r#else = stack.pop_default();
|
|
let then = stack.pop_default();
|
|
let test = stack.pop_default();
|
|
if test.to_num() == 0.0 {
|
|
r#else.call(stack);
|
|
} else {
|
|
then.call(stack);
|
|
}
|
|
}
|
|
_ => stack.push(
|
|
env.borrow()
|
|
.get(var)
|
|
.unwrap_or_else(|| panic!("unknown variable: {var}")),
|
|
),
|
|
},
|
|
Inst::Num(n) => stack.push(Val::Num(*n)),
|
|
Inst::Call => {
|
|
let f = stack.pop_default();
|
|
f.call(stack);
|
|
}
|
|
Inst::Block(insts) => stack.push(Val::Fun {
|
|
insts,
|
|
env: Rc::new(RefCell::new(Env::Closure {
|
|
local: HashMap::new(),
|
|
above: env.clone(),
|
|
})),
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn run(insts: &[Inst<'_>]) {
|
|
let mut stack = Stack::new();
|
|
eval(insts, &mut stack, &Rc::new(RefCell::new(Env::default())));
|
|
}
|