Expressions and operators
An expression is anything that has a value: a literal, a name, a call, or operators joining those.
Operators and precedence
Section titled “Operators and precedence”From the loosest binding (worked out last) to the tightest (worked out first):
| Operators | Notes | |
|---|---|---|
| 1 | ??, otherwise | left to right |
| 2 | ? : | conditional expression |
| 3 | ||, or | left to right |
| 4 | &&, and | left to right |
| 5 | not, ! | prefix; looser than the comparisons |
| 6 | == === != !== and the word forms | left to right |
| 7 | < > <= >= matches in and the word forms | left 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 |
| 13 | unary - | 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 ** 2printline (1 + 2) * 3printline 2 ** 3 ** 2printline 10 - 2 - 3printline 100 / 10 / 519951252printline(1 + 2 * 3 ** 2)printline (1 + 2) * 3printline(2 ** 3 ** 2)printline(10 - 2 - 3)printline(100 / 10 / 5)19951252~ binds looser than + and -, so "n=" ~ 1 + 2 is "n=3".
printline "n=" ~ 1 + 2n=3printline("n=" ~ 1 + 2)n=3Arithmetic
Section titled “Arithmetic”| Operator | Meaning |
|---|---|
+ | 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 ** 2set n to -2printline n ** 2printline 7 % 4printline 2 ** 0.54431.4142135623731printline(-2 ** 2)n = -2printline(n ** 2)printline(7 % 4)printline(2 ** 0.5)4431.4142135623731A minus sign straight after a complete expression is a subtraction, so 3-1 and 3 -1 are both 2.
printline 3-1set x to 3set y to x -1printline yprintline 2 * -322-6printline(3-1)x = 3y = x -1printline(y)printline(2 * -3)22-6Division and remainder by zero record an error and give NAN. See Values and types.
Joining text: ~
Section titled “Joining text: ~”~ joins two values as text. A string with {{ }} in it is joined with ~ by the reader.
printline "a" ~ "b" ~ 1printline "x" is "x" ~ ""ab1trueprintline("a" ~ "b" ~ 1)printline("x" == "x" ~ "")ab1trueComparison
Section titled “Comparison”Symbols and words
Section titled “Symbols and words”Each comparison has a symbol form and a natural form. They are the same operator.
| Symbol | Natural 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 5printline x is 5printline x is not 4printline x is more than 4printline x is less than or equal to 5printline x is greater than or equal to 6truetruetruetruefalsex = 5printline(x == 5)printline(x != 4)printline(x > 4)printline(x <= 5)printline(x >= 6)truetruetruetruefalseset x to 5printline x is not less than 5printline x is not greater than 5printline x is not greater than 4truetruefalsex = 5printline(x >= 5)printline(x <= 5)printline(x <= 4)truetruefalseThere 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.
Equality
Section titled “Equality”== 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,0and""are equal to each other in==:null == 0,"" == null,0 == false.- Text that is not a number never equals a number:
"abc" == 0is 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 0printline null === 0printline "a" is "A"set a to [1, 2]printline a is [1, 2]truefalsetruefalsefalsetrueprintline(1 == "1")printline(1 === "1")printline(null == 0)printline(null === 0)printline("a" == "A")a = [1, 2]printline(a == [1, 2])truefalsetruefalsefalsetrue<, >, <= 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 5printline 1 is less than x is less than 10printline 1 is less than x is less than 3printline 3 is more than 2 is more than 2truefalsefalsex = 5printline(1 < x < 10)printline(1 < x < 3)printline(3 > 2 > 2)truefalsefalsevalue 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 listprintline "an" in "banana"printline "name" in {name: "Ada"}printline "Ada" in {name: "Ada"}printline 3 in 1...5truetruefalsetruetruelist = ["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)truetruefalsetruetruenot 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 3printline not 5 in listprintline not x is 3printline not x is not 3printline not true and falsefalsefalsetruefalselist = [1, 5]x = 3printline(not 5 in list)printline(not x == 3)printline(not x != 3)printline(not true && false)falsefalsetruefalsematches
Section titled “matches”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/iprintline "abc" matches "b"truefalsetruefalseprintline("order 4821" matches /[0-9]+/)printline("order" matches /^[0-9]+$/)printline("xAbCx" matches /abc/i)printline("abc" matches "b")truefalsetruefalse&& (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 trueendprintline 5 and 3printline 0 or "x"printline false and loud()printline true or loud()printline true and loud()truetruefalsetrueloud was called trueblock loud() { print("loud was called ") return true}printline(5 && 3)printline(0 || "x")printline(false && loud())printline(true || loud())printline(true && loud())truetruefalsetrueloud was called true! and not are the same operator. printline !x and printline not x work as you would expect.
Chaining: dropping the repeated subject
Section titled “Chaining: dropping the repeated subject”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 7printline x is more than 5 and is less than 10printline x is 1 or is 7truetruex = 7printline(x > 5 && < 10)printline(x == 1 || == 7)truetrueA 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 5set name to "bo"printline x is 1 or 2 or 5printline name is "al" or "bo"printline x is 1 or 2truetruefalsex = 5name = "bo"printline(x == 1 || 2 || 5)printline(name == "al" || "bo")printline(x == 1 || 2)truetruefalseValue 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.
Null coalescing: ??
Section titled “Null coalescing: ??”a ?? b is a unless a is null, and then it is b. Only null counts. 0, "" and false are kept.
set missing to nullprintline missing ?? "default"set zero to 0printline zero ?? "default"printline missing ?? missing ?? "last"printline false ?? "default"default0lastfalsemissing = nullprintline(missing ?? "default")zero = 0printline(zero ?? "default")printline(missing ?? missing ?? "last")printline(false ?? "default")default0lastfalse?? 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.statedarkplainfalsesettings = {theme: "dark"}printline(settings.theme ?? "light")printline(settings.font ?? "plain")printline(mash.error.state)darkplainfalseotherwise is the natural form.
Conditional expression: ? :
Section titled “Conditional expression: ? :”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 7printline x is more than 5 ? "big" : x is more than 2 ? "mid" : "small"set size to x is more than 100 ? "huge" : "ok"printline sizeprintline "[{{ x > 0 ? 'positive' : 'not positive' }}]"bigok[positive]x = 7printline(x > 5 ? "big" : x > 2 ? "mid" : "small")size = x > 100 ? "huge" : "ok"printline(size)printline("[{{ x > 0 ? 'positive' : 'not positive' }}]")bigok[positive]?? is looser than ? :, so a ?? b ? c : d is a ?? (b ? c : d). Bracket whichever you mean.
Ranges
Section titled “Ranges”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.
Member access, indexes and calls
Section titled “Member access, indexes and calls”These bind tightest and chain from left to right: order.items[0].name, mash.collect(list).first().
a.namereads 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.