Refactor: Use Symbol for identifiers and macro hygiene

Introduces a `Symbol` struct to represent identifiers, incorporating
macro hygiene context. Updates various parts of the AST compiler and
environment to use `Symbol` instead of raw `Rc<str>` for identifiers,
improving robustness for macro expansions.

Also includes:
- Adds a new example `macro_hygiene.myc`.
- Updates `.gitignore`.
- Refactors `MacroExpander` to use `ExpansionState` for cleaner template
  expansion.
- Adjusts `VM::eval_observed` to ensure `after_eval` is called
  correctly.
- Resets `Environment` for each test run and compilation/dumping in
  `main.rs`.
This commit is contained in:
Michael Schimmel
2026-02-18 14:32:09 +01:00
parent 73ddd644c1
commit 76586c0903
12 changed files with 363 additions and 123 deletions
+11 -6
View File
@@ -200,7 +200,7 @@ macro_rules! dispatch_eval {
loop {
match func_val {
Value::Function(f) => return Ok(f(arg_vals)),
Value::Function(f) => break Ok(f(arg_vals)),
Value::Object(obj) => {
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
let old_stack_top = $self.stack.len();
@@ -225,14 +225,13 @@ macro_rules! dispatch_eval {
arg_vals = next_args;
continue;
},
Ok(val) => return Ok(val),
Err(e) => return Err(e),
res => break res,
}
} else {
return Err(format!("Object is not a closure: {}", obj.type_name()));
break Err(format!("Object is not a closure: {}", obj.type_name()));
}
},
_ => return Err(format!("Attempt to call non-function: {}", func_val)),
_ => break Err(format!("Attempt to call non-function: {}", func_val)),
}
}
},
@@ -315,7 +314,13 @@ impl VM {
/// The observed path for debugging.
fn eval_observed<O: VMObserver>(&mut self, observer: &mut O, node: &Node<BoundKind, StaticType>) -> Result<Value, String> {
observer.before_eval(self, node);
let result = dispatch_eval!(self, node, eval_observed, observer);
// Wrap in a closure to ensure after_eval is called even on early returns (e.g. from ?)
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
}