4.7 KiB
MUX Parser Study
Standalone tools for studying the TinyMUX expression parser.
See ../docs/design-parser-compatibility.md for the compatibility analysis
and ../docs/parser-escape-oracle.md for the focused escape oracle.
For end-to-end traced command behavior, see
../docs/command-escape-oracle.md.
Contents
tokenize.cpp— Stage 1: tokenizer (flat token stream)parse.cpp— Stage 2: recursive-descent parser (AST)test_corpus.txt— Test expressionsescape_oracle_cases.txt— focused cross-profile escape corpusrun_escape_oracle.sh— runner for the focused escape corpusborrowed-stream-semantics.md— reduced 2.13/Penn parser mechanismsstream_passes.cpp— tiny pass-by-pass stream vs frozen-token modelhammer_refrozen.sh— prints core deferred-boundary cases across modelsMakefile— Build rules
Usage
make
echo 'add(1,mul(2,3))' | ./tokenize
echo '[setq(0,hello)]%q0 world' | ./parse
./parse < test_corpus.txt
echo '\\\\% capacity' | ./eval --profile mux214
echo '[switch(1,1,{\\\\% capacity})]' | ./eval --profile mux213
echo '%xg' | ./eval --profile penn
echo '\\\\% capacity' | ./stream_passes --profile mux213 --model stream --passes noeval,eval
echo '\\\\% capacity' | ./stream_passes --profile mux213 --model frozen --passes noeval,eval
./hammer_refrozen.sh
./run_escape_oracle.sh
RUN_EXPLORATORY_MUX213=1 ./run_escape_oracle.sh
Stages
-
Tokenizer — Character-level scanning that mirrors eval.cpp's isSpecial tables. Identifies literals, function names, brackets, braces, %-substitutions, escapes, and structural characters.
-
Parser — Recursive-descent parser that builds an AST from the token stream. Handles static function calls (FuncCall), dynamic/computed calls (DynCall), eval brackets, brace groups, and nested argument lists.
-
Evaluator — Tree-walking evaluator for pure expressions. Supports ~50 builtin functions (arithmetic, string, list, logic, comparison, registers). Demonstrates that pure-expression MUX softcode CAN be evaluated from an AST.
echo '[add(1,mul(2,3))]' | ./eval echo '[if(eq(%0,hello),yes,no)]' | ./eval echo '[sort(3 1 4 1 5)]' | ./eval ./eval --ast # show AST alongside outputKey finding: FN_NOEVAL functions (iter, switch, case, if) need deferred argument evaluation. The evaluator must pass unevaluated AST subtrees to these handlers rather than pre-evaluated strings. This is solvable but requires a different dispatch mechanism.
Purpose
This is an exploration tool, not production code. The goal is to understand the existing parser well enough to determine whether a Ragel scanner + recursive-descent parser can reproduce its behavior.
Compatibility Profiles
The study tools now support --profile mux214|mux213|penn:
mux214models the current AST tokenizer/evaluator shape.mux213is a study profile for 2.13-like behavior, but some older deferred-noeval rows were wrong and have now been removed from the focused oracle. Treat live-engine checks and the focused oracle as the spec, not the study profile.pennrecognizes Penn-only%behavior where we have real-engine data, including%,%wa,%iL,%xg,%cg, and%=.
Focused examples:
echo '\\\\% capacity' | ./eval --profile mux214
# -> % capacity
echo '[switch(1,1,{\\\\% capacity})]' | ./eval --profile mux213
# study profile output may still lag the corrected oracle; use the
# focused oracle and live-engine checks as the spec here
echo '%xg' | ./eval --profile penn
# -> thing
These profiles are intentionally incomplete. They are meant to help identify where parser behavior differs, not to claim full engine compatibility.
Escape Oracle
The general evaluator tests in test_eval.sh are still useful, but they
mix parser behavior with ordinary function coverage. For the compatibility
work, use the focused oracle:
./run_escape_oracle.sh
That runner:
- checks the narrow escape/percent corpus across
mux214,mux213, andpenn - validates
mux214andpennby default - treats
mux213as exploratory unlessRUN_EXPLORATORY_MUX213=1is set - prints which cases still need live-server verification
- is allowed to disagree with the exploratory
mux213study profile if the profile has drifted from the corrected oracle - keeps the current effort centered on parser semantics instead of the whole evaluator surface area
The intended workflow is:
- keep adding only parser-semantic cases to
escape_oracle_cases.txt - remove or downgrade rows when the live basis is unclear instead of keeping stale guesses in the oracle
- mark cases
confirmedonly after a live engine check - use the resulting matrix to drive AST-engine changes in
mux/