diff options
Diffstat (limited to 'src/vm.rs')
| -rw-r--r-- | src/vm.rs | 105 |
1 files changed, 65 insertions, 40 deletions
@@ -1,6 +1,6 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::rc::Rc; @@ -656,7 +656,7 @@ impl VM { let ret = inner.ret(); if Type::from(&a) != *ret { - error!(self, ErrorKind::TypeMismatch(a.into(), ret.clone()), + error!(self, ErrorKind::TypeMismatch(ret.clone(), a.into()), "Value does not match return type."); } } @@ -669,6 +669,10 @@ impl VM { let ty = self.ty(ty); let top_type = self.stack.last().unwrap().into(); match (ty, top_type) { + (a, b) if matches!(a, Type::Union(_)) && a == &b => { + let last = self.stack.len() - 1; + self.stack[last] = Value::from(a); + } (Type::Unknown, top_type) if top_type != Type::Unknown => {} (a, b) if a != &b => { @@ -725,55 +729,76 @@ impl VM { Op::Call(num_args) => { let new_base = self.stack.len() - 1 - num_args; - match self.stack[new_base].clone() { - Value::Blob(blob) => { - let mut values = Vec::with_capacity(blob.fields.len()); - for _ in 0..values.capacity() { - values.push(Value::Nil); - } + let callable = &self.stack[new_base]; + + let call_callable = |callable: &Value| { + let args = &self.stack[new_base+1..]; + let args: Vec<_> = args.iter().map(|x| x.into()).collect(); + match callable { + Value::Blob(blob) => { + let mut values = Vec::with_capacity(blob.fields.len()); + for _ in 0..values.capacity() { + values.push(Value::Nil); + } - for (slot, ty) in blob.fields.values() { - values[*slot] = ty.into(); - } + for (slot, ty) in blob.fields.values() { + values[*slot] = ty.into(); + } - self.pop(); - self.push(Value::Instance(blob, Rc::new(RefCell::new(values)))); - } - Value::Function(_, block) => { - let inner = block.borrow(); - let args = inner.args(); - if args.len() != num_args { - error!(self, ErrorKind::ArgumentCount(args.len(), num_args)); + Ok(Value::Instance(blob.clone(), Rc::new(RefCell::new(values)))) } - let stack_args = &self.stack[self.stack.len() - args.len()..]; - let stack_args: Vec<_> = stack_args.iter().map(|x| x.into()).collect(); - if args != &stack_args { - error!(self, ErrorKind::ArgumentType(args.clone(), stack_args)); + Value::Function(_, block) => { + let inner = block.borrow(); + let fargs = inner.args(); + if fargs != &args { + Err(ErrorKind::ArgumentType(args.clone(), args)) + } else { + Ok(inner.ret().into()) + } + } - self.stack[new_base] = block.borrow().ret().into(); + Value::ExternFunction(slot) => { + let extern_func = self.extern_functions[*slot]; + extern_func(&self.stack[new_base+1..], false) + } - self.stack.truncate(new_base + 1); + _ => { + Err(ErrorKind::InvalidProgram) + } } - Value::ExternFunction(slot) => { - let extern_func = self.extern_functions[slot]; - let res = match extern_func(&self.stack[new_base+1..], false) { - Ok(value) => value, - Err(ek) => { - self.stack.truncate(new_base); - self.push(Value::Nil); - error!(self, ek, "Error from external function.") + }; + + let mut err = None; + self.stack[new_base] = match callable { + Value::Union(alts) => { + let mut returns = HashSet::new(); + for alt in alts.iter() { + if let Ok(res) = call_callable(&alt) { + returns.insert(res); } - }; - self.stack.truncate(new_base); - self.push(res); - } + } + if returns.is_empty() { + err = Some(ErrorKind::InvalidProgram); + Value::Nil + } else { + Value::Union(returns) + } + }, _ => { - error!(self, - ErrorKind::InvalidProgram, - format!("Tried to call non-function {:?}", self.stack[new_base])); + match call_callable(callable) { + Err(e) => { + err = Some(e); + Value::Nil + }, + Ok(v) => v + } } + }; + self.stack.truncate(new_base + 1); + if err.is_some() { + error!(self, err.unwrap()); } } |
