This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is nqcc-ts, a TypeScript/Deno implementation of the C compiler from Nora Sandler's "Writing a C Compiler" book. The compiler implements a complete pipeline from C source to executable binaries, targeting x86-64 architecture.
- Deno runtime installed
- GCC (required for preprocessing and final linking)
- x86-64 architecture (on Apple Silicon, run
arch -x86_64 zshto emulate)
Build the compiler:
deno compile --allow-all --output driver main.tsRun with file watching for development:
deno task devRun the compiler directly:
deno run --allow-all main.ts [FLAGS] <input.c>Test with intermediate outputs:
deno run --allow-all main.ts --lex input.c # Stop after lexing
deno run --allow-all main.ts --parse input.c # Stop after parsing
deno run --allow-all main.ts --validate input.c # Stop after type checking
deno run --allow-all main.ts --tacky input.c # Stop after tacky generation
deno run --allow-all main.ts --codegen input.c # Stop after code generationThe compiler follows a traditional multi-pass architecture with these stages:
- Uses GCC's
-E -Pflags to expand macros and includes - Converts
file.ctofile.i
- Tokenizes the preprocessed source
- Handles C keywords, identifiers, constants, and operators
- Supports integer, long, unsigned, and double literals
- Recursive descent parser
- Builds untyped AST (Abstract Syntax Tree)
- Handles declarations, statements, expressions, and control flow
- Resolves identifiers and scopes
- Handles function and variable declarations
- Adds labels to loop constructs for break/continue statements
- Performs semantic analysis
- Type inference and validation
- Uses symbol table for scope management
- Converts AST to three-address code (TAC)
- Intermediate representation for optimization
- Converts TAC to assembly instructions
- Handles register allocation and calling conventions
- Supports both integer and floating-point operations
- Pseudo Register Replacement (
ReplacePseudos.ts): Replaces pseudo-registers with actual registers/memory - Instruction Fixup (
InstructionFixup.ts): Handles instruction-specific adjustments
- Converts internal assembly representation to x86-64 assembly text
- Outputs
.sfiles
- Uses GCC to assemble
.sto.oobject files - Links object files to create final executable
SymbolTable(Symbols.ts): Manages C-level symbols and scopesAssemblySymbolTable(AssemblySymbols.ts): Manages assembly-level symbols
- Supports basic types:
int,long,unsigned,double,char - Complex types: arrays, pointers, functions, structs
- Type utilities in
TypeUtils.ts
- Untyped AST for initial parsing
- Typed AST after type checking
- Comprehensive node types for all C constructs
- Internal representation of x86-64 assembly
- Instruction types, operands, and addressing modes
- Core compiler phases are in individual
.tsfiles - Utility modules:
Const.ts,ConstConvert.ts,Initializers.ts,Rounding.ts,Settings.ts,TypeUtils.ts,UniqueId.ts
- Only works on x86-64 macOS
- Requires GCC for preprocessing and final linking
- Uses system calling conventions (System V AMD64 ABI)
- Uses Deno standard library assertions (
@std/assert) - Minimal external dependencies
- Use compiler flags to test individual phases
- Compare generated assembly with expected output
- Binary files are created alongside assembly files for testing