marslang rs-0.19.1

Quickstart#

A tour of Marslang in about twenty minutes: install it, run a program, and meet values, functions, loops, collections, families, errors, and packages. Every example below runs as shown, and the output under it is what it prints.

Install#

powershell
irm https://marslang.kevin-z.com/install.ps1 | iex
sh
curl -fsSL https://marslang.kevin-z.com/install.sh | sh

Open a new terminal and check it worked:

sh
marslang --version

For editing, install the Marslang extension for Visual Studio Code: it highlights .mars files and marks errors as you save.

Hello, world#

Save this as hello.mars:

mars
func m{
    out("hello, world");
}
sh
marslang hello.mars
text
hello, world

A program starts at the function m. out prints its arguments separated by spaces, then a new line; slout prints without the new line. Statements end with ;, and blocks are wrapped in { }.

Values and variables#

mars
func m{
    name = "Mars";
    moons = 2;
    gravity = 3.72;
    out(name, "has", moons, "moons and gravity", gravity);

    moons = moons + 1;
    out(moons);

    year (int) = 687;
    fixed radius = 3389.5;
    out(year, radius);
}
text
Mars has 2 moons and gravity 3.72
3
687 3389.5

name = value; creates a variable, or updates one that already exists. A type in parentheses, year (int) = 687;, is checked every time the variable is assigned, and fixed stops it from being assigned again. Without a type, a variable can hold anything.

Numbers come in three kinds: int (whole, 32-bit), longint (whole, 64-bit), and float. Whole-number arithmetic never overflows silently:

mars
func m{
    out(7 / 2, 7 % 2, 2 ** 10);
    big (int) = 2147483647;
    out(big + 1);
}
text
3.5 1 1024
error: RangeError: int overflow
    at m (main.mars:4)

A program that fails prints error:, the kind of error, and a message, then where it happened: the function, file, and line, through every call that led there. Then it stops.

Functions#

mars
func add(int a, int b) => a + b;

func describe(string planet, int moons){
    if (moons == 0){
        ret planet + " has no moons";
    }
    ret planet + " has " + string(moons) + " moons";
}

func m{
    out(add(2, 3));
    out(describe("Venus", 0));
    out(describe("Mars", 2));
}
text
5
Venus has no moons
Mars has 2 moons

Parameters are written type name. A function whose body is one expression uses =>; a longer one uses a block and ret to return a value. + joins strings, and string(...) turns a number into one.

Decisions and loops#

mars
func m{
    for (i = 1, i <= 15, i++){
        if (i % 15 == 0){
            out("FizzBuzz");
        } elif (i % 3 == 0){
            out("Fizz");
        } elif (i % 5 == 0){
            out("Buzz");
        } else {
            slout(i, " ");
        }
    }
}
text
1 2 Fizz
4 Buzz
Fizz
7 8 Fizz
Buzz
11 Fizz
13 14 FizzBuzz

The other loops:

mars
func m{
    n = 3;
    while (n > 0){
        slout(n, " ");
        n = n - 1;
    }
    out("liftoff");

    repeat 3 {
        slout("*");
    }
    out("");
}
text
3 2 1 liftoff
***

break; leaves a loop and continue; skips to its next round. Conditions treat false, null, and zero as false and everything else as true.

Collections#

mars
func m{
    planets = arr("Mercury", "Venus", "Earth");
    planets.add("Mars");
    out(planets.len(), planets.iget(3));
    for (planet, planets){
        slout(planet.len(), " ");
    }
    out("");

    moons = map();
    moons.set("Earth", 1);
    moons.set("Mars", 2);
    for (planet, moons){
        out(planet, moons.get(planet));
    }
    out(moons.get("Venus"));
}
text
4 Mars
7 5 5 4 
Earth 1
Mars 2
null

arr makes an array, and iget(i) reads position i, counting from 0. map() makes a map from keys to values, and looping over it visits the keys in the order they were added. There are also set(...) and pair(a, b). Types can describe what a collection holds: scores (map[string,int]) = map();.

Families#

A family is Marslang's class: it bundles data with the functions, called methods, that work on it.

mars
family Account{
    func init(string owner){
        me.owner = owner;
        me.balance = 0;
    }

    func deposit(int amount){
        me.balance = me.balance + amount;
        ret me;
    }

    func describe() => me.owner + ": " + string(me.balance);
}

family Savings(Account){
    func add_interest(){
        me.deposit(me.balance / 10);
    }
}

func m{
    acct = Account("Ada");
    acct.deposit(50).deposit(25);
    out(acct.describe());

    savings = Savings("Grace");
    savings.deposit(200);
    savings.add_interest();
    out(savings.describe());
}
text
Ada: 75
Grace: 220

Calling a family's name makes an instance and runs its init. Inside a method, me is that instance. family Savings(Account) inherits every method of Account. To hide a helper method from outside code, see private methods.

Errors#

mars
takepkg std.Error;

func withdraw(int balance, int amount){
    if (amount > balance){
        err(Error.RangeError, "not enough money");
    }
    ret balance - amount;
}

func m{
    run{
        out(withdraw(100, 30));
        out(withdraw(100, 300));
        out("not reached");
    } handle(Error.RangeError e){
        out("refused:", e.message);
    } then{
        out("done");
    }
}
text
70
refused: not enough money
done

err(Family, message) raises an error. run watches a block, the first handle that matches the error runs, and then always runs at the end. The errors the interpreter raises, like division by zero, are caught the same way. You can declare your own: family NotFound(Error.Base){}. See std.Error.

Packages#

The standard library comes with the interpreter:

mars
takepkg std.math;
takepkg std.strings;

func m{
    out(math.sqrt(2.0));
    out(math.gcd(12, 18));
    out(strings.upper("mars"));
}
text
1.4142135623730951
6
MARS

Your own files are packages too. With geometry.mars next to your program:

mars
fixed PI_ISH (float) = 3.14;

func area(float r) => PI_ISH * r * r;

import it by its name:

mars
takepkg geometry;

func m{
    out(geometry.area(2.0));
}

It prints 12.56. Folders work like Python's: takepkg shapes.circle; loads shapes/circle.mars, a folder with an init.mars is a package, and names starting with _ stay private to their file. See packages.

Where next#