oonta 0.1.0

OCaml to LLVM IR compiler front-end
Documentation
  • Coverage
  • 0%
    0 out of 31 items documented0 out of 7 items with examples
  • Size
  • Source code size: 1.03 MB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 4.06 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 50s Average build duration of successful builds.
  • all releases: 1m 30s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • fuad1502/oonta
    28 0 2
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • fuad1502

Oonta: OCaml to LLVM IR Compiler

CI

Oonta is a compiler front-end for the OCaml programming language: it generates LLVM intermediate representation (IR) from OCaml source code.

Oonta uses the JJIK parser generator and JLEK lexer generator to perform the parsing and lexing stages.

[!IMPORTANT] This project is still a work in progress, many OCaml features are not yet supported. For example, custom types, pattern matching, and modules are not yet supported. Additionally, the garbage collector runtime is not yet available. See the issues tab for the list of work items.

[!NOTE] This project is part of the "Compiler Toys" project, originally meant as a learning exercise on Compilers.

Quick Start

cargo install oonta
cat << EOF > main.ml
let square x = x * x
let a = square 3
let () = print_int a
let rec factorial x = if x <= 1 then 1 else x * factorial (x - 1)
let b = factorial 5
let () = print_int b
EOF
oonta --exec main.ml
./main.out
# 9
# 120

Dependencies

The oonta binary does not have any runtime dependencies other than the standard library. However, for convenience, the oonta command provides the --compile / -c and --exec / -e options to compile the generated IR to an object code and executable, respectively. Internally, oonta will invoke the following commands:

# with --compile
llc -relocation-model=pic --filetype=obj -o <output> <.ll file>
# with --exec
clang -o <output> <.o file>

On Ubuntu, install the llvm package to make those commands available.

sudo apt install llvm

[!NOTE] I will be working on my own LLVM backend as part of my compiler learning journey! ✨

[!WARNING] The generated IR uses opaque pointers. If your LLVM version is older than version 15, the --compile / --exec options might not work.

User Guide

oonta --help

Feature Highlights

Debug compile phases

Use the --verbose / -v option to debug each compile phase.

cat << EOF > main.ml
let rec factorial x = if x <= 1 then 1 else x * factorial (x - 1)
let () = print_int (factorial 5)
EOF
oonta --exec -v main.ml
=> Lexing & Parsing Start
=> Lexing & Parsing End (1 ms)
=> Build AST Start
factorial = 
FunExpr
├─▸ parameters: [x]
├─▸ captures: []
├─▸ recursive: yes
└─▸ body:
    CondExpr
    ├─▸ condition:
    │   BinOpExpr
    │   ├─▸ operator: <=
    │   ├─▸ lhs:
    │   │   VarExpr ("x")
    │   └─▸ rhs:
    │       LiteralExpr (1)
    ├─▸ then expr:
    │   LiteralExpr (1)
    └─▸ else expr:
        BinOpExpr
        ├─▸ operator: *
        ├─▸ lhs:
        │   VarExpr ("x")
        └─▸ rhs:
            ApplicationExpr
            ├─▸ function:
            │   VarExpr ("factorial")
            └─▸ binds:
                └─▸ (0)
                    BinOpExpr
                    ├─▸ operator: -
                    ├─▸ lhs:
                    │   VarExpr ("x")
                    └─▸ rhs:
                        LiteralExpr (1)

() = 
ApplicationExpr
├─▸ function:
│   VarExpr ("print_int")
└─▸ binds:
    └─▸ (0)
        ApplicationExpr
        ├─▸ function:
        │   VarExpr ("factorial")
        └─▸ binds:
            └─▸ (0)
                LiteralExpr (5)

=> Build AST End (0 ms)
=> Resolve types Start
Top level bindings:
factorial: (int -> int)
=> Resolve types End (0 ms)
=> Transform application expressions Start
=> Transform application expressions End (0 ms)
=> Build LLVM module Start
=> Build LLVM module End (0 ms)
=> Write LLVM module Start
=> Write LLVM module End (1 ms)
=> LLVM backend Start
=> LLVM backend End (117 ms)

Error reporting

Line   1|let x = foo 3
                 ^--
Error: cannot infer expression type: Unbound value foo
Line   1|let rec f x = f
                       ^
Error: cannot infer expression type: Cannot unify 'b with ('a -> 'b)
Line   1|let () = 1 + 2
                  ^----
Error: cannot bind expression of type int to ()

Building from source

  1. Install cargo tool:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  1. Clone repository.
git clone https://github.com/fuad1502/oonta.git
  1. Build oonta crate.
cd compiler_toys/oonta
cargo build
cargo test

[!NOTE] oonta only depends on jjik, jlek, and Rust's standard library for building.

Why is it called Oonta?

Oonta, is based on the Indonesian word unta, which translates to "camel".