On this page:
7.1 Cleaning and reading
7.2 Parse tree normalization
7.3 Macro compilation
7.4 Runtime support
7.5 Conservative optimization

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.