7 Compilation pipeline
This implementation strategy is to compile INTERCAL into ordinary Racket by macro expansion. The pipeline has five stages.
7.1 Cleaning and reading
"intercal.rkt" reads the source file, applies the shared clean-intercal-source routine from "sick.rkt", tokenizes the cleaned source, parses it with brag, normalizes the parse tree, and emits a Racket module whose body invokes the macro backend centered on sick-program.
At this point the INTERCAL program is no longer opaque text. It is already a structured Racket datum.
7.2 Parse tree normalization
"ick-normalize.rkt" converts the dense brag tree into a compact, explicit IR. For example, a line such as:
(20) PLEASE DO (40) NEXT |
normalizes to a form shaped like:
(20 (please (next 40))) |
Normalization performs several important tasks:
collapse redundant parse layers.
map keywords to symbolic operation names.
rewrite packed SUB chains into explicit subscript lists.
normalize modifiers like NOT, ONCE, and AGAIN.
fix unary precedence so the macro backend sees a stable IR.
7.3 Macro compilation
The macros in "sick.rkt" consume the normalized program. The key expansion step is sick-program-core, which:
collects all program variables.
builds maps from labels to runtime line numbers.
derives which lines can be abstained, which variables can be ignored, and which labels can be hijacked by COME FROM.
builds gerund-to-line maps for abstention and reinstatement.
generates one runtime dispatch clause per INTERCAL line.
The resulting Racket code is a state machine with explicit tables for abstain state, ignore state, line labels, and the NEXT stack.
7.4 Runtime support
The runtime portion of "sick.rkt" implements:
INTERCAL error reporting.
array storage and bounds checking.
bitwise operators and width-sensitive helpers.
numeric and tape-style I/O.
control-flow support for NEXT, RESUME, and COME FROM.
debugging and tracing hooks.
Semantics that are awkward in a direct compiler, such as delayed COME FROM after a resumed NEXT target, are encoded directly in these runtime tables and helpers.
7.5 Conservative optimization
This implementation does some compile-time optimization before handing the result to the normal Racket compiler. In particular, it removes:
abstain checks for lines that provably cannot be abstained.
ignore-table lookups for variables that are never ignored.
COME FROM dispatch checks for labels that can never be hijacked.
It also uses optimized implementations of hot arithmetic operators instead of list-based bit manipulation.
These optimizations are intentionally conservative: they only remove code when the frontend can prove that a check is unnecessary.