early development

A small language,
built from scratch.

first is a minimal interpreted language written in C99. Arithmetic, relational, equality, and boolean expressions, when for predicate-based branching, { } scoped blocks, and fn functions with closures and recursion — from source text to runtime value.

echo "let x = 1 + 2" | ./build/first

first — interactive session
let x = 10 + 5
15
let y = x * 2
30
let label = when y > 20 -> "big", _ -> "small"
"big"
y % 7
2
let fact = fn(n) -> when n <= 1 -> 1, _ -> n * fact(n - 1)
fact(5)
120
live tokenizer

Peek inside the lexer

Type any first expression and watch it decompose into tokens in real time — exactly what lex_next_token() produces.

input
tokens
type something…
architecture

How it works

sourceFILE*
lexerlexer.c
parserparser.c
ASTparser.h
interpreterinterpreter.c
runtimeRuntimeValue*

The interpreter is a tree-walk evaluator. Each AST node is a tagged union dispatched by interpreter_evaluate(). Variables live in a scope-chained Environment, backed by JMap — a custom open-addressing hash map.

features
//

C99 core

Zero external runtime dependencies. Builds with CMake, tested with Criterion, runs anywhere.

Streaming lexer

Tokenizes from a FILE* without loading the full source into memory. One token per call.

{ }

Scoped variables

let x = expr declares. x = expr reassigns. Scopes chain by parent pointer.

( )

Rich expressions

Arithmetic (+ − × ÷ % **), relational, equality, and boolean operators, all with correct precedence and parenthesized grouping.

Recursive descent

The parser is hand-written and easy to follow. Each grammar rule maps to a single function.

when

Predicate branching

No if/elsewhen evaluates predicate arms top to bottom with a mandatory _ -> catch-all, and is itself an expression.

fn

Functions & closures

Anonymous fn(params) -> body literals bound with let like any other value. They capture their defining scope by reference, so closures and self-recursive calls both work.

current grammar

What first can parse today

// full grammar — src/parser.c
<program>        ::= [ "module" <identifier> ] { <expression> }
<expression>     ::= <declaration> | <when> | <boolean_or>
<declaration>    ::= "let" <identifier> "=" <assignment>
                    { "," <identifier> "=" <assignment> }
<assignment>     ::= <when> | <function> | <boolean_or> [ "=" <assignment> ]
<when>           ::= "when" <arm> { ("," | newline) <arm> }
                    "_" "->" <expression>
<arm>            ::= <boolean_or> "->" <expression>
<function>       ::= "fn" "(" [ <identifier> { "," <identifier> } ] ")" "->" <expression>
<boolean_or>     ::= <boolean_and> { "||" <boolean_and> }
<boolean_and>    ::= <equality> { "&&" <equality> }
<equality>       ::= <relational> { ("==" | "!=") <relational> }
<relational>     ::= <addition> { ("<" | ">" | "<=" | ">=") <addition> }
<addition>       ::= <multiplication> { ("+" | "-") <multiplication> }
<multiplication> ::= <exponent> { ("*" | "/" | "%") <exponent> }
<exponent>       ::= <unary> { "**" <unary> }
<unary>          ::= ("+" | "-") <unary> | <primary>
<primary>        ::= "(" <expression> ")" | <block> | <literal> | <identifier> [ <call> ]
<call>           ::= "(" [ <boolean_or> { "," <boolean_or> } ] ")"
<block>          ::= "{" { <expression> } "}"
<literal>        ::= <number> | <string> | "true" | "false"