Every contributor should read this before touching the codebase. This document outlines the architectural invariants and coding standards required to maintain the strict mapping between high-level language semantics and native x86-64 assembly.
Architectural transparency is the primary design constraint. Every transformation in the pipeline — from source text to native assembly — must be independently verifiable and debuggable at the instruction level. Prefer explicitness over abstraction whenever the two are in tension.
- Standard Python only. No exotic syntax.
- Type hints on every function signature. Nothing else fancy.
- Functions under 30 lines. If longer, split it.
- One function, one job.
- Descriptive names:
parse_expression, notpeor_p_expr_r.
# Good
def parse_expression(self) -> ASTNode:
left = self.parse_term()
while self.current_token.type == TokenType.PLUS:
self.advance()
right = self.parse_term()
left = BinaryOp(op='+', left=left, right=right)
return left
# Bad — too compressed, hard to explain
def pe(self): return self._reduce(self.pt, {T.PLUS: BinaryOp})- No metaclasses, decorators beyond
@dataclass, or descriptor magic - No
*args / **kwargsunless there is a clear reason - No list/dict comprehensions longer than one line
- No chained method calls more than 2 deep
- No regex for parsing — use the lexer
- No third-party libraries for compilation logic (no PLY, ANTLR, Lark)
Use @dataclass for AST nodes and tokens. Keep them flat and readable.
# Good
@dataclass
class BinaryOp(ASTNode):
op: str
left: ASTNode
right: ASTNode
# Bad
class BinaryOp(ASTNode, metaclass=NodeMeta, slots=True, frozen=True): ...Use the project's CompilerError class. Always include line and column.
Never use bare raise Exception("something went wrong").
raise CompilerError(
message="undefined variable 'x'",
line=token.line,
col=token.col
)Write comments that explain why, not what.
# Good: explains a non-obvious constraint
# Advance past the closing paren before parsing the body —
# otherwise the body parser will consume it as an expression.
self.expect(TokenType.RPAREN)
# Bad: restates the code
self.advance() # advance the tokenFollow this order so reviewers can read top-to-bottom without forward references:
- Data structures (
@dataclass) - Main class with
__init__ - Public methods
- Private helpers
The compiler targets integer-based systems tasks on x86-64 with strict register pressure management. Correctness and pipeline traceability take precedence over throughput optimization. Avoid premature optimization; prefer algorithms whose invariants are locally verifiable over those that require global context to reason about.
Complete workflow from Nova source to native executable.
python main.py run examples/fibonacci.nv
# Writes: fibonacci.asmOptional flags:
python main.py run examples/fibonacci.nv --dump-ast # print AST
python main.py run examples/fibonacci.nv --dump-ir # print TAC before/after optimisationnasm -f macho64 fibonacci.asm -o fibonacci.o
gcc fibonacci.o -o fibonacci
./fibonaccinasm -f elf64 fibonacci.asm -o fibonacci.o
gcc fibonacci.o -o fibonacci
./fibonaccipytest tests/ -vpython main.py run examples/fibonacci.nv && \
nasm -f macho64 fibonacci.asm -o fibonacci.o && \
gcc fibonacci.o -o fibonacci && \
./fibonacci