Files
RustAst/src/ast/compiler/upvalues.rs
T
Michael Schimmel 9b16bc47be Refactor regex compilation and use is_some_and
This commit introduces several improvements:

- Pre-compiles regular expressions in `ast/tester.rs` to avoid redundant
  compilation, improving performance.
- Replaces `.extension().map_or(false, |ext| ext == "myc")` with the
  more concise and readable `.extension().is_some_and(|ext| ext ==
  "myc")`.
- Simplifies upvalue capture logic by using `.or_default()` instead of
  `.or_insert_with(HashSet::new)`.
- Adds a `Default` implementation for `TracingObserver`.
- Optimizes string literal parsing in `highlight_myc` to correctly
  handle escape sequences.
- Uses `saturating_sub` for bracket depth to prevent underflow.
2026-02-18 01:24:28 +01:00

100 lines
4.3 KiB
Rust

use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use crate::ast::nodes::{Node, UntypedKind};
use crate::ast::types::Identity;
/// Analyzes the AST to find which lambdas capture which variable declarations.
/// Returns a map: Declaration Identity -> List of Lambda Identities that capture it.
pub struct UpvalueAnalyzer;
impl UpvalueAnalyzer {
pub fn analyze(root: &Node<UntypedKind>) -> HashMap<Identity, Vec<Identity>> {
let mut capture_map: HashMap<Identity, HashSet<Identity>> = HashMap::new();
let mut scopes = vec![HashMap::new()]; // Root scope
Self::visit(root, &mut scopes, &mut capture_map, None);
// Convert HashSet back to sorted Vec for the final result
capture_map.into_iter()
.map(|(decl, lambdas)| (decl, lambdas.into_iter().collect()))
.collect()
}
fn visit(
node: &Node<UntypedKind>,
scopes: &mut Vec<HashMap<Rc<str>, Identity>>,
capture_map: &mut HashMap<Identity, HashSet<Identity>>,
current_lambda: Option<Identity>
) {
match &node.kind {
UntypedKind::Identifier(name) => {
// 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 depth > 0 {
// Captured from an outer scope!
if let Some(lambda_id) = &current_lambda {
capture_map.entry(decl_id.clone())
.or_default()
.insert(lambda_id.clone());
}
}
break;
}
}
}
UntypedKind::Def { name, value } => {
Self::visit(value, scopes, capture_map, current_lambda.clone());
if let Some(current) = scopes.last_mut() {
current.insert(name.clone(), node.identity.clone());
}
}
UntypedKind::Lambda { params, body } => {
let mut new_scope = HashMap::new();
for p in params {
new_scope.insert(p.clone(), node.identity.clone());
}
scopes.push(new_scope);
// The current node is the lambda causing captures in its body
Self::visit(body, scopes, capture_map, Some(node.identity.clone()));
scopes.pop();
}
UntypedKind::If { cond, then_br, else_br } => {
Self::visit(cond, scopes, capture_map, current_lambda.clone());
Self::visit(then_br, scopes, capture_map, current_lambda.clone());
if let Some(e) = else_br {
Self::visit(e, scopes, capture_map, current_lambda.clone());
}
}
UntypedKind::Assign { target, value } => {
Self::visit(target, scopes, capture_map, current_lambda.clone());
Self::visit(value, scopes, capture_map, current_lambda.clone());
}
UntypedKind::Call { callee, args } => {
Self::visit(callee, scopes, capture_map, current_lambda.clone());
for arg in args {
Self::visit(arg, scopes, capture_map, current_lambda.clone());
}
}
UntypedKind::Block { exprs } => {
for expr in exprs {
Self::visit(expr, scopes, capture_map, current_lambda.clone());
}
}
UntypedKind::Tuple { elements } => {
for el in elements {
Self::visit(el, scopes, capture_map, current_lambda.clone());
}
}
UntypedKind::Map { entries } => {
for (k, v) in entries {
Self::visit(k, scopes, capture_map, current_lambda.clone());
Self::visit(v, scopes, capture_map, current_lambda.clone());
}
}
UntypedKind::Nop | UntypedKind::Constant(_) | UntypedKind::Extension(_) => {}
}
}
}