marslang rs-0.19.1

Grammar#

The syntax the interpreter accepts, written as a grammar. Where the language reference explains what programs mean, this page says exactly what may be written. src/parser.rs and src/expression.rs are the implementation; a program they reject that this page allows is a bug in one of them.

Notation#

A B is A followed by B, A | B either, [ A ] optional, { A } zero or more times, ( ... ) grouping, and quoted text literal. NAME, NUMBER, and STRING are the tokens under lexical structure.

Programs#

text
program      = { item } ;
item         = import | decorated | family | function | declaration | statement ;
import       = "takepkg" package [ "=" NAME ] ";" ;
package      = { "." } NAME { "." NAME } ;
decorated    = decorator { decorator } ( function | family ) ;
decorator    = "@" NAME "." NAME [ "(" expression ")" ] ;
family       = "family" NAME [ "(" parent ")" ] "{" { [ { decorator } ] function } "}" ;
parent       = NAME | NAME "." NAME ;
function     = [ "async" ] "func" NAME [ "(" [ params ] ")" ] ( block | "=>" expression ";" ) ;
params       = param { "," param } ;
param        = type NAME ;

A program runs from func m. Top-level declarations and statements run first, in order, after the packages they import. Several decorators may share a line. takepkg .name; and takepkg ..name; are relative imports; = * is reserved. An async function, method, or nested function runs as a task when called (async); a family's init cannot be async.

Statements#

text
block        = "{" { statement } "}" ;
statement    = declaration | assignment | update | expression ";"
             | "ret" expression ";" | "break" ";" | "continue" ";"
             | if | while | repeat | for | run | match | function ;
declaration  = [ "fixed" ] [ "hot" | "cold" ] NAME [ "(" type ")" ] "=" expression ";" ;
assignment   = target "=" expression ";" ;
target       = NAME | postfix "." NAME ;
update       = NAME ( "+=" | "-=" ) expression ";" | target ( "++" | "--" ) ";" ;
if           = "if" "(" expression ")" block { "elif" "(" expression ")" block } [ "else" block ] ;
while        = "while" "(" expression ")" block ;
repeat       = "repeat" expression block ;
for          = "for" "(" NAME "," expression ")" block
             | "for" "(" steps "," expression "," steps ")" block ;
steps        = [ step | "(" step { "also" step } ")" ] ;
step         = a declaration, assignment, update, or expression, without its ";" ;
run          = "run" block { handler } [ "then" block ] ;
handler      = "handle" "(" handled ")" block ;
handled      = type NAME | "[" type { "," type } "]" NAME | type { "," type } ;
match        = "match" "(" expression ")" "{" arm { arm } "}" ;
arm          = pattern [ guard ] "=>" ( statement | block ) ;
pattern      = "_" | literal { "," literal } | kind [ NAME ] ;
guard        = "if" "(" expression ")" ;
literal      = [ "-" ] NUMBER | STRING | "true" | "false" | "fasle" | "null" ;
kind         = plain | "[" plain { "," plain } "]" ;
plain        = simple | "array" | "set" | "pair" | "map" | "dict" ;

Expressions#

text
expression   = or ;
or           = and { "or" and } ;
and          = not { "and" not } ;
not          = "not" not | comparison ;
comparison   = sum { ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) sum } ;
sum          = product { ( "+" | "-" ) product } ;
product      = sign { ( "*" | "/" | "%" ) sign } ;
sign         = ( "+" | "-" ) sign | power ;
power        = awaited [ "**" sign ] ;
awaited      = "await" awaited | postfix ;
postfix      = primary { "." NAME | "(" [ arguments ] ")" | "[" expression "]" } ;
arguments    = expression { "," expression } [ "," "reverse" "=" expression ] ;
primary      = NUMBER | STRING | "true" | "false" | "fasle" | "null" | NAME
             | "(" expression ")" | lambda | matchvalue ;
lambda       = [ "async" ] "func" "(" [ params ] ")" "=>" expression ;
matchvalue   = "match" "(" expression ")" "{" valuearm { ";" valuearm } [ ";" ] "}" ;
valuearm     = pattern [ guard ] "=>" expression ;

Precedence#

From loosest to tightest; operators on one row share a precedence and group left to right, except **.

Level Operators Notes
1 or Short-circuits; the result is a boolean
2 and Short-circuits; the result is a boolean
3 not not a == b is not (a == b)
4 == != < <= > >= a < b < c compares the boolean a < b with c, a TypeError; write a < b and b < c
5 + -
6 * / %
7 unary + - -2 ** 2 is -(2 ** 2), which is -4
8 ** Groups right to left: 2 ** 3 ** 2 is 2 ** 9
9 await await a() + await b() adds two results; await t ** 2 is (await t) ** 2
10 .name (...) [...] Member, call, and index, left to right: await load(url) awaits the call's task

A lambda's body is a whole expression, so it extends as far as it can: func(int x) => x + 1 inside a call ends at the next , or ). reverse= is the only named argument, and only in slice and lenslice. A matchvalue raises RangeError when no arm matches. It may span lines where a statement can (after =, ret, or =>); inside ( ) or [ ] it must fit on one line.

Types#

text
type         = simple | "[" type { "," type } "]" | collection ;
simple       = "int" | "longint" | "float" | "bigint" | "string" | "bool" | "any"
             | "date" | "datetime" | "duration" | "task"
             | "Function" | "Family" | NAME | NAME "." NAME ;
collection   = ( "array" | "set" ) [ "[" type "]" ]
             | "pair" [ "[" type "," type "]" ]
             | ( "map" | "dict" ) [ "[" type "," type "]" ] ;

[int, float] is a union: a value of any listed type. A family name, or alias.Family for one from a package, accepts instances of that family and of families inheriting from it. Function and Family are for parameters only.

Lexical structure#

Not in the grammar#

These are recognised only to report a clear error, or not at all:

Written What happens
a ? b : c Compile error; use if
x in items Compile error; use items.has(x)
[1, 2, 3] as a value Compile error; use arr(1, 2, 3)
a // b // starts a comment, so the rest of the line disappears; use math.div_floor(a, b)
"x is {x}" An ordinary string: braces are kept as written
func f(int a, int b = 2) Compile error; parameters have no defaults
func f(int a) -> int Compile error; there are no return type annotations
func(int x){ ... } Compile error; an anonymous function's body is one expression
await on a value that is not a task TypeError when it runs; only tasks can be awaited
:=, bitwise operators Excluded from the language