The following is an exhaustive list of nodes (syntax constructs) in MPL, including their descriptions.

Syntax Name Description
[ Block Begins collection of nodes for a Block object.
] BlockEnd Ends Block collection, creates the resulting Block object, and pushes it onto the data stack.
# Comment Begins a comment that continues until the next line feed or end of file.
{ Dict Begins a nested scope whose created locals become items of the resulting Struct object.
} DictEnd Ends the Dict scope, constructs the resulting Dict, List, or Tuple object, and pushes it onto the data stack.
NUMBERi8 Int8 Pushes an Int8 object (an 8-bit integer).
NUMBERi16 Int16 Pushes an Int16 object (a 16-bit integer).
NUMBER[i32] Int32 Pushes an Int32 object (a 32-bit integer).
NUMBERi64 Int64 Pushes an Int64 object (a 64-bit integer).
NUMBERix Intx Pushes an Intx object (a pointer-width integer).
NAME: Label Pushes a name onto the name stack for Variable to consume.
NAME Name Mentions one selected builtin, local, or visible field by source-text name: executes a builtin, calls a callable object, or otherwise pushes an immutable Ref, copied object, or new object.
.NAME NameMember Pops a Dict object or a Ref to Dict, selects the last matching field, calls it if callable, or otherwise pushes an immutable Ref, copied object, or new object.
@NAME NameRead Reads one selected local or visible field by source-text name without calling it, pushing a Ref, copied object, or new object.
.@NAME NameReadMember Pops a Dict object or a Ref to Dict, selects the last matching field, reads it without calling, and pushes a Ref, copied object, or new object.
!NAME NameWrite Writes one selected local or visible field by source-text name.
.!NAME NameWriteMember Pops a mutable Ref to Dict and writes one selected field by source-text name.
NUMBERn8 Nat8 Pushes a Nat8 object (an 8-bit natural).
NUMBERn16 Nat16 Pushes a Nat16 object (a 16-bit natural).
NUMBERn32 Nat32 Pushes a Nat32 object (a 32-bit natural).
NUMBERn64 Nat64 Pushes a Nat64 object (a 64-bit natural).
NUMBERnx Natx Pushes a Natx object (a pointer-width natural).
REALr32 Real32 Pushes a Real32 object (a 32-bit real).
REAL[r64] Real64 Pushes a Real64 object (a 64-bit real).
"TEXT" / «TEXT» Text Pushes a Text object.
( Tuple Begins a nested scope whose remaining stack items become unnamed items of the resulting Struct object.
) TupleEnd Ends the Tuple scope, constructs the resulting List or Tuple object, and pushes it onto the data stack.
; Variable Pops a name from the name stack, pops an object from the data stack, and creates a local.

Block & BlockEnd ([...])

The Block node begins collecting nodes. The matching BlockEnd node ends that collection and creates a new Block object. A Block is a Meta object whose schema includes the enclosed AST nodes; see Block and Code for source-position identity and processing. Blocks can be nested, allowing structured logic to be grouped within multiple levels of [ ... ] constructs, and each matching pair produces its own Block object.

Blocks are not compiled at the control-flow location where they appear (see Block and Code). Using Block and BlockEnd only creates a Block object. Later, that object is handled in one of the following ways:

Example

{} () {} [
  namedBlock: ["-- named block --" printCompilerMessage];
  namedBlock

  {
    blockField: ["-- named member block --" printCompilerMessage];
  }.blockField

  runner: {
    CALL: ["-- special CALL field --" printCompilerMessage];
  };
  runner

  choice: ["-- fallback --" printCompilerMessage];
  choice: {
    CALL: ["-- matched --" printCompilerMessage];
    PRE:  ["-- PRE --" printCompilerMessage FALSE];
  };
  choice

  codeLocal: {} () {} codeRef;
  ["-- block compiled for code --" printCompilerMessage] !codeLocal # a non-NIL Code

  ["-- call builtin --" printCompilerMessage] call
  TRUE ["-- if builtin --" printCompilerMessage] [] if
  ["-- ucall builtin --" printCompilerMessage] ucall
  TRUE ["-- uif builtin --" printCompilerMessage] [] uif
] "main" exportFunction

Expected Output During Compilation

-- named block --
-- named member block --
-- special CALL field --
-- PRE --
-- fallback --
-- block compiled for code --
-- call builtin --
-- if builtin --
-- ucall builtin --
-- uif builtin --

Comment (#)

The Comment node begins a comment that extends from # to the next line feed or to the end of the file. The comment text does not affect program logic and is not parsed as MPL syntax.

A comment may occupy a whole line or follow other nodes on the same line. Because the comment text is not parsed, unmatched delimiters or other syntax characters inside it have no effect.

Example

{} () {} [
  "-- comment ignored --" printCompilerMessage # ] } ) " « everything here is ignored
] "main" exportFunction

Expected Output During Compilation

-- comment ignored --

Dict & DictEnd ({...})

The Dict node begins a nested scope in which locals can be created (see Structs). The matching DictEnd node ends that scope, constructs the resulting Struct object from those created locals, and pushes it onto the data stack. The inner locals do not remain available by their local names after the scope ends.

DictEnd collects created locals only. Any other stack items left in the nested scope remain on the data stack below the pushed Struct object.

Locals created between { and } become items of the resulting Struct object. Locals with non-empty names become named fields. Locals with empty names still participate in the result. If all created locals have empty names and none of the non-Meta locals is static, the final object is a List or Tuple whose items are unnamed. Locals are created with NAME: … ; or with the builtin def; only that builtin can create a local with the empty name (1 "" followed by def). The resulting named fields or unnamed items keep the created locals’ schemas and storage properties. If several created fields have the same name, they all remain in the object; later lookup rules decide which one is selected.

See Structs for static-field rules: at DictEnd, a local made static with the builtin virtual becomes a static field whose value is packed into the resulting object’s schema instead of occupying runtime field storage, so it must be known at compile time.

Depending on the created locals, DictEnd does one of the following:

See Structs for reserved field behavior and reverse destruction order: DictEnd creates the resulting object without calling callable fields, and a field can rely on fields declared before it during its own DIE because destruction is in reverse declaration order.

Dict constructs can be nested. Nested { ... } pairs create nested Struct objects.

Example

{} () {} [
  "-- empty --" printCompilerMessage
  {} printStack _:;

  "-- named and nested --" printCompilerMessage
  {
    name: "Alice";
    stats: {
      score: 100;
      level: 2;
    };
  } printStack _:;

  "-- fields created by def --" printCompilerMessage
  { 100 "score" def 2 "level" def } printStack _:;

  "-- duplicate field names --" printCompilerMessage
  { x: 1; x: 2; } printStack _:;

  "-- unnamed fields --" printCompilerMessage
  { 1 "" def FALSE "" def } printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- empty --
()
-- named and nested --
{
  name: "Alice";
  stats: {
    score: 100;
    level: 2;
  };
}
-- fields created by def --
{
  score: 100;
  level: 2;
}
-- duplicate field names --
{
  x: 1;
  x: 2;
}
-- unnamed fields --
(1 FALSE)

Int8, Int16, Int32, Int64, and Intx

The Int8, Int16, Int32, Int64, and Intx nodes push integer objects onto the data stack. Integer literals may be negative, zero, or positive. A literal is invalid if its value does not fit the selected schema. For Intx, this check uses pointer-width limits. Integer values use two's complement representation.

Decimal

A decimal integer literal is written as digits, optionally preceded by -. If a suffix is present, it must immediately follow the digits. Decimal integer literals cannot have leading zeroes except for 0 itself. The following suffix forms are available:

Hexadecimal

A hexadecimal integer literal begins with exactly 0x, may be preceded by -, and may use digits 0 to 9 and uppercase letters A to F. If a suffix is present, it must immediately follow the hexadecimal digits. The following suffix forms are available:

Literal syntax is case-sensitive: use exactly 0x for hexadecimal notation, lowercase suffixes such as i8 and ix, and uppercase hexadecimal letters A to F.

When formatted, Int32 objects are shown without the optional i32 suffix.

The example below uses only fixed-width forms. Intx is pointer-width, so its exact limits are not shown there.

Example

{} () {} [
  "-- decimal examples --" printCompilerMessage
  -128i8 printStack _:;
  32767i16 printStack _:;
  2147483647 printStack _:;
  9223372036854775807i64 printStack _:;

  "-- hexadecimal examples --" printCompilerMessage
  0x7Fi8 printStack _:;
  -0x8000i16 printStack _:;
  0x7FFFFFFF printStack _:;
  0x7FFFFFFFFFFFFFFFi64 printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- decimal examples --
-128i8
32767i16
2147483647
9223372036854775807i64
-- hexadecimal examples --
127i8
-32768i16
2147483647
9223372036854775807i64

Name (NAME)

The Name node designates mentioning one selected builtin, local, or visible field by source-text name.

After the first character, names stop before a space, line feed, carriage return, ., #, ,, ;, or a closing ), ], or }; : introduces a label, while !, @, (, [, {, quotation marks, and guillemets are forbidden inside a name. A comma can begin a name: , and ,foo are ordinary names. See Grammar tokens for the character classes.

See Names, lookup, and overloads for lookup order and PRE handling: the Name node selects the newest matching candidate and is invalid when no candidate matches. If PRE processing succeeds but does not return a known Cond, the Name mention is invalid.

See Selection and destinations and Locations, references, and views for the read rule: Name executes a builtin, calls a callable selected object, or reads it according to its schema; mentioning a known NIL Code through Name is invalid, and reading a static Ref produces a known Ref (including known NIL). For a selected non-callable, non-static local or visible field, Name creates a new immutable Ref when the field is In-place, or copies the field when it is Ref or Text, making a copied Ref immutable regardless of the stored reference's mutability; these rules apply whether the value is known or unknown. For a selected non-callable Meta object or static local or field, Name creates a new object from compile-time information instead of using the non-static read branch.

The special PRE and CALL fields of a callable Dict must hold Blocks. An ordinary compilation failure inside the PRE Block skips that candidate. A non-Block PRE field is an error when PRE is evaluated; CALL must be a Block when the candidate is selected and called.

Selection and calling of a callable Dict through Name work as follows:

Calling a non-Meta, non-static In-place callable Dict local by NAME gives PRE and CALL mutable access to its fields. Calling through a Ref preserves that Ref's mutability; @dict const call therefore gives immutable field access.

Example

{} () {} [
  "-- builtin --" printCompilerMessage
  1 2 + printStack _:;

  blockLocal: ["-- block local --" printCompilerMessage];
  blockLocal

  codeLocal: {} () {} codeRef;
  ["-- code local --" printCompilerMessage] !codeLocal # a non-NIL Code
  codeLocal

  textLocal: 0 ("") dynamic @; # an unknown Text
  "-- text local --" printCompilerMessage
  textLocal printStack _:;

  ordinaryLocal: 0 dynamic;
  "-- In-place local --" printCompilerMessage
  ordinaryLocal printStack _:;

  mutableRef: @ordinaryLocal;
  "-- Ref local --" printCompilerMessage
  mutableRef printStack _:;

  metaLocal: ();
  "-- meta local --" printCompilerMessage
  metaLocal printStack _:;

  dict: {
    field: 0 dynamic;
    method: [
      "-- field through scope lookup --" printCompilerMessage
      field printStack _:;
    ];
  };
  dict.method

  runner: {
    CALL: ["-- dict with CALL --" printCompilerMessage];
  };
  runner

  choice: ["-- fallback --" printCompilerMessage];
  choice: {
    CALL: ["-- matched --" printCompilerMessage];
    PRE:  ["-- PRE --" printCompilerMessage FALSE];
  };
  choice
] "main" exportFunction

Expected Output During Compilation

-- builtin --
3
-- block local --
-- code local --
-- text local --
Text
-- In-place local --
Int32 Cref
-- Ref local --
Int32 Cref
-- meta local --
()
-- field through scope lookup --
Int32 Cref
-- dict with CALL --
-- PRE --
-- fallback --

NameMember (.NAME)

The NameMember node designates mentioning one selected field by source-text name (see Structs). It pops either a bare Dict object or a Ref to Dict from the data stack, selects the last field with the specified name, and then handles that field. Mentioning does not require mutable access to the outer Dict.

Candidates are tried from newest to oldest. A callable Dict field with a PRE field is selected only if that PRE logic is compilable and produces a known TRUE. A compilation failure inside the PRE Block or a known FALSE continues selection with older fields; a non-Block PRE field is an error. Potentially invalid code may therefore be tried during overload selection without making the overall mention invalid, provided each failed candidate is skipped. If that PRE logic compiles but does not return a known Cond, the mention is invalid. If no candidate matches, the mention is invalid. See the callable-Dict field rule.

Depending on what the selected field is, NameMember does one of the following:

Calling a Block field through NameMember works as follows:

Selection and calling of a callable Dict field through NameMember work as follows; the special fields must satisfy the callable-Dict field rule:

Example

{} () {} [
  dict: {
    ordinaryField: 0 dynamic;
    textField:     0 ("") dynamic @; # an unknown Text
    metaField:     ();
    staticField:   0 virtual;

    outerValue: 7;

    blockField: [
      "-- block field --" printCompilerMessage
      self.outerValue printStack _:;
    ];

    choice: ["-- fallback field --" printCompilerMessage];
    choice: {
      CALL: ["-- matched field --" printCompilerMessage];
      PRE:  ["-- PRE --" printCompilerMessage FALSE];
    };

    callableField: {
      innerValue: 9;
      CALL: [
        "-- callable Dict field --" printCompilerMessage
        self.outerValue     printStack _:;
        innerValue          printStack _:;
        @closure.innerValue printStack _:;
      ];
    };
  };

  "-- In-place field --" printCompilerMessage
  dict.ordinaryField printStack _:;

  "-- text field --" printCompilerMessage
  dict.textField printStack _:;

  "-- meta field --" printCompilerMessage
  dict.metaField printStack _:;

  "-- static field --" printCompilerMessage
  dict.staticField printStack _:;

  dict.blockField
  dict.choice
  dict.callableField
] "main" exportFunction

Expected Output During Compilation

-- In-place field --
Int32 Cref
-- text field --
Text
-- meta field --
()
-- static field --
0
-- block field --
7 Cref
-- PRE --
-- fallback field --
-- callable Dict field --
7 Cref
9 Cref
9 Cref

NameRead (@NAME)

The NameRead node designates reading one selected local or visible field by source-text name without calling it. Attempting to read a builtin is invalid. If the resolved entity is callable, reading it still does not call it.

See Locations, references, and views for view and mutability rules: NameRead returns the selected value according to its schema; a static Ref remains a known Ref (including known NIL). For a non-static local or visible field, NameRead creates a new Ref when the field is In-place and copies the field when it is Ref, Code, or Text. NameRead creates a new object from compile-time information for a Meta object or a static local or field. A static Ref local retains its Ref mutability when read. Field-view mutability follows the access path: for a non-static In-place visible field, the returned Ref is mutable exactly when the enclosing Dict is reached mutably; for a Ref field, the returned Ref is mutable exactly when both the access path and the field's own Ref schema are mutable.

Example

{} () {} [
  ordinaryLocal: 0 dynamic;
  textLocal:     0 ("") dynamic @; # an unknown Text
  metaLocal:     ();

  "-- In-place local --" printCompilerMessage
  @ordinaryLocal printStack _:;

  "-- text local --" printCompilerMessage
  @textLocal printStack _:;

  "-- meta local --" printCompilerMessage
  @metaLocal printStack _:;

  dict: {
    ordinaryField: 0 dynamic;
    textField:     0 ("") dynamic @;
    staticField:   0 virtual;

    CALL: [
      "-- In-place field --" printCompilerMessage
      @ordinaryField printStack _:;

      "-- text field --" printCompilerMessage
      @textField printStack _:;

      "-- static field --" printCompilerMessage
      @staticField printStack _:;
    ];
  };

  "-- mutable fields --" printCompilerMessage
  dict

  "-- immutable fields --" printCompilerMessage
  @dict const call
] "main" exportFunction

Expected Output During Compilation

-- In-place local --
Int32 Ref
-- text local --
Text
-- meta local --
()
-- mutable fields --
-- In-place field --
Int32 Ref
-- text field --
Text
-- static field --
0
-- immutable fields --
-- In-place field --
Int32 Cref
-- text field --
Text
-- static field --
0

NameReadMember (.@NAME)

The NameReadMember node designates reading one selected field by source-text name without calling it (see Structs). It pops either a bare Dict object or a Ref to Dict from the data stack, selects the last field with the specified name, and reads that field. If the field is callable, reading it still does not call it. Reading does not require mutable access to the outer Dict.

Depending on what the field is, NameReadMember does one of the following:

Field selection is by the last field with that name in the popped Dict. A non-static In-place field produces a mutable Ref when that Dict was reached mutably and an immutable Ref otherwise. Non-static Ref fields also respect their own mutability.

Example

{} () {} [
  dict: {
    ordinaryField: 0 dynamic;
    textField:     0 ("") dynamic @; # an unknown Text
    codeField:     {} () {} codeRef dynamic;
    metaField:     ();
    staticField:   0 virtual;
  };

  "-- mutable fields --" printCompilerMessage
  @dict .@ordinaryField printStack _:;
  @dict .@textField     printStack _:;
  @dict .@codeField     printStack _:;
  @dict .@metaField     printStack _:;
  @dict .@staticField   printStack _:;

  "-- immutable fields --" printCompilerMessage
  dict .@ordinaryField printStack _:;
  dict .@textField     printStack _:;
  dict .@codeField     printStack _:;
  dict .@metaField     printStack _:;
  dict .@staticField   printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- mutable fields --
Int32 Ref
Text
{} () {} codeRef
()
0
-- immutable fields --
Int32 Cref
Text
{} () {} codeRef
()
0

NameWrite (!NAME)

The NameWrite node designates writing one source object to a selected local or visible field by source-text name. The lookup, destination, and view rules are in Names, lookup, and overloads, Selection and destinations, and Locations, references, and views. It pops one source object and writes it to the newest local or visible field selected by source-text name, but not to a builtin; a field write requires a mutable enclosing Ref, and static fields are invalid targets.

Depending on the source and target, NameWrite does one of the following:

Known issue: a write through NameWrite or NameWriteMember accepts a Block of a different schema for a non-static Block field but leaves the original field unchanged; a Block field can hold only the Block created with it because a Block's schema is its source position; write the Block to a Code field to compile and store an exchangeable callable.

For static locals and Meta targets, the same schema and mutability rules still apply, but the update may stay entirely at compile time because there may be no runtime target storage to rewrite.

Assigning a Block to Code through NameWrite works as follows:

Writing does not call callable sources or targets automatically.

Normal Ref lifetime rules still apply. A write is invalid if it would make the stored Ref outlive its source object.

This example uses runtime output because the write effects are shown there directly. In the runtime output, the trailing FALSE and TRUE values are the results of isConst checks on the resulting references.

Example

"String" use

{} () {} [
  ordinaryLocal: 1;
  mutableReference: @ordinaryLocal;
  immutableReference: ordinaryLocal;

  2 !ordinaryLocal
  3 !mutableReference
  4 !immutableReference

  codeLocal: {} () {} codeRef;
  ["-- block compiled for code --" printCompilerMessage] !codeLocal # a non-NIL Code

  dict: {
    field: 5;
    refField: ordinaryLocal;
    method: [
      6 !field
      @ordinaryLocal !refField
      ("-- fields -- " field " " refField "\n") printList
    ];
  };

  @dict.method

  ("-- In-place local -- " ordinaryLocal "\n") printList
  ("-- mutable Ref target -- " mutableReference " " @mutableReference isConst "\n") printList
  ("-- immutable Ref target -- " immutableReference " " @immutableReference isConst "\n") printList
] "main" exportFunction

Expected Output During Compilation

-- block compiled for code --

Expected Output

-- fields -- 6 2
-- In-place local -- 2
-- mutable Ref target -- 3 FALSE
-- immutable Ref target -- 4 TRUE

NameWriteMember (.!NAME)

The NameWriteMember node designates writing one source object to a selected field by source-text name (see Structs). It pops one object whose dereferenced schema is Dict, selects the last field with the specified name, then pops one source object and writes it to that field. A successful write requires the popped object itself to be a mutable Ref to Dict.

If several fields with the same name are present, NameWriteMember selects the last one. Static fields are invalid write targets.

Source and destination rules are those of NameWrite; NameWriteMember differs only in selecting the field through a mutable Ref to Dict popped from the stack.

For Meta fields, the same schema and mutability rules still apply, but the update may stay entirely at compile time because there may be no runtime field storage to rewrite.

Compiling a Block into a Code field follows the NameWrite Block-to-Code rules; here the selected field’s Code schema supplies the signature and the same capture and lifetime restrictions apply.

Writing does not call callable sources or targets automatically.

Normal Ref lifetime rules still apply. A write is invalid if it would make the stored Ref outlive its source object.

This example uses runtime output because the write effects are shown there directly. In the runtime output, the trailing FALSE and TRUE values are the results of isConst checks on the resulting references.

Example

"String" use

{} () {} [
  ordinaryLocal: 1;

  dict: {
    ordinaryField: 2;
    mutableReferenceField: @ordinaryLocal;
    immutableReferenceField: ordinaryLocal;
    codeField: {} () {} codeRef;
  };

  3 @dict.!ordinaryField
  4 @dict.!mutableReferenceField
  5 @dict.!immutableReferenceField
  ["-- block compiled for code field --" printCompilerMessage] @dict.!codeField

  ("-- In-place field -- " dict.ordinaryField "\n") printList
  ("-- mutable Ref field -- " dict.mutableReferenceField " " @dict.@mutableReferenceField isConst "\n") printList
  ("-- immutable Ref field -- " dict.immutableReferenceField " " @dict.@immutableReferenceField isConst "\n") printList
] "main" exportFunction

Expected Output During Compilation

-- block compiled for code field --

Expected Output

-- In-place field -- 3
-- mutable Ref field -- 4 FALSE
-- immutable Ref field -- 5 TRUE

Nat8, Nat16, Nat32, Nat64, and Natx

The Nat8, Nat16, Nat32, Nat64, and Natx nodes push natural objects onto the data stack. Natural literals are zero or positive. A negative literal with a natural suffix is invalid. Natural literals always require a suffix. A literal is invalid if its value does not fit the selected schema. For Natx, this check uses pointer-width limits.

Decimal

A decimal natural literal is written as digits. The suffix must immediately follow the digits. Decimal natural literals cannot have leading zeroes except for 0 itself. The following suffix forms are available:

Hexadecimal

A hexadecimal natural literal begins with exactly 0x and may use digits 0 to 9 and uppercase letters A to F. The suffix must immediately follow the hexadecimal digits. The following suffix forms are available:

Literal syntax is case-sensitive: use exactly 0x for hexadecimal notation, lowercase suffixes such as n8 and nx, and uppercase hexadecimal letters A to F.

The example below uses only fixed-width forms. Natx is pointer-width, so its exact limits are not shown there.

Example

{} () {} [
  "-- decimal examples --" printCompilerMessage
  0n8 printStack _:;
  65535n16 printStack _:;
  4294967295n32 printStack _:;
  18446744073709551615n64 printStack _:;

  "-- hexadecimal examples --" printCompilerMessage
  0xFFn8 printStack _:;
  0xFFFFn16 printStack _:;
  0xFFFFFFFFn32 printStack _:;
  0xFFFFFFFFFFFFFFFFn64 printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- decimal examples --
0n8
65535n16
4294967295n32
18446744073709551615n64
-- hexadecimal examples --
255n8
65535n16
4294967295n32
18446744073709551615n64

Real32 and Real64

The Real32 and Real64 nodes push real objects onto the data stack. Real objects follow the IEEE 754 floating-point standard.

A real literal may begin with -. It then contains decimal digits for the integer part, followed by a fractional part (a decimal point and decimal digits), an exponent, or both in that order. An exponent begins with the lowercase e, followed by an optional + or - and decimal digits; 1E2 is a parse error: "Number has hex digit «E» in decimal integer". If a suffix is present, it must immediately follow the last digit. If no suffix is provided, the literal is Real64. The integer part and exponent cannot have leading zeroes except for 0 itself.

The magnitude of a written exponent is limited to 308 for either sign; a larger exponent is a parse error. Conversion can also reject a lexically valid literal whose value is out of range for its width, with Real number is malformed: 9e308, 3.5e38r32, and 1e-46r32 are invalid, while 1.7976931348623157e308 and 1e-45r32 are valid. See Grammar tokens.

Suffixes

Literal syntax is case-sensitive: use lowercase e for the exponent and lowercase suffixes r32 and r64.

When formatted, Real64 objects are shown without the optional r64 suffix.

Example

{} () {} [
  "-- real32 --" printCompilerMessage
  5.43r32 printStack _:;
  5.43e21r32 printStack _:;
  5.43e-21r32 printStack _:;
  -5.43r32 printStack _:;
  -5.43e21r32 printStack _:;
  -5.43e-21r32 printStack _:;

  "-- real64 default --" printCompilerMessage
  5.43 printStack _:;
  5.43e21 printStack _:;
  5.43e-21 printStack _:;
  -5.43 printStack _:;
  -5.43e21 printStack _:;
  -5.43e-21 printStack _:;

  "-- real64 suffixed --" printCompilerMessage
  5.43r64 printStack _:;
  5.43e21r64 printStack _:;
  5.43e-21r64 printStack _:;
  -5.43r64 printStack _:;
  -5.43e21r64 printStack _:;
  -5.43e-21r64 printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- real32 --
5.43r32
5.43e21r32
5.43e-21r32
-5.43r32
-5.43e21r32
-5.43e-21r32
-- real64 default --
5.43
5.43e21
5.43e-21
-5.43
-5.43e21
-5.43e-21
-- real64 suffixed --
5.43
5.43e21
5.43e-21
-5.43
-5.43e21
-5.43e-21

Text ("TEXT" / «TEXT»)

The Text node pushes a Text object onto the data stack. A Text object is a pair of a size (the count of UTF-8 code units, that is bytes) and an immutable Ref to those code units. Text literals always produce known Text objects. Unknown Text objects have the same schema, but the compiler does not know that pair at compile time. They arise, for example, when text comes from a local or field whose contents are not known at compile time. Reading text through NameRead or NameReadMember may therefore produce a Text object whose size and Ref are not known at compile time.

Literal Forms

Syntax Closing Rule Nesting Escapes
"TEXT" Ends at the next unescaped " No Backslash escape sequences
«TEXT» Ends at the matching outermost » Yes, by matching unescaped inner « ... » pairs Backslash escape sequences

Both forms may span multiple source lines.

In particular, \« and \» in «TEXT» form allow literal guillemets without affecting nesting.

Escape Sequences

In either form, a backslash (\) introduces an escape sequence. The escape sequence itself does not appear in the final string; instead, it encodes one or more UTF-8 code units (bytes). The predefined escape sequences are listed below.

Escape Produced UTF-8 Code Units
\n 0A
\r 0D
\" 22
\\ 5C
\« C2 AB
\» C2 BB

Alternatively, an escape sequence may continue with one to four pairs of hexadecimal digits that directly encode the UTF-8 code units of one character. Escape syntax is case-sensitive, and the pairs use digits 0 to 9 and uppercase letters A to F. The resulting byte sequence must match one of the following valid UTF-8 patterns:

Code Unit 1 Code Unit 2 Code Unit 3 Code Unit 4
00..7F
C2..DF 80..BF
E0 A0..BF 80..BF
E1..EC 80..BF 80..BF
ED 80..9F 80..BF
EE..EF 80..BF 80..BF
F0 90..BF 80..BF 80..BF
F1..F3 80..BF 80..BF 80..BF
F4 80..8F 80..BF 80..BF

The expected output below shows the formatter’s canonical representation of Text objects. In particular, both literal forms are printed as quoted text by printStack.

Example

{} () {} [
  "-- quoted --" printCompilerMessage
  "Hi" printStack _:;
  "\«Hi\»" printStack _:;
  "\E4BDA0\E5A5BD" printStack _:;

  "-- guillemet --" printCompilerMessage
  «Hi» printStack _:;
  ««Hi»» printStack _:;
  «\«Hi\»» printStack _:;

  "-- equality --" printCompilerMessage
  "你好" «你好» = printStack _:;
  "\n" «\n» = printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- quoted --
"Hi"
"«Hi»"
"你好"
-- guillemet --
"Hi"
"«Hi»"
"«Hi»"
-- equality --
TRUE
TRUE

Tuple & TupleEnd ((...))

The Tuple node begins a nested scope in which nodes are processed and remaining stack items are collected (see Structs). The matching TupleEnd node ends that scope, constructs the resulting Struct object from those remaining stack items, and pushes it onto the data stack. Inner locals do not remain available by their local names after the scope ends. Tuple constructs can be nested, and each matching ( ... ) pair produces its own Struct object.

TupleEnd collects remaining stack items, not created locals. Locals created between ( and ) do not become part of the result unless they are explicitly left on the data stack.

The collected items become unnamed fields after the scope's output-lifetime checks: a reference to a whole local created inside the Tuple scope is replaced by that local object, moved out of the scope; a reference to part of such a local is invalid. References to outer-scope objects remain references. The resulting fields retain the schemas of the items after this step.

Depending on the collected items, TupleEnd does one of the following:

Creating the resulting object does not call callable items automatically.

The expected output below shows the formatter’s canonical representation. In particular, a homogeneous unknown result is shown as Type Count array rather than reconstructed tuple syntax.

Example

{} () {} [
  "-- empty --" printCompilerMessage
  () printStack _:;

  "-- homogeneous known items --" printCompilerMessage
  (1 2 3) printStack _:;

  "-- homogeneous unknown items --" printCompilerMessage
  (0 dynamic 0 dynamic) printStack _:;

  "-- heterogeneous items --" printCompilerMessage
  (1 FALSE) printStack _:;

  "-- local used inside --" printCompilerMessage
  (value: 1; @value 2 +) printStack _:;

  "-- nested --" printCompilerMessage
  ((1 2) (3 FALSE)) printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- empty --
()
-- homogeneous known items --
(1 2 3)
-- homogeneous unknown items --
Int32 2 array
-- heterogeneous items --
(1 FALSE)
-- local used inside --
(3)
-- nested --
((1 2) (3 FALSE))

Label & Variable (NAME: ...;)

The Label node pushes a name onto the name stack (see Data stack and name stack). The Variable node pops a name from that stack and one source object from the data stack, then creates a local in the current scope. The created local becomes available for name lookup in that scope and nested inner scopes until the scope ends.

If several labels are pending, Variable uses the topmost one first. Starting from an empty data stack, first: second: 1 2; ; is valid: second consumes 2, then first consumes 1.

The builtins overload, private, and virtual modify the next local created by ; or by the builtin def:

After a successful local creation, those specifier effects are cleared.

; is invalid if the name stack is empty or if no source object is available on the data stack. At scope end, any unused labels or unused specifiers are also invalid.

Example

{} () {} [
  1
  2
  first:
    second:
      third: 3;
    ;
  ;

  virtual staticValue: 4;

  "-- first --" printCompilerMessage
  first printStack _:;

  "-- second --" printCompilerMessage
  second printStack _:;

  "-- third --" printCompilerMessage
  third printStack _:;

  "-- static --" printCompilerMessage
  staticValue printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- first --
1 Cref
-- second --
2 Cref
-- third --
3 Cref
-- static --
4