- About The Project
- Key Features
- Getting Started
- Usage / Examples
- Language Reference
- Architecture
- Roadmap
- Contributing
- License
- Contact / Support
Mutant Lang is a dynamically-typed, interpreted scripting language implemented as a tree-walking interpreter entirely in Rust. It was built from first principles to explore every layer of language implementation β from raw character streams to a full object-oriented runtime β without relying on any parser-generator or VM framework.
The project exists to answer one question: what does it actually take to go from a .mutant source file to a running program? Every stage is hand-rolled:
| Stage | What it does |
|---|---|
| Lexer | Converts source text into a typed Token stream, handling escape sequences, f-strings, nested block comments, and precise (line, col) tracking |
| Parser | Builds an Abstract Syntax Tree using recursive descent for statements and Pratt (TDOP) parsing for expressions with correct operator precedence |
| AST | Strongly-typed Rust enums β one variant per node kind; zero heap indirection for leaf nodes |
| Evaluator | Tree-walking interpreter with lexical scoping via chained Environment maps; control-flow is propagated through sentinel Object variants |
| REPL | rustyline-powered interactive shell with persistent state, colored output, and command history |
- Full OOP β
class,extends,super,this, constructors, and method overriding - First-class functions & closures β functions capture their defining scope
- F-string interpolation β
f"Hello {name}, you are {age} years old!" - Rich type system β integers, floats, booleans, strings, arrays, hash maps, and
null - Compound assignment operators β
+=,-=,*=,/=,%= - Bitwise operators β
&,|,^,~,<<,>> - Flexible control flow β
if/elif/else,while,for,break,continue,return - Nested comment styles β
# single-lineand/* nested /* block */ */ - Colored REPL output β integers in yellow, strings in green, booleans in cyan
- Precise error messages β every token carries its
(line, col)source position
| Tool | Minimum version |
|---|---|
| Rust toolchain | 1.87 (2024 edition) |
| Cargo | ships with Rust |
# 1. Clone the repository
git clone https://github.com/utkarsh5026/interpreter.git
cd interpreter/lib
# 2. Build a release binary
cargo build --release
# 3. The binary is now at:
./target/release/mutant-langcargo run --release ββββ βββββββ ββββββββββββ ββββββ ββββ ββββββββββββ
βββββ ββββββββ βββββββββββββββββββββββββ ββββββββββββ
ββββββββββββββ βββ βββ ββββββββββββββ βββ βββ
ββββββββββββββ βββ βββ ββββββββββββββββββ βββ
βββ βββ ββββββββββββ βββ βββ ββββββ ββββββ βββ
βββ βββ βββββββ βββ βββ ββββββ βββββ βββ
L A N G v 0 . 1
Type Ctrl-C or Ctrl-D to exit.
>>
cargo testprint("Hello, World!");let counter = 0;
counter += 1; // mutable β OK
const PI = 3.14159;
// PI = 3; // compile-time error: cannot reassign constantfn makeAdder(x) {
return fn(y) { return x + y; };
}
let add5 = makeAdder(5);
print(add5(3)); // 8let name = "Alice";
let score = 42;
print(f"Player {name} scored {score * 2} points!");
// Player Alice scored 84 points!class Animal {
constructor(name) {
this.name = name;
}
speak() { return f"{this.name} makes a sound"; }
}
class Dog extends Animal {
speak() { return f"{this.name} barks!"; }
}
let d = new Dog("Rex");
print(d.speak()); // Rex barks!fn fib(n) {
if (n <= 1) { return n; }
return fib(n - 1) + fib(n - 2);
}
for (let i = 0; i < 10; i += 1) {
print(f"fib({i}) = {fib(i)}");
}For more examples see the existing README language reference above, or explore lib/src/evaluator/ and lib/src/parser/.
| Type | Example |
|---|---|
| Integer | 42, -7 |
| Float | 3.14, .5 |
| Boolean | true, false |
| String | "hello", 'world' |
| F-String | f"value is {x + 1}" |
| Array | [1, "two", true] |
| Hash | {"key": "value", 1: true} |
| Null | null |
| Category | Operators |
|---|---|
| Arithmetic | + - * / % // (integer division) |
| Comparison | == != < > <= >= |
| Logical | && || ! |
| Bitwise | & | ^ ~ << >> |
| Assignment | = += -= *= /= %= |
len(arr); // length of array or string
first(arr); // first element
last(arr); // last element
rest(arr); // all elements except first
keys(hash); // array of hash keys
values(hash); // array of hash values
type(value); // "INTEGER" | "FLOAT" | "STRING" | ...
print(a, b, c); // print to stdout, space-separated# This is a single-line comment
/*
* This is a block comment.
* /* Nesting is supported. */
* Still in the outer comment.
*/Source Code
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β LEXER (lib/src/lexer/) β
β CharacterStream β pull-style Token iterator β
β Handles: strings, f-strings, nested commentsβ
β escape sequences, (line,col) pos β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
β Token stream
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β PARSER (lib/src/parser/) β
β Recursive descent (statements) β
β Pratt / TDOP (expressions & precedence) β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
β AST (lib/src/ast/)
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β EVALUATOR (lib/src/evaluator/) β
β Tree-walking interpreter β
β Chained Environment scoping β
β Control-flow via Object sentinels β
β (Return, Break, Continue) β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
β Object
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β REPL (lib/src/repl.rs) β
β rustyline editor + colored output β
β Persistent Environment across inputs β
ββββββββββββββββββββββββββββββββββββββββββββββββ
Objectis a single Rust enum, not a class hierarchy β pattern matching replaces dynamic dispatch everywhere- Environments are
Rc<RefCell<β¦>>chains β child scopes hold a reference to their parent without cloning the whole chain - Pratt parser handles all infix/prefix expressions cleanly with explicit precedence levels (see lib/src/parser/precedence.rs)
thiserrordrives all structured error types, giving human-readable messages with source positions for free
- Standard library (math, string utilities, I/O)
-
import/ module system - Bytecode compiler + stack-based VM
- Type inference / optional static types
- Garbage collector (replace clone-on-bind with
Rcor tracing GC) - Concurrency primitives (
async/await) - Language Server Protocol (LSP) support
- WASM compilation target
Contributions are warmly welcome. Here's the standard flow:
- Fork the repository
- Create a feature branch β
git checkout -b feat/my-feature - Commit your changes β
git commit -m "feat: add my feature" - Push to your fork β
git push origin feat/my-feature - Open a Pull Request against
main
Please make sure cargo test and cargo clippy -- -D warnings both pass before opening the PR.
Tip: The
rust-migrationbranch tracks ongoing work porting the original Java implementation to Rust. If you're working on interpreter internals, that's a good place to start reading.
Distributed under the MIT License. See LICENSE for the full text.
| Channel | Link |
|---|---|
| GitHub Issues | Open an issue |
| X (Twitter) | @utkarsh5026 |
| Utkarsh Priyadarshi |
Built with Rust β because performance and correctness shouldn't be optional.