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
+6 -9
View File
@@ -2,7 +2,7 @@ use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use crate::ast::types::{Value, StaticType, Object};
use crate::ast::nodes::{Node, UntypedKind};
use crate::ast::nodes::{Node, UntypedKind, Symbol};
use crate::ast::parser::Parser;
use crate::ast::compiler::binder::Binder;
use crate::ast::compiler::BoundKind;
@@ -13,29 +13,26 @@ use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::macros::{MacroExpander, MacroRegistry, MacroEvaluator};
pub struct Environment {
pub global_names: Rc<RefCell<HashMap<String, (u32, StaticType)>>>,
pub global_names: Rc<RefCell<HashMap<Symbol, (u32, StaticType)>>>,
pub global_values: Rc<RefCell<Vec<Value>>>,
pub debug_mode: bool,
}
/// Evaluator used during macro expansion to allow compile-time logic.
struct RuntimeMacroEvaluator {
global_names: Rc<RefCell<HashMap<String, (u32, StaticType)>>>,
global_names: Rc<RefCell<HashMap<Symbol, (u32, StaticType)>>>,
global_values: Rc<RefCell<Vec<Value>>>,
}
impl MacroEvaluator for RuntimeMacroEvaluator {
fn evaluate(&self, node: &Node<UntypedKind>, bindings: &HashMap<Rc<str>, Node<UntypedKind>>) -> Result<Value, String> {
// 1. Check if it's a simple parameter substitution
if let UntypedKind::Identifier(name) = &node.kind {
if let Some(arg_node) = bindings.get(name) {
if let UntypedKind::Identifier(sym) = &node.kind
&& let Some(arg_node) = bindings.get(&sym.name) {
return Ok(Value::Object(Rc::new(arg_node.clone()) as Rc<dyn Object>));
}
}
// 2. Full evaluation for complex compile-time expressions
// Note: This evaluator currently doesn't see macro parameters in complex expressions
// unless they are explicitly substituted. For now, we support global-only logic.
let bound_ast = Binder::bind_root(self.global_names.clone(), node)?;
let mut vm = VM::new(self.global_values.clone());
vm.run(&bound_ast)
@@ -76,7 +73,7 @@ impl Environment {
let mut values = self.global_values.borrow_mut();
let idx = values.len() as u32;
names.insert(name.to_string(), (idx, ty));
names.insert(Symbol::from(name), (idx, ty));
values.push(Value::Function(Rc::new(func)));
}