Arith is a small programming language that compiles simple arithmetic programs into executable .NET assemblies.
Rather than interpreting expressions one at a time, Arith type-checks the source code and translates it into an assembly containing .NET Common Intermediate Language (CIL, commonly called IL) and metadata. The .NET runtime executes the generated code and its JIT compiler translates the IL into machine code for the target CPU.
The goal of this project is to explore the fundamental stages of a compiler—lexing, parsing, type checking, and code generation—through a small, approachable language.
Note
The compiler implements all of language v0.1 (architecture in
docs/compiler-design.md): arith build and
arith run compile and execute every feature in
LANGUAGE_SPEC.md.
fn sum_range(start: i64, end: i64) -> i64 {
let total = 0;
for i in start..end {
total += i;
}
return total;
}
fn main() -> i32 {
let result = sum_range(1, 11);
if result > 50 {
print("large:");
print(result);
} else {
print("small:");
print(result);
}
return 0;
}
Expected output:
large:
55
- Primitive
bool,i32,i64,f32,f64, andstringtypes - Functions declared with
fn, with support forreturn - Local variables declared with
let, including reassignment - Arithmetic, comparison, and logical operators
if/else,while, and range-basedforstatementsbreakandcontinue- A built-in
printfunction that prints one value per line - Typed
mainparameters that receive parsed command-line arguments - Explicit numeric conversions
- Checked integer arithmetic
- Generation of a .NET assembly with
mainas its entry point
See LANGUAGE_SPEC.md for the complete syntax and semantics.
arith build hello.arith [-o <dir>] # compile into a .NET assembly
arith build hello.arith --aot # compile into a single native executable
arith run hello.arith [args...] # compile and run, forwarding the exit codeA main with parameters receives command-line arguments, parsed per parameter
type before it runs (LANGUAGE_SPEC.md §5.1); on a wrong
argument count or an unparsable value the program prints a usage line and
exits with code 2. With arith run, put -- before values that start with
-:
arith run greet.arith 3 hello # fn main(count: i64, label: string)
arith run negate.arith -- -5The source file must be named <program-name>.arith, where <program-name>
starts with a letter or _ and contains only letters, digits, _, and -
(a CLI rule, not part of the language); the outputs are named after it.
build produces framework-dependent artifacts:
hello.dll the compiled assembly
hello.runtimeconfig.json names the shared framework for the dotnet host
hello / hello.cmd convenience launchers
The generated program can also be run with the .NET CLI:
dotnet hello.dllWith --aot, the same emitted IL is instead compiled ahead-of-time by the
official NativeAOT toolchain into one native executable that runs without the
dotnet host (requires the platform's native linker, e.g. Xcode Command Line
Tools on macOS; see docs/il-emission-notes.md).
On failure, diagnostics are printed as file:line:col: error ARITHxxxx: message;
every code is listed in docs/diagnostics.md.
Arith source
↓ lexing
token stream
↓ parsing
abstract syntax tree (AST)
↓ name resolution and type checking
typed syntax tree
↓ IL and metadata generation
.NET assembly
The stages are separate components of the Arith.Compiler library, and every stage reports errors with their source locations.
Building the compiler requires the .NET 10 SDK.
dotnet build # build all projects
dotnet test # run the test suite
dotnet run --project src/Arith.Cli -- version # run the CLIThe version command prints the CLI version:
0.1.0
The repository is laid out as follows:
examples/— runnable example programs, from fizzbuzz to an ASCII Mandelbrot and a tail-call experiment (see examples/README.md)src/Arith.Compiler— the compiler as a library (source text, diagnostics, lexer, parser, binder, and IL emitter; architecture in docs/compiler-design.md)tests/Arith.Compiler.Tests— xUnit v3 unit tests for the compiler stagessrc/Arith.Cli— thearithcommand-line tool (build,run,version), including the artifact writer and the NativeAOT packaging behindbuild --aottests/Arith.Cli.Tests— xUnit v3 testsDirectory.Build.props/Directory.Packages.props— shared build settings and centrally managed NuGet package versions- Build outputs are written to
artifacts/
All stages of the original roadmap — lexer, parser, name resolution and type checking, IL generation for expressions and control flow, assembly emission and execution, and diagnostics with test coverage — are implemented; language v0.1 is complete.
Arrays, structs, classes, closures, generics, modules, and null are outside the scope of version 0.1 and are candidates for future versions (LANGUAGE_SPEC.md §13). Released versions are recorded in CHANGELOG.md.
The Arith compiler and this repository are released under the
MIT License. Programs you write in Arith are your own; Arith
claims no license over them or over the assemblies compiled from them. Note
that a --aot executable embeds .NET runtime components, so distributing
one must additionally comply with the applicable .NET licenses and notices —
see the official .NET license information.