Skip to content

Repository files navigation

MiruScriptX

A complete, general-purpose scripting language, written from scratch in Rust

Compiled to bytecode, run on a stack virtual machine

Rust The language declares 4 direct dependencies, two of them platform-specific, over 57 total crates, 1 of them a dev dependency 493 tests passing

CI The latest version on crates.io The latest GitHub release MIT License

Try it in your browser | Quick Start | Features | Examples | Documentation


Overview

MiruScriptX is a minimalist, dynamically typed scripting language with a clean, modern syntax, written from scratch in Rust. Write functions, closures, loops, arrays, and maps in familiar syntax, split a program across files when it outgrows one, handle the errors you expect and let the rest stop the program, then run it from a file or an interactive REPL. Programs use the .miru extension.

The name carries a lineage. MiruScriptX revives MiruScript, an earlier language by the same author written in C; the X marks the successor.

Programs are compiled to bytecode and run on a stack virtual machine. A tree walker came first and was replaced in v0.5, once a corpus of golden tests had frozen its behavior exactly; miru disasm will show you what any program compiles to.

fn greet(name) {
  return "Hello, " + name + "!"
}

let people = ["Aiko", "Ken"]
for name in people {
  print(greet(name))
}

Features

Language

  • Integers, floats, booleans, strings, nil
  • Arrays and maps, with indexing
  • Functions, closures, and recursion
  • if / else if / else, while, for ... in
  • for key, value in map to walk a map's entries, and for i, x in array for the position alongside the element
  • let [x, y] = pair to give an array's elements their own names, in a let or a loop
  • break and continue
  • Modules: import a file and reach its names through a dot
  • try, which turns a failure into a value you can check
  • Reading input with input
  • Reading and writing files, and reading the command line, with read_file, write_file, file_exists, and args
  • Two output streams, print and eprint, and an exit code chosen with exit
  • Arithmetic, comparison, and short-circuit logic, and += for changing a variable in place
  • Strings you can index and interpolate: s[0], and f"{name} scored {score}"

Engine and tooling

  • Lexer, Pratt parser, bytecode compiler, stack virtual machine
  • A standard library of string, array, math, map, and I/O builtins, including copy, repeat, pad_left, pad_right, chr, and ord
  • Higher-order builtins: map, filter, and reduce
  • File runner, a source formatter (miru fmt), and a REPL with history
  • A disassembler (miru disasm) that prints the bytecode for a program
  • Errors with a line, a column, and an underline under the token at fault
  • Errors you can catch as values, carrying the call path they came through
  • Minimal dependencies: rustyline at runtime, nix or windows-sys for raw-mode key reading, criterion for benchmarks
  • A terminal to draw on: clear, move_to, and the cursor, for programs that animate rather than print
  • A WebAssembly build and an in-browser playground, in a separate crate
  • Unit, golden, session, and end-to-end tests, benchmarks, and CI

Quick Start

Install the latest release. The script checks the download against the published checksum and refuses if it does not match:

curl -fsSL https://raw.githubusercontent.com/stiven-gjekaj/miruscriptx/main/scripts/install.sh | sh

Prebuilt binaries cover Linux and macOS on x86-64 and arm64, and Windows on x86-64. Or install from crates.io:

cargo install miruscriptx

Or build from source (a recent stable Rust toolchain is all you need):

cargo build --release

Run a program from a file:

miru run examples/greet.miru

Evaluate a short program directly from the command line with -e (or its long form, --eval):

miru -e 'print(6 * 7)'

Or start the REPL and type expressions:

miru
miru> let x = 21
miru> x * 2
42

Reformat a program in the canonical style (add -w to rewrite it in place):

miru fmt examples/greet.miru

For a step-by-step guide, start the wiki at wiki/01-introduction.md.


Examples

Runnable programs live in examples/:

Program Shows off
greet.miru Functions, arrays, and a loop
fib.miru Recursion
fizzbuzz.miru Control flow and the modulo operator
contacts.miru Maps, lookups, and iteration
greeter.miru Reading a line with input, and handling the end of it
transform.miru Higher-order functions: map, filter, reduce
shop.miru + prices.miru Two files: import, and names that belong to a file
recover.miru try: surviving an error instead of stopping at it
guess.miru A guessing game: random_int, seed, and input in a loop
words.miru Word frequency: read_file, split, and counting in a map
dice.miru Ten thousand rolls, drawn as a histogram
keys.miru Arrow keys: read_key without waiting for a line
life.miru Conway's Life, redrawn in place: clear and sleep
snake.miru Snake: key_ready, joining arrays, and a paced loop
pong.miru Pong: a ball that keeps moving while you hold still
tetris.miru Tetris: turning a piece without trigonometry, and clearing a row

Run one with miru run examples/contacts.miru.


Language at a glance

// Maps, loops, and loop control
let book = {"Aiko": "555-0100", "Ken": "555-0142"}
book["Mai"] = "555-0177"

for name in keys(book) {
  if name == "Ken" { continue }
  print(name + ": " + book[name])
}

See the language reference for the whole language on one page.


Project structure

Source becomes tokens, tokens become an abstract syntax tree, the tree is compiled to bytecode, and the bytecode runs on a stack virtual machine.

Stage Files Lines Responsibility
Lexer token.rs, lexer.rs 1509 Source text to tokens, with line, column, and span tracking
Parser ast.rs, parser.rs 1957 Recursive descent plus a Pratt expression parser
Runtime model value.rs, ops.rs, builtins.rs, random.rs 4737 Values, operator and indexing rules, the builtin library
Bytecode engine chunk.rs, globals.rs, compiler.rs, vm.rs 4609 Compiles the AST to bytecode, runs it on a stack VM, loads modules, and catches errors
Diagnostics suggest.rs 220 Chooses the name an error offers back when a program misspells one
Formatter formatter.rs 813 Reprints a program in canonical form (miru fmt)
CLI and REPL main.rs, repl.rs, keyboard.rs 1305 File runner, fmt and disasm commands, the REPL, and raw-mode key reading
Library lib.rs 1190 Ties it together (parse_program, run_source, disassemble_source)
Total 19 files 16595 Written from scratch in Rust

The playground is a separate crate: 775 lines of Rust binding the language to WebAssembly, and 1672 of hand-written HTML, CSS, and JavaScript. It is counted apart because it is not the language, and neither its code nor its dependencies are involved in running a .miru file.

src/         the language (lexer, parser, compiler, VM, CLI, REPL)
playground/  WebAssembly bindings and the in-browser playground
editors/     syntax highlighting for .miru files
examples/    runnable .miru programs
wiki/        step-by-step learning lessons
docs/        language reference, architecture, and roadmap
tests/       golden, language, session, and end-to-end tests
benches/     criterion benchmarks for the bytecode engine
scripts/     build_reference.sh regenerates the single-page reference

Documentation

Learn

A guided tour,
read in order

Wiki

Look up

The whole language
on one page

Reference

Internals

How the interpreter
is built

Architecture

Roadmap

Status and what
comes next

Milestones

Define

What a program
means, exactly

Specification

Depend

What will not
change in 1.x

Stability

Testing

cargo test

Unit tests sit next to each module. Beyond those, tests/golden.rs pins a corpus of programs to the exact result each must produce, values and errors alike, down to the line and column a caret points at. Those expectations are literals rather than regenerated, so a test cannot quietly absorb a change in behavior, which is what made rewriting the engine's hot paths safe. tests/integration.rs runs the compiled binary against the example programs. The same checks run in CI, along with cargo fmt --check and cargo clippy -D warnings.

Benchmark the engine with:

cargo bench

Contributing

Contributions are welcome. See CONTRIBUTING.md to get started, follow the Code of Conduct, and check SUPPORT.md if you need help. The changelog records what changed between versions.


Support

If you find MiruScriptX useful, you can support its development here.

ko-fi


License

Released under the MIT License. See LICENSE for the full text, and TERMS.md for the project terms.

Built from scratch in Rust. Start writing MiruScriptX with the wiki.

About

A minimalist, dynamically typed scripting language with a clean, modern syntax, written from scratch in Rust. Write functions, closures, loops, arrays, and maps in familiar syntax, then run them from a file or an interactive REPL. Programs use the .miru extension.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages