Skip to content

Repository files navigation

IQ--

A tiny, statically-typed language that compiles to native code through LLVM — written from scratch, by hand, to learn how real compilers work.

Language Backend Frontend Tests Platform License


IQ-- is a complete (if small) compiler: a hand-written lexer and recursive-descent / Pratt parser, a name resolver, a bidirectional type checker, and a backend that emits textual LLVM IR — which clang turns into a real native executable. The compiler core has zero third-party dependencies (pure C++23 standard library); the only dependency anywhere is Catch2 for the test suite.

It is an educational project — every stage is built to be read and understood, using the same techniques production compilers (Clang, rustc) use: 32-bit source offsets, table-driven character classification, string interning, LLVM-style RTTI on the AST, panic-mode error recovery with caret diagnostics, an arena allocator, canonical interned types, and bidirectional inference.

fn sum_sq(n: i32) -> i64 {
    let acc: i64 = 0;
    for i in 0..n { acc += (i as i64) * (i as i64); }
    ret acc;
}

fn main() {
    let xs = [1, 2, 3, 4];
    let [first, ..rest, last] = xs;   // destructuring with a bindable rest
    let pair = (first, last);         // tuples
    print(pair.0 + pair.1);           // 5
    print(xs[^1]);                    // 4   (index from the end)
    print(sum_sq(10));                // 285
}
$ IQ--.exe --run program.iq
5
4
285

The pipeline

 .iq source
     │
     ▼
 ┌────────┐   ┌────────┐   ┌──────────┐   ┌──────────────┐   ┌──────────┐
 │ Lexer  │──▶│ Parser │──▶│ Resolver │──▶│ Type checker │──▶│ CodeGen  │
 │ tokens │   │  AST   │   │  scopes  │   │ types + sema │   │ LLVM IR  │
 └────────┘   └────────┘   └──────────┘   └──────────────┘   └────┬─────┘
                                                                  │  .ll
                                                                  ▼
                                                          clang ─▶ native .exe

Quick start

Requires Visual Studio 2026 (MSVC v145, C++23). clang ships with Visual Studio, so no separate LLVM install is needed.

# 1. Build (Visual Studio: open IQ--.slnx and Build, or from a dev shell)
msbuild driver\IQ--.vcxproj /p:Configuration=Debug /p:Platform=x64

# 2. Compile & run an IQ-- program in one step
driver\x64\Debug\IQ--.exe --run program.iq

Every compiler stage is also inspectable on its own:

Mode What it does
--dump-tokens print the token stream from the lexer
--dump-ast print the AST as an indented tree
--dump-ast=dot emit the AST as Graphviz DOT (… | dot -Tpng -o ast.png)
--check parse + resolve + type-check, report errors only
--emit-llvm [-o f.ll] emit LLVM IR (to stdout or a file)
--run compile via clang and execute

What the language supports today

Typesi32 i64 u32 u64 f32 f64, bool, string, void, fixed arrays [T; N], and n-tuples (A, B, C, …).

Declarations & flowfn (with a void default return), top-level const, let/const locals, if/else, while, for x in lo..hi (..= inclusive), ret, break, continue.

Expressions — arithmetic, comparisons, short-circuit && ||, unary - !, assignment and compound += -= *= /= %= (to names, array elements a[i] = x, and tuple fields t.0 = x), calls, array indexing a[i] and from-end a[^k], tuple field access t.0, as casts, array/tuple literals, and print.

Functions & aggregates — arrays and tuples can be passed to and returned from functions, so multiple return values are simply a returned tuple (fn divmod(a, b) -> (i32, i32)). Parallel assignment swaps without a temp: (a, b) = (b, a).

Compile-time guarantees — name resolution (undeclared / redefined / use-before-declaration), bidirectional type inference (untyped literals are pinned by context; no implicit numeric widening — use as), suffix-vs-annotation conflict detection, all-paths-return analysis (it knows a non-breaking while true diverges), const-expression array lengths, and mutability checks.

Algorithms & data structures you can write right now

Every snippet below compiles and runs on IQ-- as it stands today.

Recursion — FibonacciEuclid's GCD
fn fib(n: i32) -> i64 {
    if n < 2 { ret n as i64; }
    ret fib(n - 1) + fib(n - 2);
}
// fib(10) -> 55
fn gcd(a: i32, b: i32) -> i32 {
    let x = a; let y = b;
    while y != 0 {
        let t = y;
        y = x % y;
        x = t;
    }
    ret x;
}
// gcd(48, 36) -> 12
Primality testBinary search (read-only array)
fn is_prime(n: i32) -> bool {
    if n < 2 { ret false; }
    let i = 2;
    while i * i <= n {
        if n % i == 0 { ret false; }
        i += 1;
    }
    ret true;
}
// is_prime(17) -> true
let xs = [1, 3, 5, 7, 9, 11];
let lo = 0; let hi = 5;
let found = -1;
while lo <= hi {
    let mid = (lo + hi) / 2;
    if xs[mid] == 7 { found = mid; break; }
    if xs[mid] < 7 { lo = mid + 1; }
    else { hi = mid - 1; }
}
// found -> 3

In-place bubble sort — fixed array, element assignment, swaps:

let xs = [5, 2, 8, 1, 9, 3];
let n = 6;
let i = 0;
while i < n {
    let j = 0;
    while j < n - 1 - i {
        if xs[j] > xs[j + 1] {
            (xs[j], xs[j + 1]) = (xs[j + 1], xs[j]);   // swap, no temp
        }
        j += 1;
    }
    i += 1;
}
for k in 0..n { print(xs[k]); }    // 1 2 3 5 8 9

Problem coverage vs. mainstream languages

How far the language reaches today, problem by problem. The mainstream languages can of course do all of these — the point is to show IQ--'s current frontier.

Problem / data structure IQ-- Rust C++ C# Java Python
Recursion (Fibonacci, factorial)
Euclid's GCD, fast exponentiation
Primality / sieve over a fixed array
Sum / min / max / count over an array
Linear & binary search (read-only)
Fixed-size tuple records (e.g. a point)
In-place sorting (bubble / insertion)
Dynamic array / stack / queue
Hash map / set
Linked list / tree / graph
String processing (length, slicing)

✅ supported  ·  ❌ needs language features not built yet (heap, struct, generics).

Feature comparison

Feature IQ-- Rust C++ C# Java Python
Static type checking ⚠️ hints
Local type inference auto var ⚠️ var n/a
Bidirectional literal inference n/a
Sized integer types (i32/u64…) ⚠️ no unsigned ❌ bignum
No implicit numeric widening n/a
Tuples (n-ary)
Multiple return values (via tuple)
Pattern destructuring (let [a,..b,c]) ⚠️ ⚠️ ⚠️
Fixed-size arrays
Dynamic collections / generics
Compiles to native code ✅ LLVM ⚠️ JIT/AOT ⚠️ JVM

The same program, three ways

Euclid's GCD — IQ-- sits comfortably in the C / Rust family:

IQ--RustPython
fn gcd(a: i32, b: i32)
        -> i32 {
    let (x, y) = (a, b);
    while y != 0 {
        let t = y;
        y = x % y;
        x = t;
    }
    ret x;
}
fn gcd(a: i32, b: i32)
        -> i32 {
    let (mut x, mut y) =
        (a, b);
    while y != 0 {
        let t = y;
        y = x % y;
        x = t;
    }
    x
}
def gcd(a, b):
    x, y = a, b
    while y != 0:
        x, y = y, x % y
    return x

What's not supported yet

Being honest about the frontier:

  • No heap — no Vec, String operations, maps, or linked structures.
  • No struct, match, generics, or modules — the pattern grammar and the tuple/field machinery are already in place to make struct and match the next natural additions.

Project layout

IQ--/
├── frontend/    IQFrontend.vcxproj  — lexer, parser, AST, resolver, type checker
├── backend/     IQBackend.vcxproj   — LLVM IR code generation
├── driver/      IQ--.vcxproj        — the command-line compiler (IQ--.exe)
├── test/        IQTests.vcxproj     — Catch2 suite (64 cases, 137 assertions)
└── iqrun.cs                         — optional .NET 10 helper (ProcessX / Zx)

Headers live under include/iq/…, implementations under src/…, mirroring Clang's layout.

Building & running the tests

msbuild test\IQTests.vcxproj /p:Configuration=Debug /p:Platform=x64
test\x64\Debug\IQTests.exe

License

Apache License 2.0.

Built as a hands-on study of modern compiler construction.

About

Compiler for the IQ-- programming language, using .iq file extension.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages