Compile to Bytecode v3

hard · compilers, bytecode, lowering, closures

Compile to Bytecode v3

Lower a v3 Program AST into stack-based bytecode with closures, arrays, maps, and indexing.

Function signature

func Compile(prog *Program) Bytecode

Bytecode types

Use the provided types:

  • Bytecode with Main []Instr and Functions []Function
  • Function with ID, Name, Params, and Code []Instr
  • Instr with Op, Int, and Str

Int is used for numbers, jump targets, and arity. Str is used for string literals and variable names.

Instruction rules

Literals / variables

  • number: PUSH_NUM <int>
  • string: PUSH_STR <string>
  • bool: PUSH_BOOL <0|1>
  • nil: PUSH_NIL
  • identifier: LOAD <name>

Unary / binary

  • unary -: NEG
  • unary !: NOT
  • + - * / == != < <= > >= map to their matching opcodes

Arrays / maps / indexing

  • array literal: push each element, then MAKE_ARRAY <count>
  • map literal: push key then value for each pair, then MAKE_MAP <count>
  • index expression: push target, push index, GET_INDEX

Statements

  • let / var: evaluate init (or push nil), then DEFINE_VAR <name>
  • const: evaluate init, then DEFINE_CONST <name>
  • assignment to identifier: evaluate expr, then STORE <name>
  • assignment to index: push target, index, value, then SET_INDEX
  • expression statement: evaluate expr, then POP
  • block: ENTER_SCOPE, compile statements, EXIT_SCOPE
  • return: evaluate expr (or push nil), then RETURN

Control flow

  • if:
    • compile condition
    • JUMP_IF_FALSE <else>
    • compile then-block
    • JUMP <end>
    • compile else-block (if any)
  • while:
    • loop start label
    • compile condition
    • JUMP_IF_FALSE <end>
    • compile body
    • JUMP <start>

Jumps use absolute instruction indexes in the current code slice.

Functions and closures

  • Each fn statement creates a Function with a unique ID.
  • In the surrounding code, emit MAKE_CLOSURE <id> then DEFINE_CONST <name>.
  • Functions can be nested; compile them where they appear.
  • Function bodies are compiled like statement lists (no extra scope wrapper).
  • If the last instruction is not RETURN, append PUSH_NIL + RETURN.

Calls

  • Compile the callee expression, then each argument, then emit CALL <arity>.

Notes

  • Assume the AST is already valid.
  • Keep the compiler readable and straightforward.

Before implementation

Use the module’s worked reconstruction and independent debugging lab to predict an intermediate artifact first. Preserve one failing counterexample while implementing the stated API. The lesson’s transfer task uses a fresh case after the guided trace.

Run tests to see results
No issues detected
    Join Discord