feat(joinir): Phase 171-C-2 Trim pattern detection in LoopBodyCarrierPromoter
Implements the Trim pattern detection logic for carrier promotion:
- find_definition_in_body(): Iterative AST traversal to locate variable definitions
- is_substring_method_call(): Detects substring() method calls
- extract_equality_literals(): Extracts string literals from OR chains (ch == " " || ch == "\t")
- TrimPatternInfo: Captures detected pattern details for carrier promotion
This enables Pattern 5 to detect trim-style loops:
```hako
loop(start < end) {
local ch = s.substring(start, start+1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
start = start + 1
} else {
break
}
}
```
Unit tests cover:
- Simple and nested definition detection
- substring method call detection
- Single and chained equality literal extraction
- Full Trim pattern detection with 2-4 whitespace characters
Next: Phase 171-C-3 integration with Pattern 2/4 routing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -130,8 +130,17 @@ pub fn is_outer_scope_variable(
|
||||
scope: Option<&LoopScopeShape>,
|
||||
) -> bool {
|
||||
match scope {
|
||||
None => false, // No scope info → assume body-local
|
||||
// No scope information: be conservative but *not* over‑strict.
|
||||
// We treat unknown as body-local only when we have a LoopScopeShape
|
||||
// that explicitly marks it so (via body_locals / definitions).
|
||||
// Here we simply say "unknown" and let the caller decide.
|
||||
None => false,
|
||||
Some(scope) => {
|
||||
// If the variable is explicitly marked as body-local, it is NOT outer.
|
||||
if scope.body_locals.contains(var_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check 1: Is it a pinned variable (loop parameter or passed-in)?
|
||||
if scope.pinned.contains(var_name) {
|
||||
return true;
|
||||
@ -151,15 +160,27 @@ pub fn is_outer_scope_variable(
|
||||
// This supports loop patterns like:
|
||||
// local i = 0 (header)
|
||||
// loop(i < 10) {
|
||||
// ...
|
||||
// i = i + 1 (latch)
|
||||
// ...
|
||||
// i = i + 1 (latch)
|
||||
// }
|
||||
if def_blocks.iter().all(|b| *b == scope.header || *b == scope.latch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Any other definition pattern (e.g. body-only or body+header)
|
||||
// is treated as body-local / internal.
|
||||
return false;
|
||||
}
|
||||
|
||||
false
|
||||
// At this point:
|
||||
// - The variable is NOT in body_locals
|
||||
// - There is no explicit definition info for it
|
||||
//
|
||||
// This typically means "function parameter" or "outer local"
|
||||
// (e.g. JsonParserBox.s, .pos, etc.). Those should be treated
|
||||
// as OuterLocal for condition analysis, otherwise we wrongly
|
||||
// block valid loops as using loop-body-local variables.
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -338,6 +359,34 @@ mod tests {
|
||||
assert!(!is_outer_scope_variable("ch", Some(&scope)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_outer_scope_variable_function_param_like() {
|
||||
// Variables that are *not* marked as body_locals and have no explicit
|
||||
// variable_definitions entry represent things like function parameters
|
||||
// or outer locals. These must be treated as OuterLocal so that valid
|
||||
// conditions such as `p < s.length()` (with `s` a parameter) are
|
||||
// accepted by Pattern 2/4.
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
let scope = LoopScopeShape {
|
||||
header: BasicBlockId(0),
|
||||
body: BasicBlockId(1),
|
||||
latch: BasicBlockId(2),
|
||||
exit: BasicBlockId(3),
|
||||
pinned: BTreeSet::new(),
|
||||
carriers: BTreeSet::new(),
|
||||
body_locals: BTreeSet::new(),
|
||||
exit_live: BTreeSet::new(),
|
||||
progress_carrier: None,
|
||||
variable_definitions: BTreeMap::new(),
|
||||
};
|
||||
|
||||
assert!(
|
||||
is_outer_scope_variable("s", Some(&scope)),
|
||||
"Function parameter–like variable should be classified as OuterLocal"
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Phase 170-ultrathink: Additional Edge Case Tests (Issue #3)
|
||||
// ========================================================================
|
||||
|
||||
Reference in New Issue
Block a user