A Vircon32-assembly targeting lua compiler
| Spanish version / en español | French version / en francais |
Target Architecture: Vircon32 Fantasy Console (32-bit)
Implementation Language: C (Flex/Bison + Custom Semantic Emitter)
Repository: github.com/wedge1020/v32lua
API Reference: doc/API.md — the full native Vircon32 API (sound, graphics, input, tilemaps, memory card, and raw I/O ports).
v32lua is a Lua compiler written in C that targets the Vircon32 fantasy
console. Instead of embedding a heavyweight bytecode interpreter, v32lua
parses Lua source code and compiles it directly into native Vircon32
assembly, and also produces the XML cartridge definition the console’s
toolchain needs to package a ROM.
While it is not yet complete, one aim of development is to make v32lua a
substitute (by no means a replacement) for the Vircon32 C Compiler in the
Vircon32 development stack. Basically, pick your choice of language — C or
Lua — and once it’s compiled down to assembly, you proceed with the build
regardless of implementation language. As a result, efforts have been made
to mimic various behaviours of the Vircon32 C Compiler to make compiler
substitution more transparent.
Designed from the ground up with retro fantasy-console constraints in mind,
v32lua features zero-cost hardware intrinsics, custom
NaN-boxing, and — beyond the native Vircon32 API — two
API compatibility layers so that carts written for TIC-80 and PICO-8
can compile and run on Vircon32 hardware with little to no source
modification.
+------------------+ +-------------------+ +------------------+
| Source (.lua) | --> | Lexer & Parser | --> | AST Construction |
+------------------+ | (Flex / Bison) | +------------------+
+-------------------+ |
v
+------------------+ +-------------------+ +------------------+
| Cartridge Config | <-- | Vircon32 Assembly | <-- | Semantic Emitter |
| (.xml) | | Emitter (.asm) | | |
+------------------+ +-------------------+ +------------------+
--#...)
__asm__ & __rawasm__)gcc/clang + make) capable of building flex/bison
generated sources.flex and bison themselves, to regenerate the lexer/parser if you’re
building from the split source tree rather than a pre-generated release.packrom) if you intend to go all
the way from .lua to a runnable .v32 cartridge, plus
v32sim if you want to run or debug
the results (also used for unit tests).The repository includes a root-level Makefile that manages building the compiler binary, running the test suite, and general project upkeep. To build the main compiler binary from source, run the default target from the repository root:
make
This produces the v32lua binary (under bin/), which turns a .lua
source file into a Vircon32 .asm file plus an accompanying cartridge
.xml. From there, assembling and packing follows the same steps as any
other Vircon32 project (assemble → packrom → run under v32sim or on real hardware).
| Target | Description | Core Actions & Dependencies |
|---|---|---|
all |
Default target. Builds the main compiler executable. | Invokes the compilation process natively inside the src/ subdirectory. |
clean |
Standard workspace cleanup utility. | Recursively wipes intermediate build artifacts out of src/ and removes generated files from testing/ and demos/. |
install |
Installs the compiler binary onto the host system. | Passes the target down to the src/ directory’s localized installation scripts. |
tests |
Executes the automated compilation testing suite. | Depends on the compiler binary (bin/v32lua) being built first, then triggers the test routines inside testing/. |
demos |
Builds the collection of available demos. | Depends on the compiler binary (bin/v32lua) being built first, then builds each demo under demos/. |
asmcheck |
Validates assembly correctness. | Requires bin/v32lua to be present, then processes assembly validations via the testing/ suite. |
monofiles |
Builds streamlined monolithic file variants (used for pasting the whole project into a single-file conversation). | Runs the monofile creation workflow sequentially inside both src/ and testing/. |
--#title "v32lua Tech Demo"
--#version "1.0"
--#texture tex_logo "logo.png"
x_pos = 160.0
y_pos = 120.0
speed = 2.5
function init()
-- Set background clear color using zero-cost GPU port mappings
ioports.gpu.bgcolor = 0xFF003366
ioports.gpu.texture = tex_logo -- set texture
ioports.gpu.region = 0 -- set region
-- define the region
ioports.gpu.minX = 0
ioports.gpu.minY = 0
ioports.gpu.maxX = 100
ioports.gpu.maxY = 50
ioports.gpu.hotX = 0
ioports.gpu.hotY = 0
end
function game_loop()
-- Update state using pure floating-point math
if ioports.inp.left > 1 then
x_pos = x_pos - speed
else if ioports.inp.right > 1 then
x_pos = x_pos + speed
end
-- Direct hardware drawing
ioports.gpu.x = x_pos
ioports.gpu.y = y_pos
ioports.gpu.draw()
-- Table access and built-in string concatenation
local frame = system.frames
if frame > 1000 then
local msg = "Demo Running: Frame " .. frame
print(msg)
end
end
Compile it with:
$ v32lua -o program.asm program.lua
v32lua emits program.asm and program.xml alongside it; hand those to
the Vircon32 assembler and packrom to produce a runnable cartridge.
$ v32lua [options] file
Available options:
-o <file>: Specifies the assembly output filename. Defaults to the
input filename with its extension replaced by .asm.-g: Generates a companion .debug file mapping assembly line offsets
back to original Lua source lines and function entry points.-v, --verbose: Reports progress through the compiler’s internal
pipeline stages as it runs.-d, --debug: Displays additional internal/operational debug
information.-w: Suppresses all compiler warnings.--version: Displays compiler version and author information.--help, -h: Displays command-line usage instructions.v32lua supports three distinct API surfaces, selected with the
--#api cartridge hint (native Vircon32 is the default when no --#api
hint is present):
--#api "tic80" -- opt into the TIC-80-compatible API surface
--#api "pico8" -- opt into the PICO-8-compatible API surface
ioports.gpu.*, ioports.spu.*, ioports.inp.*,
music.*/sfx.*, system.*, and the native tilemap.* API. Fully
documented in doc/API.md.--#api "tic80") — TIC-80-shaped calls
(spr(), btn()/btnp(), map()/mset()/mget(), sound/music
functions, and the fantasy-console-style asset sections) compiled down to
native Vircon32 instructions, including the coordinate scaling needed to
map TIC-80’s 240×136 logical screen onto Vircon32’s physical resolution.--#api "pico8") — the PICO-8-shaped
equivalent, at an earlier stage of completeness than the TIC-80 layer.Only one API surface is active per cartridge; selecting tic80 or pico8
replaces the native call surface rather than adding to it.
v32lua allows you to embed Vircon32 cartridge metadata directly in your
Lua source code using special --# line comments. The compiler parses
these hints to auto-generate the project’s .xml ROM definition and assign
sequential hardware resource IDs.
Supported hints:
| Hint | Purpose |
|---|---|
--#version "X.Y" |
Sets the cartridge version field in the XML. |
--#title "TITLE" |
Sets the cart title. |
--#api "tic80" / --#api "pico8" |
Selects a compatibility API layer (see above). |
--#texture NAME "path/image.png" |
Registers a texture resource and binds it to a compile-time constant NAME. |
--#sound NAME "path/sound.vsnd" |
Registers a sound resource and binds it to a compile-time constant NAME. |
--#tilemap NAME "path/map.csv" |
Registers a tilemap from a CSV file, embedded directly into the ROM image (see doc/API.md). |
--#include "file.lua" |
Textually splices another Lua file in at this point, before parsing begins (see below). |
--#version "1.1"
--#title "Space Grinder: Tech Demo"
-- Register textures (automatically binds 'bg_space' to ID 0, 'spr_ship' to ID 1)
--#texture bg_space "assets/background.png"
--#texture spr_ship "assets/player.png"
function init()
-- Variables declared in hints are globally available in Lua at runtime!
ioports.gpu.texture = bg_space
end
When compiled, v32lua outputs both the compiled .asm assembly and a
complete Vircon32 XML cartridge definition file linking .vtex and .vsnd
assets. With this, and the proper processing of any PNG and WAV data, you
can proceed to the packrom step. Resource IDs are assigned in source
order and are guaranteed to match their position in the generated XML.
--#include)Because Vircon32 carts are a fixed ROM assembled entirely at build time —
there is no runtime filesystem — v32lua does not support real Lua’s
dynamic require/dofile. Instead, --#include "file.lua" is a
compile-time textual paste, resolved by a preprocessing pass before the
lexer ever sees the file, exactly like C’s #include:
--#include "src/physics.lua"
--#include "src/entities.lua"
do...end or a function body — so a top-level local in an
included file behaves identically to one declared in the entry file.--#texture, --#sound, --#tilemap) declared
inside an included file get correct resource IDs and XML ordering.local of the same
name share one global — identical to what the equivalent monolithic code
would do.[BP - offset] stack frame positions..asm file, embeds runtime
support routines, generates the read-only string data section, and
outputs the .xml cartridge definition.-v)When -v is enabled, v32lua reports its progress through its pipeline
stages:
\n, \t, \r, \\,
\").--#include directives and
evaluates cartridge hints (--#...) and custom comment syntaxes.main() vs. game_loop()To accommodate different game architecture styles, the compiler supports two distinct entry point paradigms:
The Auto-Ticking Harness (game_loop): If your program declares a
game_loop() function, the compiler automatically generates a
continuous runtime harness. The CPU calls game_loop(), stalls execution
for the current frame using the hardware WAIT instruction, and loops
infinitely. This is ideal for standard arcade games and demos, and
mimics the behaviour of various other fantasy consoles.
Manual Control (main): If your program declares a main()
function, control is handed directly to __function_main. You take full
ownership of the frame cycle and must manually execute inline assembly
or hardware waits. The compiler tracks whether a WAIT instruction is
emitted inside main(); if it is missing, v32lua issues a semantic
warning at compile time.
Initialization Hook: In both models, if an init() function is
present, it is guaranteed to execute exactly once after top-level global
RAM allocations and before the main loop begins.
A program must declare at least one of main() or game_loop() — this is
the designated entry point and its absence is a compile error.
v32lua uses a 32-bit tagging architecture that packs type metadata and
payload pointers into unified values, keeping immutable ROM elements
(string literals, function pointers) distinct from dynamic RAM heap
objects (tables):
| Data Type | Hex Mask / Tag | Architecture Description |
|---|---|---|
| Nil | 0xFFC00000 |
Canonical representation for undefined/missing values. |
| Boolean False | 0xFFC00001 |
Short-circuit falsy value. |
| Boolean True | 0xFFC00002 |
Short-circuit truthy value. |
| ROM String | 0x7FC00000 |
Pointers to read-only string data sections (__string_%d) in ROM. |
| Table / Boxed Object | 0xFF800000 |
Boxed heap memory addresses (Bit 31=1, Bit 22=0). |
| Number | IEEE 754 Float | Unboxed native Vircon32 floating-point values for direct math. |
High-performance Vircon32 games cannot afford hash-table lookups for
hardware manipulation. v32lua intercepts specific table member
expressions and function calls and compiles them directly into native
hardware I/O instructions:
Zero-Cost Hardware Access: Accessing namespaces like ioports.gpu.*,
ioports.spu.*, ioports.tim.*, ioports.rng.*, ioports.car.*,
ioports.mem.*, or system.* bypasses table lookup routines entirely.
They compile directly to hardware port operations (such as
GPU_DrawingPointX or TIM_FrameCounter).
music.* / sfx.*: The native sound API compiles calls like
music.play(SOUND, channel, loop) down to a straight-line OUT
sequence when every argument is compile-time-known, falling back to a
small runtime routine only when arguments are dynamic. See
doc/API.md for the full surface, including why the SPU
port write order (stop → assign → volume → play → loop/position) is
load-bearing.
tilemap.*: A native tilemap API (tilemap.get(), tilemap.set(),
tilemap.render()) backed by a --#tilemap cart hint. Tilemap data
ships read-only in ROM and is lazily promoted to a private RAM copy the
first time a given map is written to.
Consolidated Gamepad Polling: Polling controller input is optimized
into a single variable intrinsic (ioports.inp.inputs). The compiler
polls all gamepad axes/buttons, collates the active button states into a
bitshifted 32-bit integer mask, and casts it to a Lua float in a single
register. Standalone gamepad inputs are also available
(ioports.inp.left, ioports.inp.A, etc.) as variable intrinsics.
Built-in Fast Paths: Standard Lua operations like string
concatenation (..), length (#), and unary minus (-) map directly to
optimized runtime subroutines (__builtin_strcat, __builtin_len,
__builtin_unm).
See doc/API.md for the complete, authoritative reference — this README highlights the ideas, the API doc covers every call.
Visual ASCII Error Reporting: Lexical, syntax, semantic, and internal compiler errors print highlighted, multi-line ASCII code snippets pointing directly to the offending line in the source file.
Source-to-Assembly Mapping (-g): Passing the -g debug flag
generates a companion .debug file alongside the output assembly. This
file maps relative Vircon32 assembly line offsets to original Lua source
lines and functional entry points, enabling step-through debugging under
v32sim.
Inline & Raw Assembly Bubbles: You can write native assembly
directly inside Lua using __asm__("your ASM") (which snapshots and
restores registers and the stack pointer) or __rawasm__("your ASM") for
unprotected execution. Both modes support string interpolation of Lua
variables using {var_name} syntax.
v32lua implements a subset of Lua, tailored specifically for game
development on embedded hardware.
Global Variables: Automatically registered in RAM and accessed via
symbols ([var_name], [func_name]). Address 0 is reserved for the
heap pointer and addresses 1/2 are reserved scratch words used by
the float-to-string routine; ordinary global variables begin at
address 3.
Local Variables: Declared with the local keyword. Scoped
lexically to the enclosing block (function bodies, loops, or
conditionals) and mapped to stack offsets ([BP - offset]). A local
declared at a chunk’s own top level — outside any function — is
promoted to a global instead, since its storage would otherwise live in
a stack frame that returns before any game code runs; this applies
equally to locals pulled in via --#include.
In Lua parlance, functions are “first-class citizens”, and are effectively
variables. That is borne out in v32lua as they both are transacted
within the NaN-boxing scheme.
The compiler natively supports multiple assignment and variable swapping without requiring explicit user temporaries:
local x, y, z = 10, 20, 30
x, y = y, x -- Synthesizes temporary register chains to safely swap values
v32lua provides seamless syntactic sugar for table-based OOP models:
function Player.move(dx, dy) ... end
-- Desugars to: Player["move"] = __function_Player_move
: operator): Using the colon operator
automatically evaluates the table expression and injects it as an
implicit self parameter:Player:move(5, -2)
-- Desugars to: Player.move(Player, 5, -2)
Loops: while <cond> do ... end statements supported with full
block scoping.
Loop Control: break statements jump immediately to the end label
of the current innermost loop (tracked via an internal compilation loop
stack).
Conditionals: if <cond> then ... elseif <cond> then ... else ...
end structures with short-circuit branching.
Arithmetic: +, -, *, / (mapped to Vircon32 floating-point
hardware instructions FADD, FSUB, FMUL, FDIV), and unary minus
(- via __builtin_unm).
Relational: ==, ~= (via __builtin_eq with NaN unboxing), <,
>, <=, >= (via hardware FLT, FLE, FGT, FGE).
Logical: and, or, not (with short-circuit evaluation).
String Concatenation: .. operator automatically pushes operands
and invokes the runtime subroutine __builtin_strcat.
Length Operator: # operator invokes __builtin_len to resolve
string or table lengths.
Functions can return multiple values simultaneously. The calling
convention optimizes the first three returned expressions by placing them
directly into registers R0, R2, and R3. Any additional return values
(4th and beyond) are spilled directly onto the caller’s stack frame.
All string literals declared in source code (e.g., "GAME OVER") are
collected during compilation, deduplicated, and emitted into a dedicated
data section at the end of the ROM (__string_0: string "GAME OVER"),
preventing redundant ROM consumption.
In Lua, only nil and false evaluate to false in conditional
expressions; every other value (including 0 and empty strings) is
truthy. v32lua implements this via two high-speed assembly emission
primitives:
emit_falsy_jump(reg, label): Tests if reg matches 0xFFC00000
(Nil) or 0xFFC00001 (False). If either matches, execution jumps to the
target label.
emit_truthy_jump(reg, label): Tests against Nil and False; if
neither matches, execution short-circuits to the target label.
When logical operators (and, or) are evaluated, the evaluated result
is left intact in the destination register, preserving Lua’s idiom of
returning the actual operand value rather than a strict boolean.
One of the most powerful features of v32lua is its static intrinsic
interception engine. When the compiler encounters table accesses or
function calls matching specific system paths (e.g., ioports.gpu.clear()),
it bypasses dynamic table lookups entirely and emits direct Vircon32
hardware I/O instructions (IN, OUT).
Because Lua variables are stored as NaN-boxed IEEE 754 floats while
Vircon32 hardware ports expect 32-bit integers or booleans, v32lua
automatically injects hardware conversion instructions during port reads
and writes:
CFI (Cast Float to Integer): Emitted automatically when writing
numeric values to integer GPU/Input ports.
CFB (Cast Float to Boolean): Emitted when writing boolean flags to
hardware registers, decoding Lua truthiness (only nil/false are
falsy) rather than a raw non-zero test.
CIF (Cast Integer to Float): Emitted immediately after executing
an IN instruction from integer hardware ports, ensuring the value is
immediately usable as a Lua number.
Boolean port reads: decoded into the boxed true/false
representation rather than a raw 0.0/1.0 float, since 0.0 is
truthy in Lua and would otherwise make a disconnected gamepad or memory
card read as “connected”.
The full, authoritative reference for every intrinsic — ioports.gpu.*,
ioports.inp.*, ioports.spu.*, ioports.tim.*, ioports.rng.*,
ioports.car.*, ioports.mem.*, music.*/sfx.*, tilemap.*,
memcard.*, and system.* — lives in doc/API.md, including
port-ordering caveats, call signatures, and worked examples. A short
sample of the most commonly used entries:
ioports.gpu.*)| Lua Path / Intrinsic | Vircon32 Port / Command | Access | Description & Behavior |
|---|---|---|---|
ioports.gpu.texture |
GPU_SelectedTexture |
Read / Write | Sets or reads the active texture ID used for drawing operations. |
ioports.gpu.region |
GPU_SelectedRegion |
Read / Write | Selects the texture sub-region (sprite frame) to render. |
ioports.gpu.x / ioports.gpu.y |
GPU_DrawingPointX/Y |
Read / Write | Screen coordinates for drawing placement. |
ioports.gpu.minX/minY/maxX/maxY |
GPU_RegionMin/MaxX/Y |
Read / Write | Defines the pixel boundaries of the active texture region. |
ioports.gpu.hotX/hotY |
GPU_RegionHotSpotX/Y |
Read / Write | Sets the drawing origin (hotspot) relative to the sprite region. |
ioports.gpu.draw([mode]) |
GPU_Command |
Function Call | Executes a hardware draw command: "zoom", "rotate", "rotozoom", or default. |
ioports.gpu.clear([color]) |
GPU_ClearColor + GPU_Command |
Function Call | Sets the clear color and wipes the screen. Supports preset color strings ("black", "white", "blue", "red", "green") or numeric hex values. |
ioports.inp.*)| Lua Path / Intrinsic | Vircon32 Port / Command | Access | Description & Behavior |
|---|---|---|---|
ioports.inp.gamepad |
INP_SelectedGamepad |
Read / Write | Selects the active controller index (0-3) for input polling. |
ioports.inp.status |
INP_GamepadConnected |
Read Only | Returns a Lua boolean: is the selected gamepad connected. |
ioports.inp.left/right/up/down |
INP_Gamepad* |
Read Only | D-Pad directional state (> 0 pressed, < 0 released). |
ioports.inp.A/B/X/Y/L/R/start |
INP_GamepadButton* |
Read Only | Action/shoulder button state (> 0 pressed, < 0 released). |
ioports.inp.inputs |
Custom Action Subroutine | Read Only | Collation intrinsic: polls all gamepad buttons/axes in one pass, collates them into a single 32-bit bitmask, and casts it to a Lua float. |
| Lua Path / Intrinsic | Vircon32 Instruction | Access | Description & Behavior |
|---|---|---|---|
system.halt() |
HLT |
Function Call | Emits the hardware HLT instruction, immediately terminating CPU execution or freezing the frame until the next interrupt/frame cycle. |
system.wait() |
WAIT |
Function Call | Emits the hardware WAIT instruction, pausing execution until the next interrupt/frame cycle. |
system.frames / system.cycles |
TIM_FrameCounter / TIM_CycleCounter |
Read Only | Running frame/cycle counters. |
print(x, y, ...) |
__builtin_tostring + __builtin_print |
Function Call | Coerces arguments to string representation and outputs them to the console debug terminal. First two parameters are the X, Y position on screen, in pixels. |
__asm__ & __rawasm__)For performance-critical inner loops or advanced Vircon32 hardware
manipulation, v32lua provides direct inline assembly injection.
__asm__)The __asm__ directive allows embedding raw Vircon32 assembly strings
directly inside Lua functions. Crucially, it supports variable
interpolation, enabling seamless bridging between Lua scope symbols and
assembly registers:
local speed = 5.0
__asm__( "MOV R0, {speed}\n" ..
"FADD R0, 1.5\n" ..
"MOV {speed}, R0" )
How it works: Any identifier wrapped in braces (e.g., {speed}) is
dynamically resolved by emit_interpolated_asm at compile time. If
speed is a local variable at stack offset 1, {speed} is
automatically replaced with [BP - 1]. If it is a global, it resolves
to [var_speed].
Each line of interpolated assembly is passed through the compiler’s
formatting engine, ensuring consistent indentation and comment alignment
in the output .asm file.
This standard inline assembly applies some mild guardrails and protections, in the form of backing up any existing used registers along with the stack. While it doesn’t prevent problems, it may help mitigate some caused by accident. Any register changes made here are lost outside the inline “bubble”.
__rawasm__)The __rawasm__ directive outputs the literal string directly to the
assembly stream without safeties applied. This can be quite dangerous, and
should only be used by the most knowledgeable and experienced of assembly
users. It is also the basis of the compiler’s own unit-test harness: a
test file is typically a function main() ... end wrapper around a
sequence of __rawasm__ blocks with __debugN: labels for breakpointing
under v32sim
| RAM Address | Designation | Usage |
|---|---|---|
0 |
HEAP_POINTER |
Stores the dynamic starting address for runtime table/string allocations. |
1, 2 |
FTOA_SCRATCH_PTR_A/B |
Reserved scratch words used by the float-to-string conversion routine. |
3 to HEAP_START - 1 |
Global RAM | Sequentially allocated slots for global Lua variables, resource IDs, and promoted top-level locals. |
HEAP_START and up |
Dynamic Heap | Runtime memory managed by the table allocator and string routines. |
Stack Top (SP) |
Call Stack | Function activation records, local variables, and saved register states. |
HEAP_START is computed after all code generation has finished, so
top-level statement codegen that registers late globals can never collide
with the heap.
While v32lua attempts to be a functional Lua compiler, it by no means is
a full-to-specification implementation of the language. For one, there’s
no bytecode virtual machine, nor interpreter — Lua compiles straight down
to native assembly.
Further, there are some explicit deviations from a standard implementation of the language to better suit the freestanding environment of Vircon32:
print() requires, as its first two parameters, the x and y
position on the screen.
Standalone return statements do not work (will generate a syntax
error). Give it something (nil, 0, etc.) to make it happy.
Program execution MUST reside within a function. While you can declare
functions, you must use one of the designated starting points to begin
the chain of execution (init(), game_loop(), or main()). Not
having a main() or game_loop() function leads to a compile error.
game_loop() automatically issues a WAIT before calling itself again,
making it your natural game-loop location — similar to other fantasy
consoles (like the TIC() function in TIC-80, which is similarly
required).
v32lua is a float-only implementation: there is no Lua 5.3+
integer/float dual-number type. All numbers are IEEE-754 float32, so
integers above 2^24 cannot be represented exactly — worth keeping in
mind for bit-manipulation-heavy code.
A local declared in a block lexically outside any function (a bare
do...end, or an if/while/for body at chunk level) currently has
no compiler-level guardrail if a function later reads that name — see
the roadmap item below.
Clearly, this effort is focused on making a tool for development on Vircon32, and not on being a fully-compliant Lua implementation. Efforts will be made to come as close as is possible and feasible, without sacrificing significant performance or veering away from being the tool it is intended to be.
Early on in compiler development, all optimization code was removed and factored into a separate tool, v32opt. This is designed as a general purpose Vircon32 assembly optimizer, meant for use with the C compiler and lua compiler (along with handwritten assembly). Early tests have shown some mild improvements to performance, and potential space savings by eliminating redundant instructions.
At time of writing this tool is still very much in development, but is showing
promise and will likely work for standard scenarios under the -O1, -O2,
and even -O3 optimization levels. It is meant to be inserted into the build
chain after the compiling and before assembling.
The following are known, deliberate gaps rather than bugs — either deferred to keep initial development moving, or awaiting a design decision:
pcall/error/assertstring.match/gmatch, setmetatabletonumber(s, base) — the two-argument, explicit-base formlocal declared in a block lexically outside any function at chunk
level (see above)NOTE: There was extensive AI use and interaction throughout this effort. A distinction should be made from “vibe coding”, but there is definitely a blur between human and AI. In the end, both benefit and could compensate for the other’s deficiencies.
This endeavour actually was not primarily about developing a compiler, it began as an honest attempt to get a feel for AI and its impact: its role and detriment to human thinking and education. That it has a compiler theme was merely to accentuate a point of interest. It has certainly been a learning experience. If sufficient compiler concepts and background knowledge weren’t sufficiently known going into this, the effort would have ended far less successfully.