Recoil logo

Recoil

How the compiler works

  • Home
  • Getting Started
  • Guide
  • Reference
  • PARSE
  • State Machines
  • FFI & Systems
  • Ports
  • Packages
  • f00 & Embedded
  • Examples
  • Internals

On this page

Loading…

Under the hood

The Recoil compiler is written in Rebol and turns .rcl source into C, then hands that C to a target toolchain. This page sketches the pipeline and the intermediate forms for the curious and for contributors.

source
→
AST
→
borrow check
→
facts
→
IR
→
optimize
→
C → link
AST and IR are Rebol block/object structures and are compiler internals, not a stable external API. The shapes below are representative samples and can change between versions.

End-to-end stages

Orchestrated by compile in src/compiler.r3; the CLI wrapper is recoil.r3.

StageRole
Prepareload modules, parse (to-ast), parse the header, alpha-rename bindings → statement AST
Monomorphizeinstantiate generic functions/types where required
Borrow checkanalyze — ownership / move / borrow state over the AST
Fact analysisanalyze-facts — flow of refined types / constraints (post-borrow)
AST → IRast-to-ir — typed, C-oriented IR blocks (ir-* tags)
Optimizeoptimize — constant folding, dead-code elimination, expression cleanup, optional self-tail-call rewrite
Codegen prepcollect constants, register series/global/rule info, assemble header + C helpers, select the entry wrapper
Emit Cemit-c / emit-expr — IR → C source plus include/link metadata; entry shape (main, app_main, emscripten) chosen here
Buildwrite a temp .c and invoke the target toolchain (GCC-compatible, emcc, MSVC, or ESP-IDF) and link the runtime

The AST

The parser produces a top-level block of statement nodes. Ordinary bindings are assignment-shaped (x: make i32! 1 becomes a set path, not an old decl form); if/either/match are expression-capable; type definitions use make-def.

Representative statement nodes

TagShape
func-def[func-def name spec body]
set[set name value ...]
return[return value]
while / repeat[while cond body] / [repeat var limit body]
go[go body] (statement only)
defer / defer-error[defer body]
make-def[make-def name spec] (struct/enum/sum/c-struct/fsm)

Representative expression nodes

TagShape
call[call func-name args ...]
indirect-call[indirect-call fn-ptr args]
ns-call[ns-call head selector c-name args ...] (foreign)
make / cast[make type payload] / [cast type value]
path-get / path-setfield/index read & write
enum-get[enum-get enum-type variant] (enum/sum/FSM tag)

The IR

IR is a normalized, more C-oriented form produced by ast-to-ir after borrow checking and fact analysis. Tags are prefixed ir-*.

Representative statement IR

TagShape
ir-func[ir-func name spec body]
ir-assign[ir-assign name value type ...]
ir-if / ir-while / ir-forstatement conditional & loops
ir-go[ir-go task-name captures body]
ir-match[ir-match subject sum-type branches]
ir-defer / ir-defer-errordeferred cleanup

Representative expression IR

TagShape
ir-binop[ir-binop op left right]
ir-call / ir-indirect-calldirect & fn-ptr/closure calls
ir-either-expr / ir-match-exprvalue-position conditional & match
ir-block-exprexpression block with prefix statements and a final value
ir-vector-get/set, ir-struct-get/setindexed and field access
ir-parse / ir-rulecompiled parse expression & rule
ir-const[ir-const type value]

Function spec objects

Function specs are object-based and shared across the parser, borrow checker, and codegen. Closures and fn-ptr! values use the same model plus capture metadata.

make object! [
    name: 'function-name
    params: [
        {name: 'a type: 'i32! borrowed?: false}
        {name: 'msg type: 'string! borrowed?: true}
    ]
    return-spec: [i32!]
    annotations: [#export]
]

Diagnostics

Every compile-time failure is both a human-readable string and a structured object carrying a stage (parser, module, borrow-checker, ir, codegen, cli, build), a kind, a stable code, and an optional location. The user-facing format and code ranges are documented in the Reference.

Introspection

You can inspect each stage's output:

# print the parsed AST
r3 -s recoil.r3 -a file.rcl

# emit the generated C without building a binary
r3 -s recoil.r3 --transpile file.rcl

Within the compiler, compile/ir returns IR (/optimized? selects pre- vs post-optimize). For the per-stage source map and the full ir-* inventory, see the repository docs: compiler pipeline, AST reference, and IR reference.

↑

Recoil — an ownership-first, statically-typed language with a static borrow checker.

codeberg.org/rebolek/recoil · boleslav@brezovsky.eu · Apache-2.0