ilusm.dev

ir

Intermediate representation - node constructors for every AST type (program, function, definition, return, if, while, call, id, number, string, list, object, lambda, binary op, dot access, index), an identity from_ast pass, constant-folding and dead-code-elimination optimisation passes, program validation, bytecode lowering, and pretty-print dump.

Load with: use ir

What this module does

ir bridges the parser's AST and the bytecode backend. Because the ilusm AST is already structurally IR-compatible, ir.from_ast is an identity pass. The interesting work happens in the optimisation passes:

  • Constant folding (ir.opt) - evaluates binary operations on literal numbers at compile time, e.g. 2 + 3 becomes 5 in the IR.
  • Dead-code elimination (ir.dce) - truncates any statements that follow a ret node in a block.

ir.validate checks that a program node is structurally sound before lowering, and ir.to_bc delegates to the compiler to produce bytecode.

Quick example

use ir
use prs

src = fs.rd("src/app.ilu")
ast = prs.parse(src)

# Convert to IR (identity pass here, but stable API)
prog = ir.from_ast(ast)

# Optimise
prog = ir.opt(prog)   # constant folding
prog = ir.dce(prog)   # dead code elimination

# Validate
r = ir.validate(prog)
if !r.ok: err(r.er)

# Dump human-readable
prn(ir.dump(prog))

# Lower to bytecode
bc = ir.to_bc(prog)

Functions

Node constructors

ir.prog(stmts)

Creates a program node {t:"prg", st:stmts}.

ir.fn(name, params, body)

Function definition node {t:"fun", n, pa, bd}.

ir.defn(name, val)

Variable definition node {t:"def", n, v}.

ir.rtn(val)

Return node {t:"ret", v}.

ir.ifs(cond, yes, no)

If-else node {t:"ifs", cd, th, el}.

ir.whl(cond, body)

While loop node {t:"whl", cd, bd}.

ir.call(fn_expr, args)

Function call node {t:"ca", fn, ar}.

ir.id(name)

Identifier node {t:"id", n}.

ir.num(v)

Numeric literal node {t:"nm", v}.

ir.str(v)

String literal node {t:"st", v}.

ir.list(items)

List literal node {t:"ls", el}.

ir.obj(pairs)

Object literal node {t:"ob", ps}.

ir.lam(params, body)

Lambda node {t:"lm", pa, bd}.

ir.bin(op, l, r)

Binary operation node {t:"bn", op, l, r}.

ir.dot(obj, field)

Dot access node {t:"dt", ob, f}.

ir.idx(obj, key)

Index access node {t:"ix", ob, k}.

Passes

ir.from_ast(prog)

Identity pass - returns the program unchanged. Exists as a stable hook for future transformations.

ir.opt(prog)

Constant-folding pass. Recursively evaluates binary operations on numeric literals (+, -, *, /, %). Returns an optimised program node.

ir.dce(prog)

Dead-code elimination pass. Truncates any statements after the first ret node in each block.

Validation, lowering, and dump

ir.validate(prog)

Checks structural validity: must have type "prg" and a non-nil st field. Returns {ok: tru, er: nil} or {ok: fls, er: message}.

ir.to_bc(prog)

Lowers the IR program to bytecode by delegating to cmppr (the compiler's program-compile function).

ir.dump(prog)

Returns a human-readable indented text representation of the IR using ast.pp.

Notes

  • Requires trl and ast.