Skip to content

Expressions and operators

An expression is anything that has a value: a literal, a name, a call, or operators joining those.

From the loosest binding (worked out last) to the tightest (worked out first):

OperatorsNotes
1??, otherwiseleft to right
2? :conditional expression
3||, orleft to right
4&&, andleft to right
5not, !prefix; looser than the comparisons
6== === != !== and the word formsleft to right
7< > <= >= matches in and the word formsleft to right; a run such as 1 < 2 < 3 chains
8... ..< ..range; does not chain
9~left to right
10+ -left to right
11* / %left to right
12**right to left
13unary -prefix; tighter than **
14.name [index] (args)member access, index, call

+=, -=, *= and /= sit below the whole table. They are assignments, not operators. See Variables and scope.

Brackets ( ) group, as in any language.

printline 1 + 2 * 3 ** 2
printline (1 + 2) * 3
printline 2 ** 3 ** 2
printline 10 - 2 - 3
printline 100 / 10 / 5
19
9
512
5
2

~ binds looser than + and -, so "n=" ~ 1 + 2 is "n=3".

printline "n=" ~ 1 + 2
n=3
OperatorMeaning
+add
-subtract, or negate when it is a prefix
*multiply
/divide
%remainder
**power

The unary minus binds tighter than **, as in Excel, so -2 ** 2 is 4: the negation runs first, then the power. To negate the result, write -(2 ** 2).

printline -2 ** 2
set n to -2
printline n ** 2
printline 7 % 4
printline 2 ** 0.5
4
4
3
1.4142135623731

A minus sign straight after a complete expression is a subtraction, so 3-1 and 3 -1 are both 2.

printline 3-1
set x to 3
set y to x -1
printline y
printline 2 * -3
2
2
-6

Division and remainder by zero record an error and give NAN. See Values and types.

~ joins two values as text. A string with {{ }} in it is joined with ~ by the reader.

printline "a" ~ "b" ~ 1
printline "x" is "x" ~ ""
ab1
true

Each comparison has a symbol form and a natural form. They are the same operator.

SymbolNatural forms
==is, equals, equal to, is equal to
!=is not, is not equal to
<is less than, is not greater than or equal to
>is greater than, is more than, is not less than or equal to
<=is less than or equal to, is less than or is equal to, is not greater than, is not more than
>=is greater than or equal to, is more than or equal to, is not less than
===(symbol only)
!==(symbol only)

The words are not case sensitive. The last words (to, or, is) in the longer forms are optional.

set x to 5
printline x is 5
printline x is not 4
printline x is more than 4
printline x is less than or equal to 5
printline x is greater than or equal to 6
true
true
true
true
false
set x to 5
printline x is not less than 5
printline x is not greater than 5
printline x is not greater than 4
true
true
false

There is no is between. n is between 1 and 9 is a syntax error with a hint. Write n >= 1 and n <= 9, or n in 1...9 for whole numbers.

== and != compare loosely. === and !== compare type and value.

  • A number and a string that looks like that number are equal: 1 == "1", "1e1" == "10".
  • null, false, 0 and "" are equal to each other in ==: null == 0, "" == null, 0 == false.
  • Text that is not a number never equals a number: "abc" == 0 is false.
  • Text comparison is case sensitive: "a" == "A" is false.
  • Two arrays or dictionaries are equal when they hold the same keys and values.
printline 1 is "1"
printline 1 === "1"
printline null is 0
printline null === 0
printline "a" is "A"
set a to [1, 2]
printline a is [1, 2]
true
false
true
false
false
true

<, >, <= and >= compare numbers by value. Two strings that both look like numbers also compare as numbers, so "10" < "9" is false. Other strings compare by character code, so "abc" < "abd" is true.

Comparisons chain as in mathematics. 1 < 2 < 3 is true when both 1 < 2 and 2 < 3 are true.

set x to 5
printline 1 is less than x is less than 10
printline 1 is less than x is less than 3
printline 3 is more than 2 is more than 2
true
false
false

value in container is true when:

  • the container is an array and one of its items equals the value;
  • the container is a dictionary and one of its values equals the value (not a key);
  • the container is a string and the value appears inside it.
set list to ["apple", "banana"]
printline "banana" in list
printline "an" in "banana"
printline "name" in {name: "Ada"}
printline "Ada" in {name: "Ada"}
printline 3 in 1...5
true
true
false
true
true

not binds looser than in and the comparisons, and tighter than and and or. not 5 in list is not (5 in list), and not x is 3 is not (x == 3).

set list to [1, 5]
set x to 3
printline not 5 in list
printline not x is 3
printline not x is not 3
printline not true and false
false
false
true
false

text matches /pattern/ is true when the pattern is found anywhere in the text. The right side must be a regular expression literal, or a variable that holds one. A quoted string on the right is not read as a pattern.

printline "order 4821" matches /[0-9]+/
printline "order" matches /^[0-9]+$/
printline "xAbCx" matches /abc/i
printline "abc" matches "b"
true
false
true
false

&& (and), || (or) and ! (not) work on any value, using the rules for what counts as true. The result of && and || is always true or false, never one of the operands.

The right side is not evaluated when the left side already decides the result.

block loud
print "loud was called "
return true
end
printline 5 and 3
printline 0 or "x"
printline false and loud()
printline true or loud()
printline true and loud()
true
true
false
true
loud was called true

! and not are the same operator. printline !x and printline not x work as you would expect.

MaSH lets a comparison reuse the subject of the one before it, the way people speak. There are two forms.

An operator after and or or borrows the subject of the previous comparison:

set x to 7
printline x is more than 5 and is less than 10
printline x is 1 or is 7
true
true

A plain value after or repeats the previous operator and subject. This only happens when the value is a literal (a number, string, true, false or null), not a name.

set x to 5
set name to "bo"
printline x is 1 or 2 or 5
printline name is "al" or "bo"
printline x is 1 or 2
true
true
false

Value chaining binds tighter than and, so x is 1 or 2 and y is 3 is (x is 1 or 2) and (y is 3).

If the value after or is a name, it is not chained. It starts a new test of its own.

a ?? b is a unless a is null, and then it is b. Only null counts. 0, "" and false are kept.

set missing to null
printline missing ?? "default"
set zero to 0
printline zero ?? "default"
printline missing ?? missing ?? "last"
printline false ?? "default"
default
0
last
false

?? also covers an error: when reading a name or a dictionary key with a dot throws, ?? returns the right side and records nothing. This is the neat way to read a key that may not be there:

set settings to {theme: "dark"}
printline settings.theme ?? "light"
printline settings.font ?? "plain"
printline mash.error.state
dark
plain
false

otherwise is the natural form.

condition ? a : b is a when the condition is true and b otherwise. The branches are full expressions, and conditionals nest in the second branch.

set x to 7
printline x is more than 5 ? "big" : x is more than 2 ? "mid" : "small"
set size to x is more than 100 ? "huge" : "ok"
printline size
printline "[{{ x > 0 ? 'positive' : 'not positive' }}]"
big
ok
[positive]

?? is looser than ? :, so a ?? b ? c : d is a ?? (b ? c : d). Bracket whichever you mean.

a...b and a..<b build arrays. See Values and types. A range sits below + and -, so 1...n + 1 goes from 1 to n + 1.

These bind tightest and chain from left to right: order.items[0].name, mash.collect(list).first().

  • a.name reads a property. After a dot every word is a name, including reserved words.
  • a[expression] reads by index or key.
  • a.name(args) calls a method.
  • name(args) calls a block or a built-in.

A call needs its ( directly after the name, with no space. f(1) is a call. f (1) is two things side by side. See Blocks and calls.

An empty [] is only valid on the left of =, to append. See Variables and scope.