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
+4 -5
View File
@@ -1,6 +1,5 @@
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use crate::ast::nodes::{Node, UntypedKind};
use crate::ast::nodes::{Node, UntypedKind, Symbol};
use crate::ast::types::Identity;
/// Analyzes the AST to find which lambdas capture which variable declarations.
@@ -22,15 +21,15 @@ impl UpvalueAnalyzer {
fn visit(
node: &Node<UntypedKind>,
scopes: &mut Vec<HashMap<Rc<str>, Identity>>,
scopes: &mut Vec<HashMap<Symbol, Identity>>,
capture_map: &mut HashMap<Identity, HashSet<Identity>>,
current_lambda: Option<Identity>
) {
match &node.kind {
UntypedKind::Identifier(name) => {
UntypedKind::Identifier(sym) => {
// Resolve name in scope stack (from inner to outer)
for (depth, scope) in scopes.iter().rev().enumerate() {
if let Some(decl_id) = scope.get(name) {
if let Some(decl_id) = scope.get(sym) {
if depth > 0 {
// Captured from an outer scope!
if let Some(lambda_id) = &current_lambda {