Documentation
¶
Overview ¶
Package compiler lowers the AST to bytecode (an ISeq tree).
Each method body and the program top level becomes one ISeq. Locals are resolved to flat slot indices here (Phase 0 has no closures, so each ISeq has a single flat local table; depth-addressed envs arrive with blocks in Phase 1, plan §6).
The flip-flop operator: a two-sided range literal written where a condition is expected (`x if (i == 4)..(i == 7)`). It is not a Range object but a piece of state that turns on when the left condition holds and off after the right one does — awk's and sed's range addressing.
MRI's parser marks such a range NODE_FLIP2 (`..`) or NODE_FLIP3 (`...`) by its syntactic position, and compile.c's compile_flip_flop (ruby/ruby v3_4_0:4607) lowers it against a per-occurrence slot allocated in the enclosing *local* iseq — ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) — so a block shares its state with every other invocation of that block, and two textually identical flip-flops in different iseqs do not interfere.
The generated sequence is MRI's, with one difference: MRI compiles it straight into the surrounding branch's then/else labels, while rbgo's conditions are ordinary values that the caller branches on, so this pushes true or false instead. rbgo's local slots are already depth-addressed and resolution stops at the first non-block scope, which is exactly MRI's local_iseq, so a slot allocated there has the lifetime the operator needs.
Compound assignment to an index (`a[i] op= v`) and to an attribute (`a.x op= v`), lowered so the receiver and the index arguments are evaluated exactly once and the expression's value is the assigned value — never what the `[]=` / `x=` setter happens to return.
The parser desugars both forms textually: `a[i] op= v` arrives as `a.[]=(i, a.[](i) op v)` and `a.x op= v` as `a.x=(a.x op v)`, with the receiver and index nodes SHARED (same AST pointers) between the read and the write. That shape is correct for value but wrong for effect: compiled literally it evaluates the receiver twice and every index argument twice. MRI does not: compile.c's compile_op_asgn1 evaluates the receiver and index once and reuses them with `dupn` (ruby/ruby v3_4_0 compile.c:9401), and compile_op_asgn2 does the same for an attribute with `dup` (compile.c:9535). Both then overwrite the reserved result slot with `setn`, so the expression yields the right-hand value (or, when `||=` / `&&=` short-circuits, the value already there).
rbgo's stack has no `dupn`/`setn`/`topn`, so the same effect is obtained with anonymous local slots as temporaries.
Index ¶
- func Compile(prog *ast.Program) (iseq *bytecode.ISeq, err error)
- func CompileWithEncoding(prog *ast.Program, srcEnc string) (iseq *bytecode.ISeq, err error)
- func CompileWithLocals(prog *ast.Program, localNames []string) (iseq *bytecode.ISeq, err error)
- func MagicSourceEncoding(src string) string
- type Compiler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Compile ¶
Compile lowers a Program into the top-level ISeq, treating string literals as UTF-8 (the default source encoding).
func CompileWithEncoding ¶
CompileWithEncoding lowers a Program whose source declared srcEnc (a canonical encoding name, or "" for UTF-8) via a `# encoding:` magic comment, so string literals carry that encoding.
func CompileWithLocals ¶
CompileWithLocals lowers prog for a Binding eval: it compiles in a child scope whose parent holds the binding's locals (by slot), so references to them resolve at depth 1 — reads and writes reach the binding's environment when the ISeq is run with that environment as the parent — while any new locals are scratch in the child frame.
func MagicSourceEncoding ¶
MagicSourceEncoding returns the canonical registry encoding name declared by a `# encoding:` / `# coding:` magic comment on the first line of src (or the second when the first is a shebang), or "" for UTF-8 or no declaration. Only the encodings the negotiation cares about are recognised by canonical spelling; anything else (including plain UTF-8) yields "" so literals keep the default.