2024-11-11 22:50:58 +00:00
|
|
|
use crate::base::*;
|
2024-11-01 03:53:48 +00:00
|
|
|
use crate::parser::*;
|
|
|
|
use crate::value::*;
|
2024-11-11 01:12:19 +00:00
|
|
|
use imbl::HashMap;
|
2024-11-01 03:53:48 +00:00
|
|
|
use imbl::Vector;
|
2024-11-11 01:12:19 +00:00
|
|
|
use std::cell::RefCell;
|
2024-11-01 03:53:48 +00:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct LudusError {
|
2024-11-11 22:50:58 +00:00
|
|
|
pub 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
|
2024-12-05 19:02:41 +00:00
|
|
|
|
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-01 03:53:48 +00:00
|
|
|
|
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-01 03:53:48 +00:00
|
|
|
|
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-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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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),
|
2024-12-04 20:03:09 +00:00
|
|
|
(Pattern::String(x), Value::InternedString(y)) => match_eq(x, y, ctx),
|
2024-12-08 03:29:10 +00:00
|
|
|
(Pattern::String(x), Value::AllocatedString(y)) => match_eq(&x.to_string(), &**y, ctx),
|
|
|
|
(Pattern::Interpolated(_, StringMatcher(matcher)), Value::InternedString(y)) => {
|
|
|
|
match matcher(y.to_string()) {
|
|
|
|
Some(matches) => {
|
|
|
|
let mut matches = matches
|
|
|
|
.iter()
|
|
|
|
.map(|(word, string)| {
|
|
|
|
(word, Value::AllocatedString(Rc::new(string.clone())))
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
ctx.append(&mut matches);
|
|
|
|
Some(ctx)
|
|
|
|
}
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
}
|
2024-11-07 23:57:01 +00:00
|
|
|
(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) => {
|
2024-12-06 18:21:25 +00:00
|
|
|
let ludus_type = r#type(value);
|
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-11 22:50:58 +00:00
|
|
|
}
|
2024-11-07 23:57:01 +00:00
|
|
|
// todo: add splats to these match clauses
|
|
|
|
(Pattern::Tuple(x), Value::Tuple(y)) => {
|
2024-11-22 00:54:50 +00:00
|
|
|
let has_splat = x
|
|
|
|
.iter()
|
|
|
|
.any(|patt| matches!(patt, (Pattern::Splattern(_), _)));
|
2024-11-18 18:25:54 +00:00
|
|
|
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);
|
2024-11-22 00:54:50 +00:00
|
|
|
match_pattern(&patt.0, &list, ctx);
|
|
|
|
} else if match_pattern(&x[i].0, &y[i], ctx).is_none() {
|
|
|
|
while ctx.len() > to {
|
|
|
|
ctx.pop();
|
2024-11-07 23:57:01 +00:00
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
return None;
|
2024-11-07 23:57:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(ctx)
|
|
|
|
}
|
|
|
|
(Pattern::List(x), Value::List(y)) => {
|
2024-11-22 00:54:50 +00:00
|
|
|
let has_splat = x
|
|
|
|
.iter()
|
|
|
|
.any(|patt| matches!(patt, (Pattern::Splattern(_), _)));
|
2024-11-19 01:01:27 +00:00
|
|
|
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();
|
2024-11-22 00:54:50 +00:00
|
|
|
for (i, (patt, _)) in x.iter().enumerate() {
|
|
|
|
if let Pattern::Splattern(patt) = &patt {
|
2024-11-19 01:01:27 +00:00
|
|
|
let list = Value::List(y.skip(i));
|
2024-11-22 00:54:50 +00:00
|
|
|
match_pattern(&patt.0, &list, ctx);
|
|
|
|
} else if match_pattern(patt, y.get(i).unwrap(), ctx).is_none() {
|
|
|
|
while ctx.len() > to {
|
|
|
|
ctx.pop();
|
2024-11-07 23:57:01 +00:00
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
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
|
2024-11-11 22:50:58 +00:00
|
|
|
(Pattern::Dict(x), Value::Dict(y)) => {
|
2024-11-22 00:54:50 +00:00
|
|
|
let has_splat = x
|
|
|
|
.iter()
|
|
|
|
.any(|patt| matches!(patt, (Pattern::Splattern(_), _)));
|
2024-11-21 01:10:17 +00:00
|
|
|
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
|
2024-11-11 22:50:58 +00:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
let to = ctx.len();
|
2024-11-21 01:10:17 +00:00
|
|
|
let mut matched = vec![];
|
2024-11-22 00:54:50 +00:00
|
|
|
for (pattern, _) in x {
|
2024-11-21 01:10:17 +00:00
|
|
|
match pattern {
|
|
|
|
Pattern::Pair(key, patt) => {
|
|
|
|
if let Some(val) = y.get(key) {
|
2024-11-22 00:54:50 +00:00
|
|
|
if match_pattern(&patt.0, val, ctx).is_none() {
|
2024-11-21 01:10:17 +00:00
|
|
|
while ctx.len() > to {
|
|
|
|
ctx.pop();
|
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
return None;
|
2024-11-21 01:10:17 +00:00
|
|
|
} else {
|
|
|
|
matched.push(key);
|
|
|
|
}
|
|
|
|
} else {
|
2024-11-11 22:50:58 +00:00
|
|
|
return None;
|
2024-11-21 01:10:17 +00:00
|
|
|
};
|
2024-11-11 22:50:58 +00:00
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
Pattern::Splattern(pattern) => match pattern.0 {
|
2024-11-21 01:10:17 +00:00
|
|
|
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);
|
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
ctx.push((w, Value::Dict(unmatched)));
|
2024-11-21 01:10:17 +00:00
|
|
|
}
|
|
|
|
Pattern::Placeholder => (),
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2024-11-11 22:50:58 +00:00
|
|
|
}
|
|
|
|
Some(ctx)
|
|
|
|
}
|
2024-11-07 23:57:01 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-22 00:54:50 +00:00
|
|
|
pub fn match_clauses<'src>(
|
2024-11-11 01:12:19 +00:00
|
|
|
value: &Value<'src>,
|
|
|
|
clauses: &'src Vec<MatchClause<'src>>,
|
2024-11-22 00:54:50 +00:00
|
|
|
ctx: &mut Vec<(&'src str, Value<'src>)>,
|
2024-11-11 01:12:19 +00:00
|
|
|
) -> 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, _)) => {
|
2024-11-22 00:54:50 +00:00
|
|
|
let guard_res = eval(ast, ctx);
|
2024-11-21 01:10:17 +00:00
|
|
|
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(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-11-22 00:54:50 +00:00
|
|
|
pub fn apply<'src>(
|
2024-11-11 01:12:19 +00:00
|
|
|
callee: Value<'src>,
|
|
|
|
caller: Value<'src>,
|
2024-11-22 00:54:50 +00:00
|
|
|
ctx: &mut Vec<(&'src str, Value<'src>)>,
|
2024-11-11 01:12:19 +00:00
|
|
|
) -> 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)
|
|
|
|
}
|
|
|
|
}
|
2024-11-11 22:50:58 +00:00
|
|
|
(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 {
|
2024-12-06 19:08:34 +00:00
|
|
|
Base::Nullary(f) => {
|
|
|
|
if args.len() != 0 {
|
|
|
|
Err(LudusError {
|
|
|
|
msg: "wrong arity: expected 0 arguments".to_string(),
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(f())
|
|
|
|
}
|
|
|
|
}
|
2024-12-05 01:19:41 +00:00
|
|
|
Base::Unary(f) => {
|
2024-11-15 02:30:42 +00:00
|
|
|
if args.len() != 1 {
|
|
|
|
Err(LudusError {
|
|
|
|
msg: "wrong arity: expected 1 argument".to_string(),
|
|
|
|
})
|
|
|
|
} else {
|
2024-12-05 01:19:41 +00:00
|
|
|
Ok(f(&args[0]))
|
2024-11-15 02:30:42 +00:00
|
|
|
}
|
|
|
|
}
|
2024-12-05 01:19:41 +00:00
|
|
|
Base::Binary(r#fn) => {
|
2024-11-15 02:30:42 +00:00
|
|
|
if args.len() != 2 {
|
|
|
|
Err(LudusError {
|
|
|
|
msg: "wrong arity: expected 2 arguments".to_string(),
|
|
|
|
})
|
|
|
|
} else {
|
2024-12-05 01:19:41 +00:00
|
|
|
Ok(r#fn(&args[0], &args[1]))
|
2024-11-15 02:30:42 +00:00
|
|
|
}
|
2024-12-05 01:19:41 +00:00
|
|
|
}
|
|
|
|
Base::Ternary(f) => {
|
|
|
|
if args.len() != 3 {
|
|
|
|
Err(LudusError {
|
|
|
|
msg: "wrong arity: expected 3 arguments".to_string(),
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(f(&args[0], &args[1], &args[2]))
|
|
|
|
}
|
|
|
|
}
|
2024-11-15 02:30:42 +00:00
|
|
|
},
|
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> {
|
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)),
|
2024-12-04 20:03:09 +00:00
|
|
|
Ast::String(s) => Ok(Value::InternedString(s)),
|
2024-12-05 00:07:03 +00:00
|
|
|
Ast::Interpolated(parts) => {
|
|
|
|
let mut interpolated = String::new();
|
|
|
|
for part in parts {
|
|
|
|
match &part.0 {
|
|
|
|
StringPart::Data(s) => interpolated.push_str(s.as_str()),
|
|
|
|
StringPart::Word(w) => {
|
|
|
|
let val = if let Some((_, value)) =
|
|
|
|
ctx.iter().rev().find(|(name, _)| w == name)
|
|
|
|
{
|
|
|
|
value.clone()
|
|
|
|
} else {
|
|
|
|
return Err(LudusError {
|
|
|
|
msg: format!("unbound name {w}"),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
interpolated.push_str(val.interpolate().as_str())
|
|
|
|
}
|
|
|
|
StringPart::Inline(_) => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Value::AllocatedString(Rc::new(interpolated)))
|
|
|
|
}
|
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-21 21:41:46 +00:00
|
|
|
if let Ast::Splat(_) = member.0 {
|
|
|
|
let to_splat = eval(&member.0, ctx)?;
|
|
|
|
match to_splat {
|
|
|
|
Value::List(list) => vect.append(list),
|
|
|
|
_ => {
|
|
|
|
return Err(LudusError {
|
|
|
|
msg: "only lists may be splatted into lists".to_string(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vect.push_back(eval(&member.0, ctx)?)
|
|
|
|
}
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
2024-11-09 19:10:08 +00:00
|
|
|
Ok(Value::List(vect))
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
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-21 21:41:46 +00:00
|
|
|
Ast::Word(w) | Ast::Splat(w) => {
|
2024-11-06 22:37:57 +00:00
|
|
|
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),
|
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-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() {
|
2024-11-22 00:54:50 +00:00
|
|
|
let arg = eval(arg, ctx)?;
|
2024-11-11 01:12:19 +00:00
|
|
|
args.push(arg);
|
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
if args.iter().any(|arg| matches!(arg, Value::Placeholder)) {
|
2024-11-11 01:12:19 +00:00
|
|
|
Ok(Value::Args(Rc::new(args)))
|
|
|
|
} else {
|
|
|
|
Ok(Value::Tuple(Rc::new(args)))
|
|
|
|
}
|
|
|
|
}
|
2024-11-21 21:41:46 +00:00
|
|
|
Ast::Dict(terms) => {
|
2024-11-11 01:12:19 +00:00
|
|
|
let mut dict = HashMap::new();
|
2024-11-21 21:41:46 +00:00
|
|
|
for term in terms {
|
|
|
|
let (term, _) = term;
|
|
|
|
match term {
|
|
|
|
Ast::Pair(key, value) => {
|
|
|
|
let value = eval(&value.0, ctx)?;
|
|
|
|
dict.insert(*key, value);
|
|
|
|
}
|
|
|
|
Ast::Splat(_) => {
|
|
|
|
let resolved = eval(term, ctx)?;
|
|
|
|
let Value::Dict(to_splat) = resolved else {
|
|
|
|
return Err(LudusError {
|
|
|
|
msg: "cannot splat non-dict into dict".to_string(),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
dict = to_splat.union(dict);
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2024-11-11 01:12:19 +00:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
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);
|
|
|
|
};
|
|
|
|
}
|
2024-11-22 00:54:50 +00:00
|
|
|
Err(LudusError {
|
2024-11-08 01:41:38 +00:00
|
|
|
msg: "no match".to_string(),
|
2024-11-22 00:54:50 +00:00
|
|
|
})
|
2024-11-08 01:41:38 +00:00
|
|
|
}
|
2024-11-11 01:12:19 +00:00
|
|
|
Ast::Match(value, clauses) => {
|
|
|
|
let value = eval(&value.0, ctx)?;
|
|
|
|
match_clauses(&value, clauses, ctx)
|
|
|
|
}
|
2024-12-04 20:03:09 +00:00
|
|
|
Ast::Fn(name, clauses, doc) => {
|
2024-11-11 01:12:19 +00:00
|
|
|
let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> {
|
|
|
|
name,
|
|
|
|
body: clauses,
|
2024-12-04 20:03:09 +00:00
|
|
|
doc,
|
2024-11-11 01:12:19 +00:00
|
|
|
}));
|
|
|
|
ctx.push((name, the_fn.clone()));
|
|
|
|
Ok(the_fn)
|
|
|
|
}
|
2024-11-11 22:50:58 +00:00
|
|
|
Ast::FnDeclaration(_name) => 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) => {
|
2024-11-21 22:10:50 +00:00
|
|
|
let times_num = match eval(×.0, ctx) {
|
|
|
|
Ok(Value::Number(n)) => n as usize,
|
2024-11-08 01:41:38 +00:00
|
|
|
_ => {
|
|
|
|
return Err(LudusError {
|
|
|
|
msg: "repeat may only take numbers".to_string(),
|
|
|
|
})
|
|
|
|
}
|
2024-11-21 22:10:50 +00:00
|
|
|
};
|
2024-11-08 01:41:38 +00:00
|
|
|
for _ in 0..times_num {
|
2024-11-11 01:12:19 +00:00
|
|
|
eval(&body.0, ctx)?;
|
2024-11-08 01:41:38 +00:00
|
|
|
}
|
|
|
|
Ok(Value::Nil)
|
|
|
|
}
|
2024-11-11 22:50:58 +00:00
|
|
|
Ast::Do(terms) => {
|
|
|
|
let mut result = eval(&terms[0].0, ctx)?;
|
2024-11-22 00:54:50 +00:00
|
|
|
for (term, _) in terms.iter().skip(1) {
|
|
|
|
let next = eval(term, ctx)?;
|
2024-11-11 22:50:58 +00:00
|
|
|
let arg = Value::Tuple(Rc::new(vec![result]));
|
|
|
|
result = apply(next, arg, ctx)?;
|
|
|
|
}
|
|
|
|
Ok(result)
|
2024-11-21 21:41:46 +00:00
|
|
|
}
|
|
|
|
Ast::Pair(..) => {
|
|
|
|
unreachable!()
|
|
|
|
}
|
2024-11-21 21:57:52 +00:00
|
|
|
Ast::Loop(init, clauses) => {
|
|
|
|
let mut args = eval(&init.0, ctx)?;
|
|
|
|
loop {
|
|
|
|
let result = match_clauses(&args, clauses, ctx)?;
|
|
|
|
if let Value::Recur(recur_args) = result {
|
|
|
|
args = Value::Tuple(Rc::new(recur_args));
|
|
|
|
} else {
|
|
|
|
return Ok(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ast::Recur(args) => {
|
|
|
|
let mut vect = Vec::new();
|
|
|
|
for arg in args {
|
|
|
|
vect.push(eval(&arg.0, ctx)?);
|
|
|
|
}
|
|
|
|
Ok(Value::Recur(vect))
|
|
|
|
}
|
2024-11-01 03:53:48 +00:00
|
|
|
}
|
|
|
|
}
|