fix blasted merge conflicts
This commit is contained in:
parent
e86f077247
commit
97547b1f7f
|
@ -1,8 +1,6 @@
|
|||
// use crate::process::{LErr, Trace};
|
||||
use crate::validator::VErr;
|
||||
use crate::value::Value;
|
||||
use ariadne::{sources, Color, Label, Report, ReportKind};
|
||||
use std::collections::HashSet;
|
||||
|
||||
// pub fn report_panic(err: LErr) {
|
||||
// let mut srcs = HashSet::new();
|
||||
|
@ -32,7 +30,6 @@ use std::collections::HashSet;
|
|||
// stack.push(label);
|
||||
// srcs.insert((*input, *src));
|
||||
// }
|
||||
|
||||
// Report::build(ReportKind::Error, (err.input, err.span.into_range()))
|
||||
// .with_message(format!("Ludus panicked! {}", err.msg))
|
||||
// .with_label(Label::new((err.input, err.span.into_range())).with_color(Color::Red))
|
||||
|
|
632
src/process.rs
632
src/process.rs
|
@ -1,632 +0,0 @@
|
|||
use crate::base::*;
|
||||
use crate::parser::*;
|
||||
use crate::spans::*;
|
||||
use crate::validator::FnInfo;
|
||||
use crate::value::Value;
|
||||
use chumsky::prelude::SimpleSpan;
|
||||
use imbl::HashMap;
|
||||
use imbl::Vector;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LErr<'src> {
|
||||
pub input: &'static str,
|
||||
pub src: &'static str,
|
||||
pub msg: String,
|
||||
pub span: SimpleSpan,
|
||||
pub trace: Vec<Trace<'src>>,
|
||||
pub extra: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Trace<'src> {
|
||||
pub callee: Spanned<Ast>,
|
||||
pub caller: Spanned<Ast>,
|
||||
pub function: Value<'src>,
|
||||
pub arguments: Value<'src>,
|
||||
pub input: &'static str,
|
||||
pub src: &'static str,
|
||||
}
|
||||
|
||||
impl<'src> LErr<'src> {
|
||||
pub fn new(
|
||||
msg: String,
|
||||
span: SimpleSpan,
|
||||
input: &'static str,
|
||||
src: &'static str,
|
||||
) -> LErr<'src> {
|
||||
LErr {
|
||||
msg,
|
||||
span,
|
||||
input,
|
||||
src,
|
||||
trace: vec![],
|
||||
extra: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type LResult<'src> = Result<Value<'src>, LErr<'src>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Process<'src> {
|
||||
pub input: &'static str,
|
||||
pub src: &'static str,
|
||||
pub locals: Vec<(String, Value<'src>)>,
|
||||
pub prelude: Vec<(String, Value<'src>)>,
|
||||
pub ast: &'src Ast,
|
||||
pub span: SimpleSpan,
|
||||
pub fn_info: std::collections::HashMap<*const Ast, FnInfo>,
|
||||
}
|
||||
|
||||
impl<'src> Process<'src> {
|
||||
pub fn resolve(&self, word: &String) -> LResult<'src> {
|
||||
let resolved_local = self.locals.iter().rev().find(|(name, _)| word == name);
|
||||
|
||||
match resolved_local {
|
||||
Some((_, value)) => Ok(value.clone()),
|
||||
None => {
|
||||
let resolved_prelude = self.prelude.iter().rev().find(|(name, _)| word == name);
|
||||
match resolved_prelude {
|
||||
Some((_, value)) => Ok(value.clone()),
|
||||
None => Err(LErr::new(
|
||||
format!("unbound name `{word}`"),
|
||||
self.span,
|
||||
self.input,
|
||||
self.src,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn panic(&self, msg: String) -> LResult<'src> {
|
||||
Err(LErr::new(msg, self.span, self.input, self.src))
|
||||
}
|
||||
|
||||
pub fn bind(&mut self, word: String, value: &Value<'src>) {
|
||||
self.locals.push((word, value.clone()));
|
||||
}
|
||||
|
||||
pub fn match_eq<T>(&self, x: T, y: T) -> Option<&Process<'src>>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
if x == y {
|
||||
Some(self)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_pattern(&mut self, patt: &Ast, val: &Value<'src>) -> Option<&Process<'src>> {
|
||||
use Ast::*;
|
||||
match (patt, val) {
|
||||
(NilPattern, Value::Nil) => Some(self),
|
||||
(PlaceholderPattern, _) => Some(self),
|
||||
(NumberPattern(x), Value::Number(y)) => self.match_eq(x, y),
|
||||
(BooleanPattern(x), Value::Boolean(y)) => self.match_eq(x, y),
|
||||
(KeywordPattern(x), Value::Keyword(y)) => self.match_eq(x, y),
|
||||
(StringPattern(x), Value::InternedString(y)) => self.match_eq(x, y),
|
||||
(StringPattern(x), Value::AllocatedString(y)) => self.match_eq(&x.to_string(), y),
|
||||
(InterpolatedPattern(_, StringMatcher(matcher)), Value::InternedString(y)) => {
|
||||
match matcher(y.to_string()) {
|
||||
Some(matches) => {
|
||||
let mut matches = matches
|
||||
.iter()
|
||||
.map(|(word, string)| {
|
||||
(
|
||||
word.clone(),
|
||||
Value::AllocatedString(Rc::new(string.clone())),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.locals.append(&mut matches);
|
||||
Some(self)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
(WordPattern(w), val) => {
|
||||
self.bind(w.to_string(), val);
|
||||
Some(self)
|
||||
}
|
||||
(AsPattern(word, type_str), value) => {
|
||||
let ludus_type = r#type(value);
|
||||
let type_kw = Value::Keyword(type_str);
|
||||
if type_kw == ludus_type {
|
||||
self.bind(word.to_string(), value);
|
||||
Some(self)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(TuplePattern(x), Value::Tuple(y)) => {
|
||||
let has_splat = x.iter().any(|patt| matches!(patt, (Splattern(_), _)));
|
||||
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
|
||||
return None;
|
||||
};
|
||||
let to = self.locals.len();
|
||||
for i in 0..x.len() {
|
||||
if let 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);
|
||||
self.match_pattern(&patt.0, &list);
|
||||
} else if self.match_pattern(&x[i].0, &y[i]).is_none() {
|
||||
self.locals.truncate(to);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(self)
|
||||
}
|
||||
(ListPattern(x), Value::List(y)) => {
|
||||
let has_splat = x.iter().any(|patt| matches!(patt, (Splattern(_), _)));
|
||||
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
|
||||
return None;
|
||||
};
|
||||
let to = self.locals.len();
|
||||
for (i, (patt, _)) in x.iter().enumerate() {
|
||||
if let Splattern(patt) = &patt {
|
||||
let list = Value::List(y.skip(i));
|
||||
self.match_pattern(&patt.0, &list);
|
||||
} else if self.match_pattern(patt, y.get(i).unwrap()).is_none() {
|
||||
self.locals.truncate(to);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(self)
|
||||
}
|
||||
// TODO: optimize this on several levels
|
||||
// - [ ] opportunistic mutation
|
||||
// - [ ] get rid of all the pointer indirection in word splats
|
||||
(DictPattern(x), Value::Dict(y)) => {
|
||||
let has_splat = x.iter().any(|patt| matches!(patt, (Splattern(_), _)));
|
||||
if x.len() > y.len() || (!has_splat && x.len() != y.len()) {
|
||||
return None;
|
||||
};
|
||||
let to = self.locals.len();
|
||||
let mut matched = vec![];
|
||||
for (pattern, _) in x {
|
||||
match pattern {
|
||||
PairPattern(key, patt) => {
|
||||
if let Some(val) = y.get(key) {
|
||||
if self.match_pattern(&patt.0, val).is_none() {
|
||||
self.locals.truncate(to);
|
||||
return None;
|
||||
} else {
|
||||
matched.push(key);
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
}
|
||||
Splattern(pattern) => match pattern.0 {
|
||||
WordPattern(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);
|
||||
}
|
||||
self.bind(w.to_string(), &Value::Dict(unmatched));
|
||||
}
|
||||
PlaceholderPattern => (),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Some(self)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_clauses(
|
||||
&mut self,
|
||||
value: &Value<'src>,
|
||||
clauses: &'src [Spanned<Ast>],
|
||||
) -> LResult<'src> {
|
||||
{
|
||||
let root = self.ast;
|
||||
let to = self.locals.len();
|
||||
let mut clauses_iter = clauses.iter();
|
||||
while let Some((Ast::MatchClause(patt, guard, body), _)) = clauses_iter.next() {
|
||||
if self.match_pattern(&patt.0, value).is_some() {
|
||||
let pass_guard = match guard.as_ref() {
|
||||
None => true,
|
||||
Some(guard_expr) => self.visit(guard_expr)?.bool(),
|
||||
};
|
||||
if !pass_guard {
|
||||
self.locals.truncate(to);
|
||||
continue;
|
||||
}
|
||||
let result = self.visit(body);
|
||||
self.locals.truncate(to);
|
||||
self.ast = root;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
let patterns = clauses
|
||||
.iter()
|
||||
.map(|clause| {
|
||||
let (Ast::MatchClause(patt, ..), _) = clause else {
|
||||
unreachable!("internal Ludus error")
|
||||
};
|
||||
let patt = &patt.as_ref().0;
|
||||
patt.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
dbg!(&patterns);
|
||||
Err(LErr {
|
||||
input: self.input,
|
||||
src: self.src,
|
||||
msg: "no match".to_string(),
|
||||
span: self.span,
|
||||
trace: vec![],
|
||||
extra: format!("expected {value} to match one of\n{}", patterns),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(&mut self, callee: Value<'src>, caller: Value<'src>) -> LResult<'src> {
|
||||
use Value::*;
|
||||
match (callee, caller) {
|
||||
(Keyword(kw), Dict(dict)) => {
|
||||
if let Some(val) = dict.get(kw) {
|
||||
Ok(val.clone())
|
||||
} else {
|
||||
Ok(Nil)
|
||||
}
|
||||
}
|
||||
(Dict(dict), Keyword(kw)) => {
|
||||
if let Some(val) = dict.get(kw) {
|
||||
Ok(val.clone())
|
||||
} else {
|
||||
Ok(Nil)
|
||||
}
|
||||
}
|
||||
(Fn(f), Tuple(args)) => {
|
||||
// can't just use the `caller` value b/c borrow checker nonsense
|
||||
let args = Tuple(args);
|
||||
let to = self.locals.len();
|
||||
let mut f = f.borrow_mut();
|
||||
for i in 0..f.enclosing.len() {
|
||||
let (name, value) = f.enclosing[i].clone();
|
||||
if !f.has_run && matches!(value, Value::FnDecl(_)) {
|
||||
let defined = self.resolve(&name);
|
||||
match defined {
|
||||
Ok(Value::Fn(defined)) => f.enclosing[i] = (name.clone(), Fn(defined)),
|
||||
Ok(Value::FnDecl(_)) => {
|
||||
return self.panic(format!(
|
||||
"function `{name}` called before it was defined"
|
||||
))
|
||||
}
|
||||
|
||||
_ => unreachable!("internal Ludus error"),
|
||||
}
|
||||
}
|
||||
self.locals.push(f.enclosing[i].clone());
|
||||
}
|
||||
f.has_run = true;
|
||||
let input = self.input;
|
||||
let src = self.src;
|
||||
self.input = f.input;
|
||||
self.src = f.src;
|
||||
let result = self.match_clauses(&args, f.body);
|
||||
self.locals.truncate(to);
|
||||
self.input = input;
|
||||
self.src = src;
|
||||
result
|
||||
}
|
||||
// TODO: partially applied functions shnould work! In #15
|
||||
(Fn(_f), Args(_partial_args)) => todo!(),
|
||||
(_, Keyword(_)) => Ok(Nil),
|
||||
(_, Args(_)) => self.panic("only functions and keywords may be called".to_string()),
|
||||
(Base(f), Tuple(args)) => match f {
|
||||
BaseFn::Nullary(f) => {
|
||||
let num_args = args.len();
|
||||
if num_args != 0 {
|
||||
self.panic(format!("wrong arity: expected 0 arguments, got {num_args}"))
|
||||
} else {
|
||||
Ok(f())
|
||||
}
|
||||
}
|
||||
BaseFn::Unary(f) => {
|
||||
let num_args = args.len();
|
||||
if num_args != 1 {
|
||||
self.panic(format!("wrong arity: expected 1 argument, got {num_args}"))
|
||||
} else {
|
||||
Ok(f(&args[0]))
|
||||
}
|
||||
}
|
||||
BaseFn::Binary(r#fn) => {
|
||||
let num_args = args.len();
|
||||
if num_args != 2 {
|
||||
self.panic(format!("wrong arity: expected 2 arguments, got {num_args}"))
|
||||
} else {
|
||||
Ok(r#fn(&args[0], &args[1]))
|
||||
}
|
||||
}
|
||||
BaseFn::Ternary(f) => {
|
||||
let num_args = args.len();
|
||||
if num_args != 3 {
|
||||
self.panic(format!("wrong arity: expected 3 arguments, got {num_args}"))
|
||||
} else {
|
||||
Ok(f(&args[0], &args[1], &args[2]))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit(&mut self, node: &'src Spanned<Ast>) -> LResult<'src> {
|
||||
let (expr, span) = node;
|
||||
self.ast = expr;
|
||||
self.span = *span;
|
||||
self.eval()
|
||||
}
|
||||
|
||||
pub fn eval(&mut self) -> LResult<'src> {
|
||||
use Ast::*;
|
||||
let (root_node, root_span) = (self.ast, self.span);
|
||||
let result = match root_node {
|
||||
Nil => Ok(Value::Nil),
|
||||
Boolean(b) => Ok(Value::Boolean(*b)),
|
||||
Number(n) => Ok(Value::Number(*n)),
|
||||
Keyword(k) => Ok(Value::Keyword(k)),
|
||||
String(s) => Ok(Value::InternedString(s)),
|
||||
Interpolated(parts) => {
|
||||
let mut interpolated = std::string::String::new();
|
||||
for part in parts {
|
||||
match &part.0 {
|
||||
StringPart::Data(s) => interpolated.push_str(s.as_str()),
|
||||
StringPart::Word(w) => {
|
||||
let val = self.resolve(w)?;
|
||||
interpolated.push_str(val.interpolate().as_str())
|
||||
}
|
||||
StringPart::Inline(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
Ok(Value::AllocatedString(Rc::new(interpolated)))
|
||||
}
|
||||
Block(exprs) => {
|
||||
let to = self.locals.len();
|
||||
let mut result = Value::Nil;
|
||||
for expr in exprs {
|
||||
result = self.visit(expr)?;
|
||||
}
|
||||
self.locals.truncate(to);
|
||||
Ok(result)
|
||||
}
|
||||
If(cond, if_true, if_false) => {
|
||||
let truthy = self.visit(cond)?;
|
||||
let to_visit = if truthy.bool() { if_true } else { if_false };
|
||||
self.visit(to_visit)
|
||||
}
|
||||
List(members) => {
|
||||
let mut vect = Vector::new();
|
||||
for member in members {
|
||||
let member_value = self.visit(member)?;
|
||||
match member.0 {
|
||||
Ast::Splat(_) => match member_value {
|
||||
Value::List(list) => vect.append(list),
|
||||
_ => {
|
||||
return self
|
||||
.panic("only lists may be splatted into lists".to_string())
|
||||
}
|
||||
},
|
||||
_ => vect.push_back(member_value),
|
||||
}
|
||||
}
|
||||
Ok(Value::List(vect))
|
||||
}
|
||||
Tuple(members) => {
|
||||
let mut vect = Vec::new();
|
||||
for member in members {
|
||||
vect.push(self.visit(member)?);
|
||||
}
|
||||
Ok(Value::Tuple(Rc::new(vect)))
|
||||
}
|
||||
Word(w) | Ast::Splat(w) => {
|
||||
let val = self.resolve(&w.to_string())?;
|
||||
Ok(val)
|
||||
}
|
||||
Let(patt, expr) => {
|
||||
let val = self.visit(expr)?;
|
||||
let result = match self.match_pattern(&patt.0, &val) {
|
||||
Some(_) => Ok(val),
|
||||
None => self.panic("no match".to_string()),
|
||||
};
|
||||
result
|
||||
}
|
||||
Placeholder => Ok(Value::Placeholder),
|
||||
Arguments(a) => {
|
||||
let mut args = vec![];
|
||||
for arg in a.iter() {
|
||||
args.push(self.visit(arg)?)
|
||||
}
|
||||
let result = if args.iter().any(|arg| matches!(arg, Value::Placeholder)) {
|
||||
Ok(Value::Args(Rc::new(args)))
|
||||
} else {
|
||||
Ok(Value::Tuple(Rc::new(args)))
|
||||
};
|
||||
result
|
||||
}
|
||||
Dict(terms) => {
|
||||
let mut dict = HashMap::new();
|
||||
for term in terms {
|
||||
match term {
|
||||
(Ast::Pair(key, value), _) => {
|
||||
dict.insert(*key, self.visit(value)?);
|
||||
}
|
||||
(Ast::Splat(_), _) => {
|
||||
let resolved = self.visit(term)?;
|
||||
let Value::Dict(to_splat) = resolved else {
|
||||
return self.panic("cannot splat non-dict into dict".to_string());
|
||||
};
|
||||
dict = to_splat.union(dict);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Ok(Value::Dict(dict))
|
||||
}
|
||||
LBox(name, expr) => {
|
||||
let val = self.visit(expr)?;
|
||||
let boxed = Value::Box(name, Rc::new(RefCell::new(val)));
|
||||
self.bind(name.to_string(), &boxed);
|
||||
Ok(boxed)
|
||||
}
|
||||
Synthetic(root, first, rest) => {
|
||||
let root_val = self.visit(root)?;
|
||||
let first_val = self.visit(first)?;
|
||||
let mut result = self.apply(root_val.clone(), first_val.clone());
|
||||
if let Err(mut err) = result {
|
||||
err.trace.push(Trace {
|
||||
callee: *root.clone(),
|
||||
caller: *first.clone(),
|
||||
function: root_val,
|
||||
arguments: first_val,
|
||||
input: self.input,
|
||||
src: self.src,
|
||||
});
|
||||
return Err(err);
|
||||
};
|
||||
let mut prev_node;
|
||||
let mut this_node = first.as_ref();
|
||||
for term in rest.iter() {
|
||||
prev_node = this_node;
|
||||
this_node = term;
|
||||
let caller = self.visit(term)?;
|
||||
let callee = result.unwrap();
|
||||
result = self.apply(callee.clone(), caller.clone());
|
||||
|
||||
if let Err(mut err) = result {
|
||||
err.trace.push(Trace {
|
||||
callee: prev_node.clone(),
|
||||
caller: this_node.clone(),
|
||||
function: caller,
|
||||
arguments: callee,
|
||||
input: self.input,
|
||||
src: self.src,
|
||||
});
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
When(clauses) => {
|
||||
for clause in clauses.iter() {
|
||||
let WhenClause(cond, body) = &clause.0 else {
|
||||
unreachable!()
|
||||
};
|
||||
if self.visit(cond)?.bool() {
|
||||
return self.visit(body);
|
||||
};
|
||||
}
|
||||
self.panic("no match".to_string())
|
||||
}
|
||||
Match(scrutinee, clauses) => {
|
||||
let value = self.visit(scrutinee)?;
|
||||
self.match_clauses(&value, clauses)
|
||||
}
|
||||
Fn(name, clauses, doc) => {
|
||||
let doc = doc.map(|s| s.to_string());
|
||||
let ptr: *const Ast = root_node;
|
||||
let info = self.fn_info.get(&ptr).unwrap();
|
||||
let FnInfo::Defined(_, _, enclosing) = info else {
|
||||
unreachable!()
|
||||
};
|
||||
let enclosing = enclosing
|
||||
.iter()
|
||||
.filter(|binding| binding != name)
|
||||
.map(|binding| (binding.clone(), self.resolve(binding).unwrap().clone()))
|
||||
.collect();
|
||||
let the_fn = Value::Fn::<'src>(Rc::new(RefCell::new(crate::value::Fn::<'src> {
|
||||
name: name.to_string(),
|
||||
body: clauses,
|
||||
doc,
|
||||
enclosing,
|
||||
has_run: false,
|
||||
input: self.input,
|
||||
src: self.src,
|
||||
})));
|
||||
|
||||
let maybe_decl_i = self.locals.iter().position(|(binding, _)| binding == name);
|
||||
|
||||
match maybe_decl_i {
|
||||
None => self.bind(name.to_string(), &the_fn),
|
||||
Some(i) => {
|
||||
let declared = &self.locals[i].1;
|
||||
match declared {
|
||||
Value::FnDecl(_) => {
|
||||
self.locals[i] = (name.to_string(), the_fn.clone());
|
||||
}
|
||||
_ => unreachable!("internal Ludus error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(the_fn)
|
||||
}
|
||||
FnDeclaration(name) => {
|
||||
let decl = Value::FnDecl(name);
|
||||
self.bind(name.to_string(), &decl);
|
||||
Ok(decl)
|
||||
}
|
||||
Panic(msg) => {
|
||||
let msg = self.visit(msg)?;
|
||||
self.panic(format!("{msg}"))
|
||||
}
|
||||
Repeat(times, body) => {
|
||||
let times_num = match self.visit(times) {
|
||||
Ok(Value::Number(n)) => n as usize,
|
||||
_ => return self.panic("`repeat` may only take numbers".to_string()),
|
||||
};
|
||||
for _ in 0..times_num {
|
||||
self.visit(body)?;
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
Do(terms) => {
|
||||
let mut result = self.visit(&terms[0])?;
|
||||
for term in terms.iter().skip(1) {
|
||||
let next = self.visit(term)?;
|
||||
let arg = Value::Tuple(Rc::new(vec![result]));
|
||||
result = self.apply(next, arg)?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
Loop(init, clauses) => {
|
||||
let mut args = self.visit(init)?;
|
||||
loop {
|
||||
let result = self.match_clauses(&args, clauses)?;
|
||||
if let Value::Recur(recur_args) = result {
|
||||
args = Value::Tuple(Rc::new(recur_args));
|
||||
} else {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
Recur(args) => {
|
||||
let mut vect = Vec::new();
|
||||
for arg in args {
|
||||
vect.push(self.visit(arg)?);
|
||||
}
|
||||
Ok(Value::Recur(vect))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.ast = root_node;
|
||||
self.span = root_span;
|
||||
result
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user