Releases: marslang-project/marslang
Release list
Marslang rs-0.9.0
rs-0.9.0 — Installation and the user package directory
Marslang installs without a Rust toolchain, and packages installed for a user
are importable from every program that user writes.
Install
install.ps1 and install.sh download the archive for the platform they run
on, verify it against the release's SHA256SUMS, put marslang in a per-user
directory, and add that directory to PATH. Nothing is compiled and nothing
outside the home directory is written, so neither script needs administrator or
root rights.
irm https://marslang.kevin-z.com/install.ps1 | iexcurl -fsSL https://marslang.kevin-z.com/install.sh | shBoth take the release to install (-Version / --version), the package sets to
install (-Use / --use, from std and ext), the install directory, and a
switch to leave PATH alone. std is built into the interpreter; ext is
accepted, reports that extension packages are not published yet, and installs
nothing.
.github/workflows/release.yml builds the
archives when an rs-* tag is pushed: Windows x86-64, Linux x86-64 and
AArch64, and macOS on both architectures. It runs the tests for every target it
can execute, writes SHA256SUMS across the archives, and uploads them to that
tag's release.
The user package directory
A package that the program's own directory does not provide is now looked for in
the user's package directory: MARSLANG_PKGS, or marslang_pkgs in the home
directory. marslang pkgs prints the directory in use.
takepkg greet; // a program file, or a package installed for you
The program's directory is searched first, so a file beside the program always
wins over an installed package of the same name, and installing something later
cannot change a program that already works. The two never mix inside one
package: a package found in the user directory runs its parents' init.mars
files from there as well. A failed import lists every file that was looked for,
in both directories.
Validation
cargo test passes 112 tests (13 unit, 98 execution, 1 memory) on Windows Rust
and on WSL-native Rust.
Marslang rs-0.8.1 — Access, type-check, and padding fixes
rs-0.8.1 — Access, type-check, and padding fixes
A patch release fixing the four findings of the rs-0.8.0 review. std.Decorator
now also lists the markers it provides.
Fixes
| Issue | Now |
|---|---|
| A private method taken as a value could be called from outside its family | Every call of a bound method is checked against the method running at that moment, including methods stored in fields or containers |
| A rejected insertion still converted and restricted the value being inserted | An insertion checks all of a container's restrictions as one plan and applies it only if every part passes; a map set checks key and value together |
| Nested unions rejected shared containers that a different choice would have accepted | Union choices are recorded while planning and retried depth-first when a later constraint conflicts (up to 64 attempts) |
pad_start/pad_end could return fewer characters than requested |
The padded result is measured; a fill that merges with its neighbours (a lone combining accent, one flag letter) raises RangeError |
Changed: a container's restrictions must all hold
Every element must satisfy each of its container's restrictions as stored, so
conflicting restrictions now raise TypeError and change nothing:
x (array[int]) = arr(1);
y (array[float]) = x; // TypeError: cannot satisfy both
Previously the alias converted x's elements to floats even though x promised
int, leaving the array inconsistent with its own annotation.
std.Decorator lists its markers
std/Decorator.mars now exports one value per marker,
so @Decorator.NAME is accepted only for a name the package provides, an unknown
marker reports the available list, and out(Decorator.private) prints private.
Markers that the interpreter does not apply yet still report "not implemented yet".
Validation
cargo test passes 111 tests (13 unit, 97 execution, 1 memory) on Windows Rust and
on WSL-native Rust.
Marslang rs-0.8.0 — Private methods and reliability fixes
rs-0.8.0 — Private methods and reliability fixes
This release adds private methods through std.Decorator and fixes the six findings
of the rs-0.7.0 review, including two ways to crash the interpreter.
Private methods
takepkg std.Decorator;
family Account{
@Decorator.private
func _audit(string action){ out("audit " + action); }
@Decorator.subclass
func _limit() => 100;
func deposit(int amount){ me._audit("deposit"); }
}
family Savings(Account){
func limit() => me._limit(); // allowed
}
func m{
Account()._audit("x"); // TypeError: _audit is private to Account
}
std.Decoratoris a namespace of markers written above family methods; importing
it makes@Decorator.NAMEavailable (or@alias.NAMEwith an alias).@Decorator.private: only methods of the declaring family may call the method.@Decorator.subclass: methods of families inheriting from it may call it too.- Calls and bound-method values from anywhere else raise
TypeError. Mistakes such
as a missing import, unknown markers, orprivatetogether withsubclassare
compile errors.static,class, andoverloadare planned. std.containersnow marks its internal helper methods private.
Fixes
| Issue | Now |
|---|---|
time.sleep(1e30) crashed the interpreter |
Raises a catchable RangeError; then blocks still run |
| An error whose message contained itself overflowed the stack when printed | Prints <cycle> |
| A panic inside any native Rust function crashed the interpreter | Becomes a catchable Error (defense in depth) |
| Shared containers could be partly changed by a failed type check | Every change is computed first and written together; failed alternatives and annotations change nothing |
strings.find/rfind could return a position past the end |
Searches match whole characters only |
math.gcd(INT_MIN, 1) raised overflow |
Returns 1; gcd(INT_MIN, 0) still raises overflow |
priority_queue.items() was missing |
Returns items in pop order without changing the queue |
Changed: string searches match whole characters
find, rfind, contains, starts_with, ends_with, split, and replace in
std.strings only match at character boundaries, so a position from find always
works with lenslice. A combining accent inside a character is not found on its
own, and "\r\n" is one character, so split(text, "\n") does not split Windows
line endings; use lines(text).
Validation
cargo test passes 106 tests (13 unit, 92 execution, 1 memory) on Windows Rust and
on WSL-native Rust.
Remaining work
The remaining decorators, opaque native handles, a bytes type, program arguments,
closures and function-typed parameters, match, file/JSON/random/regex packages,
the per-user package directory and installer, and async remain pending.
Marslang rs-0.7.0 — Error handling, cycle collection, and new standard packages
rs-0.7.0 — Error handling, cycle collection, and new standard packages
This release adds error handling, fixes the five findings of the rs-0.6.0
interpreter review (including a memory leak), and adds four standard packages.
Error handling
family ParseError(Error){}
func m{
run{
err(ParseError, "empty input");
} handle(ParseError e){
out(e); // ParseError: empty input
} handle([TypeError, RangeError] e){
out(e.message);
} then{
out("always runs");
}
}
- Errors are families.
Erroris the base;TypeError,RangeError,
OutOfBoundsError, andSyntaxErrorinherit from it. Declare your own with
family Name(Error){}. Errors have amessagefield and print asName: message. err(Family, message)raises;err(e)raises a caught error again.handle(Type e),handle([T1, T2] e), andhandle(T1, T2)(no name). The first
matching handler runs; unmatched errors continue outward. Handlers also catch the
interpreter's own errors, such as division by zero, and package errors by
alias.Family.thenalways runs: after the body or a handler, while an error propagates, and on
earlyret,break, orcontinue.lasterr()returns the most recently handled error, ornull.
Memory and interpreter fixes
- Cycles are reclaimed. A cycle collector frees unreachable self-referencing
data (an array containing itself, instances pointing at each other) while the
program runs and when it ends. Previously every such cycle leaked. - Family type annotations accept inherited families and compare declarations, not
names: a parameter typedParentaccepts aChild, and same-named families from
different packages are distinct.alias.Familynames a package's family. - Union annotations no longer change a value while trying an alternative that fails.
obj.f(args)selectsfbefore evaluating the arguments.- Output is flushed before
in()/inln()read, so prompts appear; on a terminal each
out/sloutappears immediately. Output errors are reported. - The
anytype annotation accepts every value:func push(any item).
Standard packages
| Package | Contents |
|---|---|
std.containers |
stack, queue, deque, priority_queue (lowest priority first, ties in push order); written in Marslang |
std.math |
Now 51 functions: adds gcd, lcm, is_even, is_odd, div_floor, div_ceil, factorial, perm, comb, keeping the arguments' integer kind |
std.strings |
split, join, lines, trim, find, replace, upper/lower, repeated, padding; positions count characters |
std.types |
kind, family_name, is_instance, is_number |
std.time |
now, monotonic, sleep |
std.strings is named so that takepkg does not hide the built-in string()
conversion. Native helpers live in std/rs/string.rs and std/rs/time.rs;
rs.core gains family_name and is_instance.
Validation
cargo test passes 98 tests (12 unit, 85 execution, 1 memory) on Windows Rust and
on WSL-native Rust. The memory test measures allocations: no bytes remain after a
run, and peak memory stays flat as a program creates more garbage cycles.
Remaining work
Opaque native handles, a bytes type, program arguments, closures and function-typed
parameters, decorators, match, file/JSON/random/regex packages, the per-user
package directory and installer, and async remain pending.
Marslang rs-0.6.0 — Rust interpreter and packages
rs-0.6.0 — Rust interpreter and packages
Marslang is now an interpreted language. The JavaScript backend is removed:
marslang file.mars parses, checks, and runs a program directly in Rust. Node is no
longer needed to run programs or tests.
Interpreter
src/interp.rsis a tree-walking interpreter over the resolved syntax tree;
src/value.rsholds runtime values. The previousruntime.js.incsemantics are
preserved: numeric kinds, checked integers, fixed containers, deep copies,
grapheme-based strings, slicing, and the error kinds.- CLI:
marslang file.marsormarslang run file.marsruns a program;
marslang check file.marsreports compile errors without running;
marslang --version. Thecompile ... -o out.jscommand is removed. - Runtime errors print
error: <Kind>: <message>and exit with status 1. - Calls nested deeper than 10,000 levels raise
RangeError: maximum call depth exceeded. - The REPL shows only the output produced by each new line and discards lines that
fail to compile or run. - String segmentation uses the
unicode-segmentationcrate.
Behavior changes from rs-0.5.0:
- Numbers compare by exact value across every kind:
1 == longint(1)and
longint(5) < 10are true, and equal numbers share set elements and map keys. - Calling a function, method, or built-in with the wrong number of arguments raises
TypeError; reading a missing family field raisesTypeError. - A block function without
retreturnsnull. - Containers print readably, such as
[1, 2],{"k": 1}, andBox { v: 3 }. The
display format is still not a stable serialization format. - Integer overflow of 32-bit operands reports
int overflow.
Packages
takepkg is a package system, similar to Python's:
takepkg std.math;loads a standard package built intomarslang.takepkg util;loadsutil/init.mars(a package directory) orutil.marsfrom
the main program's directory;takepkg shapes.circle;maps to subdirectories.takepkg .sibling;andtakepkg ..parent;are relative imports inside packages.- Importing
a.brunsa/init.marsfirst. Each package loads once. Packages export
functions, families, andfixed/hotbindings; names starting with_are
private. Circular imports are rejected.
See Packages.
Standard library in Marslang
std.math (42 functions, six constants) is written entirely in Marslang in
std/math.mars, including argument checks, rounding, signs, integer pow, hypot,
and interpolation. Only irreducible primitives are native Rust packages under
std/rs/: rs.core (raise a named error, read a value's kind) and rs.math
(platform float functions). Only standard packages may import rs.*.
build.rs registers every std/*.mars file as std.NAME and every std/rs/*.rs
file as rs.NAME, so adding a standard package needs no list edits.
Validation
cargo test passes 78 tests (9 unit, 69 execution, none ignored) on Windows Rust
and on WSL-native Rust. Execution tests run programs in the interpreter and assert
their output or runtime errors.
Remaining work
A per-user package directory (such as C:\Users\<user>\marslang_pkgs) and an
installer are planned. Wildcard imports, decorators, deque, further standard
packages, match, run/handle/then, async, and self-hosting remain pending.
Marslang rs-0.5.0 — Initial math package design
rs-0.5.0 — Initial math package design
This release introduces the first Marslang-written standard-library package and
runtime numeric type tracking. The math package is an early, provisional design:
more functionality will be added, and its API may evolve.
std.math
takepkg std.math; provides math.min, math.max, math.abs, and math.clamp.
Explicit aliases and storing functions in variables are supported. The algorithms
are written in std/math.mars, compiled by Rust, and bundled into the generated
JavaScript with the shared runtime.
takepkg std.math;
func m{
out(math.clamp(12,0,10)); // 10
}
Arguments must have matching numeric types (int, longint, or finite float).
Clamp includes both bounds and rejects reversed bounds. Integer overflow,
incorrect argument counts, and invalid numeric arguments raise runtime errors.
See the math API for details and current limits.
Dynamic typing and numeric behavior
Unannotated variables can change type on reassignment. Annotated bindings retain
runtime checks, and fixed still prevents reassignment. Numeric kinds survive
function calls, returns, containers, family fields, and copies: 1.0 remains a
float even when its value is integral. Numeric unions prefer the existing kind.
Arithmetic now uses runtime operand kinds, including through union-typed calls.
Integer arithmetic checks overflow; float arithmetic retains float results.
Mixed longint/float arithmetic requires an explicit conversion. Scalar equality
and collection lookups retain their value-based numeric behavior. Fixed containers
cannot have their numeric kinds changed through a differently annotated alias.
Migration: integer calculations that previously bypassed overflow checks through
unannotated or union-typed values can now raise an error. Use longint or float
explicitly when those numeric semantics are required. := remains excluded.
Repository and validation
.marsand.js.incfiles are excluded from GitHub Linguist language detection.- The public API reference documents the package and dynamic typing rules.
cargo testpasses 63 tests: 7 unit and 56 integration, none ignored, using
WSL-native Rust with Windows Node. The toolchains use separate target directories.
Remaining work
More math APIs, decimal/fraction support, full filesystem modules/exports, other
standard packages, decorators, and self-hosting remain pending. Infinity/NaN math
semantics are deferred; this initial package requires finite float inputs.
Marslang rs-0.4.0
rs-0.4.0
The canonical Marslang source extension is now .mars.
Migration
Rename your .mrs source files to .mars and update references in commands and
scripts. The bundled files are now hello.mars, stdlib.mars, and
docs/api/examples/strings.mars. Generated output still uses .js.
cargo run -- compile hello.mars -o hello.js
node hello.jsThe CLI accepts explicitly supplied source paths without enforcing an extension,
so older filenames can still be compiled. CLI help, tests, and current API
documentation use .mars. Historical release notes retain their original names.
Validation
cargo test: 55 passing tests (7 unit and 48 integration), none ignored.
The renamed bundled example compiles and runs under Node, printing 3 and
78.53975. The renamed Unicode API example is covered by an execution test.
Language behavior and the limitations documented in
rs-0.3.0 are otherwise unchanged.
Marslang rs-0.3.0
rs-0.3.0
Rust bootstrap compiler release, 2026-09-18. Backend: JavaScript running on Node.
Core language and runtime
- Structured expressions and lexical scope resolution: bare assignment updates
the nearest existing binding; annotations andcoldexplicitly declare locals. - Runtime type checks, checked signed 32/64-bit integers, noncoercing equality,
identity-based container equality, and boolean short-circuiting. - Fixed bindings, recursive container immutability through aliases, mutable deep
.copy()with cycle/shared-reference preservation, and hot constant substitution. while, iterable and three-partfor,break, andcontinue; snapshot iteration.- Working array/set/pair APIs and insertion-ordered
map()/dict()with null for
missing keys, inspection methods, and typed collection mutation checks.
Unicode strings and slicing
String length, iteration, reversal, and slicing count extended grapheme clusters.
Combining marks and joined emoji stay together. Node must provide Intl.Segmenter.
String .reverse() returns a new string without changing the original.
String and array .slice(start,end) and .lenslice(start,length) accept an
optional third boolean argument, including the slicing-only reverse=true form.
Reverse slicing counts from the end and preserves the original order:
func m{
out("ABCDE".lenslice(0,3,reverse=true)); // CDE
}
Invalid bounds raise OutOfBoundsError. Zero-length slices are valid at the end.
Incorrect argument types or counts raise TypeError.
Fixes and migration notes
The bundled example now executes successfully. Regressions addressed include
repeated assignment, comparisons mistaken for declarations, comment markers in
strings, compact blocks, compound me expressions, and shadowed built-ins.
Explicit redeclarations in the same scope and undefined names are rejected.
Programs relying on accidental JavaScript coercion or out-of-range array slicing
must be updated. Array slices now use strict bounds rather than silently clipping
or accepting negative indices. Array reversal still mutates the array.
Comma-separated function parameters from rs-0.2.0 remain required.
Documentation and validation
The API reference covers syntax, strings, collections,
built-ins, and errors, with a runnable example. Discussion documents are removed
from the tracked tree and remain local; docs/api/ and docs/releases/ are exempt
from the documentation ignore rule.
Validation: cargo test passes 55 tests (7 unit and 48 integration), none ignored.
Execution tests run generated JavaScript under Node and cover the API example.
Remaining limitations
Self-hosting, a unified lexer/parser with source locations, complete modules and
exports, standard-library packages, decorators, deque, async, match, and
run/handle/then remain pending. General named arguments are deferred. Unicode
segmentation follows the host's Unicode/ICU version. The compiler still targets
Node rather than a native runtime.