Sort out and refactor reduction scanner system #2
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Reduction System Design Notes
Problem at hand
The reduction scanner needs to support hierarchical rule groups (precedence levels) while remaining composable and traceable. A flat rule list (reduction-scanner-1/2) works for simple cases but doesn't express same-level POSITION_MAJOR semantics cleanly — declaration order within a level shouldn't affect which operator wins.
What reduction-scanner-3 demonstrates
Rules are organized into precedence levels via
sub_system(...). The outer scanner is RULE_MAJOR — it tries each level in order until one matches. Each level is aSub_Scan_Rulewrapping an inner POSITION_MAJOR scanner over its own rules. This gives correct left-to-right associativity within a level regardless of rule declaration order.Current implementation — what we have
Classes in the experiment:
Rule(condition, handler)— pairs a condition with an opaque handler function.match(sequence)returns aRule_Matchor null.Rule_Match(rule, match)— wraps a rule and its condition match.actiongetter returnsrule.handler.Sub_Scan_Rule(sub_system)— delegatesmatch()to inner scanner'sfind_reduction_candidate. Returns aSub_Scan_Rule_Match.Sub_Scan_Rule_Match(rule, sub_scan_candidate)— wraps the outer rule and the inner candidate.actiongetter chains intosub_scan_candidate.match.action.matchgetter chains intosub_scan_candidate.match.match.In
reduction-scanner.mjs:find_reduction_candidate(sequence)— returns{ sequence, rule, match }wherematchis aRule_Matchperform_reduction— destructures candidate, callsmatch.action({ reduction_system, rule, sequence, match: match.match })Migration: positional → keyed action callbacks
Previously actions were called as
rule.action(scanner, sequence, match)— positional arguments. This doesn't scale as the context grows and makes refactoring fragile. The current direction is a single keyed object:This lets handlers destructure only what they need, and new fields can be added to the context without breaking existing handlers.
Problems with current shapes
.match—candidate.matchis aRule_Match, so the underlying condition match iscandidate.match.match. Two levels of the same name.ruleis redundant —candidate.ruleandcandidate.match.ruleare the same object in normal cases.Sub_Scan_Rule_Matchchains into the inner candidate via getters, but if the inner candidate gains new fields there's no explicit decision about which propagate outward.Proposed unified approach
Reduction_Candidate(match_start, match_end, rule_chain, match, sequence)A single defined shape returned by any
rule.match(sequence), at any nesting level:match_start,match_end— position in sequence, same as the leaf condition matchrule_chain— array of rules from outermost to innermost,rule_chain.at(-1)is the leaf rule that actually matchedmatch— the raw condition match from the leaf conditionsequence— reference to the sequence being reduced (useful in action context and error reporting)Rule.match(sequence)— creates a fresh candidate:Sub_Scan_Rule.match(sequence)— gets inner candidate, nests it:perform_reduction— dispatches via leaf rule, passes full candidate as context:Action handlers receive
{ reduction_system, match_start, match_end, rule_chain, match, sequence }— fully traceable, no positionals, nesting is transparent.Traceability via
rule_chainFor a nested match on
10 ^ 5:rule_chain[0]identifies the precedence level,rule_chain.at(-1)is the specific rule. Arbitrary nesting depth works the same way — always walk from outermost to leaf. Error messages can render the full chain without manually traversing the sub-system graph.Compatibility with flat rule systems
The flat case (no sub-systems) produces candidates with
rule_chain = [rule]— a chain of length one. All existing experiment patterns still work, just with the new candidate shape and keyed action callbacks.