2024-11-01 03:53:48 +00:00
|
|
|
use crate::parser::*;
|
|
|
|
use crate::value::*;
|
|
|
|
use imbl::Vector;
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct LudusError {
|
2024-11-08 01:41:38 +00:00
|
|
|
msg: String,
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
|
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-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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> {
|
2024-11-01 03:53:48 +00:00
|
|
|
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)),
|
2024-11-01 03:53:48 +00:00
|
|
|
Ast::Block(exprs) => {
|
2024-11-07 23:57:01 +00:00
|
|
|
let to = ctx.len();
|
2024-11-01 03:53:48 +00:00
|
|
|
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();
|
|
|
|
}
|
2024-11-01 03:53:48 +00:00
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
Ast::If(cond, if_true, if_false) => {
|
2024-11-06 22:37:57 +00:00
|
|
|
let truthy = eval(&cond.0, ctx)?.bool();
|
2024-11-01 03:53:48 +00:00
|
|
|
if truthy {
|
2024-11-06 23:28:29 +00:00
|
|
|
eval(&if_true.0, ctx)
|
2024-11-01 03:53:48 +00:00
|
|
|
} else {
|
2024-11-06 23:28:29 +00:00
|
|
|
eval(&if_false.0, ctx)
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
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)?);
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
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),
|
2024-11-08 01:41:38 +00:00
|
|
|
None => Err(LudusError {
|
|
|
|
msg: "No match".to_string(),
|
|
|
|
}),
|
2024-11-07 23:57:01 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-08 00:05:24 +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!(),
|
2024-11-08 01:41:38 +00:00
|
|
|
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!(),
|
2024-11-08 01:41:38 +00:00
|
|
|
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(×.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!(),
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
}
|