Comments
// and /* */ designate single-line and multi-line comments, as expected.
Separators
-
Any number of whitespaces (outside of string constants) are equivalent to one whitespace, indentation is ignored by the MUFL compiler.
-
A period
(.)terminates a statement.
Identifiers
Identifiers are sequences or letters, digits, or _ characters that start with a letter or _. Identifiers are case sensitive.
Keywords
Keywords can be written in all uppercase or all lowercase, however, an identifier conflicts with a keyword even when the identifier uses mixed case. So, because there is a keyword type, name Type is not a valid identifier.
Constants
Integers are represented by decimal numbers. For negative values, the minus sign and digits must be enclosed in parentheses: (-14).
String constants are represented in multiple ways.
Quoted string "He said \"Hello\"". Escape character \ can escape quotes and non-printables, such as \n.
Multiline string is indicated by a dollar sign $, followed by one of the delineators, either * or $ and is terminated by the same delineator. Within the body of string constant all text is taken verbatim. For example:
ThisString = $$Hello
World$.
AnotherString = $*Goodbye
World*.
Multiline strings cannot contain escape characters
Identifier string starts with a dollar sign, followed by an identifier with possible inclusion of :: in it. This syntax intended to be used to name fields in records.
S1 = $ThisString. // identical to "ThisString"
_assert (S1 == "ThisString").
S2 = $This::String. // identical to "This::String"
_assert (S2 == "This::String").
// not valid:
// $hello:world
// $1234
Binary constants are represented as hexadecimal strings:
ValidBinary1 = 0xabcd1234DF. // must have an even number of case-insensitive hexadecimal digits
EmptyBinary = 0x. // this is also valid for completeness, indicates zero-length binary
Binary constants can be additionally represented as base-58 strings, in this case the prefix is 58b.
ValidBase58Binary = 58b123.
_print ValidBase58Binary.
Prints:
0x003C
Boolean constants are represented as identifiers TRUE and FALSE (valid in all uppercase only).
In summary:
| Purpose | Example | Notes |
|---|---|---|
| true value | TRUE |
|
| false value | FALSE |
|
| number | 1, 2, 3, (-5) |
Parenthesis required for negative numbers |
| single line string in quotes | "hello \"great\" world\n" |
|
| multiple lines first form | $$multiline string$ |
Quotes allowed unescaped |
| multiple lines second form | $*multiline string* |
Quotes allowed unescaped |
| short string identifier | $some_field_name |
Whitespace not allowed, used for names of fields |
| a HEX binary blob | 0xAB123456CD |
Expressions
Functional Reductions
Reduction refers to function invocations and container lookups. Reductions use Curry notation, i.e. two sub-expressions separated by whitespace: a b.
Arithmetic Operators
MUFL supports standard arithmetic operations +, -, *, / and % for addition, subtraction, multiplication, division and modulo, respectively.
A = 12.
B = 5.
_print "A + B = " (A + B) "\n".
_print "A - B = " (A - B) "\n".
_print "A * B = " (A * B) "\n".
_print "A / B = " (A / B) "\n".
_print "A % B = " (A % B) "\n".
Prints:
A + B = 17
A - B = 7
A * B = 60
A / B = 2
A % B = 2
Syntactically, arithmetic expressions always need to be separated from reduction expressions with parenthesis. This expression is not valid: a b + c, one must always disambiguate it with either (a b) + c or a (b + c).
Comparison Operators
MUFL supports standard set of comparison operations ==, <, >, <=, and >=. Any value can be compared with any value. Within specific domains, such as strings and integers, comparisons are guaranteed to produce an expected well-ordered result. Comparison between domains or within non-well-ordered domains have consistent results, but no specific order should be relied on.
A = 12.
B = 5.
_print "A == B IS " (A == B) "\n".
_print "A != B IS " (A != B) "\n".
_print "A < B IS " (A < B) "\n".
_print "A <= B IS " (A <= B) "\n".
_print "A > B IS " (A > B) "\n".
_print "A >= B IS " (A >= B) "\n".
Prints:
A == B IS %%FALSE
A != B IS %%TRUE
A < B IS %%FALSE
A <= B IS %%FALSE
A > B IS %%TRUE
A >= B IS %%TRUE
Predicate Operators
MUFL supports predicate operators || for disjunction and && for conjunction. Any value that is neither NIL nor FALSE behaves as TRUE in this context.
_print "TRUE && TRUE = " (TRUE && TRUE) "\n".
_print "FALSE || FALSE = " (FALSE || FALSE) "\n".
Prints:
TRUE && TRUE = %%TRUE
FALSE || FALSE = %%FALSE
Logical NOT
MUFL supports logical operator ! for not. Any value except for FALSE and NIL behaves as TRUE in this context.
_print "!TRUE = " (!TRUE) "\n".
_print "!FALSE = " (!FALSE) "\n".
_print "!NIL = " (!NIL) "\n".
Prints:
!TRUE = %%FALSE
!FALSE = %%TRUE
!NIL = %%TRUE
Collections
These syntactic constructs represent collections:
(,)an empty dictionary (synonymous to[])("name1"->"value1", "name2"->"value")a simple string dictionary(1->"value1", "name2"->2)a dictionary that maps some strings into some integers and some integers into some strings["a", "b", "c"]a dictionary mapping integer indices 0, 1, and 2 to strings “a”, “b”, “c”; synonymous to(0->"a", 1->"b", 2->"c")(1,2,3)a dictionary representing a set, where each of the three elements maps into valueTRUE("a"->(1->"one", 2->"two))a two-level dictionary[[1,2],[3,4]]a two-level dictionary representing a 2x2 matrix of numbers["a", "b", 4->"d", "e"]a number dictionary where indices are numbers 0 through 5 with a gap at 2 and 3
The Ternary Operator
Much like other programming languages, the ternary operator enables a choice inside of an expression.
Note semicolon separating ‘then’ from ‘else’ clause of the ternary operator
some_value = TRUE.
A = (some_value ?? 1; 5). // if some_value is TRUE then return 1 otherwise return 5.
_print "A = " A "\n".
Prints:
A = 1
As illustrated above, the return type of a ternary operator is a disjunction of the types returned by its then- and else-subexpressions.
Variable Declaration and Assignment
Variables are declared and initialized in the same statement. Declaration without initialization is invalid:
A = 1. // type of A is inferred as int
B is int = 2. // type of B is declared as int
// C is str. // INVALID
// D is str = 2. // INVALID because not compatible.
Variable Mutation
While the equal sign (=) is used to initialize a variable, it cannot be used to mutate it. The mutation operator -> is used to reassign a new value to a variable. The mutation operator is also used to mutate values inside dictionaries.
Dictionary and Set Operators
Slices
Similar to Python slices, MUFL enables a quick way to retrieve parts of collections (arrays, dictionaries, or strings).
There are two kinds of slice-operators: a strict slice indicated by @! and a non-strict slice indicated by @?. The difference between strict and non-strict slices is that strict slices generate a runtime error when they encounter a value beyond the boundary of the container being sliced.
A = [$a, $b, $c, $d, $e].
_assert (A@!(0..1) == [$a, $b]).
_assert (A@?(3..7) == [$d, $e]).
// The following syntax would cause an error because strict slices do not permit access outside of the collection:
// A@!(3..7)
// Slicing allows you to quickly reorder elements in a container
_print (A@?[1,0,2]) "\n".
_assert (A@?[1,0,2] == [$b, $a, $c]).
Prints:
["b","a","c"]
The slice operator is a convenient way to extract substrings from strings:
A = "My fair lady".
_print A@?(0..1) "\n".
_print A@?(8..10000).
Prints:
My
lady
Combinators
The set combinator is a special syntax that allows you to merge two immutable sets into one.
Syntax: A = B'C.
A, B, C - immutable sets.
B and C should have compatible index and value types.
A has the same index and value types as B.
In the case when both B and C have some keys that are the same, the values from C is used, consequently, you can use the combinator to bulk-update values in a dictionary.
Example:
A = ( 1->"hello", 2->"world" ).
B = ( 3->"set",4->"combinator" ).
C = A'B.
_print C "\n".
Prints the combined set:
(1->"hello",2->"world",3->"set",4->"combinator",)
Number Sequences
Sometimes it is useful (for example, for loops) to construct a sequence of integer numbers. MUFL provides the INTEGER SEQUENCE domain that represents such a sequence.
These syntactic constructs represent integer sequences:
(x..y)inclusive range between x and y(x..<y)range in which y is excluded(x..y ++z)range from x to y with step z. If the step matches y exactly, it is included.(x..y --z)same as above but the step is negative. x must be greater than y.(x..>y --z)same as above, but y is expressly excluded
For more information, refer to the scan statement.