Skip to content

Releases: marslang-project/marslang

Marslang rs-0.9.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 13:46

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 | iex
curl -fsSL https://marslang.kevin-z.com/install.sh | sh

Both 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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 20 Sep 03:31

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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 19 Sep 15:11

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.Decorator is a namespace of markers written above family methods; importing
    it makes @Decorator.NAME available (or @alias.NAME with 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, or private together with subclass are
    compile errors. static, class, and overload are planned.
  • std.containers now 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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 19 Sep 14:24

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. Error is the base; TypeError, RangeError,
    OutOfBoundsError, and SyntaxError inherit from it. Declare your own with
    family Name(Error){}. Errors have a message field and print as Name: message.
  • err(Family, message) raises; err(e) raises a caught error again.
  • handle(Type e), handle([T1, T2] e), and handle(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.
  • then always runs: after the body or a handler, while an error propagates, and on
    early ret, break, or continue.
  • lasterr() returns the most recently handled error, or null.

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 typed Parent accepts a Child, and same-named families from
    different packages are distinct. alias.Family names a package's family.
  • Union annotations no longer change a value while trying an alternative that fails.
  • obj.f(args) selects f before evaluating the arguments.
  • Output is flushed before in()/inln() read, so prompts appear; on a terminal each
    out/slout appears immediately. Output errors are reported.
  • The any type 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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 19 Sep 08:13

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.rs is a tree-walking interpreter over the resolved syntax tree;
    src/value.rs holds runtime values. The previous runtime.js.inc semantics are
    preserved: numeric kinds, checked integers, fixed containers, deep copies,
    grapheme-based strings, slicing, and the error kinds.
  • CLI: marslang file.mars or marslang run file.mars runs a program;
    marslang check file.mars reports compile errors without running;
    marslang --version. The compile ... -o out.js command 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-segmentation crate.

Behavior changes from rs-0.5.0:

  • Numbers compare by exact value across every kind: 1 == longint(1) and
    longint(5) < 10 are 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 raises TypeError.
  • A block function without ret returns null.
  • Containers print readably, such as [1, 2], {"k": 1}, and Box { 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 into marslang.
  • takepkg util; loads util/init.mars (a package directory) or util.mars from
    the main program's directory; takepkg shapes.circle; maps to subdirectories.
  • takepkg .sibling; and takepkg ..parent; are relative imports inside packages.
  • Importing a.b runs a/init.mars first. Each package loads once. Packages export
    functions, families, and fixed/hot bindings; 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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 18 Sep 13:18

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

  • .mars and .js.inc files are excluded from GitHub Linguist language detection.
  • The public API reference documents the package and dynamic typing rules.
  • cargo test passes 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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 18 Sep 06:17

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

The 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

Choose a tag to compare

@Unknownuserfrommars Unknownuserfrommars released this 18 Sep 04:53

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 and cold explicitly 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-part for, break, and continue; 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.