⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠤⠒⠒⠀⣈⠙⣿⣷⠶⣦⣄⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊⠁⠀⢀⣤⣶⢿⣶⠿⠿⢥⡴⠈⠙⢿⣶⣄⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠴⠊⠀⠀⠀⣠⣴⣮⡿⠛⠁⣺⠀⠀⠀⠀⠀⢀⣀⣿⡿⣿⢄⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢠⠔⠁⢀⡀⢀⢴⣺⡙⠋⢸⡏⠀⠀⡏⣀⢠⣴⠶⠭⠗⠛⠋⠙⠿⣾⣂⠀
⠀⠀⠀⠀⠀⢀⠔⢡⠀⠀⣨⣗⣏⠋⠀⡇⠀⢸⣃⠤⣒⢽⡊⠭⠒⠈⠏⠉⢿⣷⣶⣄⠸⡟⡄
⠀⠀⠀⢀⡔⠁⠀⣼⣦⣖⡗⠋⢸⠀⢀⣧⡶⢛⣥⡾⠃⠁⠀⠀⠀⠘⠀⢧⢸⣿⣮⣿⣇⡼⡇
⠀⠀⢠⠊⢀⣄⡀⢸⣹⡍⠀⠀⣸⣴⡿⠋⡠⠘⠁⠀⠀⠀⠀⠀⠰⢣⣄⣼⣿⡟⢿⣿⣿⡃⡇
⠀⣠⠏⣀⣿⣵⡣⠀⠀⣇⣤⣾⣿⠏⡠⠊⠀⠀⠀⠀⠀⠀⠀⡰⠓⢦⣿⣧⣿⠻⡼⢏⣿⣷⠃
⠀⡐⢹⠀⣟⠏⡞⢃⢀⣰⣿⣿⡿⡫⠊⠀⠀⠀⠀⠀⠀⠀⢀⡔⢁⠀⠸⣻⣿⣷⣄⢳⡼⣝⡎
⢰⠁⢿⣠⣟⢍⣇⣸⣾⣿⣿⢏⠔⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⢘⣆⣴⡿⠙⢿⣫⣴⣿⡟⠀⠀
⡆⠀⢘⣿⣿⣿⣿⣿⣿⣿⡿⡣⠋⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⣸⣾⢿⡁⢠⣿⣿⣟⠟⠀⠀⠀
⡇⣆⣞⡟⢟⠿⣿⣿⣿⡿⡱⠁⠀⠀⠀⠀⠀⢀⡠⠊⠀⠀⣤⡤⡄⠉⠁⢸⣿⣿⣿⢿⠏⠀⠀⠀⠀
⡇⢻⣻⣄⠈⢧⠀⡼⠱⠁⠀⠀⠀⢀⡠⠖⠁⠀⠀⣰⣶⢽⡹⡟⠀⣠⣾⣾⣿⠕⠁⠀⠀⠀⠀⠀
⢠⢸⡧⠈⢆⠈⢡⣇⣇⣀⣠⡔⠢⡁⠀⠀⣤⣲⣦⣿⣉⡾⢀⣷⣾⣿⠟⡻⠊⠀⠀⠀⠀⠀⠀⠀
⠈⢺⣷⣄⠀⠢⢸⣸⣿⣿⣿⣯⣶⣷⣤⣶⡿⢿⢻⠁⣩⣔⣿⣿⢟⡵⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠻⡬⠳⣄⠀⡟⣿⣿⣿⣿⣿⣿⣿⣟⣥⡧⢼⣿⣯⣿⠟⣫⠖⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠈⠪⢆⠀⠐⡙⢿⣿⣯⡝⣍⣧⣤⣷⡿⢛⣩⠔⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠁⠒⠬⢐⣭⣉⣉⣩⠭⠽⠟⠊⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

Precious

A Gollum-themed programming language that compiles to C++.

github.com/korrykatti/precious

Install

cmake -B build && cmake --build build
./build/precious your_file.precious
./out

Outputs a Linux ELF binary named after the input file via g++. The compiler generates C++ source code and lets g++ handle register allocation, optimization, and code generation.

Entry Point

Every program defines fn the_precious() — Precious' equivalent of C's main. The compiler emits a real int main() that calls it; a gives(n) inside it becomes the process exit code. Only fn definitions may live at top level.

// fib.precious
fn fib(n) {
    if (n < 2) {
        gives(n);
    }
    gives(fib(n - 1) + fib(n - 2));
}

fn the_precious() {
    my i = 0;
    while (i < 10) {
        say(fib(i));
        i = i + 1;
    }
    gives(0);
}

The compiler turns that into this C++, then g++ makes the binary:

#include <bits/stdc++.h>

long fib(long n);
long the_precious();

int main() {
    return the_precious();
}

long fib(long n)
{
    if (n < 2) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

long the_precious()
{
    long i = 0;
    while (i < 10) {
        std::cout << (fib(i)) << "\n";
        i = i + 1;
    }
    return 0;
}

Missing, duplicate, or misplaced entry points get gollum errors: "Where is the precious?!", "There can be only one precious!", "The precious takes no arguments!". Statement examples below are snippets — they live inside a function body.

Variables

Declare with my. Assign with =.

my x = 42;
x = 10;

Type Annotations

Optional : type after the variable name. Inferred if omitted.

PreciousC++ typeDescription
numberlong64-bit integers
wordstd::stringStrings
questionlongBooleans
decimaldoubleFloating-point
lettercharSingle characters
my x: number = 5;
my name: word = "gollum";
my y = 10;              // inferred as number
my msg = "hello";       // inferred as word

Operators

Arithmetic

my result = 2 + 3 * 4 - 1;  // 13
my negative = -5;            // unary minus
gives(result);

Comparison

== != < > <= >= — return 1 or 0.

Boolean

and, or, ! — precedence: ! > and > or.

if (x > 0 and x < 10) { gives(1); }
if (!0) { gives(2); }
if (x == 1 or x == 5) { gives(3); }

If / Elif / Else

my x = 5;
if (x == 5) {
    gives(10);
} elif (x > 3) {
    gives(20);
} else {
    gives(30);
}

While Loop

my i = 0;
while (i < 5) {
    i = i + 1;
}
gives(i);

Scopes

my x = 5;
{
    my y = 10;
    gives(x + y);
}

Print

say(expr) prints to stdout. Works with integers, string literals, and string variables.

say(42);          // prints 42
say("hello");     // prints hello

my msg: word = "gollum";
say(msg);         // prints gollum

Functions

Define with fn. Return with gives. Parameters support type annotations. Call order doesn't matter — the compiler emits forward declarations.

fn add(a, b) {
    gives(a + b);
}

fn multiply(a, b) {
    gives(a * b);
}

fn greet(name: word) {
    say(name);
}

fn the_precious() {
    say(add(2, 3));         // prints 5
    greet("precious");      // prints precious

    my result = multiply(6, 7);
    say(result);            // prints 42
}

Return type is auto-detected from gives, or annotate it: fn greet() -> word { ... }.

Arrays

Indexed collections. Require explicit type annotations: type[size].

my numbers: number[3] = [10, 20, 30];
say(numbers[0]);    // prints 10
say(numbers[2]);    // prints 30

numbers[1] = 99;
say(numbers[1]);    // prints 99

my words: word[2] = ["hello", "world"];
say(words[0]);      // prints hello

my i: number = 1;
say(numbers[i]);    // prints value at index 1

Array Push / Pop

Add and remove elements dynamically with push and pop.

my arr: number[] = [1, 2, 3];
push arr, 4;
say(arr[3]);        // prints 4
pop arr;
say(arr[2]);        // prints 3

my words: word[] = ["hello", "world"];
push words, "foo";
say(words[2]);      // prints foo

String Concatenation

Join strings with +.

my first = "hello";
my second = " world";
my combined = first + second;
say(combined);      // prints hello world

my a = "foo";
a = a + "bar" + "baz";
say(a);             // prints foobarbaz

String Indexing

Access individual characters with [].

my msg = "hello";
say(msg[0]);        // prints h
say(msg[4]);        // prints o

Keywords

KeywordWhat it does
myDeclare a variable
givesReturn a value / set exit code
fnDefine a function
sayPrint to stdout
if / elif / elseConditionals
whileLoop
forC-style for loop / for-each
inFor-each iteration
break / continueLoop control
switch / case / defaultPattern matching
push / popArray push/pop
and / or / !Boolean logic
number / word / question / decimal / letterTypes
[ ]Array literals and index access

Tests

bash run_tests.sh

Resources