JacquelineCasey / Nom

0 stars 0 forks source link

Nom

Nom is a prototype / toy programming language. I am developing it for fun, and to learn more about the inner workings of the compilation process.

Quick Start

Nom should work with the current version of Rust. At time of writing, that is Rust 1.80.0 (2021 Edition). To build and test this project:

Then, you can have the project compile and run your Nom code. For instance:

More about Nom

Nom is a compiled programming language, in the same sense that Java is compiled. A Nom program is transformed into a simple set of instructions, which run on a virtual machine (the Nom VM, or the Nom runtime.) You could split hairs and say that it is interpretted, but you can do the same thing for Java, C#, etc (although they do have a JIT, and Nom certainly does not).

Here is a small program in Nom.

// Ruthlessly inefficient function computing fibonacci numbers.
fn fib(n: i32) -> i32 {
    var result: i32 = 0;
    if n == 1 {
        result = 1;
    }
    else if n == 2 {
        result = 1;
    }
    else {
        result = fib(n-2) + fib(n-1);
    };

    result
}

fn main() -> i32 {
    fib(20)
}

This and many other programs can be found in the samples folder. These are also the programs used in the tests, so you can verify that they all work and produce correct output.

Nom was strongly influenced by Rust and Zig, and to a lesser extent every other language I know. It intends to offer direct control of memory, as these languages do, and a rich type system. It is currently a very simple language, but I intend for it to become more complex over time. The main goal of the project is to let me learn about implementing a language, so by necessity the language will get more and more complicated to let me learn more.

Features:

Further documentation on the Nom programming language (not documentation for the project) can be found in nom_docs.md. I'll try to avoid letting it get too out of date.

Upcoming Features (probably):

This is a fairly high level roadmap. For a much more detailed version, see the project tab or the issues tab on GitHub.

More about the Project

Documentation for the code of the project is included in the code itself. However, it can be viewed in HTML form by running cargo doc. This only shows the (small) public interface, but documentation exists for private items as well, which can be viewed by running cargo doc --document-private-items.

Most of the code is contained in the library crate, rooted at lib.rs. There is also a simple binary crate (the single file, main.rs) which compiles and runs a program provided through standard input. In the future, main.rs should look more like the frontend of a compiler, handling flags and options and so on; right now, it is a very thin wrapper around the library.

The library code is broken up into modules. The first layer of modules is organized primarily by which stage of the compilation process they implement. This first layer is described below, but each of these primary modules may be further broken up into submodules.

The Nom runtime isn't particularly fast, as one might imagine from a toy language. While I say there are optimizations, they are currently very simple. That being said, a carefully written Nom program (written with an understanding of how it compiles) can achieve performance similar to a translation of that program into Python (even performing around 10% better), which is a pleasant surprise. Make sure you build the project in release mode (cargo build --release) before doing benchmarks.

I find a test driven approach is very helpful in developing this project. There are a couple of unit tests for some modules, but the majority of tests are integration tests for new language features. To extend the language, I recommend writing a test or two with the new feature, then going through all the modules in order updating as necessary. Rust's type system is very helpful here - often I need to make just a few well thought out changes to, for instance, the AST type, and after that the rest of the work is just going through the remaining modules and making sure they compile. Rust points out all the places that need to consider how the handle the new AST variant, which is very helpful.

When I am done, cargo test should pass, with no compilation warnings either. cargo clippy (or cargo clippy -- -D clippy::pedantic) should also emit no warnings (to be fair, I have been known to silence those lints I disagree with). Finally, cargo fmt should be used to fix any formatting issues.