feat(control_tree): Phase 124 add reads to StepTreeFacts

- Add reads: BTreeSet<String> to StepTreeFacts
- Add add_read() API and merge_reads() in merge()
- Add extract_variables_from_ast() helper for AST traversal
- Extract reads from:
  - If/Loop condition AST
  - Return value AST
  - All Variable nodes recursively (BinaryOp, UnaryOp, FunctionCall, MethodCall, FieldAccess, Index, Assignment RHS, Print)
- SSOT: extract_variables_from_ast() is the single source for reads collection
This commit is contained in:
nyash-codex
2025-12-18 05:58:08 +09:00
parent fb4ec2c2bf
commit 95b25e54ad
2 changed files with 83 additions and 5 deletions

View File

@ -471,7 +471,7 @@ fn extract_facts_from_tree(root: &StepNode, features: &StepTreeFeatures) -> Step
facts facts
} }
/// Walk StepNode tree to collect facts (Phase 120) /// Walk StepNode tree to collect facts (Phase 120, Phase 124)
fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) { fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) {
match node { match node {
StepNode::Block(nodes) => { StepNode::Block(nodes) => {
@ -481,18 +481,23 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) {
} }
StepNode::If { StepNode::If {
cond, cond,
cond_ast,
then_branch, then_branch,
else_branch, else_branch,
.. ..
} => { } => {
facts.add_cond_sig(cond.to_compact_string()); facts.add_cond_sig(cond.to_compact_string());
// Phase 124: Extract reads from condition AST
extract_variables_from_ast(&cond_ast.0, facts);
walk_for_facts(then_branch, facts); walk_for_facts(then_branch, facts);
if let Some(else_branch) = else_branch { if let Some(else_branch) = else_branch {
walk_for_facts(else_branch, facts); walk_for_facts(else_branch, facts);
} }
} }
StepNode::Loop { cond, body, .. } => { StepNode::Loop { cond, cond_ast, body, .. } => {
facts.add_cond_sig(cond.to_compact_string()); facts.add_cond_sig(cond.to_compact_string());
// Phase 124: Extract reads from condition AST
extract_variables_from_ast(&cond_ast.0, facts);
walk_for_facts(body, facts); walk_for_facts(body, facts);
} }
StepNode::Stmt { kind, .. } => { StepNode::Stmt { kind, .. } => {
@ -506,10 +511,16 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) {
if let Some(name) = target.as_ref() { if let Some(name) = target.as_ref() {
facts.add_write(name.clone()); facts.add_write(name.clone());
} }
// Note: Assign RHS is not in StepStmtKind
// We rely on If/Loop condition AST for reads extraction
} }
StepStmtKind::Print => {} StepStmtKind::Print => {}
StepStmtKind::Return { .. } => { StepStmtKind::Return { value_ast } => {
facts.add_exit(ExitKind::Return); facts.add_exit(ExitKind::Return);
// Phase 124: Extract reads from return value AST
if let Some(ast) = value_ast {
extract_variables_from_ast(&ast.0, facts);
}
} }
StepStmtKind::Break => { StepStmtKind::Break => {
facts.add_exit(ExitKind::Break); facts.add_exit(ExitKind::Break);
@ -546,6 +557,60 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) {
} }
} }
/// Extract Variable names from AST (Phase 124: reads collection)
///
/// SSOT for reads extraction:
/// - Recursively walk AST tree
/// - Add Variable { name } to facts.reads
/// - Ignore other node types
fn extract_variables_from_ast(ast: &ASTNode, facts: &mut StepTreeFacts) {
match ast {
ASTNode::Variable { name, .. } => {
facts.add_read(name.clone());
}
// Recursively walk binary/unary operations
ASTNode::BinaryOp { left, right, .. } => {
extract_variables_from_ast(left, facts);
extract_variables_from_ast(right, facts);
}
ASTNode::UnaryOp { operand, .. } => {
extract_variables_from_ast(operand, facts);
}
// Function calls
ASTNode::FunctionCall { arguments, .. } => {
for arg in arguments {
extract_variables_from_ast(arg, facts);
}
}
// Method calls
ASTNode::MethodCall { object, arguments, .. } => {
extract_variables_from_ast(object, facts);
for arg in arguments {
extract_variables_from_ast(arg, facts);
}
}
// Field access
ASTNode::FieldAccess { object, .. } => {
extract_variables_from_ast(object, facts);
}
// Array/Index access
ASTNode::Index { target, index, .. } => {
extract_variables_from_ast(target, facts);
extract_variables_from_ast(index, facts);
}
// Assignment (RHS only)
ASTNode::Assignment { value, .. } => {
extract_variables_from_ast(value, facts);
}
// Print
ASTNode::Print { expression, .. } => {
extract_variables_from_ast(expression, facts);
}
// Ignore literals, keywords, and other non-variable nodes
_ => {}
}
}
fn merge_features(mut a: StepTreeFeatures, b: StepTreeFeatures) -> StepTreeFeatures { fn merge_features(mut a: StepTreeFeatures, b: StepTreeFeatures) -> StepTreeFeatures {
a.has_if |= b.has_if; a.has_if |= b.has_if;
a.has_loop |= b.has_loop; a.has_loop |= b.has_loop;

View File

@ -1,7 +1,7 @@
//! StepTreeFacts - raw structural facts extraction (Phase 120) //! StepTreeFacts - raw structural facts extraction (Phase 120, Phase 124)
//! //!
//! Responsibility: //! Responsibility:
//! - Collect raw structural facts from StepNode tree (exits/writes/required_caps/cond_sig) //! - Collect raw structural facts from StepNode tree (exits/writes/reads/required_caps/cond_sig)
//! - NO formatting, NO decision-making, NO signature generation //! - NO formatting, NO decision-making, NO signature generation
//! - Pure "facts only" - data collection without interpretation //! - Pure "facts only" - data collection without interpretation
//! //!
@ -9,6 +9,10 @@
//! - Facts are collected during tree traversal //! - Facts are collected during tree traversal
//! - BTreeSet for deterministic iteration (order stability) //! - BTreeSet for deterministic iteration (order stability)
//! - No dependency on contract or signature logic //! - No dependency on contract or signature logic
//!
//! Phase 124 Changes:
//! - Added reads: BTreeSet<String> for variable references
//! - reads tracks Variable(name) occurrences in expressions, conditions, and assignments
use crate::mir::control_tree::{ExitKind, StepCapability}; use crate::mir::control_tree::{ExitKind, StepCapability};
use std::collections::BTreeSet; use std::collections::BTreeSet;
@ -20,6 +24,9 @@ pub struct StepTreeFacts {
pub exits: BTreeSet<ExitKind>, pub exits: BTreeSet<ExitKind>,
/// Variable writes (Local declarations + Assignment targets) /// Variable writes (Local declarations + Assignment targets)
pub writes: BTreeSet<String>, pub writes: BTreeSet<String>,
/// Variable reads (Variable references in expressions, conditions, assignments)
/// Phase 124: Tracks all Variable(name) occurrences for Return(Variable) support
pub reads: BTreeSet<String>,
/// Required capabilities (structural features like NestedLoop, TryCatch, etc.) /// Required capabilities (structural features like NestedLoop, TryCatch, etc.)
pub required_caps: BTreeSet<StepCapability>, pub required_caps: BTreeSet<StepCapability>,
/// Condition signatures (compact string representations of if/loop conditions) /// Condition signatures (compact string representations of if/loop conditions)
@ -43,6 +50,11 @@ impl StepTreeFacts {
self.writes.insert(var); self.writes.insert(var);
} }
/// Add a variable read (Phase 124)
pub fn add_read(&mut self, var: String) {
self.reads.insert(var);
}
/// Add a required capability /// Add a required capability
pub fn add_capability(&mut self, cap: StepCapability) { pub fn add_capability(&mut self, cap: StepCapability) {
self.required_caps.insert(cap); self.required_caps.insert(cap);
@ -57,6 +69,7 @@ impl StepTreeFacts {
pub fn merge(&mut self, other: StepTreeFacts) { pub fn merge(&mut self, other: StepTreeFacts) {
self.exits.extend(other.exits); self.exits.extend(other.exits);
self.writes.extend(other.writes); self.writes.extend(other.writes);
self.reads.extend(other.reads); // Phase 124: merge reads
self.required_caps.extend(other.required_caps); self.required_caps.extend(other.required_caps);
self.cond_sig.extend(other.cond_sig); self.cond_sig.extend(other.cond_sig);
} }