Module rewriters

Module rewriters 

Source
Expand description

AST-style rewriters consumed by super::MilkEvaluator::preprocess_expression.

Each rewrite_* / wrap_* function takes an expression string and returns a normalised form evalexpr can parse. The passes are idempotent and are run in the order documented in super::preprocess.

ConstantsΒ§

KNOWN_ARITY πŸ”’
Fixed arity for MD2 EEL2 builtins whose argument count is known up front. Used by rewrite_arity_mismatched_semis to decide when a mixed , + ; argument list can be unambiguously normalised.

FunctionsΒ§

arg_already_wrapped πŸ”’
Return true when s (already a comma-piece) consists of a single (...) group at depth 0. Detects the idempotent case where wrap_semi_chain_args has already run.
call_name_before_paren πŸ”’
Return the identifier bytes immediately preceding paren_pos (skipping inter-token whitespace). None if the byte before ( after whitespace isn’t an identifier β€” i.e. ( is a grouping paren.
call_name_skips_semi_rewrite πŸ”’
Return true when the identifier preceding paren_pos names a builtin that uses ;-chain semantics inside its args (loop, exec2, exec3, while).
count_top_level_byte πŸ”’
Count top-level target occurrences in s (at paren depth 0).
find_amp_pipe_operands πŸ”’
Locate the first & or | in s that is a single (non-&& / non-||, non-&= / non-|=) operator, plus the byte offsets of its left and right operand spans. Returns None if no eligible operator remains or either operand boundary can’t be resolved.
find_double_op_pos πŸ”’
Locate the next <op_char><op_char> (i.e. && or ||) in s.
find_left_operand_start πŸ”’
find_logical_left_operand_start πŸ”’
Walk left from op_pos to find the start of the logical operator’s left operand. Spans comparison/arithmetic ops at depth 0 (so rg4 > 1.2 && change2 gives rg4 > 1.2 as the left operand). Stops at the nearest depth-0 (, ,, ;, &&, ||, or assignment =.
find_logical_right_operand_end πŸ”’
Walk right from op_end (the byte just past the 2-char operator) to find the end of the right operand. Mirror of find_logical_left_operand_start.
find_right_operand_end πŸ”’
find_unary_bang_operand πŸ”’
Locate the next unary ! plus its operand end in s, starting at byte offset from.
is_unary_bang_context πŸ”’
True if the byte at pos is ! in unary position.
known_arity_for πŸ”’
Return the fixed arity of name if known.
replace_top_level_semis_with_commas πŸ”’
Single-pass ;β†’, substitution at paren depth 0 of s.
rewrite_amp_pipe_to_band_bor πŸ”’
Rewrite a & b to band(a, b) and a | b to bor(a, b), leaving &&, ||, &=, |= untouched. MD2 expression syntax treats single & / | as numeric short-circuit AND/OR (returning 0.0/1.0), not as bitwise ops, so we map them onto our registered band/bor builtins which already preserve those semantics. Operates iteratively: each pass rewrites one operator from the left, then the next scan picks up any nested &/| that the previous edit’s right operand contained.
rewrite_arity_mismatched_semis πŸ”’
Arity-aware companion of rewrite_semis_in_call_args. Walks every function-call paren group and, for builtins with a known fixed arity, converts top-level ; separators inside the arg list to , when doing so unambiguously yields the expected arg count.
rewrite_chain_assignments πŸ”’
Rewrite a = b = … = <expr> into <last> = <expr>; <prev> = <last>; … so evalexpr β€” which makes = return Empty β€” doesn’t choke on the outer assignments. Detected via a strict left-to-right scan: each step requires a bare identifier followed by = and another bare identifier followed by =, no operators between them. Real-world presets in the corpus: dx=dx=(y*dx)*cos(time)*… (the LHS is repeated β€” a common author typo, but evalexpr still has to accept).
rewrite_chained_at_this_level πŸ”’
Apply the chained-comparison rewrite to a single comma-piece.
rewrite_chained_comparisons πŸ”’
Rewrite Python-style chained comparisons (A > B <= C <= 1) into the explicit AND-chain ((A > B) && (B <= C) && (C <= 1)) that evalexpr’s left-associative comparison precedence can type-check.
rewrite_logical_op πŸ”’
Inner rewriter for one operator family. op_char is b'&' (for &&) or b'|' (for ||); func_name is the destination builtin ("band" / "bor").
rewrite_logical_to_bandbor πŸ”’
Rewrite a && b to band(a, b) and a || b to bor(a, b), preserving the corpus shape Float && Float (which evalexpr 13 strict-types as Boolean op Boolean and rejects when the operands come back as Float). Two-pass with operator-precedence respect: (1) && is rewritten first (higher precedence in C/EEL2/evalexpr β€” a || b && c parses as a || (b && c)), then (2) || is rewritten.
rewrite_semis_in_call_args πŸ”’
Walk s and, for every function-call paren group, rewrite top-level ; in the args to , when the args contain no top-level , already. Calls named loop / exec2 / exec3 / while are skipped because their interceptors rely on ;-chain syntax inside the arg body.
rewrite_unary_bang_to_bnot πŸ”’
Rewrite unary !x to bnot(x). evalexpr 13 type-checks the ! operator as Boolean β†’ Boolean; MD2 EEL2 treats it as numeric NOT (returns 1.0 if x == 0.0, else 0.0) β€” same semantics as bnot.
top_level_comparison_ops πŸ”’
Return a list of (start, end) byte offsets for every top-level comparison operator in s (<, >, <=, >=, ==, !=).
wrap_bare_cmp_assignment πŸ”’
Wrap <ident> = <rhs> when <rhs> contains a top-level comparison operator. evalexpr’s = type-checks the RHS against the LHS’s stored type; auto-init seeds every variable as Float(0.0), so assigning a bare Boolean from rand(100) >= 30 blows up at runtime.
wrap_bare_cmp_assignment_one πŸ”’
Single-statement helper for wrap_bare_cmp_assignment. Returns Some(rewritten) if a <ident> = <rhs> shape with top-level cmp in <rhs> was found, None otherwise (caller passes through).
wrap_boolean_assignment_rhs πŸ”’
Rewrite Boolean-producing parenthesised comparisons to a Float-returning milkif(<cmp>, 1, 0) call. evalexpr is strictly typed: a (a > b) produces Boolean, which then dies on *///+ against a Float neighbour.
wrap_chain_args_in_parens πŸ”’
Wrap each comma-separated arg of a function call whose body still contains a top-level ; in (...), so evalexpr parses the arg as a single value-producing statement chain.
wrap_paren_balanced_cmp πŸ”’
Paren-balanced sibling of wrap_boolean_assignment_rhs. The regex pass only matches (cmp) whose interior contains no ( or ,; the walker variant tracks paren depth and catches (lev1-gmegabuf(1)>0) and (y<=(0.4+0.1*cos(mang))). Recurses inner-first so a chain (a > (b > c)) gets each level wrapped. Skips ( that follow an identifier (function-call form).
wrap_semi_chain_args πŸ”’
Split args on each top-level , and wrap any arg whose interior contains a top-level ; in (...). Helper for wrap_chain_args_in_parens.