RayCompiler is a lightweight, educational Just-In-Time (JIT) compiler for a simple procedural language. Implemented in modern C++ and built upon the powerful LLVM compiler infrastructure, it serves as a practical demonstration of core compiler design principles, inspired by the official LLVM "Kaleidoscope" tutorial.
This project implements a complete compiler pipeline, from source text to native machine code, demonstrating the fundamental stages of compilation:
- Lexical Analysis: The
Lexerconsumes raw source code and converts it into a stream of semantic tokens (e.g.,tok_def,tok_identifier,tok_number). - Syntactic Analysis: The
Parserprocesses the token stream to build an Abstract Syntax Tree (AST). The AST is a hierarchical, in-memory representation of the source code's structure, defined ininclude/ast.h. - Code Generation: The
Codegenmodule traverses the AST and emits LLVM Intermediate Representation (IR). This IR is then passed to LLVM's JIT compilation engine, which dynamically compiles and executes the code at runtime.
This compiler is an ideal resource for anyone interested in learning how programming languages are implemented.
The custom language supported by RayCompiler is expression-oriented and includes:
- First-Class Functions: Support for
defto define functions with typed arguments. - Conditional Logic:
if/then/elseexpressions for control flow. - Binary Expressions: Standard arithmetic (
+,-,*,/) and comparison (<,>) operators with correct precedence parsing. - Variable Bindings: Simple variable assignment using the
=operator within a function's scope. - JIT Execution: Code is compiled and run on-the-fly, with top-level expressions being wrapped in anonymous functions.
- Comments: Ignores lines starting with
#.
Follow these instructions to build and run the compiler on your local machine.
- A modern C++ toolchain (GCC or Clang).
clang++is recommended. - The LLVM development libraries. Version 14 or newer is recommended.
makefor simplified building.
On Debian/Ubuntu-based systems, you can install all prerequisites with:
sudo apt-get update
sudo apt-get install clang llvm-dev makeA Makefile is provided to simplify the compilation process.
-
Clone the repository:
git clone git@github.com:rayhuang2006/RayCompiler.git cd RayCompiler -
Create a
Makefilein the root directory with the following content:# Makefile for RayCompiler # Compiler and flags CXX := clang++ CXXFLAGS := -std=c++17 -Iinclude LLVM_FLAGS := $(shell llvm-config --cxxflags --ldflags --system-libs --libs core mcjit native) # Source files and executable name SRCS := $(wildcard src/*.cpp) TARGET := ray_compiler .PHONY: all clean # Default target all: $(TARGET) # Linking the executable $(TARGET): $(SRCS) $(CXX) $(CXXFLAGS) $(SRCS) $(LLVM_FLAGS) -o $(TARGET) @echo "Build complete. Executable: $(TARGET)" # Clean up build artifacts clean: rm -f $(TARGET)
-
Run
maketo build the executable:make
This will create the
ray_compilerexecutable in the project root.
The compiler reads source from standard input and executes it.
-
Create a source file, for example
example.ray:# example.ray - Demonstrates functions and conditionals. # A function to compute the hypotenuse. def hypot(a, b) (a*a + b*b); # The return value is the last expression # A function using a conditional. def check_sign(x) if x > 0 then 1 # Positive else -1; # Negative or zero # Execute and print the result of top-level expressions. hypot(3, 4); # Expected: 25 (since it returns a*a+b*b) check_sign(-5); # Expected: -1
-
Pipe the source file into the compiler:
./ray_compiler < example.ray
The output will include the generated LLVM IR for each function followed by the computed results of the top-level expressions.
The repository is organized into a clean and logical structure to separate concerns.
RayCompiler/
├── .git/ # Git version control metadata
├── include/ # Header files for the core components
│ ├── ast.h # Defines the Abstract Syntax Tree nodes
│ ├── lexer.h # Public interface for the Lexer
│ └── parser.h # Public interface for the Parser
├── src/ # Source code implementations
│ ├── codegen.cpp # LLVM IR generation logic
│ ├── lexer.cpp # Lexical analyzer implementation
│ └── parser.cpp # Syntactic analyzer (parser) implementation
├── .gitignore # Files and directories to be ignored by Git
└── README.md # This file
Contributions are welcome! This project is an excellent platform for experimenting with new compiler features. If you have an idea for an improvement, please follow these steps:
- Fork the Project.
- Create your Feature Branch (
git checkout -b feature/AmazingFeature). - Commit your Changes (
git commit -m 'Add some AmazingFeature'). - Push to the Branch (
git push origin feature/AmazingFeature). - Open a Pull Request.
- Implement a
forloop construct. - Add more data types (e.g., integers, booleans).
- Enhance error reporting with source line and column numbers.
- Add user-definable binary operators.
- Build a small standard library of common functions.
Distributed under the MIT License.