Nodes

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

Example output sections record only MPL-level output. Module-loading wrapper lines and lines such as <BASE> or Compilation succeeded are omitted.

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 value 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 value 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).
INT.FRACT[EXP]r32 Real32 Pushes a Real32 object (a 32-bit real).
INT.FRACT[EXP][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: it carries compile-time information only and does not by itself occupy runtime storage. Its schema includes the enclosed AST nodes and source location, so two Blocks written with identical text at different source positions still have different schemas and therefore cannot be interchanged or used where the same Block schema is required. 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. 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

  ["-- 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. 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, and if all created locals have empty names, the final object is a List or Tuple whose items are unnamed. They can be created either with NAME:; or with the builtin 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.

A field created from a local made static with the builtin virtual becomes a static field. Its value is packed into the resulting object’s schema instead of occupying runtime field storage, so it must be known at compile time. Later operations therefore treat static fields differently from non-static fields.

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

Creating the resulting object does not call callable fields automatically. Special field names such as ASSIGN, CALL, DIE, INIT, and PRE only affect later behavior when the object is used by other nodes or builtins.

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 values 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 limits --" printCompilerMessage
  -128i8 printStack _:;
  127i8 printStack _:;
  -32768i16 printStack _:;
  32767i16 printStack _:;
  -2147483648 printStack _:;
  2147483647 printStack _:;
  -2147483648i32 printStack _:;
  2147483647i32 printStack _:;
  -9223372036854775808i64 printStack _:;
  9223372036854775807i64 printStack _:;

  "-- hexadecimal limits --" printCompilerMessage
  -0x80i8 printStack _:;
  0x7Fi8 printStack _:;
  -0x8000i16 printStack _:;
  0x7FFFi16 printStack _:;
  -0x80000000 printStack _:;
  0x7FFFFFFF printStack _:;
  -0x80000000i32 printStack _:;
  0x7FFFFFFFi32 printStack _:;
  -0x8000000000000000i64 printStack _:;
  0x7FFFFFFFFFFFFFFFi64 printStack _:;
] "main" exportFunction

Expected Output During Compilation

-- decimal limits --
-128i8
127i8
-32768i16
32767i16
-2147483648
2147483647
-2147483648
2147483647
-9223372036854775808i64
9223372036854775807i64
-- hexadecimal limits --
-128i8
127i8
-32768i16
32767i16
-2147483648
2147483647
-2147483648
2147483647
-9223372036854775808i64
9223372036854775807i64

Name (NAME)

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

Candidates are tried from newest to oldest. Visible Dict fields follow the same order. A callable Dict candidate with a PRE field is selected only if that PRE logic is compilable and produces a known TRUE. If that PRE logic is not compilable or produces a known FALSE, lookup continues with older candidates. 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.

Depending on what the selected entity is, Name does one of the following:

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

Example

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

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

  codeLocal: {} () {} codeRef;
  ["-- code local --" printCompilerMessage] !codeLocal
  codeLocal

  textLocal: 0 ("") dynamic @;
  "-- 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. It pops either a bare Dict value 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. If that PRE logic is not compilable or produces a known FALSE, selection continues with older fields. 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.

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:

Example

{} () {} [
  dict: {
    ordinaryField: 0 dynamic;
    textField:     0 ("") dynamic @;
    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.

Depending on what the name resolves to, NameRead does one of the following:

For visible fields, mutability depends on how the enclosing Dict was reached. A non-static In-place field produces a mutable Ref when that Dict was reached mutably and an immutable Ref otherwise. A non-static Ref field likewise yields a mutable or immutable copied Ref according to the access path.

Example

{} () {} [
  ordinaryLocal: 0 dynamic;
  textLocal:     0 ("") dynamic @;
  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. It pops either a bare Dict value 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 @;
    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. It pops one source object from the data stack and writes it to either (1) a local accessible through scope lookup rules, or (2) a field within a Dict that is accessible through scope lookup. Attempting to write to a builtin is invalid.

If several entities with the same name are available, NameWrite selects the newest one. Visible Dict fields follow the same newest-to-oldest rule. Writing to a field requires the enclosing Dict to be reachable through a mutable Ref. Static fields are invalid write targets.

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

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

  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. It pops one value 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 value itself to be a mutable Ref to Dict.

If several fields with the same name are present, NameWriteMember selects the last one. The popped value must itself be a mutable Ref to Dict. Static fields are invalid write targets.

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

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.

Assigning a Block to Code through NameWriteMember 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;

  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 limits --" printCompilerMessage
  0n8 printStack _:;
  255n8 printStack _:;
  0n16 printStack _:;
  65535n16 printStack _:;
  0n32 printStack _:;
  4294967295n32 printStack _:;
  0n64 printStack _:;
  18446744073709551615n64 printStack _:;

  "-- hexadecimal limits --" printCompilerMessage
  0x0n8 printStack _:;
  0xFFn8 printStack _:;
  0x0n16 printStack _:;
  0xFFFFn16 printStack _:;
  0x0n32 printStack _:;
  0xFFFFFFFFn32 printStack _:;
  0x0n64 printStack _:;
  0xFFFFFFFFFFFFFFFFn64 printStack _:;
] "main" exportFunction

Expected Output During Compilation

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

Real32 and Real64

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

A real literal may begin with -. It then contains decimal digits for the integer part, a decimal point, and decimal digits for the fractional part. It may also contain an exponent introduced by e, followed by an optional + or - and decimal digits. 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.

Suffixes

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

When formatted, Real64 values 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 (Code Unit count) and an immutable Ref to the UTF-8 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 Terminator Character Code Point Produced UTF-8 Code Units
\n 0x6E 0A
\r 0x72 0D
\" 0x22 22
\\ 0x5C 5C
0x00AB C2 AB
0x00BB 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 values. 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. 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 their values are explicitly left on the data stack.

The collected items become unnamed items of the resulting Struct object. They keep their schemas and storage properties.

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. 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. Chains such as first: second: 2; ; are valid.

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