rudus/src/vm.rs

458 lines
15 KiB
Rust
Raw Normal View History

use crate::base::*;
use crate::parser::*;
use crate::value::*;
2024-11-11 01:12:19 +00:00
use imbl::HashMap;
use imbl::Vector;
2024-11-11 01:12:19 +00:00
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct LudusError {
pub msg: String,
}
// oy
// lifetimes are a mess
// I need 'src kind of everywhere
// But (maybe) using 'src in eval
// for ctx
// means I can't borrow it mutably
// I guess the question is how to get
// the branches for Ast::Block and Ast::If
// to work with a mutable borrow of ctx
2024-11-09 19:10:08 +00:00
// pub struct Ctx<'src> {
// pub locals: Vec<(&'src str, Value<'src>)>,
// // pub names: Vec<&'src str>,
// // pub values: Vec<Value<'src>>,
// }
2024-11-09 19:10:08 +00:00
// impl<'src> Ctx<'src> {
// pub fn resolve(&self, name: &'src str) -> Value {
// if let Some((_, val)) = self.locals.iter().rev().find(|(bound, _)| *bound == name) {
// val.clone()
// } else {
// unreachable!()
// }
// }
2024-11-09 19:10:08 +00:00
// pub fn store(&mut self, name: &'src str, value: Value<'src>) {
// self.locals.push((name, value));
// }
// }
2024-11-07 23:57:01 +00:00
pub fn match_eq<T, U>(x: T, y: T, z: U) -> Option<U>
where
T: PartialEq,
{
if x == y {
Some(z)
} else {
None
}
}
2024-11-11 01:12:19 +00:00
pub fn match_pattern<'src, 'a>(
2024-11-07 23:57:01 +00:00
patt: &Pattern<'src>,
val: &Value<'src>,
ctx: &'a mut Vec<(&'src str, Value<'src>)>,
) -> Option<&'a mut Vec<(&'src str, Value<'src>)>> {
match (patt, val) {
(Pattern::Nil, Value::Nil) => Some(ctx),
(Pattern::Placeholder, _) => Some(ctx),
(Pattern::Number(x), Value::Number(y)) => match_eq(x, y, ctx),
(Pattern::Boolean(x), Value::Boolean(y)) => match_eq(x, y, ctx),
(Pattern::Keyword(x), Value::Keyword(y)) => match_eq(x, y, ctx),
(Pattern::String(x), Value::String(y)) => match_eq(x, y, ctx),
(Pattern::Word(w), val) => {
ctx.push((w, val.clone()));
Some(ctx)
}
2024-11-15 02:55:19 +00:00
(Pattern::As(word, type_str), value) => {
let ludus_type = value.ludus_type();
2024-11-15 02:55:19 +00:00
let type_kw = Value::Keyword(type_str);
if type_kw == ludus_type {
ctx.push((word, value.clone()));
Some(ctx)
} else {
None
}
}
2024-11-07 23:57:01 +00:00
// todo: add splats to these match clauses
(Pattern::Tuple(x), Value::Tuple(y)) => {
2024-11-18 18:25:54 +00:00
let has_splat = x.iter().any(|patt| {
if let (Pattern::Splattern(_), _) = patt {
true
} else {
false
}
});
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
2024-11-07 23:57:01 +00:00
return None;
};
let to = ctx.len();
for i in 0..x.len() {
2024-11-18 18:25:54 +00:00
if let Pattern::Splattern(patt) = &x[i].0 {
let mut list = Vector::new();
for i in i..y.len() {
list.push_back(y[i].clone())
}
let list = Value::List(list);
match_pattern(&(*patt).0, &list, ctx);
} else {
if let None = match_pattern(&x[i].0, &y[i], ctx) {
while ctx.len() > to {
ctx.pop();
}
return None;
2024-11-07 23:57:01 +00:00
}
}
}
Some(ctx)
}
(Pattern::List(x), Value::List(y)) => {
2024-11-19 01:01:27 +00:00
let has_splat = x.iter().any(|patt| {
if let (Pattern::Splattern(_), _) = patt {
true
} else {
false
}
});
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
2024-11-07 23:57:01 +00:00
return None;
};
let to = ctx.len();
for i in 0..x.len() {
2024-11-19 01:01:27 +00:00
if let Pattern::Splattern(patt) = &x[i].0 {
let list = Value::List(y.skip(i));
match_pattern(&(*patt).0, &list, ctx);
} else {
if let None = match_pattern(&x[i].0, &y.get(i).unwrap(), ctx) {
while ctx.len() > to {
ctx.pop();
}
return None;
2024-11-07 23:57:01 +00:00
}
}
}
Some(ctx)
}
2024-11-21 01:10:17 +00:00
// TODO: optimize this on several levels
// - [ ] opportunistic mutation
// - [ ] get rid of all the pointer indirection in word splats
(Pattern::Dict(x), Value::Dict(y)) => {
2024-11-21 01:10:17 +00:00
let has_splat = x.iter().any(|patt| {
if let (Pattern::Splattern(_), _) = patt {
true
} else {
false
}
});
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
return None;
};
let to = ctx.len();
2024-11-21 01:10:17 +00:00
let mut matched = vec![];
for i in 0..x.len() {
let (pattern, _) = &x[i];
match pattern {
Pattern::Pair(key, patt) => {
if let Some(val) = y.get(key) {
if let None = match_pattern(&patt.0, val, ctx) {
while ctx.len() > to {
ctx.pop();
return None;
}
} else {
matched.push(key);
}
} else {
return None;
2024-11-21 01:10:17 +00:00
};
}
2024-11-21 01:10:17 +00:00
Pattern::Splattern(pattern) => match &(*pattern).0 {
Pattern::Word(w) => {
// TODO: find a way to take ownership
// this will ALWAYS make structural changes, because of this clone
// we want opportunistic mutation if possible
let mut unmatched = y.clone();
for key in matched.iter() {
unmatched.remove(*key);
}
ctx.push((*w, Value::Dict(unmatched)));
}
Pattern::Placeholder => (),
_ => unreachable!(),
},
_ => unreachable!(),
}
}
Some(ctx)
}
2024-11-07 23:57:01 +00:00
_ => None,
}
}
2024-11-11 01:12:19 +00:00
pub fn match_clauses<'src, 'a>(
value: &Value<'src>,
clauses: &'src Vec<MatchClause<'src>>,
ctx: &'a mut Vec<(&'src str, Value<'src>)>,
) -> Result<Value<'src>, LudusError> {
let to = ctx.len();
2024-11-21 01:10:17 +00:00
for MatchClause { patt, body, guard } in clauses.iter() {
2024-11-11 01:12:19 +00:00
if let Some(ctx) = match_pattern(&patt.0, value, ctx) {
2024-11-21 01:10:17 +00:00
let pass_guard = match guard {
None => true,
Some((ast, _)) => {
let guard_res = eval(&ast, ctx);
match &guard_res {
Err(_) => return guard_res,
Ok(val) => val.bool(),
}
}
};
if !pass_guard {
while ctx.len() > to {
ctx.pop();
}
continue;
}
2024-11-11 01:12:19 +00:00
let res = eval(&body.0, ctx);
while ctx.len() > to {
ctx.pop();
}
return res;
}
}
Err(LudusError {
msg: "no match".to_string(),
})
}
pub fn apply<'src, 'a>(
callee: Value<'src>,
caller: Value<'src>,
ctx: &'a mut Vec<(&'src str, Value<'src>)>,
) -> Result<Value<'src>, LudusError> {
match (callee, caller) {
(Value::Keyword(kw), Value::Dict(dict)) => {
if let Some(val) = dict.get(kw) {
Ok(val.clone())
} else {
Ok(Value::Nil)
}
}
(Value::Dict(dict), Value::Keyword(kw)) => {
if let Some(val) = dict.get(kw) {
Ok(val.clone())
} else {
Ok(Value::Nil)
}
}
2024-11-11 01:12:19 +00:00
(Value::Fn(f), Value::Tuple(args)) => {
let args = Value::Tuple(args);
match_clauses(&args, f.body, ctx)
}
(Value::Fn(_f), Value::Args(_args)) => todo!(),
(_, Value::Keyword(_)) => Ok(Value::Nil),
(_, Value::Args(_)) => Err(LudusError {
msg: "you may only call a function".to_string(),
}),
2024-11-15 02:30:42 +00:00
(Value::Base(f), Value::Tuple(args)) => match f {
Base::Unary(_name, f) => {
if args.len() != 1 {
Err(LudusError {
msg: "wrong arity: expected 1 argument".to_string(),
})
} else {
f(&args[0])
}
}
Base::Binary(_name, r#fn) => {
if args.len() != 2 {
Err(LudusError {
msg: "wrong arity: expected 2 arguments".to_string(),
})
} else {
r#fn(&args[0], &args[1])
}
}
Base::Ternary(_name, f) => {
if args.len() != 3 {
Err(LudusError {
msg: "wrong arity: expected 3 arguments".to_string(),
})
} else {
f(&args[0], &args[1], &args[2])
}
}
},
2024-11-11 01:12:19 +00:00
_ => unreachable!(),
}
}
2024-11-06 22:37:57 +00:00
pub fn eval<'src, 'a>(
2024-11-11 01:12:19 +00:00
ast: &'src Ast<'src>,
2024-11-06 22:37:57 +00:00
ctx: &'a mut Vec<(&'src str, Value<'src>)>,
) -> Result<Value<'src>, LudusError> {
match ast {
Ast::Nil => Ok(Value::Nil),
2024-11-06 22:37:57 +00:00
Ast::Boolean(b) => Ok(Value::Boolean(*b)),
Ast::Number(n) => Ok(Value::Number(*n)),
2024-11-06 23:28:29 +00:00
Ast::Keyword(k) => Ok(Value::Keyword(k)),
Ast::String(s) => Ok(Value::String(s)),
Ast::Block(exprs) => {
2024-11-07 23:57:01 +00:00
let to = ctx.len();
let mut result = Value::Nil;
for (expr, _) in exprs {
result = eval(expr, ctx)?;
}
2024-11-07 23:57:01 +00:00
while ctx.len() > to {
ctx.pop();
}
Ok(result)
}
Ast::If(cond, if_true, if_false) => {
2024-11-06 22:37:57 +00:00
let truthy = eval(&cond.0, ctx)?.bool();
if truthy {
2024-11-06 23:28:29 +00:00
eval(&if_true.0, ctx)
} else {
2024-11-06 23:28:29 +00:00
eval(&if_false.0, ctx)
}
}
Ast::List(members) => {
let mut vect = Vector::new();
for member in members {
2024-11-06 22:37:57 +00:00
vect.push_back(eval(&member.0, ctx)?)
}
2024-11-09 19:10:08 +00:00
Ok(Value::List(vect))
}
Ast::Tuple(members) => {
let mut vect = Vec::new();
for member in members {
2024-11-06 22:37:57 +00:00
vect.push(eval(&member.0, ctx)?);
}
Ok(Value::Tuple(Rc::new(vect)))
}
2024-11-06 22:37:57 +00:00
Ast::Word(w) => {
let val = if let Some((_, value)) = ctx.iter().rev().find(|(name, _)| w == name) {
value.clone()
} else {
2024-11-18 18:25:54 +00:00
return Err(LudusError {
msg: format!("unbound name {w}"),
});
2024-11-06 22:37:57 +00:00
};
Ok(val)
}
2024-11-07 23:57:01 +00:00
Ast::Let(patt, expr) => {
let val = eval(&expr.0, ctx)?;
2024-11-11 01:12:19 +00:00
match match_pattern(&patt.0, &val, ctx) {
2024-11-07 23:57:01 +00:00
Some(_) => Ok(val),
None => Err(LudusError {
msg: "No match".to_string(),
}),
2024-11-07 23:57:01 +00:00
}
}
2024-11-11 01:12:19 +00:00
Ast::Placeholder => Ok(Value::Placeholder),
Ast::Error => unreachable!(),
Ast::Arguments(a) => {
let mut args = vec![];
for (arg, _) in a.iter() {
let arg = eval(&arg, ctx)?;
args.push(arg);
}
if args.iter().any(|arg| {
if let Value::Placeholder = arg {
true
} else {
false
}
}) {
Ok(Value::Args(Rc::new(args)))
} else {
Ok(Value::Tuple(Rc::new(args)))
}
}
Ast::Dict(pairs) => {
let mut dict = HashMap::new();
for Pair { key, value } in pairs {
let value = eval(&value.0, ctx)?;
dict.insert(*key, value);
}
Ok(Value::Dict(dict))
}
Ast::Box(name, expr) => {
let val = eval(&expr.0, ctx)?;
let boxed = Value::Box(name, Rc::new(RefCell::new(val)));
ctx.push((name, boxed.clone()));
Ok(boxed)
}
Ast::Synthetic(root, first, rest) => {
let root = eval(&root.0, ctx)?;
let first = eval(&first.0, ctx)?;
let mut curr = apply(root, first, ctx)?;
for term in rest.iter() {
let next = eval(&term.0, ctx)?;
curr = apply(curr, next, ctx)?;
}
Ok(curr)
}
Ast::When(clauses) => {
for clause in clauses.iter() {
let WhenClause { cond, body } = &clause.0;
if eval(&cond.0, ctx)?.bool() {
return eval(&body.0, ctx);
};
}
return Err(LudusError {
msg: "no match".to_string(),
});
}
2024-11-11 01:12:19 +00:00
Ast::Match(value, clauses) => {
let value = eval(&value.0, ctx)?;
match_clauses(&value, clauses, ctx)
}
Ast::Fn(name, clauses) => {
let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> {
name,
body: clauses,
}));
ctx.push((name, the_fn.clone()));
Ok(the_fn)
}
Ast::FnDeclaration(_name) => todo!(),
Ast::Panic(msg) => {
let msg = eval(&msg.0, ctx)?;
Err(LudusError {
msg: msg.to_string(),
})
}
Ast::Repeat(times, body) => {
let mut times_num = 0;
match eval(&times.0, ctx) {
Ok(Value::Number(n)) => {
times_num = n as usize;
}
_ => {
return Err(LudusError {
msg: "repeat may only take numbers".to_string(),
})
}
}
for _ in 0..times_num {
2024-11-11 01:12:19 +00:00
eval(&body.0, ctx)?;
}
Ok(Value::Nil)
}
Ast::Do(terms) => {
let mut result = eval(&terms[0].0, ctx)?;
for i in 1..terms.len() {
let next = eval(&terms[i].0, ctx)?;
let arg = Value::Tuple(Rc::new(vec![result]));
result = apply(next, arg, ctx)?;
}
Ok(result)
} // Ast::Loop(_, _) => todo!(),
// Ast::Recur(_) => todo!(),
}
}