evaluator
This commit is contained in:
parent
40fdac182f
commit
c4c6ba80c6
2 changed files with 162 additions and 2 deletions
157
src/eval.rs
Normal file
157
src/eval.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
use core::fmt;
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
collections::{HashMap, hash_map::Entry},
|
||||||
|
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 std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Val::Num(n) => write!(f, "{n}"),
|
||||||
|
Val::Fun { insts, env } => write!(f, "{} {env:?}", Inst::Block(insts.to_vec())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Val<'_> {
|
||||||
|
fn to_num(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Val::Num(n) => *n,
|
||||||
|
Val::Fun { insts: _, env: _ } => 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, stack: &mut Vec<Self>) {
|
||||||
|
match self {
|
||||||
|
Val::Num(_) => stack.push(self.clone()),
|
||||||
|
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 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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval<'a>(insts: &'a [Inst<'a>], stack: &mut Vec<Val<'a>>, env: &Rc<RefCell<Env<'a>>>) {
|
||||||
|
for i in insts {
|
||||||
|
match i {
|
||||||
|
Inst::Set(var) => env.borrow_mut().set(var, stack.pop().unwrap_or_default()),
|
||||||
|
Inst::Var(var) => match *var {
|
||||||
|
"+" => {
|
||||||
|
let a = stack.pop().unwrap_or_default();
|
||||||
|
let b = stack.pop().unwrap_or_default();
|
||||||
|
stack.push(Val::Num(a.to_num() + b.to_num()));
|
||||||
|
}
|
||||||
|
"print" => {
|
||||||
|
println!("{}", stack.pop().unwrap_or_default());
|
||||||
|
}
|
||||||
|
_ => stack.push(env.borrow().get(var).expect("idk this var")),
|
||||||
|
},
|
||||||
|
Inst::Num(n) => stack.push(Val::Num(*n)),
|
||||||
|
Inst::Call => {
|
||||||
|
let f = stack.pop().unwrap_or_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 = Vec::new();
|
||||||
|
eval(insts, &mut stack, &Rc::new(RefCell::new(Env::default())));
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{inst::Inst, parse::parse};
|
use crate::{eval::run, inst::Inst, parse::parse};
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
|
|
||||||
|
mod eval;
|
||||||
mod inst;
|
mod inst;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
||||||
|
|
@ -9,5 +10,7 @@ fn main() {
|
||||||
let _ = io::stdin().read_to_string(&mut src).unwrap();
|
let _ = io::stdin().read_to_string(&mut src).unwrap();
|
||||||
|
|
||||||
let insts = parse(&src);
|
let insts = parse(&src);
|
||||||
println!("{}!\n{insts:#?}", Inst::Block(insts.clone()));
|
eprintln!("{}!\n{insts:#?}", Inst::Block(insts.clone()));
|
||||||
|
|
||||||
|
run(&insts);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue