How berylang/compiler works
1. What this repository is
Bery is a compiler for a custom .bry language. The codebase is organized as a classic front end → semantic analysis → LLVM-based back end pipeline, with src/main.cpp acting as the entry point that likely wires the phases together.
2. Overall compilation flow
Source files are first tokenized by the lexer, then parsed into an AST, then semantically validated, and finally lowered into LLVM IR and optimized before native code generation. The README explicitly describes this as a custom C++ frontend with an LLVM backend.
3. Lexing layer
src/lexer/ contains the tokenizer (lexer.cpp/.h) and token definitions (token.h). This layer is the first boundary from raw text into compiler-internal structure, producing tokens consumed by the parser.
4. Parsing and AST construction
src/parser/ implements a recursive-descent parser (parser.cpp/.h) and a tree of AST node types under src/parser/ast/. The AST is split by language construct: literals, expressions, functions, control flow, variable declarations, arrays, imports, and blocks/program nodes. This layer owns syntactic structure, but not type correctness.
5. Semantic analysis
src/sema/ is the validation layer. It includes a symboltable for scope and identifier management, a typechecker for static typing and compatibility rules, and a sema coordinator that walks the AST. This phase appears responsible for scope resolution, type checking, and implicit casts as described in the README.
6. Import handling
src/importer/ is a distinct subsystem for module/import support. It includes an importer and a mangler, which suggests imported symbols are resolved and names are adjusted to avoid collisions or encode module identity before later compilation stages.
7. Code generation architecture
src/codegen/ translates the validated AST into LLVM IR. There is a higher-level codegen.cpp/.h facade plus src/codegen/irgen/ split by concern: declarations.cpp, expressions.cpp, and controlflow.cpp. That layout indicates lowering is organized by AST category rather than by output file.
8. LLVM integration and optimization
src/codegen/llvm/ contains the LLVM-facing implementation (irgen.cpp/.h) and an optimizer module. This is the boundary where Bery’s internal IR generation becomes actual LLVM IR, and then optimization passes are likely applied before emitting machine code or object output.
9. Language surface represented in examples
The examples/ directory is a good map of supported language features. It includes arrays, components, control flow, datatypes, expressions, functions, and imports, which implies the compiler already covers a fairly broad statically typed language core: variables, conditionals, loops, function calls, casting, strings, and module usage.
10. Build and project structure
CMakeLists.txt indicates a CMake-based native build, consistent with a C++ compiler project. The repository is structured as a single compiler executable with modular subsystems rather than multiple services or runtime components.
11. Important architectural boundary
The most important separation is between syntax and semantics: parser code builds the AST without enforcing type rules, while sema validates and annotates that AST before code generation. That boundary is critical for any diagram because codegen depends on semantic correctness, not raw parse trees.
12. How to diagram the system
A clear architecture diagram should show: .bry source files entering the lexer, tokens flowing to the parser, AST flowing into semantic analysis, validated AST feeding the code generator, then LLVM IR passing through optimization and onward to native output. Supporting boxes should sit beside the main path for imports, symbol table/type checking, and AST node definitions.
Main entry points: sema.cpp Sema · irgen IR Gen · irgen.cpp LLVM IR · parser.cpp Parser
Components 19
Compiler pipeline
Main source-to-native compilation flow.
- Main · Wires compiler phases together.
src/main.cpp
Front end
Lexing, parsing, and AST construction.
- Lexer · Converts source text into tokens.
src/lexer/lexer.cpp - Token · Token definitions shared with the parser.
src/lexer/token.h - Parser · Builds the AST from token streams.
src/parser/parser.cpp - AST · Language node hierarchy for literals, expressions, functions, control flow, variables, arrays, imports, and programs.
src/parser/ast
Semantic analysis
Scope, types, and import resolution.
- Sema · Coordinates scope and type validation over the AST.
src/sema/sema.cpp - Symbols · Tracks identifiers and lexical scopes.
src/sema/symboltable.cpp - Types · Enforces static typing and compatibility rules.
src/sema/typechecker.cpp - Importer · Handles module/import resolution before later compilation stages.
src/importer/importer.cpp - Mangler · Encodes imported or scoped names to avoid collisions.
src/importer/mangler.cpp
Back end
LLVM IR generation and optimization.
- Codegen · Top-level AST-to-LLVM lowering entrypoint.
src/codegen/codegen.cpp - IR Gen · Dispatches lowering by AST category.
src/codegen/irgen - Decls · Lowers declarations and variable-related constructs.
src/codegen/irgen/declarations.cpp - Exprs · Lowers expressions into IR.
src/codegen/irgen/expressions.cpp - Flow · Lowers control-flow constructs into IR.
src/codegen/irgen/controlflow.cpp - LLVM IR · Translates internal lowering into LLVM IR.
src/codegen/llvm/irgen.cpp - Optimizer · Applies LLVM optimizations before output.
src/codegen/llvm/optimizer.cpp
Connections 20
- Main → Lexer: starts · Compiler begins by tokenizing source.
- Lexer → Token: emits · Tokenizer produces token objects.
- Token → Parser: feeds · Parser consumes token streams.
- Parser → AST: builds · Parser constructs the AST hierarchy.
- AST → Sema: analyzed by · Semantic analysis walks the parsed tree.
- Sema → Symbols: uses · Scope and identifier tracking support sema.
- Sema → Types: uses · Type rules are enforced during semantic analysis.
- Sema → Importer: resolves · Imports are handled as part of semantic/module processing.
- Importer → Mangler: names · Imported symbols are mangled for uniqueness.
- Sema → Codegen: validates for · Only semantically valid AST reaches code generation.
- Codegen → IR Gen: delegates · Facade routes lowering to IR generation.
- IR Gen → Decls: lowers · Declaration-related constructs are lowered separately.
- IR Gen → Exprs: lowers · Expression lowering is isolated.
- IR Gen → Flow: lowers · Control flow lowering is isolated.
- Decls → LLVM IR: produces · Lowered constructs become LLVM IR.
- Exprs → LLVM IR: produces · Expression lowering feeds LLVM IR generation.
- Flow → LLVM IR: produces · Control-flow lowering feeds LLVM IR generation.
- LLVM IR → Optimizer: optimizes · LLVM IR is optimized before final emission.
- Examples → Parser: exercises · Example programs cover supported syntax and semantics.
- Build → Main: builds · CMake builds the compiler executable.