Tetral Language Specification

Technical Specification for the Tetral Language

Table Of Contents

1.Introduction1.1.Core Principles1.2.Other Principles2.Scope3.Concepts4.Data Types4.1.Type Categories4.2.The Boolean Type4.3.Integral Types4.4.Floating Point Types4.5.Array Types4.6.The String Type4.7.Struct Types4.8.Enum Types4.9.Variant Types4.10.Interface Types4.11.Class Types4.12.Nullables4.13.Type Compatibility And Assignability4.14.Type Conversions4.14.1.Implicit Conversions4.14.2.Explicit Conversions4.14.3.Boolean Conversion4.15.Type Parameters4.16.Type Comparability5.Limits6.Language6.1.Identifier Semantics6.2.Comparison Semantics6.2.1.Arithmetic Types6.2.2.Array And String Types6.2.3.Struct, Variant and Class Types6.2.4.Enum Types6.2.5.Nullable Types6.3.Equality Semantics6.3.1.Arithmetic/Boolean Types6.3.2.Array And String Types6.3.3.Struct Types6.3.4.Class/Interface Types6.3.5.Enum Types6.3.6.Variant Types6.3.7.Nullable Types6.4.Overflow/Underflow Semantics6.5.Default Initialization6.6.String Conversion Semantics6.6.1.Booleans6.6.2.Arithmetic Types6.6.3.Arrays6.6.4.Structs and Classes6.6.5.Variant Types6.6.6.Enum Types6.6.7.Nullable Type6.7.Notation6.8.Lexical Elements6.8.1.Keywords6.8.2.Identifiers6.8.3.Constants6.8.3.1.Integer Constants6.8.3.2.Floating Point Constants6.8.3.3.Character Constants6.8.3.4.Predefined Constants6.8.4.Operators6.8.5.Semicolons6.8.6.Comments6.9.Expressions6.9.1.Primary Expressions6.9.1.1.New Expression6.9.1.2.'Super' Expression6.9.1.3.'This' Expression6.9.1.4.Cast Expression6.9.1.5.Object Literals6.9.1.6.Array Literals6.9.1.7.String Literals6.9.2.Trail Expressions6.9.3.Index Access Expression6.9.4.Property Access Expression6.9.5.Function Call Expression6.9.6.Coalescence Expression6.9.7.Unary Expressions6.9.7.1.Unary Increment Expressions6.9.7.2.Unary Decrement Expressions6.9.7.3.Unary Arithmetic Expressions6.9.7.4.Unary Bitwise Negation Expressions6.9.7.5.Unary Logical Negation Expressions6.9.8.Power (POW) Expression6.9.9.Multiplicative Expressions6.9.10.Additive Expressions6.9.11.Shift Expressions6.9.12.Relational Expressions6.9.13.Equality Expressions6.9.14.XOR Expressions6.9.15.Bitwise AND Expressions6.9.16.Bitwise OR Expressions6.9.17.Logical AND Expressions6.9.18.Logical OR Expressions6.9.19.Conditional Expression6.9.20.Assignment Expression6.9.21.Top Level Expression6.9.22.Loop Condition Expression6.10.Type Expressions6.10.1.Const Type Expressions6.10.2.Type Name Expressions6.10.3.Parameterized Type Expressions6.10.4.Nullable Type Expressions6.10.5.Array Type Expressions6.11.Declarations6.11.1.Declaration Modifiers6.11.2.Type Parameter Lists6.11.3.Module Declarations6.11.4.Import Declarations6.11.5.Function Declarations6.11.6.Extension Function Declarations6.11.7.Variable Declarations6.11.8.Struct Declarations6.11.9.Enum Declarations6.11.10.Variant Declarations6.11.11.Interface Declarations6.11.12.Class Declarations6.11.12.1.Class Fields6.11.12.2.Class Constructors6.11.12.3.Class Methods6.11.12.4.Class Access Modifiers6.12.Statements6.12.1.Block Statements6.12.2.Loop Flow Control Statements6.12.3.If Statements6.12.4.Labeled Statements6.12.5.For Loop Statements6.12.6.For-Each Loop Statement6.12.7.Do While Loop Statements6.12.8.Regular While Loop Statements6.12.9.Return Statements6.12.10.Assert Statements6.13.Translation Unit7.Intermediate Representation (Bytecode Format)7.1.File Header7.1.1.The STRTABLE Entry7.1.2.The STRDATA Entry7.1.3.The TYPETABLE Entry7.1.4.The FUNCTABLE Entry7.1.5.The INSTRBLOCK Entry7.1.6.The RDATA Entry7.1.7.The MDATA Entry7.1.8.The ADATA Entry7.2.String Pool Table7.3.String Pool Data7.4.Type Table7.4.1.Arrays, Nullable Definitions and Reference Definitions7.4.2.Function Signatures7.4.3.Structs7.4.4.Enums7.4.5.Interfaces7.4.6.Classes7.4.7.Parameter List Instantiation7.4.8.Imported Type7.5.Function Table7.6.Instructions Block7.7.Data Block7.8.Import List7.9.Export Table8.Bytecode Instructions8.1.General Purpose Instructions8.2.Stack Memory Instructions8.3.Global Memory Instructions8.4.Closure Instructions8.5.Heap Memory Instructions8.6.Function Call Instructions8.7.Foreign Symbol Instructions8.8.Conversion Instructions8.9.Unary Instructions8.10.Binary Instructions8.10.1.Integer-only Binary Instructions8.10.2.Boolean-only Binary Instructions8.10.3.Arithmetic Binary Instructions8.10.4.Comparison Instructions8.10.5.String/Array Instructions9.Parsing and Compilation10.Interpreter11.The Standard Library11.1.The std Module11.2.The std::strings Module11.3.The std::math Module11.4.The std::io Module11.5.The std::reflection Module11.6.The std::json Module12.Compiler Command Line Interface12.1.Commands12.1.1.The compile Command12.1.2.The run Command12.1.3.The test Command12.1.4.The help Command12.1.5.The version Command12.2.Flags12.2.1.General Flags12.2.2.Compiler Flags12.2.3.Test Command Flags13.Annexes13.1.Annex A. Class Implementation Details13.2.Annex B. JSON Message Format13.3.Annex C. Error and Warning IDs13.4.Annex D. Tetral File Extensions

1.Introduction🔗

Tetral, from the Greek word for "four," is a scripting language intended to be interpreted to provide a platform-agnostic execution system and which is intended to be executed fast with little checks on the IR past the initial compilation.

1.1.Core Principles🔗

Additionally, Tetral is designed with the following 4 core principles in mind, hence the name.

Statically Typed
Types must be specified and known at compile-time to allow for the compiler to optimize as much as possible and to provide easy readability for programmers.
Compiled
Compiled to an intermediate representation (also referred to as a bytecode,) which can be executed on any platform as long as an interpreter exists for the platform.
Interpreted
Interpreted by a native interpreter which performs next to checks on the bytecode and executes it to ensure fast execution speed.
Platform-Agnostic
As long as an interpreter exists, it shouldn't matter how the IR was compiled, it should be possible to execute it.

1.2.Other Principles🔗

There are more principles this language was designed around, but they aren't as flashy.

No Memory Interaction
A programmer should never have to care about memory or be able to interact with raw memory.
Explicit Null Control

This idea is that object types, should never be null, unless a null value is explicitly allowed. For example, if a variable is declared of an object type, it should be initialized to a value, rather than null. And you shouldn't be able to assign a null value to a non-null type.

The exception for this are arrays and strings, which are null initialized. But null arrays and strings are treated as just a regular array that has a length of 0.

This only goes for object types, primitives are initialized to a zero value, while strings and arrays always null-initialized by default, but null arrays and strings are just treated as having a length of 0.

Reference Counted
No garbage collector, just reference counting of objects. When the reference count of an object reaches 0, it is freed and all its properties, if object types as well, have their reference counters decremented.

2.Scope🔗

This document defines the Tetral (Also referred to as TetralScript, or TetralLang) programming language and the Tetral Bytecode format.

3.Concepts🔗

Source File
A UTF-8 encoded text file that contains Tetral source code.
Bytecode/IR/Intermediate Representation
These terms will be used interchangeably in this document, but they all refer to the compiled representation of Tetral source code.
Program
Either a valid Tetral source file or a Tetral bytecode file that can be executed by the interpreter.
Symbol

A symbol is an object which can be referred to with a specified name. Examples of symbols include, but are not limited to:

  • Functions
  • Structs
  • Enums
  • Classes
  • Local Variables
  • Global Variables
  • Imported Symbols
Namespace
A namespace is a list of names delimited by :: characters.
Simple Name
A simple name is the name of a symbol in a source file with no namespace prefix.
Fully Qualified Name
A fully qualified name is the name of a symbol with a namespace prefixed to it.

4.Data Types🔗

A data type is a specific unit of memory that holds data in a specific layout to represent some piece of information.

4.1.Type Categories🔗

Values types are types whose value is stored on the stack and whose lifetime depends on the current stack scope.

Reference types are types which exist in the heap and the user only receives a reference to that type's data on the heap.

4.2.The Boolean Type🔗

The boolean type represents a false/true state.

4.3.Integral Types🔗

Integral types are arithmetic value types of varying sizes used to represent whole numbers. Integral types may be signed or unsigned.

Tetral provides the following integral types:

Table 1 - Tetral Integral Types
Type NameSize and alignmentDescriptionMinimum valueMaximum value
uint81Unsigned 8-bit integer0255
int81Signed 8-bit integer-128127
uint162Unsigned 16-bit integer065535
int162Signed 16-bit integer-3276832767
uint324Unsigned 32-bit integer04294967295
int324Signed 32-bit integer-21474836482147483647
uint648Unsigned 64-bit integer018446744073709551615
int648Signed 64-bit integer-92233720368547758089223372036854775807

4.4.Floating Point Types🔗

The floating-point types are arithmetic types which hold a number value according to the corresponding IEEE-745 standard format.

Table 2 - Tetral Floating Point Types
Type NameSize and alignmentDescriptionIEEE Format
float32432-bit floating point typebinary32
float64864-bit floating point typebinary64

4.5.Array Types🔗

Array types are reference types which hold a sequential list of a specific data type.

Each unit of data contained in an array is referred to as an "element," and the amount of items the array holds is called its "length."

An array's length cannot be changed after creation, but all items within an array are mutable unless declared otherwise by a user.

4.6.The String Type🔗

The String type is a reference type which holds a sequential list of UTF-8 code units.

The string type is immutable, none of its elements can be changed. The amount of UTF-8 code units stored in a string is called its "length."

4.7.Struct Types🔗

Struct types are value types made up of "properties," each of which has a name and a pre-set type, determined by the user.

Structs cannot hold any properties that directly reference itself.

4.8.Enum Types🔗

Enum types are value types that declare a list of named constant values with the first always starting at value 0 and each subsequent value having a value one higher than the last.

The name for the underlying value of an enum's named constant is its "ordinal" value.

4.9.Variant Types🔗

Variant types are a value type which define a list of variants the variant type itself can take the form of.

Each variant of a variant type can declare 0 or more properties, each of which has a name and a pre-set type.

The index of a variant in a variant type is referred to as its "ordinal" value.

Variant types can hold type parameters.

4.10.Interface Types🔗

An interface type is a reference type, but not a type which can itself be instantiated.

Interfaces declare 0 or more methods which must be defined by any class wishing to implement the interface.

Implementing an interface means declaring a class as being an implementer of an interface type and defining a function body for each of the methods the interface declares.

Interfaces can hold type parameters.

4.11.Class Types🔗

Class types are reference types which define class members. Class members are either constructors, methods or fields.

Methods are functions that exist within the namespace of a class. Methods are also divided into two separate types: static and non-static. Static methods belong to the class, while non-static methods belong to a class instance.

Class members can, like methods, be declared either static or non-static in which case the same rules for ownership apply.

Members are like the properties of structs and as such they are subject to the same alignment and padding behaviors as defined for struct properties.

Classes can extend one other class and can implement as many non-conflicting interfaces as described in the limits section (See 5. Limits)

Class types can hold type parameters.

4.12.Nullables🔗

A nullable type is a value type which marks another type as nullable, meaning its value may not exist.

4.13.Type Compatibility And Assignability🔗

Type compatibility determines if a type can be passed to a function, set as the value of a variable, a variant/struct property, or a class field. The following table only determines semantic compatibility, it does not list conversions that may or not need to be performed when assigning a value of one type to an object of another, compatible, type.

Table 3 - Type Compatibility Table
TypeCan be assigned from
StringsOnly other Strings.
ArraysOnly arrays of the same component type.
Unsigned IntegersAny unsigned integer of the same or smaller size.
Signed IntegersAny signed integer of the same or smaller size, or any unsigned integer of a smaller size.
Floating point numbersAny floating point of the same or smaller size, or any integer.
StructsOnly other values of the same struct type.
EnumsOnly other values of the same enum type.
VariantsOnly other values of the same variant type.
InterfacesAny class which implements the interface.
ClassesAny value of the same class or a derived class.
NullablesOnly values of the same nullable type.
ReferencesOnly values of the same reference type.

4.14.Type Conversions🔗

This section defines the conversion operations that must occur between types. If there is no conversion listed from one type to another in this section, then it means that nothing must be done to convert from one type to another, or that the conversion is not possible. See the previous section for type compatibility and assignability.

4.14.1.Implicit Conversions🔗

These conversions must be done implicitly without requiring an explicit cast operation by a user of the language. When the term "usual arithmetic conversions" is referenced, the following conversions are what they reference.

Table 4 - Implicit conversion operations
Source TypeTarget TypeConversion
Any signed or unsigned integral typeA larger signed or integral typeThe value is converted from the smaller to the equivalent value in the larger type.
Any signed or unsigned integral typeA floating point typeThe value is converted from the integral type the closest possible floating point value.
A 32-bit floating point valueA 64-bit floating point valueThe value is converted to the closest value possible in the larger type.

4.14.2.Explicit Conversions🔗

These conversions must be explicitly done by users by using a casting expression (Defined in the language grammar).

Table 5 - Explicit conversions
Source TypeTarget TypeConversion
Any integral typeAny integral type of smaller sizeThe resulting value is the larger integral's value reduced modulo 2N, where N is the number of value bits in the target type. The resulting bit pattern is interpreted according to the target type's signedness.
A 64-bit floating point valueA 32-bit floating point valueThe larger type's value is converted to the closest equivalent value of the smaller type.
Any floating point valueAny integral valueThe floating point value is first rounded down, then converted to the equivalent integral value.

4.14.3.Boolean Conversion🔗

The following table defines how values of different types are to be converted to boolean values. Unless it is stated that a type cannot be converted to a boolean value in the following table, then any value of that type is "boolean-assignable."

Table 6 - Boolean conversions
ValueBoolean Result
Boolean valueNo conversion needed.
Any arithmetic type0 values evaluate to false, non-zero values evaluate to true.
strings/arraysIf the array/string has a length of 0, evaluates to false. Otherwise, evaluates to true.
EnumsEnum values with the ordinal value 0 evaluate to false, while non-zero ordinal values evaluate to true.
VariantsVariants with the ordinal 0 evaluate to false, while other ordinals evaluate to true.
InterfacesCannot be evaluated as a boolean value.
Classes
Structs
NullablesEvaluate to a boolean value corresponding to the presence state of the nullable.

4.15.Type Parameters🔗

Type parameters allow for types to be specified with incomplete types, allowing them to be filled in later.

4.16.Type Comparability🔗

This section defines which types are compatible with each other. For comparison semantics see 6.2. Comparison Semantics. Thus, this section also defines the term "comparable type."

If a data type is not listed here, it cannot be compared to any other type.

5.Limits🔗

The following limits are defined for features and aspects of the Tetral Language.

Table 7 - Language Limits
AspectLimit
Maximum number of implemented interfaces16
Maximum number of function/method/constructor declaration arguments62
Maximum number of function/method/constructor call arguments62
Maximum number of enum constants per enum type1024
Maximum number of variants per variant type256
Maximum call stack depth512
Maximum number of nested functions8
Maximum length of an identifier256

6.Language🔗

Tetral files are written in UTF-8 encoded text files, referred to as "source files." They must follow the grammar defined in the following sections.

6.1.Identifier Semantics🔗

An Identifier can denote a function, a struct, an interface, an enum, a class, a local variable or a global variable. An identifier with an identical value can reference a different symbol at different points in a program.

A difference must be made between the identifiers used in primary-expressions and identifiers used in other contexts. In a primary expression, the identifier serves as a symbol that references a previously declared or imported symbol.

Symbol lookup is performed in ascending scopes from the current scopes. The order being: Local Scope, Current Function Scope, Global Scope.

6.2.Comparison Semantics🔗

This section details the semantics for comparing two values of specific types by considering two hypothetical operands: left and right.

6.2.1.Arithmetic Types🔗

Constraints
  1. The right operand must be assignable to the left operand's type.
Evaluation
  1. The usual arithmetic conversions are performed.
  2. If the left operand's value is lesser than the right operand's, then the right operand is greater.
  3. If the left operand's value is greater than the right operand's, then the left operand is greater.
  4. If both operands have an equal value, neither is greater.

6.2.2.Array And String Types🔗

Constraints
  1. Both operands must be arrays of the same, comparable type.
Evaluation
  1. If the left operand's length is 0, then the right operand is greater.
  2. If the right operand's length is 0, then the left operand is greater.
  3. If all elements in both operands are equal, neither operand is greater or lesser, they are equal.
  4. If the first non-equal value in the left operand is lesser than the equivalent value in the right operand, the right operand is greater.
  5. If the first non-equal value in the left operand is greater than the equivalent value in the right operand, the left operand is greater.

6.2.3.Struct, Variant and Class Types🔗

Constraints
  1. Struct, Variant and Class types cannot be compared.

6.2.4.Enum Types🔗

Constraints
  1. Both operands must be of the same enum type.
Evaluation
  1. Enum comparison is performed identically to arithmetic type comparison, with the ordinal value of both enums being used to compare them.

6.2.5.Nullable Types🔗

Constraints
  1. Both operands must hold the same type of value.
  2. Both operands must hold a comparable value.
Evaluation
  1. If bother operands have a presence state of false, they are equal and neither is greater.
  2. If either operand has a false presence state, that operand is lesser.
  3. The operands are then compared by their held value.

6.3.Equality Semantics🔗

This section details the semantics for checking the equality of two values of specific types by considering two hypothetical operands: left and right.

6.3.1.Arithmetic/Boolean Types🔗

Constraints
  1. The right operand must be assignable to the left operand's type.
Evaluation
  1. The usual arithmetic conversions are performed.
  2. If the values of both operands are equal, they are equal.

6.3.2.Array And String Types🔗

Constraints
  1. Both operands must be of the same type.
Evaluation
  1. If the operand's lengths differ, the operands are not equal.
  2. If all component values in both operands are equal, they are equal. Otherwise, they are not equal.

6.3.3.Struct Types🔗

Constraints
  1. Both operands must be of the same type.
Evaluation
  1. The value of each property of both operands are checked for equality.
  2. If all properties in both operands are equal, the operands are equal. Otherwise, they are not equal.

6.3.4.Class/Interface Types🔗

Constraints
  1. The right operand must be assignable to the left operand's type.
Evaluation
  1. If the operands are not of the same type, they are not equal.
  2. The value of each property of both operands are checked for equality.
  3. If all properties in both operands are equal, the operands are equal. Otherwise, they are not equal.

6.3.5.Enum Types🔗

Constraints
  1. Both operands must be of the same enum type.
Evaluation
  1. For two enum values to be equal, their ordinal values must be equal.

6.3.6.Variant Types🔗

Constraints
  1. Both operands must be of the same variant type.
Evaluation
  1. If the operands' variant ordinal values are not the same, they are not equal.
  2. Each variant property is then checked for equality in both operands.
  3. If all properties are equal, then the variant values are equal, otherwise they are not.

6.3.7.Nullable Types🔗

Constraints
  1. Both operands must hold the same type of value.
Evaluation
  1. If bother operands have a presence state of false, they are equal.
  2. If either operand has a false presence state, the operands are not equal.
  3. The operands then have their equality checked by their held value.

6.4.Overflow/Underflow Semantics🔗

Overflows and underflows happen when an arithmetic type's value exceeds its maximum representable value, or when its value subceeds it's minimum representable value.

In such cases, the value wraps around without any errors.

In cases where overflow or underflow is detectable by a compiler, the user should be warned about it.

6.5.Default Initialization🔗

Default initialization occurs when a variable or class/struct property is declared with no value.

Arithmetic types are initialized to the type's equivalent of the value 0. Booleans to the value false. Array, string, nullable reference types and nullable value-reference-types are initialized to null. Enumeration types are initialized to the enum value with the ordinal 0.

Variants are initialized to the variant with the ordinal 0. If the variant with value 0 requires arguments, then the type cannot be default-initialized.

Structs are initialized with all properties set to their default values as specified in the struct declaration.

If a type cannot be default initialized, such as in the case of a class with no no-argument constructors, variants where the 0 ordinal variant requires property values to be specified or an interface type. Then the declaration is considered invalid and a value must be explicitly set.

6.6.String Conversion Semantics🔗

This section details how certain types are converted to their string representations. The string data type itself is omitted, as it does not need to be converted to a string.

6.6.1.Booleans🔗

Booleans evaluate to a string with the value "true" or "false" depending on the value of the boolean.

6.6.2.Arithmetic Types🔗

Arithmetic types are converted to string representation of their numerical value in base 10.

For floating point, if required, scientific notation may be used.

6.6.3.Arrays🔗

Arrays evaluate to a comma-delimited string prefixed and suffixed with the "[" and "]" characters respectively.

Empty arrays evaluate to just "[]".

6.6.4.Structs and Classes🔗

Structs and classes evaluate to a string prefixed with the simple name of the type, followed by the "{" character.

Each property is then listed with its name, followed by a ": " sequence. After the delimiter, the property's value is converted to a string and appended. Each property's string representation is delimited with a "," string. If the property is a string, the string value must be prefixed and suffixed with double quotes.

Private fields on classes are included in the string.

6.6.5.Variant Types🔗

Variant types are prefixed with the simple name of the variant type, followed by a "." and the simple name of the variant.

If the variant has declared properties, then append "(" to the string and append a string representation of each property's string representation to the string, each property delimited with a ", " sequence. The property strings are then followed by a ")".

6.6.6.Enum Types🔗

Values of an enum type are converted to string by using the enum constant's name as the string representation.

6.6.7.Nullable Type🔗

If the value is a nullable-reference or a value-type-reference and the pointer is null, then the string representation becomes "null".

If the nullable's presence state is false, then the string representation is "none". Otherwise, the string representation is the present value's string representation.

6.7.Notation🔗

References to other syntactic rules are in italic, literal values are in bold. An italic syntactic rule followed by a colon (:) defines the rule. Alternative definitions of the rule are defined on new lines. Optional parts are described with the subscript "opt". And parts which may repeat and appear several times will be described with the suffix "rep".

Fig.1 - Basic Syntax Notation Example
( expression )

Indicates a syntax element enclosed with "()" which might contain an expression, or several expressions.

6.8.Lexical Elements🔗

keyword identifier constant string-literal operator
Lexical Analysis Rules
  1. Lexical analysis shall consume the largest sequence of source characters that constitutes a valid token.
  2. Tokens, outside of character or string literals, ignore all white space. White space is defined as any Unicode codepoint with the White_Space binary property.
  3. Any use of a literal (eg: const) in the definition of the language grammar is case-sensitive and thus must match the keyword exactly.
  4. No Unicode normalization is performed on the parsed tokens.

6.8.1.Keywords🔗

Syntax
real-keyword type-keyword alias-keyword contextual-keyword if else break continue return while for struct module import export do const assert native this override static protected public private constructor this super class enum struct interface variant cast bitcast extendable new from implements extends mutable readonly void uint8 bool int8 uint16 int16 uint32 int32 uint64 int64 float32 float64 string boolean byte ubyte char short ushort int uint long ulong float double
Lexical Analysis Rules
  1. When lexical analysis encounters a sequence which can be both a keyword or an identifier, then keywords take precedence over identifiers.
  2. After lexical analysis, alias keywords shall have the same syntactic meaning as their corresponding real keywords. For alias token mappings, see Table 8 - Alias token mappings.
  3. Contextual keywords are only keywords in specific contexts. Those contexts are defined in the grammar as they appear. Outside the context which will be defined, contextual-keywords's are identifiers.
Table 8 - Alias token mappings
Alias TokenMapped Token
booleanbool
byteint8
ubyteuint8
charuint8
shortint16
ushortuint16
intint32
uintuint32
longint64
ulonguint64

6.8.2.Identifiers🔗

Syntax
identifier-start identifier-continue nondigit XID_Start character (Defined by Unicode) digit nondigit XID_Continue character (Defined by Unicode) 0 1 2 3 4 5 6 7 8 9 _ $ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Constraints
  1. Identifiers must reference a valid symbol that is accessible in the current semantic context. (Lookup semantics defined in 6.1. Identifier Semantics.)
  2. An identifier cannot be longer than the defined maximum length of an identifier. (Defined in 5. Limits.)
Semantics
  1. A symbol lookup is performed as defined in 6.1. Identifier Semantics.
  2. The result of an identifier expression, is value of the symbol it is referencing.
  3. An identifier which references a valid semantic variable or a constant is a valid lvalue.

6.8.3.Constants🔗

Syntax
integer-constant floating-point-constant character-constant predefined-constant
6.8.3.1.Integer Constants🔗
Syntax
sign decimal-integer-constant sign octal-constant sign hexadecimal-constant sign binary-constant + - i I u U l L UL ul uL Ul b B ub UB uB Ub s S us US uS Us 0o octal-sequence integer-suffix 0O octal-sequence integer-suffix octal-digit octal-sequence underscore octal-digit 0 1 2 3 4 5 6 7 0x hexadecimal-sequence integer-suffix 0X hexadecimal-sequence integer-suffix hexadecimal-digit hexadecimal-sequence underscore hexadecimal-digit 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F 0b binary-sequence integer-suffix 0B binary-sequence integer-suffix binary-digit binary-sequence underscore binary-digit 0 1 digit-sequence integer-suffix digit digit-sequence underscore digit _
Constraints
  1. Unsuffixed integer constants cannot represent a value that exceeds the maximum value of an unsigned 64-bit integer.
  2. Suffixed integer constants cannot represent a value that exceeds the suffix specifier's type. (See Table 9 - Suffix type mappings.)
  3. Any integer constant with the value of -0 is invalid.
Semantics
  1. Unsuffixed integer constants result in one of three types of integral types based on the smallest type that can hold the constant value. (See Table 10 - Unsuffixed integer types.)
  2. Suffixed integer constants result in a value with the integer type specified by the suffix. (See Table 9 - Suffix type mappings.)
Table 9 - Suffix type mappings
SuffixMapped Type
b, Bint8
s, sint16
i, Iint32
l, Lint64
ub, UB, uB, Ubuint8
us, US, uS, Usuint16
ui, UI, uI, Uiuint32
ul, UL, uL, Uluint64
Table 10 - Unsuffixed integer types
Minimum ValueMaximum ValueResulting Type
-21474836482147483647int32
21474836484294967295uint32
42949672969223372036854775807int64
-9223372036854775808-2147483649
922337203685477580818446744073709551615uint64
6.8.3.2.Floating Point Constants🔗
Syntax
sign fractional-part exponent-part floating-point-suffix digit-sequence digit-sequence . digit-sequence digit-sequence . . digit-sequence e sign digit-sequence E sign digit-sequence f F d D
Constraints
  1. An unsuffixed floating point constant must contain a valid floating point value that can be represented as a 64-bit floating point value.
  2. A suffixed floating point constant must contain a valid floating value that can be represented by the type specified by the suffix. (See Table 11 - Floating point suffix type table.)
  3. Hexadecimal/Octal/Binary floating point numbers, are explicitly not supported in Tetral grammar.
Semantics
  1. The result of an unsuffixed floating point constant is a 64-bit floating point number.
  2. The result of a suffixed floating point constant is a floating point value of the type matching the suffix. (See Table 11 - Floating point suffix type table.)
Table 11 - Floating point suffix type table
SuffixesType
f, Ffloat32
d, Dfloat64
6.8.3.3.Character Constants🔗
Syntax
' char-sequence ' Any valid UTF-8 character except the single-quote, backslash or newline character escape-sequence simple-escape-sequence hex-escape-sequence \' \" \\ \t \r \n \T \R \N \$ \u hexadecimal-digit \U hexadecimal-digit
Constraints
  1. A character constant's contained codepoint cannot be larger than one which can be encoded in a single UTF-8 byte.
Semantics
  1. Character constants result in a single unsigned 8-bit integer, equal to the character constant's UTF-8 encoded byte value.
  2. An escaped sequence leads to the escaped character's value being used. See the table below for escaped character values.
Table 12 - String/Character escape sequence values.
Escape SequenceResulting codepoint name.Unicode codepoint.
\'Single quote characterU+0027
\"Double quote characterU+0022
\\Slash characterU+002F
\t, \TTabulation characterU+0009
\n, \NLinefeed characterU+000A
\r, \RCarriage Return characterU+000D
\$Dollar Sign characterU+0024
6.8.3.4.Predefined Constants🔗
Syntax
true false null nan inf -inf +inf
Semantics
  1. The result of the true and false keywords are a boolean value of the corresponding value.
  2. The result of the null value is a pointer with a null value. A null value can be implicitly cast to any nullable reference type or any nullable value-type-reference type. The null value can also be assigned to any type marked as nullable with the ? keyword.
  3. The result of the nan keyword is a 32-bit floating point value with the special Not-A-Number value.
  4. The result of the inf, -inf and +inf keywords are a 32-bit floating point value with an infinite value of the corresponding signedness. If no sign is specified, then the result the same as with the + sign.

6.8.4.Operators🔗

Syntax
( ) { } [ ] : ; , . ... & &= && &&= | |= * *= / /= + += - -= ^ ^= % %= << <<= >> >>= < <= > >= ++ -- == != = ! ~ => ** **= || ||= ? ?? ??= ?. !.

6.8.5.Semicolons🔗

Syntax
;
Lexical Analysis Rules
  1. The semicolon as a marker for the end of an expression or for a statement is completely optional and need not be used unless there may be ambiguity in the syntax.
  2. Certain syntactic elements (For example, the for-loop-statement) explicitly use semicolons to delimit parts of the grammar. In such cases the semicolon is required.

6.8.6.Comments🔗

Except within a string or character literal, the characters // and # denote the beginning of a line comment. The comment consumes the rest of the source file's line, or until the end of input has been reached.

Except within a string or character literal, the characters /* denote the beginning of a block comment. The comment consumes input until a matching */ sequence has been found, or until the end of input has been reached.

Except within a string or character literal, the characters /** denote the beginning of a documentation comment. The comment consumes input until a matching */ sequence has been found, or until the end of input has been reached.

In almost all cases, comments are simply skipped over and ignored, except for documentation comments (Starting with /**) which are linked to the specific declaration that they prefix.

6.9.Expressions🔗

6.9.1.Primary Expressions🔗

Syntax
constant identifier object-literal array-literal string-literal cast-expression super-expression this-expression new-expression ( expression )
6.9.1.1.New Expression🔗
Syntax
object-instantiation-expression array-instantiation-expression new function-call new type-name-expression function-call new type-expression [ expression ] new type-expression [ expression ] function-call
Constraints
  1. New expressions can only be used to instantiate structs, classes and arrays.
Semantics
  1. Instantiates a new instance of a specific type.
  2. If the type name was not specified, the type must be inferred from the surrounding context.
Object Instantiation
Constraints
  1. The specified arguments must match a constructor that exists on the type.
Semantics
  1. Instantiates an object by calling the appropriate constructor.
Array Instantiation
Constraints
  1. The specified expression must result in an unsigned 32-bit integer, this expression's result will be referred to as the instantiated array's length.
  2. The array type must be default initialize-able.
Semantics
  1. Instantiates an array of the defined length and initializes each value in the created array to the array type's default initialized value.
  2. If the specified length of the array is 0, then the array is not allocated and is instead assigned to a null pointer, aka, an empty array.
6.9.1.2.'Super' Expression🔗
Syntax
super
Constraints
  1. May only be used inside a class or interface method declaration or a class constructor declaration.
Semantics
  1. The result of the expression is a pointer to the this object's parent object in the current context.
6.9.1.3.'This' Expression🔗
Syntax
this
Constraints
  1. May only be used inside a class, struct or interface method declaration or a class constructor declaration.
Semantics
  1. The result of the expression is a reference to the this object in the semantic context in which it used.
6.9.1.4.Cast Expression🔗
Syntax
value-cast-expression bit-cast-expression cast ( type-expression , expression ) bitcast ( type-expression , expression )
Constraints
  1. Cast expressions can only cast between types which have a defined implicit or explicit conversion. (See 4.14. Type Conversions.)
  2. Bit-casting can only be performed on arithmetic types.
  3. Bit-casting can only cast from one type to another type of the same size.
Semantics
  1. Casts a value returned by an expression to a different type.
  2. Regular casting operations are defined in 4.14. Type Conversions.
  3. Bit-casing only treats the expression's value as a different type, no conversion operations are performed.
6.9.1.5.Object Literals🔗
Syntax
{ object-literal-property-list } object-literal-property object-literal-property-list , object-literal-property identifier : expression
Constraints
  1. If an object literal's struct type cannot be inferred from the surrounding context, then the expression is invalid.
  2. An object literal may only initialize properties that exist on the struct type.
  3. An object literal may not declare any duplicate properties.
Semantics
  1. The type of struct initialized by an object literal must be inferred from the surrounding context.
  2. Semantically, an object literal is identical to constructing the struct type and setting each property separately.
6.9.1.6.Array Literals🔗
Syntax
[ array-literal-values ] expression array-literal-values , expression
6.9.1.7.String Literals🔗
Syntax
line-string-literal multiline-string-literal string-literal line-string-literal string-literal multiline-string-literal " line-string-content " line-string-character line-string-content line-string-character line-string-content string-template """ multiline-string-content """ multiline-string-character multiline-string-content multiline-string-character multiline-string-content string-template $identifier ${expression} Any valid UTF-8 character except ", \, $ or newline character. escape-sequence Any valid UTF-8 character except """, \ or $. escape-sequence
Constraints
  1. All expressions and identifiers declared inside a string must return a non-void value.
Semantics
  1. A string literal will always result in a value of a string type.
  2. It can be assumed that each line in a multiline string will have some level of indentation. After parsing, this indentation must be stripped from the start of each line. Since indentation can vary from line to line, the amount indentation removed depends on the line with the least indentation.
  3. The template expressions $identifier and ${expression} with an identifier as the expression are semantically identical.
  4. For templated strings, each templated expression is evaluated in declaration order and appended to the resulting string. (See 6.6. String Conversion Semantics.)
  5. Semantically, a templated string literal is identical to declaring each part of the string as its own separate expression and concatenating it together.

6.9.2.Trail Expressions🔗

Syntax
primary-expression property-access-expression index-access-expression function-call-expression coalescence-expression

6.9.3.Index Access Expression🔗

Syntax
trail-expression [ expression ]
Constraints
  1. The type returned by the target trail-expression must be an indexable type. (See Table 13 - Type Indexing)
  2. The indexing expression must be a type used to index into the target expression's type. (See Table 13 - Type Indexing)
  3. If the type being accessed is a string, then the index may not be written to.
Semantics
  1. Accesses an indexable element in an indexable value.
  2. A valid index access expression is a valid lvalue.
  3. Accessing a non-readonly array is a valid mutable lvalue.
Runtime Errors
  1. It is a runtime error if the index being accessed is beyond the length of the indexed operand.
Table 13 - Type Indexing
TypeIndexing TypeValue type returned by index access
String Typeuint32Single UTF-8 byte, (uint8)
Any Array Typeuint32The array's component type

6.9.4.Property Access Expression🔗

Syntax
trail-expression . identifier trail-expression ?. identifier trail-expression !. identifier
Constraints
  1. The property being accessed must exist on the object operand.
  2. A property access must not violate class/interface access semantics.
  3. The . operator cannot be used on the object operand. Either the !. or ?. operator must be used by the user to acknowledge the nullability value.
Semantics
  1. The result of the . and !. operator is a copy of the value of the property or virtual property being accessed.
  2. The result of the ?. operator is a nullable copy of the value of the property or virtual property being accessed. If the object operand is an empty nullable, then the resulting value is an empty nullable.
  3. Accessing a property of a nullable type with the ?. operator causes the nullable to be flattened.
  4. A valid property access expression is a valid lvalue.
Runtime Errors
  1. It is a runtime error if the object operand is an empty nullable value.

6.9.5.Function Call Expression🔗

Syntax
trail-expression function-call type-parameter-instatiation ( arguments-list ) expression arguments-list , expression
Constraints
  1. The value returned by the target trail-expression must be a function.
  2. All arguments passed to the function must be assignable to that function's declared arguments.
Semantics
  1. Invokes a function with the specified arguments.
  2. Calling arguments are evaluated from left to right.
  3. The value returned by the expression is dependent on the return type of the function being invoked.
  4. When the trail-expression matches multiple overloading functions, the one that would require the least number of type conversions is selected.

6.9.6.Coalescence Expression🔗

Syntax
trail-expression trail-expression ?? expression
Constraints
  1. The left operand must be a nullable value.
  2. The right operand must be assignable to the left operand, or be a non-nullable value of an assignable type to the left operand.
Semantics
  1. The result of the ?? operator is the left operand if it is present. Otherwise, the result of the operator is the right operand.
  2. If the left operand is returned, the right operand must not be evaluated.

6.9.7.Unary Expressions🔗

Syntax
trail-expression unary-increment-expression unary-decrement-expression unary-arithmetic-expression unary-bitwise-negation-expression unary-logical-negation-expression
6.9.7.1.Unary Increment Expressions🔗
Syntax
++ unary-expression unary-expression ++
Constraints
  1. The operand must be a valid lvalue that can be modified.
  2. The operand must be of an integral or floating point type.
Semantics
  1. If the increment operator is placed before the operand, the result is the value of the operand incremented by 1. As a side effect, the resulting value is written to the operand object.
  2. If the increment operator is placed after the operand, the result is the value of the operand. As a side effect, the operand's value is incremented by 1 and written to the operand object.
6.9.7.2.Unary Decrement Expressions🔗
Syntax
-- unary-expression unary-expression --
Constraints
  1. The operand must be a valid lvalue that can be modified.
  2. The operand must be of an integral or floating point type.
Semantics
  1. If the decrement operator is placed before the operand, the result is the value of the operand decremented by 1. As a side effect, the resulting value is written to the operand object.
  2. If the decrement operator is placed after the operand, the result is the value of the operand. As a side effect, the operand's value is decremented by 1 and written to the operand object.
6.9.7.3.Unary Arithmetic Expressions🔗
Syntax
- unary-expression + unary-expression
Constraints
  1. For the - operator, the operand must be of an arithmetic type.
  2. For the + operator, the operand must be of an arithmetic type or a boolean. (Not boolean-like, an explicit boolean.)
Semantics
  1. The result of the - operator is the negative of the operand's value, or positive, if the operand's value is already negative. If the operand's type an unsigned type, the resulting type is a signed type of the same size.
  2. The result of the + operator, for arithmetic values, is the value of the operand.
  3. The result of the + operator, for boolean values, is the value 0, if the boolean value is false, otherwise the result is 1.
6.9.7.4.Unary Bitwise Negation Expressions🔗
Syntax
~ unary-expression
Constraints
  1. The operand must be a valid lvalue.
  2. The operand must be of an integral type.
Semantics
  1. The result of the expression is the value of the operand with all the bits flipped (All zeroes set to ones and vice versa)
6.9.7.5.Unary Logical Negation Expressions🔗
Syntax
! unary-expression
Constraints
  1. The operand's value must be boolean compatible. (Defined in 4.14.3. Boolean Conversion.)
Semantics
  1. The result of the ! operator is the value of the operand, converted to a boolean, and then the value inverted.

6.9.8.Power (POW) Expression🔗

Syntax
unary-expression power-expression ** unary-expression
Constraints
  1. Both operands must be of a compatible arithmetic type.
Semantics
  1. The usual arithmetic conversions are applied.
  2. The result of the ** operator is the left operand raised to the power of the right operand.

6.9.9.Multiplicative Expressions🔗

Syntax
power-expression multiplicative-expression * power-expression multiplicative-expression / power-expression multiplicative-expression % power-expression
Constraints
  1. Both operands must be of a compatible arithmetic type.
  2. In the case of the * operator, if the left operator is a string, the right operand must be an integral.
Semantics
  1. The usual arithmetic conversions are applied.
  2. The result of the * operator, when applied to two arithmetic types, is the left operand multiplied by the right operand.
  3. The result of the * operator, when applies to a string operand and an integral operand, is the value of the string operand with its content repeated an amount of times specified by the integral operand.
  4. The result of the / operator is the left operand divided by the right operand.
  5. The result of the % operator is the remainder of dividing the left operand by the right operand.

6.9.10.Additive Expressions🔗

Syntax
multiplicative-expression additive-expression + multiplicative-expression additive-expression - multiplicative-expression
Constraints
  1. For the - operator, both operands must be a compatible arithmetic type.
  2. For the + operator, either both operands must be of a compatible arithmetic type, or the left operand must be a string, and the other operand must be a non-void value.
Semantics
  1. The usual arithmetic conversions are applied.
  2. The result of the + operator, when both operands are arithmetic, is the left operand plus the right operand.
  3. The result of the + operator, when the left operand is a string, is the string operand's string value concatenated with the string representation of the other operand. (See 6.6. String Conversion Semantics.)
  4. The result of the - operator is the left operand minus the right operand.

6.9.11.Shift Expressions🔗

Syntax
additive-expression bitshift-expression << additive-expression bitshift-expression >> additive-expression
Constraints
  1. Both operands must be of an integral type.
Semantics
  1. The result of the << operator is the value of the left operand with its bits shifted to the left by the value of the right operand.
  2. The result of the >> operator is the value of the left operand with its bits shifted to the right by the value of the right operand.
  3. In the case the right operand is a negative value, the operation acts as the inverse operation: left shift becomes right shift and vice versa.

6.9.12.Relational Expressions🔗

Syntax
bitshift-expression relational-expression < bitshift-expression relational-expression <= bitshift-expression relational-expression > bitshift-expression relational-expression >= bitshift-expression
Constraints
  1. Both operands must be of a compatible comparable type (See 6.2. Comparison Semantics.)
Semantics
  1. If both operands are compatible arithmetic types, then the usual arithmetic conversions are applied.
  2. The result of the < operator is a boolean true value if the left operand's value is less than the right operand's value.
  3. The result of the <= operator is analogous to the result of the < operator, except that it also returns a boolean true value when the left operand's value is equal to the right operand's value.
  4. The result of the > operator is a boolean true value if the left operand's value is greater than the right operand's value.
  5. The result of the >= operator is analogous to the result of the > operator, except that it also returns a boolean true value when the left operand's value is equal to the right operand's value.

6.9.13.Equality Expressions🔗

Syntax
relational-expression equality-expression == relational-expression equality-expression != relational-expression
Constraints
  1. Both operands must be of a compatible type.
Semantics
  1. If both operands are compatible arithmetic types, then the usual arithmetic conversions are applied.
  2. The result of the == operator is a boolean true value if the left operand's value is equal to the right operand's value.
  3. The result of the != operator is the inverse of the == operator's value.

6.9.14.XOR Expressions🔗

Syntax
equality-expression xor-expression ^ equality-expression
Constraints
  1. Both operands must be of a valid integral type
Semantics
  1. The usual arithmetic conversions are applied.
  2. The result of the ^ operator is the value of the left operand with every bit set to true if and only if the corresponding bit is set in only one of the operands.

6.9.15.Bitwise AND Expressions🔗

Syntax
xor-expression bitwise-and-expression & xor-expression
Constraints
  1. Both operands must be of a valid integral type
Semantics
  1. The usual arithmetic conversions are applied.
  2. The result of the & operator is the value of the left operand with every bit set to true if the corresponding bit in the right operand is also set to true.

6.9.16.Bitwise OR Expressions🔗

Syntax
bitwise-and-expression bitwise-or-expression | bitwise-and-expression
Constraints
  1. Both operands must be of a valid integral type
Semantics
  1. The usual arithmetic conversions are applied.
  2. The result of the | operator is the value of the left operand with every bit set to true if the bit is set in either the left operand or the right operand.

6.9.17.Logical AND Expressions🔗

Syntax
bitwise-or-expression logical-and-expression && bitwise-or-expression
Constraints
  1. Both operands must be of a valid integral or boolean-like type.
Semantics
  1. The usual arithmetic conversions are applied if both operands are arithmetic types.
  2. The result of the && operator is a boolean true value if both operands evaluate to a true value.
  3. If the first operand evaluates to a false value, the second operator must not be evaluated.

6.9.18.Logical OR Expressions🔗

Syntax
logical-and-expression logical-or-expression || logical-and-expression
Constraints
  1. Both operands must be of a valid integral or boolean-like type.
Semantics
  1. The usual arithmetic conversions are applied if both operands are arithmetic types.
  2. The result of the || operator is a boolean true value if at least one of the operands are a boolean true value.
  3. If the left operand evaluates to a true value, the right operand must not be evaluated.

6.9.19.Conditional Expression🔗

Syntax
logical-or-expression logical-or-expression ? expression : conditional-expression
Constraints
  1. The condition expression must result in a boolean-like value.
  2. Both the left and the right operands must result in a compatible type.
Semantics
  1. The condition operand is evaluated first, if it results in a true value, the left operand is the result of the expression, otherwise the right operand is the result.
  2. If the left operand is the result, the right operand must not be evaluated, and vice versa.

6.9.20.Assignment Expression🔗

Syntax
conditional-expression conditional-expression assignment-operator assignment-expression = &= &&= |= *= /= += -= ^= %= <<= >>= **= ||= ??=

6.9.21.Top Level Expression🔗

Syntax
assignment-expression

6.9.22.Loop Condition Expression🔗

Syntax
( expression ) expression
Constraints
  1. Loop condition expressions must always result in a value that can be assigned in some way to a true/false value.
Description

Loop condition expressions declare an expression that may or may not be surrounded by parentheses. Loop condition expressions declared with parentheses cannot have any part of the expression outside the parentheses

6.10.Type Expressions🔗

Syntax
array-type-expression

6.10.1.Const Type Expressions🔗

Syntax
type-keyword

6.10.2.Type Name Expressions🔗

Syntax
const-type-expression identifier module-name :: identifier

6.10.3.Parameterized Type Expressions🔗

Syntax
type-name-expression type-name-expression type-parameter-instatiation < type-expression-list > type-expression type-expression-list , type-expression

6.10.4.Nullable Type Expressions🔗

Syntax
parameterized-type-expression parameterized-type-expression ?
Semantics
  1. Declares a type to be nullable.
  2. Variables, properties, fields or function arguments marked as nullable may be set as null.

6.10.5.Array Type Expressions🔗

Syntax
nullable-type-expression array-type-expression [] array-type-expression readonly []

6.11.Declarations🔗

Syntax
function-declaration extension-function-declaration variable-declaration struct-declaration enum-declaration variant-type-declaration interface-declaration class-declaration module-declaration import-declaration module-declaration import-declaration identifier module-name :: identifier

6.11.1.Declaration Modifiers🔗

Syntax
declaration-modifier native export const
Constraints
  1. No declaration modifier may be set more than once.

6.11.2.Type Parameter Lists🔗

Syntax
< type-parameter-list > type-parameter type-parameter-list , type-parameter identifier identifier extends type-expression class identifier enum identifier variant identifier interface identifier struct identifier

6.11.3.Module Declarations🔗

Syntax
regular-module-declaration native-module-declaration module module-name native module module-name from line-string-literal
Concepts
Module name
The name of the module itself declared after the module keyword.
Native Library Name
The string literal declared after the from keyword in a native module declaration. It is a file name for the dynamically-loadable native C/C++ library that any native functions declared in the file will be linked to. The string must not include the file extension. (eg: .dll)
Constraints
  1. The file name/path referenced by the native library path, if present, must be a valid path to a dynamically linkable library. The path must be accessible to the compiler and known at compile time.
  2. It is not required for a module declaration to declare a name unique to the file it's declared in. Multiple files may share the same namespace.
Semantics
  1. A module declaration is used by import statements to know from which file to import certain symbols.
  2. Adding a module declaration is fully optional.
  3. Omitting the module declaration means the file will be able to export any symbols.

6.11.4.Import Declarations🔗

Syntax
import module-name
Constraints
  1. The import statement must result in at least one symbol being included in the source file. If no symbols are found with the namespace specified in the import, then the import is considered a failure.
Semantics
  1. Import all export symbols with the specified module name namespace into the source file.
  2. All IR, or source files, the compiler is able to find that share the specified namespace are imported into the current source file.
  3. When an IR file, or valid source file is found that is declared as native, the native module need not be loaded.
  4. During importing, any currently existing native bindings should also be checked for import. This is because native bindings can be registered without a pre-existing Tetral IR or source file to represent the declarations.

6.11.5.Function Declarations🔗

Syntax
declaration-modifiers type-expression identifier function-definition function-argument-declaration function-argument-list , function-argument-declaration type-expression identifier ... type-expression identifier type-arguments ( function-argument-list ) block-statement
Constraints
  1. The name and parameter types must not match a function that already exists in the current scope.
  2. Function argument lists can only ever have one variadic argument: the last one. Multiple variadic arguments or a variadic argument not being the last one are invalid.
  3. Functions declared with the export keyword can only be declared in the global scope.
  4. Functions declared with the native keyword must not have a function body.
Description

Declares a function that will be accessible in the current scope.

6.11.6.Extension Function Declarations🔗

Syntax
declaration-modifiers type-expression extension-function-name function-definition type-expression :: identifier
Constraints
  1. The type referenced in the extension-function-name must be a valid type.
  2. The function may not access any private variables declared in the extended type if it is a class type.
Semantics
    Declares a function which can be called and accessed as though it were a method belonging to any arbitrary type.

6.11.7.Variable Declarations🔗

Syntax
declaration-modifiers type-expression identifier declaration-modifiers type-expression identifier = expression
Constraints
  1. Must not have the name of an already accessible variable in the current scope.
  2. If a value is specified for the declaration, the variable's resulting value must match the declared type, or be easily convertible to the variable's type.
  3. If the variable is declared with const, then a value must be specified.
  4. Variable declared with the const modifier may not be modified at a later time.
  5. Variables declared with the export keyword must only be declared in the global scope.
  6. Variables cannot be declared with the following keywords:
    • native
  7. If an initializer value is present, the initializer cannot reference the declared variable.
  8. If the variable's type cannot be default-initialized, then a value must be explicitly specified. (See 6.5. Default Initialization)
Semantics
  1. Declares a variable or a constant that will be accessible in the current scope.
  2. If an initializer value is present, it becomes the value of the variable.
  3. If no initializer value is set, then the value must be default-initialized. (See 6.5. Default Initialization)

  4. Variables declared with the export keyword are made available for access outside the declaring file.

6.11.8.Struct Declarations🔗

Syntax
declaration-modifiers struct identifier { property-declaration } type-expression identifier type-expression identifier = expression
Constraints
  1. A struct must not be declared with the native modifier.
  2. Struct's must only be declared in the main scope of a source file.
  3. Each struct property must have a unique name.
  4. Each struct property, if a default value is set, must have an expression whose type is assignable to the property's type.
  5. Struct properties may not directly or indirectly reference the declared struct type unless through a nullable value-type-reference.
Semantics
  1. Declares a struct data type and its properties.

6.11.9.Enum Declarations🔗

Syntax
declaration-modifiers enum-prefix { enum-constant-declaration-list } enum identifier identifier enum-constant-declaration-list , identifier enum-constant-declaration-list , identifier ,
Constraints
  1. An enum type may not be declared with the same name as an existing interface, enum, struct, class or function in the same namespace.
  2. Each enum constant declaration must have a unique name.
Semantics

6.11.10.Variant Declarations🔗

Syntax
declaration-modifiers variant-prefix { variant-declaration-list } variant identifier type-arguments variant-declaration variant-declaration-list , identifier variant-declaration-list , identifier , identifier identifier ( function-argument-list )
Constraints
  1. A Variant may not be declared with the same name as an existing declaration in the same namespace.
  2. Each declared variant of a variant type declaration must have a unique name.
Semantics
  1. Declares a variant type with the specified name and variants.
  2. Declares each variant's properties.

6.11.11.Interface Declarations🔗

Syntax
declaration-modifiers interface-prefix { interface-method-declaration } interface identifier type-arguments interface-extensions-declaration extends type-name-list type-name-expression type-name-list , type-name-expression type-expression identifier function-definition
Constraints
  1. Interfaces can only be declared in a source file's global scope.
Semantics

6.11.12.Class Declarations🔗

Syntax
declaration-modifiers class-prefix { class-property-declaration } class identifier type-arguments class-extension-declaration class-implements-declaration extends type-name-expression implements type-name-list class-property-modifier public private protected static override const class-field-declaration class-method-declaration class-constructor-declaration class-property-modifiers type-expression identifier class-property-prefix class-property-prefix = expression class-property-prefix function-definition class-property-modifiers constructor ( function-argument-list ) block-statement
Constraints
  1. Classes may only be declared in a file's global scope.
  2. Classes must not be declared with the same name as an already existing function, struct, enum, interface or global variable.
  3. Classes implementing interfaces must declare a valid method implementation for every function outlined by its interfaces.
  4. Classes may not implement 2 or more interfaces with methods whose implementation would cause other constraints to be violated.
  5. Classes may not extend themselves, directly or indirectly.
  6. Classes may not extend a class that has not been marked with the extendable keyword.
  7. Classes may only extend other valid classes.
  8. Classes may only implement valid interfaces.
  9. Classes may not extend classes if the extension would cause an inheritence cycle.
Semantics
  1. The same padding and alignment semantics apply to classes as do to structs.
6.11.12.1.Class Fields🔗
Constraints
  1. Classes may not have 2 or more fields with the same name.
  2. Fields declared with the const modifier must either have a value specified by default or be initialized in the constructor.
  3. If a field has a default value, its value must be a valid expression that results in a type compatible with the field's type.
Semantics
  1. Declares an accessible property.
6.11.12.2.Class Constructors🔗
Constraints
  1. Classes may not have 2 or more constructors with the same function signature.
  2. Constructors may not be declared with the override or static keyword.
  3. Constructors may not return a value.
  4. All constraints that apply to regular function argument lists also apply to constructor arguments.
  5. If a class is declared with a base type that requires explicit initialization, then each constructor declared in a class must initialize the base type with a super-constructor-expression.
Semantics
6.11.12.3.Class Methods🔗
Constraints
  1. The declared return type must be a valid type.
  2. The same constraints that apply to regular function declarations also apply to method declarations, except for those which cannot be applied to method declarations.
  3. Classes may only declare functions with the same name if the argument signatures differs.
  4. Classes may not override any methods declared in an inherited type that have the const keyword.
  5. Classes may not override any methods declared in an inherited type that have the const keyword.
  6. Classes overriding methods from an inherited class or interface must use the override modifier.
  7. A method declared with the static modifier cannot also use the modifier override.
Semantics
  1. Declares a member function that belongs to the class the method was declared in.
6.11.12.4.Class Access Modifiers🔗
Constraints
  1. Each modifier is mutually exclusive to another access modifier.
Semantics
  1. Access modifiers modify how fields and methods may be accessed from outside the class the symbol was declared in.
  2. The private keyword ensures a class symbol is only accessible in the class it was declared in.
  3. The protected keyword ensures a class symbol is only accessible in the class it was declared in, or in a class extending the class.
  4. The public modifier allows a class symbol to be accessed from anywhere.
  5. Omitting the access modifier is equivalent to using the public access modifier.

6.12.Statements🔗

Syntax
labeled-statement declaration body-statement block-statement for-loop-statement for-each-loop-statement do-while-statement while-statement if-statement return-statement control-flow-statement assert-statement expression

6.12.1.Block Statements🔗

Syntax
{ statement }
Description

Declares a list of statements delimited by { and } characters.

Variables declared inside of block statements cannot be referenced from outside of that block.

6.12.2.Loop Flow Control Statements🔗

Syntax
continue identifier break identifier
Description

Influences the way a loop executes. Continue statements instruct the loop to move on to the next iteration of execution, while break statements cause the loop to be exited early before its condition can evaluate to a false value.

Control Flow statements can be declared with a label. This label is optional, if no label is specified, the statement changes how the immediate loop behaves. If a label is set, then it controls how the referenced loop behaves.

Constraints
  1. Can only be used inside a loop
  2. If a label is specified, the label can only reference a label of a loop the statement is inside.

6.12.3.If Statements🔗

Syntax
if loop-condition-expression body-statement if loop-condition-expression body-statement else body-statement
Description

Evaluates a condition and executes either a body, an else statement or moves on without executing.

Constraints
  1. The if statement's condition must result in a value that can be assigned to a true/false value.

6.12.4.Labeled Statements🔗

Syntax
identifier : for-loop-statement identifier : for-each-loop-statement identifier : do-while-statement identifier : while-statement

6.12.5.For Loop Statements🔗

Syntax
for ( variable-declaration ; expression ; expression ) body-statement
Terminology

A for loop has three operands, the first, second and third operand. The first, if present, declares a variable, the second evaluates a condition, and a third expression performs some operation to move the for-loop forward.

Constraints
  1. The second operand, if present, must result in a boolean-like value.
Semantics

6.12.6.For-Each Loop Statement🔗

Syntax
for ( type-expression identifier : expression ) body-statement
Constraints
  1. The right operand, after the identifier, must result in an array or a string.
  2. The variable declaration must be of the type that the right operand contains.
Semantics
  1. Iterates over each element of the right operand and performs some operation.
  2. The element being iterated over is the one declared in the identifier and must be available as a variable.

6.12.7.Do While Loop Statements🔗

Syntax
do block-statement while loop-condition-expression
Constraints
  1. The loop's condition must result in a value that can be evaluated as a boolean.
Description

Evaluates a block of code until the defined loop condition results in a false value. The loop's block will be evaluated at least once until the condition is reached.

6.12.8.Regular While Loop Statements🔗

Syntax
while loop-condition-expression block-statement
Constraints
  1. The loop's condition must result in a value that can be evaluated as a boolean.
Description

Evaluates a block of code until the defined loop condition results in a false value. If the condition results in a false value when the loop is first reached, the loop's block is not evaluated at all.

6.12.9.Return Statements🔗

Syntax
return expression
Description

Causes the current function to halt execution and return to the caller.

Constraints
  1. Can only be used inside a function body.
  2. If the return statement is used in a non-void function, it must always return a value.
  3. If the return statement is used in a void function, it must never return a value.
  4. The value returned by a return statement, must be assignable to the function's return type.

6.12.10.Assert Statements🔗

Syntax
assert expression assert expression : expression
Semantics
  1. Evaluates the condition operand. If the condition results in a true boolean-like value, nothing happens. Otherwise, an error is thrown.
  2. If the message operand is present, it is evaluated and used as the error message for the thrown error.
  3. If no message operand is present, it must be created from the values of the variables present in the condition expression. (See Message Generation section below).
  4. The inclusion of the assert statement in the compiled output is optional, as is the execution of the assert statement
Constraints
  1. The expression the assert statement evaluates must always result in a Boolean-like value.
  2. The message expression, if present, must always result in a String value.
Message Generation

The messages used by assert statements, if one is not explicitly provided, should be composed of the values of each variable used in the expression, for example, when asserting that a variable named "foo" is equal to 10, the automatic error message should be composed of the expression string and followed by the variable's value. In effect, turning the statement into this:

1
assert foo == 10 : "foo == 10, foo: ${foo}"
Examples
1
2
3
assert true // Does nothing
assert false // Assertion failed!
assert false : "message" // Assertion failed: message

6.13.Translation Unit🔗

module-header-declarations declaration declaration

7.Intermediate Representation (Bytecode Format)🔗

The result of the compilation of a Tetral source file should be a Tetral Bytecode file. This section outlines the bytecode format.

7.1.File Header🔗

Each Tetral IR file is prefixed with a file header which provides information about the file's contents. The layout of the header is as follows.

The header begins with an ASCII string that must always have the value TetralLang. This is immediately followed by a 2 byte unsigned integer denoting the file's version.

The file version integer is an incrementing integer which specifies what version of the Tetral bytecode format the file complies with.

Table 14 - Tetral File Versions
Version numberDescription
1The first version of the Tetral Language

The vile version integer is then followed by a repeating pattern. Each instance of this pattern begins with a single unsigned byte, the entry type. No entry may be defined twice, but not all entries are required to appear in a file.

The following types are valid.

Table 15 - Bytecode Header Entry Types
NameHexadecimal ValueDescription
END0x00Special entry type used to mark the end of the header.
STRTABLE0x01String table information
STRDATA0x02String pool information
TYPETABLE0x03Type table information
FUNCTABLE0x04Function table information
INSTRBLOCK0x05Instruction block information
RDATA0x06Read only data block information
MDATA0x06Pre-defined metadata properties
ADATA0x06List of arbitrary key,value string pairs.
MODINFO0x07Information about the import and export list.

7.1.1.The STRTABLE Entry🔗

Comprised of a 64-bit unsigned integer: an offset of the string table's data from the start of the file, and a 32-bit unsigned integer: the number of entries in the string table.

(See 7.2. String Pool Table)

7.1.2.The STRDATA Entry🔗

Comprised of two 64-bit unsigned integers, the first is an offset and the second is a size. The offset specifies where, from the start of the file, the file's string pool data is located and the size specifies the size (in bytes) of the file's string pool data.

(See 7.3. String Pool Data)

7.1.3.The TYPETABLE Entry🔗

Comprised of a 64-bit unsigned integer: an offset of the type table's data from the start of the file, and a 32-bit unsigned integer: the number of entries in the type table.

(See 7.4. Type Table)

7.1.4.The FUNCTABLE Entry🔗

Comprised of a 64-bit unsigned integer: an offset of the function table's data from the start of the file, and a 32-bit unsigned integer: the number of entries in the function table.

(See 7.5. Function Table)

7.1.5.The INSTRBLOCK Entry🔗

Comprised of the following orders. The values appear in the binary in the order they are defined here.

Instruction block offset
A 64-bit unsigned integer that specifies the file's instruction block offset from the start of the file.
Instruction block size
A 32-bit unsigned integer that specifies the file's instruction block size in bytes.
Instruction count
A 32-bit unsigned integer that specifies how many instructions are in the instruction block.

(See 7.6. Instructions Block)

7.1.6.The RDATA Entry🔗

Comprised of two 64-bit unsigned integers, they are as follows and appear in the order they are listed.

  1. The offset of the data block from the start of the file.
  2. The size of the data block, in bytes.

(See 7.7. Data Block)

7.1.7.The MDATA Entry🔗

Comprised of the following values which and appear in the order they are listed.

The file's global scope size
A 64-bit unsigned integer that determines how many bytes are required to represent the variables stored in the file's global scope.
Entrypoint function index

An 32-bit unsigned integer index into the function table from where the execution of the instructions in the file should begin.

A value (in hexadecimal) of 0xFFFFFFFF means that the file has no entrypoint and cannot be executed by itself.

7.1.8.The ADATA Entry🔗

This entry begins with a 32-bit unsigned integer that denotes the number of key value pairs stored in the section.

It is then followed by that many number of key-value pairs of strings. Each string is prefixed with a 16-bit unsigned integer denoting the length of the string and is then followed by that many bytes of UTF-8 bytes.

7.2.String Pool Table🔗

The String Pool Table, or just the String Table, is a list of 32-bit unsigned integer pairs. The number of entries in the table is determined in the STRTABLE entry in the header.

Each 32-big unsigned integer is defined as follows:

String Pool Offset
The first 32-bit unsigned integer is an address of a string's content in the String Pool Data.
String Length
The second integer is the number of UTF-8 code units in the string.

The first entry in the list may be a "null" string entry, meaning an entry with offset 0 and length 0, used as a placeholder for an empty, or null, string.

If this section is present in a bytecode file, then the String Pool Data section must be present too, as well as the corresponding entries in the header.

7.3.String Pool Data🔗

A block of UTF-8 encoded codepoints. The size and location of this section is determined by the STRPOOL entry in the header.

If this section is present in a bytecode file, then the String Pool Table section must be present too, as well as the corresponding entries in the header.

7.4.Type Table🔗

This section is a list of type definitions. The number of definitions in this section and the section's location in the file is determined by the TYPETABLE entry in the header.

Each definition is prefixed with 2 values, an unsigned 32-bit integer and an 8-bit unsigned integer.

The first integer is the "type index" of the type definition. This index is used in function table entries, other type table entries, and in some IR instructions to reference the type definition.

The second 8-bit unsigned integer marks the type of the entry. The following values are considered valid.

Table 16 - Type Table Definition Types
NameHexadecimal Value
ARRAY0x00
FUNCSIGN0x01
STRUCT0x02
ENUM0x03
INTERFACE0x04
CLASS0x05
NULLABLE0x06
PARAMETERIZED0x07
TYPEPARAMETER0x08
PARAMLIST0x09
IMPORTED0x0A

Depending on the type, one of the schemas defined in the following sections is used for the type.

Some entries may reference a "null type index," this is a type index constant with the hexadecimal value of 0xFFFFFFFF and means that the type index references no type

7.4.1.Arrays, Nullable Definitions and Reference Definitions🔗

These are defined in one section since their schema is identical, but each type results in a different type construct.

These entries define only a single 32-bit unsigned integer value. It is the type index of the type they reference.

The referenced type index may not be a null type index.

7.4.2.Function Signatures🔗

Function signatures are defined with the following values. The values appear in the binary in the order they are defined here.

Return type index
32-bit unsigned integer index into the type table, referencing the type the function signature uses as a return type. This may not be a null type index.
Variadic state
8-bit unsigned integer with a value of either 0 or 1 used to determine if the function takes in a variable number of arguments.
Parameter list index
A 32-bit unsigned integer referencing the PARAMLIST entry that outlines the type arguments for this type. If the type has no type arguments, this will be a null type index.
Arguments count
A 32-bit unsigned integer stating how many entries will be in the next argument list.
Argument type indexes list

An array of 32-bit unsigned type indexes referencing the type of the corresponding function argument's type.

None of these may be null type indices. If the variadic state of the function is set to 1, then the last parameter to appear in this list must reference an entry with the type ARRAY.

7.4.3.Structs🔗

Structs are defined with the following values. The values appear in the binary in the order they are defined here.

Name index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's name.
Namespace index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's namespace. If the type has no namespace, the referenced entry will be the table's null entry.
Type parameter list index
A 32-bit unsigned integer referencing the PARAMLIST entry that outlines the type arguments for this type. If the type has no type arguments, this will be a null type index.
Flags
Property count
Property list

7.4.4.Enums🔗

Enumerations are defined with the following values. The values appear in the binary in the order they are defined here.

Name index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's name.
Namespace index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's namespace. If the type has no namespace, the referenced entry will be the table's null entry.
Type parameter list index
A 32-bit unsigned integer referencing the PARAMLIST entry that outlines the type arguments for this type. If the type has no type arguments, this will be a null type index.
Constant count
Constant list

7.4.5.Interfaces🔗

Interfaces are defined with the following values. The values appear in the binary in the order they are defined here.

Name index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's name.
Namespace index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's namespace. If the type has no namespace, the referenced entry will be the table's null entry.
Type parameter list index
A 32-bit unsigned integer referencing the PARAMLIST entry that outlines the type arguments for this type. If the type has no type arguments, this will be a null type index.
Extends count
Extends list
Member count
Method list

7.4.6.Classes🔗

Classes are defined with the following values. The values appear in the binary in the order they are defined here.

Name index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's name.
Namespace index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's namespace. If the type has no namespace, the referenced entry will be the table's null entry.
Type parameter list index
A 32-bit unsigned integer referencing the PARAMLIST entry that outlines the type arguments for this type. If the type has no type arguments, this will be a null type index.
Parent type index
Implements count
Implements list
Member count
Method list

7.4.7.Parameter List Instantiation🔗

Parameter lists define the types another type requires to be complete.

Parameter lists are defined with the following values. The values appear in the binary in the order they are defined here.

Referenced type index
Type count
Type list

7.4.8.Imported Type🔗

Imported types are types which are not defined in the current file, but are instead defined in another file and must be imported. They are defined with the following values. The values appear in the binary in the order they are defined here.

Name index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's name.
Namespace index
32-bit unsigned integer index referring to the entry in the file's string table that is used as the type's namespace. If the type has no namespace, the referenced entry will be the table's null entry.

7.5.Function Table🔗

7.6.Instructions Block🔗

7.7.Data Block🔗

7.8.Import List🔗

7.9.Export Table🔗

8.Bytecode Instructions🔗

8.1.General Purpose Instructions🔗

8.2.Stack Memory Instructions🔗

8.3.Global Memory Instructions🔗

8.4.Closure Instructions🔗

8.5.Heap Memory Instructions🔗

8.6.Function Call Instructions🔗

8.7.Foreign Symbol Instructions🔗

8.8.Conversion Instructions🔗

8.9.Unary Instructions🔗

8.10.Binary Instructions🔗

8.10.1.Integer-only Binary Instructions🔗

8.10.2.Boolean-only Binary Instructions🔗

8.10.3.Arithmetic Binary Instructions🔗

8.10.4.Comparison Instructions🔗

8.10.5.String/Array Instructions🔗

9.Parsing and Compilation🔗

10.Interpreter🔗

11.The Standard Library🔗

This section defines the modules that are part of the Tetral Standard Library. Whether any function defined in the following sections is implemented natively or within Tetral itself is explicitly left up to implementations to decide on.

11.1.The std Module🔗

The most basic standard library functions that would be required by a program.

11.1.1.The Optional Variant🔗

variant Optional<T> {
EMPTY,
PRESENT(T value)
}

The optional type is monadic type to represent a value which either is present or empty.

11.1.2.The Result Variant🔗

variant Result<V, E> {
ERROR(E err),
SUCCESS(V value)
}

11.1.3.The readln Function🔗

string readln()

Asks for input from the standard input and blocks execution until input has been given.

11.1.4.The println Function🔗

void println(string message)

Prints a string to the standard output and appends a newline (U+000A) character.

11.1.5.The getenv Function🔗

string getenv(string name)

The getenv function takes as input a single string: the variable name and returns a value associated with that environment variable.

If no value is associated with that value, a null/empty string is returned instead.

11.1.6.The setenv Function🔗

void setenv(string name, string value)
void setenv(string name, string value, bool overwrite)

This function is defined with two overloads. Both definitions take in a variable name and value. One of the overloads takes in a boolean value. If this value is false, then the envirnoment variable will not be overwritten if it is already set.

The function changes a value in or adds to the list of environment variables.

11.1.7.Numeric Parsing Functions🔗

int32? parseInt(string s)
uint32? parseUint(string s)
int64? parseInt64(string s)
uint64? parseUint64(string s)
float32? parseFloat(string s)
float64? parseFloat64(string s)

Every one of these functions takes a string as an input and parses the beginning of that string into a number of the equivalent value of the respective type.

11.2.The std::strings Module🔗

string stringFromUtf8(uint8[] utf8bytes)
string stringFromUtf16(uint16[] utf16bytes)
string stringFromUtf32(uint32[] codepoints)

string string::trim()
string string::trimLeft()
string string::trimRight()

string string::substring(uint32 start)
string string::substring(uint32 start, uint32 end)

uint32? string::indexOf(uint8 utf8char)
uint32? string::indexOf(uint8 utf8char, uint32 start)
uint32? string::indexOf(uint32 codepoint)
uint32? string::indexOf(uint32 codepoint, uint32 start)
uint32? string::indexOf(string other)
uint32? string::indexOf(string other, uint32 start)

uint32? string::lastIndexOf(uint8 utf8char)
uint32? string::lastIndexOf(uint8 utf8char, uint32 end)
uint32? string::lastIndexOf(uint32 codepoint)
uint32? string::lastIndexOf(uint32 codepoint, uint32 end)
uint32? string::lastIndexOf(string other)
uint32? string::lastIndexOf(string other, uint32 end)

bool string::contains(uint8 utf8char)
bool string::contains(uint8 utf8char, uint32 end)
bool string::contains(uint32 codepoint)
bool string::contains(uint32 codepoint, uint32 end)
bool string::contains(string other)
bool string::contains(string other, uint32 end)

bool string::replace(string sequence, string with)

bool string::startsWith(string other)
bool string::endsWith(string other)

uint32? string::codepointAt(uint32 index)
uint32 string::codepoints()

uint32[] string::toUtf32()
uint16[] string::toUtf16()
uint8[] string::toUtf8()

bool string::isEmpty()
bool string::isBlank()

string string::toUpperCase()
string string::toLowerCase()

11.3.The std::math Module🔗

11.4.The std::io Module🔗

11.5.The std::reflection Module🔗

11.6.The std::json Module🔗

12.Compiler Command Line Interface🔗

This section details the CLI (Command Line Interface) of the default Tetral implementation.

The usage of the tetral command looks like so:

tetral [OPTIONS] [COMMAND] [COMMAND ARGUMENTS]

12.1.Commands🔗

12.1.1.The compile Command🔗

Command Usage
tetral [OPTIONS] compile [IN FILES]
Description

The compile command takes in a list of file paths that are parsed and then compiled into one single bytecode format. The files in the list can be source files or bytecode files.

12.1.2.The run Command🔗

Command Usage
tetral [OPTIONS] run [PROGRAM FILE] [RUNTIME ARGUMENTS]
Description

The run command takes in the name of a single program file to execute and runs it. The [RUNTIME ARGUMENTS] arguments may be omitted. These arguments are passed to the script's main function as an array of string arguments with the real-path of the script file always being the first.

12.1.3.The test Command🔗

Command Usage
tetral [OPTIONS] test [TEST DIRECTORY]
Description

The test command takes in a file path to a directory of Tetral program files and executes each after another.

Any errors thrown during the execution of the files are ignored and instead are checked against a list of "expected" errors.

Test Execution

For each valid test file:

  1. If the file is a Tetral source file, it is parsed and compiled. If any errors occurred during parsing or compilation, they are checked against a list of expected parsing errors.
  2. The test file's entrypoint is then executed as normal.
  3. Every function with a name prefixed with test_ is then executed. The function's name is considered to be the name of the test case. These functions do not need to be exported.
  4. If any errors occurred, they are checked against a list of expected runtime errors. If all match, the test is considered passed. If there are more errors than expected or an expected error did not occur, or if an error did not match the expected one, the test is considered a failure and execution moves on to the next case.
  5. When all test cases in a single file have been executed, the tests_shutdown function is called, if one exists. The shutdown function does not need to be exported.
  6. If a test result directory is specified, test results, IR, ASTs and config information are dumped to the specified directory.

12.1.4.The help Command🔗

Prints text about the supported flags, commands and their arguments to the console. The version info is printed as well.

12.1.5.The version Command🔗

Prints information about the version of the Tetral language, compiler and interpreter in use.

12.2.Flags🔗

Certain flags will be referred to as "Value Flags," these are flags which require a value be provided after the flag's name. The value must be specified after an equals sign (U+003D.) For example:

--disable-warn=unused.variable

12.2.1.General Flags🔗

The --lib-directory Value Flag

The flag specifies a file path from which Tetral can load libraries during compilation or execution.

The flag can be specified multiple times to append more library directories.

The --no-implicit-std Flag

Specifies that the default std modules should not be imported automatically.

When specified, script files must explicitly import the std modules.

The --implicit-import Value Flag

Specifies a module name that will be automatically imported without a source file having to explcitly state it's using symbols from that module.

12.2.2.Compiler Flags🔗

The --drop-line-numbers Flag
Instructs the compiler to omit all PUSHLINE instructions from the compiled output.
The --error-warnings Flags
Instructs the compiler to treat all warnings as errors.
The --disable-warn Value Flag
Instructs the compiler to not report certain warnings
The --ignore-asserts Flag
Instructs the compiler to omit all assert statements from the compiled output.
The --json-messages Flag

Instructs the compiler to print all messages in a JSON format instead of the default human-readable format.

This will result in messages with a schema defined in 13.2. Annex B. JSON Message Format

Regardless of this flag being used or not, the compiler will always print to either the standard output or the standard error output.

The --text-compile/-T Flag

Writes a textual representation of the compiled Bytecode format to the output file instead of a binary format.

Textual IR files are not intended to be read and as such are not valid source or bytecode files. This flag is intended for debugging and human analysis of the compiled output.

The --output-file/-o Value Flag

Tells the compiler the file path to output the compiled output to.

If this flag is not specified, then the file that is written to will be same path as the input file(s), except with the appropriate extension, depending on if the --text-compile flag was specified, or not. (See 13.4. Annex D. Tetral File Extensions)

12.2.3.Test Command Flags🔗

The --verbose/-v Flag
Instructs the test executor to print more information to the standard output than it normally would, providing information about how long each test took to execute.
The --test-output-dir/-TO Value Flag

Specifies a directory to which the test executor will dump information about each executed test to.

Each found test file will be executed and then information about the result will be printed to a dedicated directory nested inside the specified directory.

Printed information includes:

  1. A test file's AST, if parsed from a source file. (Dumped into a ast.json file.)
  2. A test file's config. (Dumped into a config.json file)
  3. A test file's compiled IR, if parsed from a source file. (Dumped into a bytecode.tlir file)
  4. A summary of the test results. (Dumped into a results.md file)

13.Annexes🔗

13.1.Annex A. Class Implementation Details🔗

  1. Class methods are lifted out of classes and become global functions with the class' name and a dot character (U+002E) prefixed to the method's name.
  2. For non-static methods, a hidden argument, the this argument, is prepended to the method's arguments.
  3. Class types exist in memory in the same way as struct types, with their functions becoming free-floating.
  4. If a class is exported, then all of its lifted functions are also exported.
  5. All constructors of a class are similarly transformed into global functions with the type's name and a dot character (U+002E) prefixed and the method name changed to <constructor>.
  6. A compiler-only statement is inserted into the body of each function, which calls the interpreter's allocator to allocate enough memory for the type, then the super constructor is invoked, if present, and then the rest of the constructor body.

13.2.Annex B. JSON Message Format🔗

Each logged JSON message is logged as a JSON object with the following properties.

level
A string determining the "level" of the message. Will always have one of the following values:
  • FATAL
  • ERROR
  • WARN
  • INFO
  • TRACE
message_code
The message code, defined in 13.3. Annex C. Error and Warning IDs
message
Human readable, formatted message.
location

This property may be omitted for certain messages, the presence of this property is not guaranteed.

If it is present, it will be a JSON object with the following properties:

start_line and end_line
Line number range the message is referencing, starting at 1.
start_index and end_index
Index range of the characters in the input the message is referencing, starting at 0.

13.3.Annex C. Error and Warning IDs🔗

This section details the IDs of compiler warning and errors. These IDs are used to provide translations of the compiler and for the --disable-warn argument.

Table 17 - Compiler Message IDs
Message IDTypeDefault Value
lexer.invalidcodepointFatal Tokenizer ErrorInvalid UTF-8 byte found in source file
lexer.invalidoctoFatal Tokenizer ErrorInvalid octal sequence
lexer.invalidhexFatal Tokenizer ErrorInvalid hexadecimal sequence
lexer.invalidbinFatal Tokenizer ErrorInvalid binary sequence
lexer.unclosedstringFatal Tokenizer ErrorUnclosed string constant
lexer.stringlinebreakFatal Tokenizer ErrorLinebreak inside single-line string
lexer.invalidescapeFatal Tokenizer ErrorInvalid escape sequence
lexer.invalidunicodeFatal Tokenizer ErrorInvalid Unicode sequence
lexer.invalidcharFatal Tokenizer ErrorCharacter constant too long, may only contain one UTF-8 character
parser.expectedFatal Parser ErrorExpected %0, found %1
parser.duplicateflagParser ErrorDeclaration modifier already set
parser.funcdecl.trailingcommaParser ErrorIllegal trailing comma in arguments declaration
parser.funcdecl.nativebodyParser ErrorNative functions cannot have a function body
parser.invalidlabeledFatal Parser ErrorIllegal statement following loop label
parser.typeexpr.invalidFatal Parser ErrorInvalid type expression
parser.module.nativenofromParser ErrorNative module declaration has no 'from' part to declare native library name.
parser.module.invalidnameFatal Parser ErrorExpected module separator '::' to be followed by module name element.
parser.call.trailingcommaParser ErrorIllegal trailing comma in function call arguments.
parser.call.malformedParser ErrorMalformed call expression.
parser.unexpectedFatal Parser ErrorToken not expected here, don't know how to parse.

13.4.Annex D. Tetral File Extensions🔗

Table 18 - Tetral File Extensions
File ExtensionDescription
.tl or .tetralTetral source files
.tlirTetral Bytecode file
.tltirText representation of a Tetral Bytecode file