rudus/src/vm.rs

215 lines
6.3 KiB
Rust
Raw Normal View History

use crate::parser::*;
use crate::value::*;
use imbl::Vector;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct LudusError {
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
pub struct Ctx<'src> {
2024-11-06 22:37:57 +00:00
pub locals: Vec<(&'src str, Value<'src>)>,
2024-11-07 23:57:01 +00:00
// pub names: Vec<&'src str>,
// pub values: Vec<Value<'src>>,
}
2024-11-06 22:37:57 +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-06 22:37:57 +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
}
}
pub fn matchh<'src, 'a>(
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)
}
// todo: add splats to these match clauses
(Pattern::Tuple(x), Value::Tuple(y)) => {
if x.len() != y.len() {
return None;
};
let to = ctx.len();
for i in 0..x.len() {
if let None = matchh(&x[i].0, &y[i], ctx) {
while ctx.len() > to {
ctx.pop();
}
return None;
}
}
Some(ctx)
}
(Pattern::List(x), Value::List(y)) => {
if x.len() != y.len() {
return None;
};
let to = ctx.len();
for i in 0..x.len() {
if let None = matchh(&x[i].0, y.get(i).unwrap(), ctx) {
while ctx.len() > to {
ctx.pop();
}
return None;
}
}
Some(ctx)
}
(Pattern::Pair(_, _), _) => todo!("dictionary patterns still to do"),
(Pattern::Dict(_), _) => todo!("dictionary patterns still to do"),
_ => None,
}
}
2024-11-06 22:37:57 +00:00
pub fn eval<'src, 'a>(
ast: &Ast<'src>,
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)?)
}
Ok(Value::List(Rc::new(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 {
unreachable!()
};
Ok(val)
}
2024-11-07 23:57:01 +00:00
Ast::Let(patt, expr) => {
let val = eval(&expr.0, ctx)?;
match matchh(&patt.0, &val, ctx) {
Some(_) => Ok(val),
None => Err(LudusError {
msg: "No match".to_string(),
}),
2024-11-07 23:57:01 +00:00
}
}
Ast::Placeholder => todo!(),
2024-11-08 00:04:58 +00:00
Ast::Error => todo!(),
Ast::Dict(_) => todo!(),
Ast::Arguments(_) => todo!(),
Ast::Pair(_, _) => todo!(),
Ast::Box(_, _) => todo!(),
Ast::Synthetic(_, _, _) => todo!(),
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-08 00:04:58 +00:00
Ast::Match(_, _) => todo!(),
Ast::MatchClause(_, _) => todo!(),
Ast::Fn(_, _) => todo!(),
Ast::FnDeclaration(_) => todo!(),
Ast::FnClause(_, _) => 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 {
let _ = eval(&body.0, ctx)?;
}
Ok(Value::Nil)
}
2024-11-08 00:04:58 +00:00
Ast::Do(_) => todo!(),
Ast::Loop(_, _) => todo!(),
Ast::Recur(_) => todo!(),
}
}