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.
Run tests to see results
No issues detected