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.