I wanted to build a compiler from scratch that has type checking support, native code generation, and basic IDE integration through a language server. I also wanted to learn Rust at the same time (who thought that's a good idea) so I used it to build the compiler.
In order to cover all these aspects -- the language we are building is very restricted and very basic. It is inspired by "Untyped Arithmetic Expressions" from the book "Types and Programming Languages" by Benjamin C. Pierce.
I have written a series of blog posts that accompany this project and explain the various stages of the compiler and how it works.
The code is also heavily commented and assumes almost no knowledge of Rust.
Needless to say (but here I am saying it), the code is not idiomatic Rust nor is it intended to be performant or used for anything practical except learning. But, it works and it has automated tests.
The project requires quite some dependencies, so I added a Dockerfile to make it easier to build and run the project without having to install all of them.
If you are using VSCode, there is an associated devcontainer.json file that will allow you to open the project in a container and have all the dependencies installed for you.
I recommend this approach since just building the compiler inside the container will mean the compiler will generate native code that matches the container environment itself.
If you would still like to build natively then you will need the following (consult the Dockerfile to get a good idea for what might be missing if you run into issues):
Rust 1.72.1 (eb26296b5 2023-08-03)or later (I recommend to use rustup to install and manage Rust).- NodeJS 18.x.x (needed for VSCode Language Server TypeScript client)
- LLVM 16.x.x (needed for the LLVM backend, you need a distribution that ships with
llvm-configwhich most binary distribution don't have)
Then run the following:
npm installThis will install the node modules for the VSCode Language Server client.- If you want to check the VSCode integration, you will find a launch configuration called
Launch Clientin the VSCode debugger.
- If you want to check the VSCode integration, you will find a launch configuration called
cargo runThis will build the compiler and run it on the input file/examples/good.ntlcproducing a native executable which you can find in the/bindirectory.- The generated executable doesn't print any output but you can check its exit code which matches the evaluation result of the corresponding NTLC program.
- That can be done by running
echo $?after running the compiler.
bindirectory contains the output of the compiler that we are building after it has been applied to the input file/examples/good.ntlccompilercontains the source code of the NTLC compiler. Each stage of the compiler is in a separate file.examplesdirectory contains example NTLC programs (barely).lsp-extension: contains the source code of the VSCode Language Server TypeScript client.lsp-server: contains the source code of the NTLC Language Server (this is started automatically by the VSCode when it activates the extension).
Please open an issue