Brick compiles .brc source files to C code, then delegates to gcc or clang for machine code generation. The compiler itself is written in C++20.
.bricada → Lexer → Token Stream → Parser → AST → Type Checker → C Codegen → .c file → gcc -O3 → binary
↕
Macro Expander
Build Eval
src/lexer/)File: lexer.cpp, lexer.h
Tokenizes .brc source into a flat token stream. Handles:
fn, struct, if, while, for, return, match, defer, const, etc.)0xFF), binary (0b1010), octal (0o777), with suffixes (42u8, 0x1Au16)3.14f32, 3.14f64)'a', '\n'"hello" with escape sequencestrue, falsenull+ - * / % = == != < > <= >= && || ! & | ^ ~ << >> ++ -- += -= *= /= @ . , ; : () [] {} ->// line-onlysrc/parser/)Files: parser.cpp, parser.h, ast.h, package.cpp, package.h, build_eval.cpp, build_eval.h, macro_expander.cpp, macro_expander.h
Recursive-descent parser that produces an AST. Architecture:
Parser
├── Top-level declarations: structs, enums, unions, functions, consts, macros, interfaces, impl blocks, type aliases
├── Expressions: binary ops, unary ops, literals, identifiers, calls, arrays, pointer deref, sizeof/alignof
├── Statements: if, while, for, for-in, return, break, continue, match, defer, block scopes
├── Package resolution: using, export, private, nested packages (MATH.VEC2)
├── Build system: build {}, emit {} compile-time eval
├── Macro system: macro definition, $interpolation, $macro() explicit call, hygiene, varargs
└── C interop: include, link, extern fn, @system
The AST is an untyped tree. Type checking happens separately in the codegen stage.
Before codegen, the macro expander processes all macro definitions and build/emit blocks:
macro definitions during parsingmacro_name(...) call, expand body with $param substitutionsbuild { ... } evaluates code at compile timeemit { ... } generates code from the build context__-prefixed variables get unique gensymsargs... captures remaining argumentsbuild {} blocks execute at compile time. Variables inside don’t exist in the final binary. Uses a simple interpreter:
emit {} inside build generates codeT.name, T.size, T.fields for type reflectionusing PACKAGE → searches for PACKAGE.brc:
-I <dir> paths<dir>/PACKAGE/PACKAGE.brc (nested)<dir>/PACKAGE/main.brcBRICK_PATH environment variable pathsMATH.VEC2 → MATH/VEC2.brc (dots → directory separators)export/private visibility enforcement during type checkingsrc/codegen/)Files: codegen.cpp, codegen.h, type_checker.cpp, type_checker.h
Two-phase code generation:
type_checker.cpp)codegen.cpp)Produces readable C code with #line directives for debugging.
Type Map:
| Brick | C |
|---|---|
i8 |
int8_t |
i16 |
int16_t |
i32 |
int32_t |
i64 |
int64_t |
u8 |
uint8_t |
u16 |
uint16_t |
u32 |
uint32_t |
u64 |
uint64_t |
f32 |
float |
f64 |
double |
bool |
uint8_t |
usize |
size_t |
isize |
ptrdiff_t |
String |
BrickString |
void |
void |
T[N] |
T name[N] |
T[] |
T* name; int64_t name_cnt; int64_t name_cap |
Codegen details:
fn → static inline (always_inline attribute) — zero call overheadexport fn → visible (no static)extern fn → declaration only, no bodyblock → BlockCtx* with bump allocator.reset() → block_reset(ctx)error("msg") → fprintf(stderr, "msg"); exit(1)@block → block_alloc(ctx, size)defer → __attribute__((cleanup)) or inline at scope exitsizeof(T) → sizeof(T)alignof(T) → _Alignof(T)match → switch with if guardsfor x in N → for (int64_t __i = 0; __i < N; __i++)const → static const (compiler-substituted)impl block → vtbl struct + wrapper functionsinterface → vtbl struct + void* data wrapperString → {char* data; int64_t len} struct#line directives for every .brc line (GDB-friendly)Files: embedded_runtime.cpp, embedded_runtime.h
The runtime (block_memory.c, io.c, hot_reload.c, pool_allocator.c) is either:
brick run / brick build)runtime/block_memory.c etc.embedded_runtime.cpp converts runtime C files into C++ string literals at build time.
runtime/block_memory.c, .h)block_alloc(ctx, size) — ~3 CPU cyclesblock_alloc_aligned(ctx, size, align)block_reset(ctx) — O(1), ~5 nsblock_set_tls(ctx) / block_get_tls() — thread-local blocksblock_enable_double_buffer(ctx) / block_swap_buffers(ctx) — zero-pause hot reloadruntime/hot_reload.c, .h)dlopen + inotify — watches .so for changesLoadLibrary + ReadDirectoryChangesW — watches .dllHR_WAITING → HR_LOADING → HR_OK / HR_ERRORHR_THREAD_CREATE, HR_MUTEX_LOCK, etc.runtime/io.c, .h)print() with {0} formatting — int, float, string, boolBrickString type: {char* data; int64_t len}brick_string_create() — allocates from blockruntime/pool_allocator.c, .h)pool_create(), pool_destroy(), pool_add_slot()pool_alloc(size) — O(1) allocationpool_free(ptr) — O(1) freepool_slot_stats(slot) — monitoringvisualizer/memvis.cpp)memvis_run() — start visualizermemvis_attach() — attach to running processmemvis_read_shm()SConstruct, src/SConscript, runtime/SConscript, tests/SConscript)SConstruct
├── src/SConscript → brick compiler binary
├── runtime/SConscript → runtime C objects
├── tests/SConscript → test binaries
└── visualizer/SConscript → TUI visualizer
brick <input.brc> [-o output] # compile to C
brick build <files> [-o output] # compile to binary
brick run <input.brc> # compile and run
brick new <project> # scaffold
brick bind <header.h> # C bindings
vscode-ext/)syntaxes/brick.tmLanguage.json)server/src/server.ts).brc files, provides:
memoryWebview.ts)debugger/).gdbinit: Auto-loads in project directorygdb_commands.py: Custom commands:
info blocks — show all block contextsblock <name> — inspect a specific blockgdb_pretty_printers.py: Python pretty-printers for BlockCtx, BrickString#line directives in generated C map source locations back to .brc files for breakpoints and stack traces.
tests/test_codegen.cpp (198+ tests), tests/test_lexer.cpp, tests/test_parser.cpp, tests/test_macros.cpptests/test_integration.sh (30+ tests)tests/features/test_*.brc (16 feature tests)tests/test_macro_errors.sh.github/workflows/ci-windows.yml| Operation | Time |
|---|---|
| Block allocation | ~3 cycles |
| Global reset | ~5 ns (O(1)) |
fn call (static inline) |
0 cycles (inlined) |
fn call (export) |
~1-2 cycles |
| Interface dispatch (vtbl) | ~2-3 cycles |
| Pool alloc (≤64B) | O(1) |
| Compiler throughput | ~50K lines/sec |
| Generated C performance | ~gcc -O3 native |