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
+9 -9
View File
@@ -1,7 +1,7 @@
use std::rc::Rc;
use crate::ast::lexer::{Lexer, Token, TokenKind};
use crate::ast::types::{Identity, NodeIdentity, Value, Keyword};
use crate::ast::nodes::{Node, UntypedKind};
use crate::ast::nodes::{Node, UntypedKind, Symbol};
pub struct Parser<'a> {
lexer: Lexer<'a>,
@@ -95,7 +95,7 @@ impl<'a> Parser<'a> {
"true" => UntypedKind::Constant(Value::Bool(true)),
"false" => UntypedKind::Constant(Value::Bool(false)),
"void" => UntypedKind::Constant(Value::Void),
_ => UntypedKind::Identifier(id),
_ => UntypedKind::Identifier(id.into()),
}
}
_ => return Err(format!("Unexpected token in atom: {:?} at {:?}", token.kind, token.location)),
@@ -114,8 +114,8 @@ impl<'a> Parser<'a> {
let head = self.parse_expression()?;
let result = if let UntypedKind::Identifier(name) = &head.kind {
match name.as_ref() {
let result = if let UntypedKind::Identifier(ref sym) = head.kind {
match sym.name.as_ref() {
"if" => self.parse_if(identity),
"fn" => self.parse_fn(identity),
"def" => self.parse_def(identity),
@@ -151,7 +151,7 @@ impl<'a> Parser<'a> {
fn parse_def(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
let name_node = self.parse_expression()?;
let name = match name_node.kind {
UntypedKind::Identifier(s) => s,
UntypedKind::Identifier(sym) => sym,
_ => return Err("Expected identifier for def name".to_string()),
};
@@ -205,7 +205,7 @@ impl<'a> Parser<'a> {
fn parse_macro_decl(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
let name_node = self.parse_expression()?;
let name = match name_node.kind {
UntypedKind::Identifier(s) => s,
UntypedKind::Identifier(sym) => sym,
_ => return Err("Expected identifier for macro name".to_string()),
};
let params = self.parse_param_vector()?;
@@ -217,7 +217,7 @@ impl<'a> Parser<'a> {
})
}
fn parse_param_vector(&mut self) -> Result<Vec<Rc<str>>, String> {
fn parse_param_vector(&mut self) -> Result<Vec<Symbol>, String> {
if *self.peek() != TokenKind::LeftBracket {
return Err(format!("Expected parameter vector [...] for fn, found {:?}", self.peek()));
}
@@ -227,7 +227,7 @@ impl<'a> Parser<'a> {
while *self.peek() != TokenKind::RightBracket {
let token = self.advance()?;
match token.kind {
TokenKind::Identifier(name) => params.push(name),
TokenKind::Identifier(name) => params.push(name.into()),
_ => return Err(format!("Expected identifier in param vector, found {:?}", token.kind)),
}
}
@@ -313,7 +313,7 @@ impl<'a> Parser<'a> {
fn make_id_node(&self, name: &str, identity: Identity) -> Node<UntypedKind> {
Node {
identity,
kind: UntypedKind::Identifier(Rc::from(name)),
kind: UntypedKind::Identifier(Symbol::from(name)),
ty: (),
}
}