Refactor VM evaluation for observer pattern
The `dispatch_eval` macro has been replaced with explicit `eval_internal` and `eval_core` methods. This change aims to streamline the evaluation process and better support the observer pattern by providing clearer hooks for observing VM execution.
This commit is contained in:
+236
-185
@@ -128,141 +128,6 @@ pub struct VM {
|
||||
frames: Vec<CallFrame>,
|
||||
}
|
||||
|
||||
macro_rules! dispatch_eval {
|
||||
($self:ident, $node:ident, $eval_method:ident $(, $observer:ident)?) => {
|
||||
match &$node.kind {
|
||||
BoundKind::Nop => Ok(Value::Void),
|
||||
BoundKind::Constant(v) => Ok(v.clone()),
|
||||
BoundKind::Parameter { .. } => Ok(Value::Void),
|
||||
BoundKind::DefGlobal { global_index, value, .. } => {
|
||||
let val = $self.$eval_method($($observer,)? value)?;
|
||||
let idx = *global_index as usize;
|
||||
let mut globals = $self.globals.borrow_mut();
|
||||
if idx >= globals.len() { globals.resize(idx + 1, Value::Void); }
|
||||
globals[idx] = val.clone();
|
||||
Ok(val)
|
||||
},
|
||||
BoundKind::Get { addr, .. } => $self.get_value(*addr),
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val = $self.$eval_method($($observer,)? value)?;
|
||||
$self.set_value(*addr, val.clone())?;
|
||||
Ok(val)
|
||||
},
|
||||
BoundKind::DefLocal { slot, value, captured_by, .. } => {
|
||||
let val = $self.$eval_method($($observer,)? value)?;
|
||||
let final_val = if !captured_by.is_empty() { Value::Cell(Rc::new(RefCell::new(val))) } else { val };
|
||||
let frame = $self.frames.last().ok_or("No call frame")?;
|
||||
let abs_index = frame.stack_base + (*slot as usize);
|
||||
if abs_index == $self.stack.len() { $self.stack.push(final_val.clone()); }
|
||||
else if abs_index < $self.stack.len() { $self.stack[abs_index] = final_val.clone(); }
|
||||
else { return Err(format!("Stack gap at local {}", slot)); }
|
||||
Ok(final_val)
|
||||
},
|
||||
BoundKind::If { cond, then_br, else_br } => {
|
||||
let c = $self.$eval_method($($observer,)? cond)?;
|
||||
if c.is_truthy() { $self.$eval_method($($observer,)? then_br) }
|
||||
else if let Some(e) = else_br { $self.$eval_method($($observer,)? e) }
|
||||
else { Ok(Value::Void) }
|
||||
},
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut last = Value::Void;
|
||||
for e in exprs { last = $self.$eval_method($($observer,)? e)?; }
|
||||
Ok(last)
|
||||
},
|
||||
BoundKind::Lambda { params, upvalues, body, positional_count } => {
|
||||
let mut captured = Vec::with_capacity(upvalues.len());
|
||||
for addr in upvalues { captured.push($self.capture_upvalue(*addr)?); }
|
||||
let closure = Closure::new(
|
||||
params.ty.original.clone(),
|
||||
body.ty.original.clone(),
|
||||
body.clone(),
|
||||
captured,
|
||||
*positional_count
|
||||
);
|
||||
Ok(Value::Object(Rc::new(closure)))
|
||||
},
|
||||
BoundKind::Call { callee, args } => {
|
||||
let mut func_val = $self.$eval_method($($observer,)? callee)?;
|
||||
|
||||
macro_rules! get_arg_vals {
|
||||
($s:ident, $a:ident) => { $s.prepare_args($a)? };
|
||||
($s:ident, $o:ident, $a:ident) => { $s.prepare_args_observed($o, $a)? };
|
||||
}
|
||||
|
||||
let mut arg_vals = match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
let mut is_complex = false;
|
||||
for e in elements {
|
||||
if matches!(e.kind, BoundKind::Tuple { .. }) { is_complex = true; break; }
|
||||
vals.push($self.$eval_method($($observer,)? e)?);
|
||||
}
|
||||
if is_complex { get_arg_vals!($self, $($observer,)? args) } else { vals }
|
||||
}
|
||||
_ => { get_arg_vals!($self, $($observer,)? args) }
|
||||
};
|
||||
|
||||
if $node.ty.is_tail {
|
||||
match func_val {
|
||||
Value::Object(obj) => return Ok(Value::TailCallRequest(Box::new((obj, arg_vals)))),
|
||||
Value::Function(f) => return Ok((f.func)(arg_vals)),
|
||||
_ => return Err(format!("Tail call target is not a function: {}", func_val)),
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match func_val {
|
||||
Value::Function(f) => break Ok((f.func)(arg_vals)),
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
let old_stack_top = $self.stack.len();
|
||||
let closure_rc = Rc::new(closure.clone());
|
||||
$self.frames.push(CallFrame { stack_base: old_stack_top, closure: Some(closure_rc.clone()) });
|
||||
if let Some(count) = closure.positional_count && arg_vals.len() == count as usize {
|
||||
$self.stack.extend(arg_vals);
|
||||
} else {
|
||||
$self.unpack(&closure.parameter_node, &arg_vals, &mut 0)?;
|
||||
}
|
||||
let result = $self.$eval_method($($observer,)? &closure.exec_node);
|
||||
$self.frames.pop();
|
||||
$self.stack.truncate(old_stack_top);
|
||||
match result {
|
||||
Ok(Value::TailCallRequest(payload)) => {
|
||||
let (next_obj, next_args) = *payload;
|
||||
func_val = Value::Object(next_obj);
|
||||
arg_vals = next_args;
|
||||
continue;
|
||||
},
|
||||
res => break res,
|
||||
}
|
||||
} else { break Err(format!("Object is not a closure: {}", obj.type_name())); }
|
||||
},
|
||||
_ => break Err(format!("Attempt to call non-function: {}", func_val)),
|
||||
}
|
||||
}
|
||||
},
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
for e in elements { vals.push($self.$eval_method($($observer,)? e)?); }
|
||||
Ok(Value::make_tuple(vals))
|
||||
},
|
||||
BoundKind::Record { fields } => {
|
||||
let mut keys = Vec::with_capacity(fields.len());
|
||||
let mut values = Vec::with_capacity(fields.len());
|
||||
for (k, v) in fields {
|
||||
let key = $self.$eval_method($($observer,)? k)?;
|
||||
let val = $self.$eval_method($($observer,)? v)?;
|
||||
if let Value::Keyword(kw) = key { keys.push(kw); values.push(val); }
|
||||
else { return Err(format!("Record key must be keyword, got {}", key)); }
|
||||
}
|
||||
Ok(Value::make_record(keys, values))
|
||||
},
|
||||
BoundKind::Expansion { bound_expanded, .. } => { $self.$eval_method($($observer,)? bound_expanded) }
|
||||
BoundKind::Extension(ext) => { Err(format!("Execution of extension '{}' not implemented yet", ext.display_name())) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl VM {
|
||||
pub fn new(globals: Rc<RefCell<Vec<Value>>>) -> Self {
|
||||
Self {
|
||||
@@ -369,9 +234,236 @@ impl VM {
|
||||
|
||||
#[inline(always)]
|
||||
fn eval(&mut self, node: &ExecNode) -> Result<Value, String> {
|
||||
dispatch_eval!(self, node, eval)
|
||||
self.eval_internal(&mut NoOpObserver, node)
|
||||
}
|
||||
|
||||
fn eval_observed<O: VMObserver>(
|
||||
&mut self,
|
||||
observer: &mut O,
|
||||
node: &ExecNode,
|
||||
) -> Result<Value, String> {
|
||||
self.eval_internal(observer, node)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn eval_internal<O: VMObserver>(
|
||||
&mut self,
|
||||
obs: &mut O,
|
||||
node: &ExecNode,
|
||||
) -> Result<Value, String> {
|
||||
if O::ACTIVE {
|
||||
obs.before_eval(self, node);
|
||||
let result = self.eval_core(obs, node);
|
||||
obs.after_eval(self, node, &result);
|
||||
result
|
||||
} else {
|
||||
self.eval_core(obs, node)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn eval_core<O: VMObserver>(
|
||||
&mut self,
|
||||
obs: &mut O,
|
||||
node: &ExecNode,
|
||||
) -> Result<Value, String> {
|
||||
match &node.kind {
|
||||
BoundKind::Nop => Ok(Value::Void),
|
||||
BoundKind::Constant(v) => Ok(v.clone()),
|
||||
BoundKind::Parameter { .. } => Ok(Value::Void),
|
||||
BoundKind::DefGlobal {
|
||||
global_index,
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
let idx = *global_index as usize;
|
||||
let mut globals = self.globals.borrow_mut();
|
||||
if idx >= globals.len() {
|
||||
globals.resize(idx + 1, Value::Void);
|
||||
}
|
||||
globals[idx] = val.clone();
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::Get { addr, .. } => self.get_value(*addr),
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
self.set_value(*addr, val.clone())?;
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::DefLocal {
|
||||
slot,
|
||||
value,
|
||||
captured_by,
|
||||
..
|
||||
} => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
let final_val = if !captured_by.is_empty() {
|
||||
Value::Cell(Rc::new(RefCell::new(val)))
|
||||
} else {
|
||||
val
|
||||
};
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
let abs_index = frame.stack_base + (*slot as usize);
|
||||
if abs_index == self.stack.len() {
|
||||
self.stack.push(final_val.clone());
|
||||
} else if abs_index < self.stack.len() {
|
||||
self.stack[abs_index] = final_val.clone();
|
||||
} else {
|
||||
return Err(format!("Stack gap at local {}", slot));
|
||||
}
|
||||
Ok(final_val)
|
||||
}
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let c = self.eval_internal(obs, cond)?;
|
||||
if c.is_truthy() {
|
||||
self.eval_internal(obs, then_br)
|
||||
} else if let Some(e) = else_br {
|
||||
self.eval_internal(obs, e)
|
||||
} else {
|
||||
Ok(Value::Void)
|
||||
}
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut last = Value::Void;
|
||||
for e in exprs {
|
||||
last = self.eval_internal(obs, e)?;
|
||||
}
|
||||
Ok(last)
|
||||
}
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
let mut captured = Vec::with_capacity(upvalues.len());
|
||||
for addr in upvalues {
|
||||
captured.push(self.capture_upvalue(*addr)?);
|
||||
}
|
||||
let closure = Closure::new(
|
||||
params.ty.original.clone(),
|
||||
body.ty.original.clone(),
|
||||
body.clone(),
|
||||
captured,
|
||||
*positional_count,
|
||||
);
|
||||
Ok(Value::Object(Rc::new(closure)))
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
let mut func_val = self.eval_internal(obs, callee)?;
|
||||
|
||||
let mut arg_vals = match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
let mut is_complex = false;
|
||||
for e in elements {
|
||||
if matches!(e.kind, BoundKind::Tuple { .. }) {
|
||||
is_complex = true;
|
||||
break;
|
||||
}
|
||||
vals.push(self.eval_internal(obs, e)?);
|
||||
}
|
||||
if is_complex {
|
||||
self.prepare_args_internal(obs, args)?
|
||||
} else {
|
||||
vals
|
||||
}
|
||||
}
|
||||
_ => self.prepare_args_internal(obs, args)?,
|
||||
};
|
||||
|
||||
if node.ty.is_tail {
|
||||
match func_val {
|
||||
Value::Object(obj) => {
|
||||
return Ok(Value::TailCallRequest(Box::new((obj, arg_vals))))
|
||||
}
|
||||
Value::Function(f) => return Ok((f.func)(arg_vals)),
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Tail call target is not a function: {}",
|
||||
func_val
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match func_val {
|
||||
Value::Function(f) => break Ok((f.func)(arg_vals)),
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
let old_stack_top = self.stack.len();
|
||||
let closure_rc = Rc::new(closure.clone());
|
||||
self.frames.push(CallFrame {
|
||||
stack_base: old_stack_top,
|
||||
closure: Some(closure_rc.clone()),
|
||||
});
|
||||
if let Some(count) = closure.positional_count
|
||||
&& arg_vals.len() == count as usize
|
||||
{
|
||||
self.stack.extend(arg_vals);
|
||||
} else {
|
||||
self.unpack(&closure.parameter_node, &arg_vals, &mut 0)?;
|
||||
}
|
||||
let result = self.eval_internal(obs, &closure.exec_node);
|
||||
self.frames.pop();
|
||||
self.stack.truncate(old_stack_top);
|
||||
match result {
|
||||
Ok(Value::TailCallRequest(payload)) => {
|
||||
let (next_obj, next_args) = *payload;
|
||||
func_val = Value::Object(next_obj);
|
||||
arg_vals = next_args;
|
||||
continue;
|
||||
}
|
||||
res => break res,
|
||||
}
|
||||
} else {
|
||||
break Err(format!(
|
||||
"Object is not a closure: {}",
|
||||
obj.type_name()
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => break Err(format!("Attempt to call non-function: {}", func_val)),
|
||||
}
|
||||
}
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
for e in elements {
|
||||
vals.push(self.eval_internal(obs, e)?);
|
||||
}
|
||||
Ok(Value::make_tuple(vals))
|
||||
}
|
||||
BoundKind::Record { fields } => {
|
||||
let mut keys = Vec::with_capacity(fields.len());
|
||||
let mut values = Vec::with_capacity(fields.len());
|
||||
for (k, v) in fields {
|
||||
let key = self.eval_internal(obs, k)?;
|
||||
let val = self.eval_internal(obs, v)?;
|
||||
if let Value::Keyword(kw) = key {
|
||||
keys.push(kw);
|
||||
values.push(val);
|
||||
} else {
|
||||
return Err(format!("Record key must be keyword, got {}", key));
|
||||
}
|
||||
}
|
||||
Ok(Value::make_record(keys, values))
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => self.eval_internal(obs, bound_expanded),
|
||||
BoundKind::Extension(ext) => Err(format!(
|
||||
"Execution of extension '{}' not implemented yet",
|
||||
ext.display_name()
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn resolve_tail_calls(&mut self, mut result: Value) -> Value {
|
||||
while let Value::TailCallRequest(payload) = result {
|
||||
let (next_obj, next_args) = *payload;
|
||||
@@ -390,20 +482,6 @@ impl VM {
|
||||
result
|
||||
}
|
||||
|
||||
fn eval_observed<O: VMObserver>(
|
||||
&mut self,
|
||||
observer: &mut O,
|
||||
node: &ExecNode,
|
||||
) -> Result<Value, String> {
|
||||
observer.before_eval(self, node);
|
||||
let wrapper = |vm: &mut Self, obs: &mut O| -> Result<Value, String> {
|
||||
dispatch_eval!(vm, node, eval_observed, obs)
|
||||
};
|
||||
let result = wrapper(self, observer);
|
||||
observer.after_eval(self, node, &result);
|
||||
result
|
||||
}
|
||||
|
||||
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
|
||||
match addr {
|
||||
Address::Local(idx) => {
|
||||
@@ -532,62 +610,35 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_args(&mut self, args: &ExecNode) -> Result<Vec<Value>, String> {
|
||||
let mut arg_vals = Vec::new();
|
||||
match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
self.eval_and_flatten(elements, &mut arg_vals)?;
|
||||
}
|
||||
_ => {
|
||||
VM::flatten_value(self.eval(args)?, &mut arg_vals);
|
||||
}
|
||||
}
|
||||
Ok(arg_vals)
|
||||
}
|
||||
|
||||
fn prepare_args_observed<O: VMObserver>(
|
||||
fn prepare_args_internal<O: VMObserver>(
|
||||
&mut self,
|
||||
observer: &mut O,
|
||||
obs: &mut O,
|
||||
args: &ExecNode,
|
||||
) -> Result<Vec<Value>, String> {
|
||||
let mut arg_vals = Vec::new();
|
||||
match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
self.eval_observed_and_flatten(observer, elements, &mut arg_vals)?;
|
||||
self.eval_and_flatten_internal(obs, elements, &mut arg_vals)?;
|
||||
}
|
||||
_ => {
|
||||
VM::flatten_value(self.eval_observed(observer, args)?, &mut arg_vals);
|
||||
VM::flatten_value(self.eval_internal(obs, args)?, &mut arg_vals);
|
||||
}
|
||||
}
|
||||
Ok(arg_vals)
|
||||
}
|
||||
|
||||
fn eval_and_flatten(
|
||||
fn eval_and_flatten_internal<O: VMObserver>(
|
||||
&mut self,
|
||||
elements: &[ExecNode],
|
||||
into: &mut Vec<Value>,
|
||||
) -> Result<(), String> {
|
||||
for e in elements {
|
||||
match &e.kind {
|
||||
BoundKind::Tuple { elements: sub } => self.eval_and_flatten(sub, into)?,
|
||||
_ => into.push(self.eval(e)?),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_observed_and_flatten<O: VMObserver>(
|
||||
&mut self,
|
||||
observer: &mut O,
|
||||
obs: &mut O,
|
||||
elements: &[ExecNode],
|
||||
into: &mut Vec<Value>,
|
||||
) -> Result<(), String> {
|
||||
for e in elements {
|
||||
match &e.kind {
|
||||
BoundKind::Tuple { elements: sub } => {
|
||||
self.eval_observed_and_flatten(observer, sub, into)?
|
||||
self.eval_and_flatten_internal(obs, sub, into)?
|
||||
}
|
||||
_ => into.push(self.eval_observed(observer, e)?),
|
||||
_ => into.push(self.eval_internal(obs, e)?),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user