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 value TRUE
  • ("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.