Skip to content

utkarsh5026/interpreter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

427 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Mutant Lang

A fast, expressive scripting language β€” built from scratch in Rust.

Build Status Version Rust MSRV License: MIT PRs Welcome


Mutant Lang Banner


Table of Contents


About The Project

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

Key Features

  • 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-line and /* 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

Getting Started

Prerequisites

Tool Minimum version
Rust toolchain 1.87 (2024 edition)
Cargo ships with Rust

Installation

# 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-lang

Running the REPL

cargo run --release
  β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
  β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β•šβ•β•β–ˆβ–ˆβ•”β•β•β•
  β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
  β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
  β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
  β•šβ•β•     β•šβ•β• β•šβ•β•β•β•β•β•    β•šβ•β•   β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•β•β•   β•šβ•β•
                    L A N G   v 0 . 1
  Type Ctrl-C or Ctrl-D to exit.

>>

Running the test suite

cargo test

Usage / Examples

Hello, World

print("Hello, World!");

Variables & Constants

let counter = 0;
counter += 1; // mutable β€” OK

const PI = 3.14159;
// PI = 3;          // compile-time error: cannot reassign constant

Functions & Closures

fn makeAdder(x) {
    return fn(y) { return x + y; };
}

let add5 = makeAdder(5);
print(add5(3));   // 8

F-String Interpolation

let name = "Alice";
let score = 42;
print(f"Player {name} scored {score * 2} points!");
// Player Alice scored 84 points!

Classes & Inheritance

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!

Fibonacci

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/.


Language Reference

Data Types

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

Operators

Category Operators
Arithmetic + - * / % // (integer division)
Comparison == != < > <= >=
Logical && || !
Bitwise & | ^ ~ << >>
Assignment = += -= *= /= %=

Built-in Functions

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

Comments

# This is a single-line comment

/*
 * This is a block comment.
 * /* Nesting is supported. */
 * Still in the outer comment.
 */

Architecture

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        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key design decisions

  • Object is 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)
  • thiserror drives all structured error types, giving human-readable messages with source positions for free

Roadmap

  • 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 Rc or tracing GC)
  • Concurrency primitives (async/await)
  • Language Server Protocol (LSP) support
  • WASM compilation target

Contributing

Contributions are warmly welcome. Here's the standard flow:

  1. Fork the repository
  2. Create a feature branch β€” git checkout -b feat/my-feature
  3. Commit your changes β€” git commit -m "feat: add my feature"
  4. Push to your fork β€” git push origin feat/my-feature
  5. 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-migration branch 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.


License

Distributed under the MIT License. See LICENSE for the full text.


Contact / Support

Channel Link
GitHub Issues Open an issue
X (Twitter) @utkarsh5026
LinkedIn Utkarsh Priyadarshi

Built with Rust β€” because performance and correctness shouldn't be optional.

About

A complete interpreter for a dynamic programming language built from scratch in Rust πŸ¦€

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages