Misty Programming Language:

Operators

expression => operand [ternary]

operand => name | literal | special_value | paren | prefix | infix | self

literal => text | number | function

Expressions are made up of names and literals, fortified by prefix (or unary), infix (or binary), suffix, and trinary operators.

Paren

paren
=> '(' expression ')'
=> '(' parameter_list ')' '=>' expression

Parentheses can be used to explicitly control the order of evaluation of expressions. Parentheses can also be used to create short-form functions. See Functions.

Prefix

prefix => prefix_operator operand | array | function

prefix_operator => 'abs' | 'arity' | 'char' | 'code' | 'fix' | 'int' | 'length' | 'name' | 'not' | 'object' | 'parameters' | '+' | '-' | '~'

Prefix operators take an operand. These are operators, not functions, so the operand does not have to be wrapped in parentheses.

abs

Absolute value. If the operand is a positive number, the result is the number. If the operand is a negative number, the result is that number subtracted from 0. Otherwise, the result is null.

abs -4    # 4
abs 4     # 4

arity

Arity. If the operand is a function, return the maximum number of parameters permited. Otherwise, the result is null. It can report the arity of methods in fixed objects.

char

Make a character. If the operand is a number, the result is the character having that Unicode code point. If the operand is a text, the result is the first character of the text, or '' if the text is empty. Otherwise, the result is null.

char 65       # 'A'
char 8364     # '€'
char 'abc'    # 'a'
char ''       # ''

code

Character code. If the operand is a non-empty text, then the result is the Unicode code point of the first character. Otherwise, the result is null.

code 'A'        # 65
code "Misty"    # 77
code 65         # null
code null       # null

fix

Make an object or array immutable. The result is an immutable reference. Once a reference is made immutable, it cannot be made mutable. It is not possible to modify an object or array with a fixed reference. It is not possible to make a new object with the object operator with a fixed reference. It is not possible to get a function from an object with a fixed reference. All references obtained from a fixed object or array will also be fixed. See Security. The original object is not affected.

Note that numbers, texts, methods, functions, and the special values true, false, and null are always immutable.

function

Make a function value. See Functions.

function name(parameter_list) {
    body         
}

int

Integer. If the operand is a number, then the result is the integer part of the number. Otherwise, the result is null. (Also see the number.trunc method.)

int 3.1415926     # 3
int -3.1415926    # -3

length

Length. If the operand is an array, the result is the number of elements in the array. If the operand is a text, the result is the number of characters in the text. Otherwise, the result is 0.

length {key : 3}    # 0
length [7, 11]      # 2
length []           # 0
length 'length'     # 6
length ''           # 0
length 1            # 0
length null         # 0
length true         # 0
length false        # 0

name

Name. If the operand is a function, it returns the name as a text. If it doesn't have a name, it returns an empty text. Otherwise, the result is null. It can report the name of functions in fixed objects.

not

Logical not. If the operand has a falsy value (false, null, or '' (the empty text)) then the result is true. Otherwise, the result is false.

not 0          # false
not (3 > 4)    # true
not ''         # true

object

Make an object. If the operand is null then the result is a new empty object. If the operand is not an object, then a 'type' exception is raised. If the operand is fixed, then a 'fix' exception is raised. The result is a new empty object that delegates to the operand. See Objects.

object null      # {}
object stooge    # a new empty object that inherits from the stooge object

parameters

Parameters. If the operand is a function, it returns the names of the parameters as a fixed array of texts. If it doesn't have parameters, it returns a fixed empty array. The length of the array is the same as the arity. Otherwise, the result is null. It can report the parameters of functions in fixed objects.

+

Convert to number. If the operand is a number, the result is the number. If the operand is a non-empty text, the result is the text converted as a real decimal number. If the operand is false, or the empty text, the result is 0. If the operand is null, the result is null. If the operand is true, the result is 1. If the operand is an object and the object has a number method, then the result is the value returned by operand.number(). Otherwise, the result is null.

+ 2.7182817    # 2.7182817
+ '12.00'      # 12
+ '12pt'       # null
+ '$20'        # null
+ ''           # 0
+ false        # 0
+ true         # 1
+ null         # null

-

Negate. If the operand is a number, the result is the number subtracted from 0. Otherwise, the result is null.

- 4    # -4
--4    # 4
- 0    # 0
Value ~ Value
true "true"
false "false"
null "null"
number number.text()
object object.text()
array array.text()
text text

~

Convert to text. It is short for calling the text method. Arrays and object are serialized. See JSON.

~ null    # 'null'
~ true    # 'true'
~ 12.00   # '12'
~ {}      # '{}' 

Infix Operators

infix
=> operand (relational operand)+
=> operand (logical | arithmetic | concatenation) operand
=> operand (subscription | invocation | augmentation | appendation | possession)
=> operand 'array' operand
=> operand 'is' ['not'] characteristic
=> operand 'apply' operand

Relational Infix Operators

relational => '=' | '<>' | '<' | '<=' | '>' | '>=' | 'eq'

Relational operators can be strung together in useful ways, so that

a = b = c = d

has a similar result to

a = b and b = c and c = d

=

Equal. If the operands are similar, as determined by the first operand's equal method, then the result is true.

a = b

is equivalent to

a.equal(b)

Also see the eq operator, which determines if the operands are the same object.

Examples:

if a = b = null then          # compute result if a and b are both null
    result : 10 = '10'        # result is false
    result : 'true' = true    # result is false
fi

<>

Not equal. If the operands are not similar, as determined by the first operand's equal method, then the result is false.

a <> b

is equivalent to

not (a.equal(b))
Example:
result : 'false' <> false    # result is true

<

Less than. If the first operand is an object, and if it has an lt method, then the result is the value return from invoking left.lt(right).

Otherwise, if the operands are not of the same type, then null is the lowest value, followed by booleans, followed by texts, followed by numbers, followed by the remaining types: functions, objects, and arrays.

If the operands are the same type, then if the left operand is less than the right operand, then the result is true. If the left operand is greater than or equal to the right operand the result is false. If they are both booleans, false is less than true.

If the operands are arrays, or if the operands are objects, then the system will arbitrarily decide which is smaller.

Example:

return 0 <= i < n then i else null

<=

Less than or equal. This is similar to < except that the le method is used if available, and true is returned if the values are equal.

>

Greater than. This is similar to <= except that the gt method is used if available, and otherwise the result is complemented.

>=

Greater than or equal. This is similar to < except that the ge method is used if available, and otherwise the result is complemented.

eq

Equal. If the operands are the same value then the result is true. eq and = produce the same result for simple values and functions. They can produce different results for objects and arrays.

Logical Infix Operators

logical => 'and' | 'default' | 'or'

These are short-circuiting operators. If the result can be determined by looking only at the left operand, then the right operand is not evaluated. The values false, null, and '' (the empty text) are falsy. All other values are truthy.

and

And. If the left operand is falsy, then the result is the left operand. Otherwise, the result is the right operand.

left and right  is more convenient than  left then right else left.

3 < 4 and 5 < 6    # true

default

Default. If the left operand is not null, then the result is the left operand. Otherwise, the result is the right operand. It is like or except it tests that the left operand is null instead of falsy.

left default right  is more convenient than  left is not null then left else right.

define foo       # foo is null
foo default 0    # 0

or

Or. If the left operand is truthy, then the result is the left operand. Otherwise, the result is the right operand.

left or right  is more convenient than  left then left else right.

3 < 4 or 6 < 5    # true

Arithmetic Infix Operators

arithmetic => 'max' | 'min' | 'mod' | '+' | '-' | '*' | '/' | '÷' | '**'

These operators work only for numbers. If either operand is not a number, or if the operation creates a value that is too large to be contained in a Misty number, then the result is null.

max

Maximum. The result is the larger of the two operands. If either operand is not a number, the result is null.

3 max 4    # 4

max can be used with min to constrain values within an acceptable range.

2 max 3 min 7    # 3
4 max 3 min 7    # 4
9 max 3 min 7    # 7

To determine if a value lies between two other values, uses relational operators.

if 3 <= value <= 7 then ... fi

min

Minimum. The result is the smaller of the two operands. If either operand is not a number, the result is null.

3 min 4    # 3

mod

The result of

a mod b

is

a - ((a / b).floor() * b)

If b is 0, or if either operand is not a number, then the result is null. b is not required to be an integer. The result will have the same sign as b.

3 mod 4     # 3
13 mod 4    # 1

+

Addition. If either operand is not a number, the result is null.

3 + 4    # 7

-

Subtraction. If either operand is not a number, the result is null.

3 - 4    # -1

*

Multiplication. If either operand is not a number, the result is null. If it can be determined that either operand is zero, the other operand might not be evaluated.

3 * 4        # 12
0.10 * 20    # 2

/

Division. If either operand is not a number, the result is null.

3 / 4     # 0.75
16 / 4    # 4
0 / 0     # null
3 / 0     # null

÷

Integer division. a ÷ b produces a result that is similar to (a / b).trunc().

3 ÷ 4     # 0
17 ÷ 4    # 4
0 ÷ 0     # null
3 / 0     # null

**

Exponentiation.

3 ** 4      # 81
3 ** 0      # 1
0 ** 0      # null

Text Concatenation Operators

concatenation => '~' | '~~~'

~

Concatenation. Convert the two operands to texts. Make a new text which combines their characters. If both operands are '' or null, then the result is ''. If the first operand is '' or null, then the result is the second operand. If the second operand is '' or null, then the result is the first operand.

3 ~ 4                          # '34'
'beginning' ~ "and" ~ 'end'    # 'beginningandend'
'' ~ 'ok'                      # 'ok'
null ~ null                    # ''
~ null ~ ~ null                # 'nullnull'

~~~

Concatenation with a space. Convert the two operands to texts. Make a new text which combines their characters with a space inserted between them if both are neither empty nor null. If both operands are '' or null, then the result is ''. If the first operand is '' or null, then the result is the second operand. If the second operand is '' or null, then the result is the first operand.

3 ~~~ 4                            # '3 4'
'beginning' ~~~ "and" ~~~ 'end'    # 'beginning and end'
'' ~~~ 'ok'                        # 'ok'
null ~~~ null                      # ''
~ null ~~~ ~ null                  # 'null null'

Possession Infix Operators

possession
=> 'can' ['not']
=> 'has' ['not']
=> 'owns' ['not']

can

Method possession. The can infix operator is used to determine if an object contains a particular function. The expression object can name has the same result as object[name] is function.

has

Inherited possession. The has infix operator is used to determine if an object contains a particular member. The expression object has name returns true if the object[name] is a property of the object, including its prototype chain. If the left operand is not an object, then it returns false.

owns

Possession. The owns infix operator is used to determine if an object contains a particular member. The expression object has name returns true if the object[name] is a property of the object, excluding its prototype chain. If the left operand is not an object, then it returns false.

is Operator

characteristic => 'array' | 'blob' | 'boolean' | 'char' | 'data' | 'digit' | 'end' | 'even' | 'false' | 'falsy' | 'fix' | 'function' | 'int' | 'letter' | 'lower' | 'method' | 'number' | 'null' | 'object' | 'odd' | 'pattern' | 'space' | 'text' | 'true' | 'truthy' | 'upper' | string_literal | number_literal

The is infix operator returns either true or false from its determination of a characteristic of the first operand. The is keyword can optionally be followed by the word not, which reverses the meaning of the characteristic. So if

value is an int 

is true, then

value is not an int 

will be false.

is array

Is this an array? If the operand is an array, the result is true. Otherwise, the result is false.

0 is array             # false
{} is array            # false
[] is array            # true
[] is not array        # false
pattern [] is array    # false
"array" is array       # false
true is array          # false

is blob

Is this a blob? A blob is a pile of data. Misty does not have operators for creating or manipulating blobs, but does allow them to transit through the system. References to blobs can be stored in variables, objects, and arrays. Blobs can be passed to or returned from functions. Blobs have no methods. Blogs have no operators except for is blob. If the operand is a blob, the result is true. Otherwise, the result is false. Generally, a blob is blob, and a blob is truthy.

0 is blob        # false
"blob" is blob   # false

is boolean

Is this a boolean? If the operand is true or false, the result is true. Otherwise, the result is false.

0 is boolean        # false
0 is not boolean    # true
false is boolean    # true
"true" is boolean   # false
true is boolean     # true

is char

Is this a character? If the operand is a text with a length of 1, then the result is true. Otherwise, the result is false.

1 is char         # false
"1" is char       # true
"char" is char    # false
'' is char        # false
true is char      # false

is data

Is this a data value? If the operand is a not null and not a function, then the result is true. Otherwise, the result is false.

0 is data                 # true
"0" is data               # true
"9" is data               # true
"null" is data            # true
null is data              # false
function () {} is data    # false
'' is data                # true
false is data             # true

is digit

Is this a digit? If the operand is a text with a length of 1 and is one of the 10 digit characters, then the result is true. Otherwise, the result is false.

0 is digit           # false
"0" is digit         # true
"9" is digit         # true
"09" is digit        # false
"digit" is digit     # false
'' is digit          # false
true is digit        # false
["0"] is digit       # false

is end

Is this a line ending character? If the operand is a text with a length of 1, and if it contains a Unicode line ending character, then the result is true. Otherwise, the result is false.

0 is end                 # false
"" is end                # false
" " is end               # false
"\n" is end              # true
'\r' is end              # true
'\r\n' is end            # false
'[\n]' is end            # false
['\n'] is end            # false
pattern [end] is end     # false

is even

Is this an even number? If the operand is a number and its integer part is even, then the result is true. Otherwise, the result is false.

0 is even           # true
1 is even           # false
(13 / 4) is even    # false
(13 / 0) is even    # false
(17 / 4) is even    # true
98.6 is even        # true
"0" is even         # false
true is even        # false

is false

Is this false? If the operand is false, then the result is true. Otherwise, the result is false. There is no advantage to using this form over the = form.

"false" is false    # false
false is false      # true
0 is false          # false
'' is false         # false

is falsy

Is this a falsy value? If the operand is false, null, or the empty text, then the result is true. Otherwise, the result is false.

"false" is falsy    # false
false is falsy      # true
0 is falsy          # false
'' is falsy         # true
null is falsy       # true
{} is falsy         # false
[] is falsy         # false

is fix

Is this fixed? If the operand is immutable, then the result is true. Otherwise, the result is false. All booleans, numbers, texts, functions, and blobs are immutable. Objects and arrays are initially mutable, but an immutable reference can be obtained with the fix operator.

0 is fix           # true
"0" is fix         # true
[] is fix          # false
{} is fix          # false
(fix []) is fix    # true
(fix {}) is fix    # true
true is fix        # true

is function

Is this a function? If the operand is a function, then the result is true. Otherwise, the result is false. If the operand is a dot expression or a subscript expression, then is can determine the result even if the object is fixed.

0 is function                          # false
function () {} is function             # true
function () {return $} is function     # true
"function" is function                 # false
true is function                       # false
(fix {koda: function () {}}).koda is function # true

is int

Is this an integer? If the operand is a number and if its fraction part is zero, then the result is true. Otherwise, the result is false.

0 is int             # true
(13 / 4) is int      # false
65.0000000 is int    # true
65.0000001 is int    # false
null is int          # false
true is int          # false

is letter

Is this a letter? If the operand is a text with a length of 1 and is a letter, then the result is true. Otherwise, the result is false.

0 is letter          # false
"0" is letter        # false
"letter" is letter   # false
"l" is letter        # true
"L" is letter        # true
'' is letter         # false
true is letter       # false

is lower

Is this a lower case letter? If the operand is a text with a length of 1 and is a lower case letter, then the result is true. Otherwise, the result is false.

0 is lower          # false
"0" is lower        # false
"lower" is lower    # false
"l" is lower        # true
"L" is lower        # false
'' is lower         # false
true is lower       # false

is method

Is this a method? If the operand is a function that uses $, then the result is true. Otherwise, the result is false. If the operand is a dot expression or a subscript expression, then is can determine the result even if the object is fixed.

0 is method                          # false
function () {} is method             # false
function () {return $} is method     # true
"method" is method                   # false
true is method                       # false
(fix {koda: function () {return $}}).koda is method # true

is null

Is this null? If the operand is null, then the result is true. Otherwise, the result is false. There is no advantage to using this form over the = form or eq form.

null is null        # true
(13 / 0) is null    # true
0 is null           # false
"null" is null      # false
"" is null          # false
false is null       # false

is number

Is this a number? If the operand is a number, then the result is true. Otherwise, the result is false.

0 is number           # true
(13 / 4) is number    # true
(13 / 0) is number    # false
98.6 is number        # true
"0" is number         # false
true is number        # false

is object

Is this an object? If the operand is an object, then the result is true. Otherwise, the result is false.

0 is object                 # false
{} is object                # true
[] is object                # false
"object" is object          # false
"{}" is object              # false
function () {} is object    # false
pattern [] is object        # true
true is object              # false

is odd

Is this an even number? If the operand is a number and its integer part is odd, then the result is true. Otherwise, the result is false. This is not always the same as is not even.

0 is odd           # false
1 is odd           # true
(13 / 4) is odd    # true
(13 / 0) is odd    # false
98.6 is odd        # false
"0" is odd         # false
true is odd        # false

is space

Is this whitespace? If the operand is a text with a length of 1 and is a whitespace character, then the result is true. Otherwise, the result is false.

0 is space          # false
32 is space         # false
char 32 is space    # true
"0" is space        # false
" " is space        # true
"\t" is space       # true
"\r" is space       # true
"space" is space    # false
"     " is space    # false
"L" is space        # false
'' is space         # false
true is space       # false

is text

Is this a text? If the operand is a text, then the result is true. Otherwise, the result is false.

0 is text           # false
"0" is text         # true
"number" is text    # true
'' is text          # true
true is text        # false

is true

Is this true? If the operand is true, then the result is true. Otherwise, the result is false. There is no advantage to using this form over the = form or eq form.

"true" is true    # false
true is true      # true
1 is true         # false 

is truthy

Is this a truthy value? If the operand is false, null, or the empty text, then the result is false. Otherwise, the result is true.

"false" is truthy    # true
false is truthy      # false
0 is truthy          # true
'' is truthy         # false
null is truthy       # false
{} is truthy         # true
[] is truthy         # true

is upper

Is this an upper case letter? If the operand is a text with a length of 1 and is an upper case letter, then the result is true. Otherwise, the result is false.

0 is upper          # false
"0" is upper        # false
"UPPER" is upper    # false
"u" is upper        # false
"U" is upper        # true
'' is upper         # false
true is upper       # false

Suffix Operators

.{ }

augmentation => '.' object_literal

Augmentation. If the left operand is a mutable object, then the name/value pairs within the { } are put into the operand, and the result is the operand. Otherwise, a 'type' exception is raised. These expressions can be used as statements. (Also see the object.combine method.)

define stooge : {first_name : 'Curly'}
stooge.{last_name : 'Howard'}
    # stooge is {first_name : 'Curly', last_name : 'Howard'}

.[ ]

appendation => '.' array_literal

Appendation. If the left operand is a mutable array, then the values within the [ ] are put into the operand, and the result is the operand. Otherwise, a 'type' exception is raised. These expressions can be used as statements. (Also see the object.combine method.)

define list : ['Moe', 'Larry]
list.['Curly', 'Shemp']
    # list is ['Moe', 'Larry', 'Curly', 'Shemp']

.

subscription
=> '.'name
=> '[' expression ']'

Selection. This identifies a member in a value. It is generally used only with objects, but it can be used to invoke built-in methods on other types. This is a special case of subscription.

stooge.last_name     # 'Howard'
stooge.first_name    # 'Curly'

[ ]

Subscription. The expression in the brackets is used as a key to select a member a member of an object. If the left operand is an array, then the value in the brackets is a number (not a text) used to select an element. It will be truncated to an integer if necessary. If the left operand is a text, then the number in the brackets is used to select a character.

On retrieval, if the object does not contain the key, then null will be returned. On storage, a 'fix' exception will be raised if the object is immutable. On storage to a mutable array, if the key is not a number or if the key violates the bounds of the array, an 'array' exception will be raised.

stooge['first_name'][4]    # 'y'
null['foo']                # null

( )

invocation => '(' expression, ')'

Invocation. The ( ) suffix operator invokes a function value. The function invocation pattern can be used only with function values that do not use the $ operator..

function_value(parameters...)

The method invocation pattern is used with members that are function values.

object.method_name(parameters...)

If the value is not function, a 'function' exception is raised.

If a function is invoked with too many parameters, an 'arity' exception is raised.

Self

$

self => '$'

Self. The $ operator allows a method to have access to the object that was used in the invocation. See Functions.

Apply

apply

Application. The apply operator provides an alternate way of invoking a function or method. It works like the () operator except that it takes a single expression (which could be an array of arguments) rather than an argument list. The left operand is an expression that yields a function or method value. The right operand is null if the function should be called with no parameters, or an array of parameters, or a single parameter that is not null or an array. Also see the object.call method. apply uses the call process, so apply can be used to invoke anything that () can invoke.

Example:

my_object[methodname] apply my_array

Ternary

ternary => then_else

There is one ternary operator. A ternary operator takes three operands. A ternary expression must be wrapped in parens when it appears in the condition part of an if statement.

then else

then_else => 'then' expression 'else' expression

Conditional expression. If the left operand is truthy, then the result is the middle operand. Otherwise, the result is the right operand. The unused operand is not evaluated.

message : t.reserved then "Already reserved." else "Already defined."

Operator Precedence

( )   [ ]   .   apply
.{ }
prefix operators
is ...
**
*   /   ÷   mod
+   -
max   min
~   ~~~
=   <>   <   >   <=   >=   eq
and   or   default   array
then else

Unary operators group right to left. All other operators group left to right. The member and invocation operators have the highest priority (tightest). The trinary operator has the lowest priority (loosest). Grouping can be made explicit with parens.

'$' ~ 3 + 4    // '$7'

() Parenthesis