Lowering to Bytecode
Why lower?
- AST is ideal for analysis; bytecode is ideal for execution
- linear IR reduces execution complexity
- deterministic output improves debugging and testing
Core contract
- node-kind -> deterministic instruction sequence
- preserve evaluation order
- preserve side effects
- preserve errors where possible
Stack-bytecode pattern
- emit operands before operators (postfix/RPN)
- unary:
[operand] OpNot/OpNeg - binary:
[left] [right] OpAdd/OpSub/... - identifiers become
LOAD, not special-cased pushes
Calls and scope
- lower arguments in source order
CALL name arity- blocks with locals:
ENTER_SCOPEthen body thenEXIT_SCOPE
Determinism goals
- same AST always emits same instructions
- roundtrip checks:
AST -> BC -> BC1 -> ASTbehavior - clear failure on unknown node kinds
Why this matters
- wrong order often returns “valid” but wrong answers
- wrong scoping leaks locals
- stable lowering allows you to evolve to register IR later
Quick checks
1 + 2 * 3becomes postfix emit order(a + b) / cemitsADDbeforeDIVf(1, x, 2)emits three arg loads, then call- block enters/exits scope explicitly
1 / 1