Language specification

Lexical Rules

Whitespaces and comments

Whitespaces are like in Java: characters for which java.lang.Character.isWhitespace(c) == true (e.g. space, tab, end-of-line, etc.).

Comments are like in Java.

  • Single-line comment: starts with //, ends with end-of-line or end-of-file.
  • Multiline comment: starts with /*, ends with */.
  • Error if there is no */ after /*.

Identifiers

Identifiers are like in Java: first character has java.lang.Character.isJavaIdentifierStart(c) == true, other characters have java.lang.Character.isJavaIdentifierPart(c) == true. Simple definition: sequence of letters, digits and underscores (_), first character is not a digit. Can contain non-English letters. Identifiers are case-sensitive.

Keywords:

Keyword is one of the following reserved identifiers:

and break class create delete else false for function if in index key limit list map mutable not null operation or query return set sort true update val var while
  • A keyword cannot be used as a general identifier, i. e. as a name of a class, function, variable, etc.
  • Longest possible keyword/identifier is taken, i. e. string “format” is an identifier “format”, not keyword “for” and identifier “mat”.

Operators and delimiters

List of operators and delimiters:

!! != % %= ( ) * *= + += , - -= . / /= : ; < <= = == > >= ? ?. ?: @ [ ] { }
  • Longest possible operator/delimiter is taken, i. e. string <= is a single operator <=, not two operators < and =.

Integer literals

  • Decimal: regex /[0-9]+/.

Maximum decimal value: 9223372036854775807 (2^63 - 1). Error if the value is greater.

  • Hex: regex /0x[0-9A-Fa-f]+/, e.g. 0x0, 0xABCD, etc.

Maximum hex value: 0x7FFFFFFFFFFFFFFF (2^63 - 1). Error if the value is greater.

  • Cannot have a letter directly after an integer literal, e. g. 1234X is an error, not two tokens 1234, X.

String literals

  • Enclosed in single (‘) or double (“) quotes.
  • There is no difference between single-quoted and double-quoted strings, i. e. ‘Hello’ and “Hello” are equal string literals.
  • Cannot contain an end-of-line character (0x0A), i. e. closing quote must be on the same line as the open quote.
  • Error if there is no closing quote on the same line.

Escape sequences

Simple escape sequences: \b \t \r \n \" \' \\

Unicode escape sequence: \u1234 \uABCD \uAbCd etc. - must have exactly 4 hex digits.

Error if wrong escape sequence is specified (\ character, but not one of valid escape sequences).

Byte array literals

  • Syntax: x"..." or x'...', only hex digits (upper or lower case) can be used within quotes.

Examples: x'' x"123456" x"DeadBeef"

  • Must start with lower-case x, not upper-case X.
  • Must contain an even (2*N) number of hex digits (because 1 byte = 2 hex digits).
  • Cannot contain escape sequences or end-of-lines.

Types

General

A type of an attribute, parameter, variable, etc. can be:

  • name of a built-in or user-defined type (Identifier)
  • nullable type
  • tuple type
  • collection type

Built-in types

Basic built-in types are:

boolean byte_array integer json range text

Built-in type aliases are:

name = text pubkey = byte_array timestamp = integer tuid = text

Type alias A = T means that entities (attributes, variables, etc.) of type A will effectively have type T during compilation.

Special types

Special types cannot be used in code explicitly (in attribute declarations, etc.), but they are used by the compiler internally as types of some expressions.

  • Special types are: unit, null.
  • Names of special types cannot be used in code as types. Trying to use “unit” as a type causes an error e. g. “Unknown type name”. “null” is a keyword, so using it as a type is a syntax error.

Nullable type

The idea was taken from Kotlin.

Syntax:

NullableType: Type "?"

Examples:

  • integer?
  • list<text>?

Error if the underlying type is nullable, e. g. integer??.

Tuple type

Consists of one or more fields. Each field must have a type and may have a name.

Syntax:

TupleType: "(" TupleTypeField ( "," TupleTypeField )* ")"

TupleTypeField: ( Identifier ":" )? Type

Examples:

  • (integer)
  • (integer, text)
  • (x: integer, y: integer)
  • (p: text, q: byte_array, list<integer>)

Error if same field name is used more than once.

Collection types

Collection types are: list, set, map.

Syntax:

  • "list" "<" Type ">"
  • "set" "<" Type ">"
  • "map" "<" Type "," Type ">"

Examples:

  • list<integer>
  • set<text>
  • map<text, byte_array>

Subtypes

Purpose: if type B is a subtype of type A, a value of type B can be assigned to a variable of type A.

  1. T is subtype of T.

  2. T is subtype of T?.

  3. null is subtype of T?.

  4. Tuple type T1 is subtype of tuple type T2 if:

    • the number of fields is the same
    • names of corresponding fields are the same (if a field has no name, the other field must have no name)
    • type of each field of T1 is a subtype of the type of the corresponding field of T2

Examples:

  • (integer, text) is subtype of (integer, text?)
  • (integer, text?) is subtype of (integer?, text?)
  • (integer, text?) is not subtype of (integer, text), because text? is not subtype of text
  • (x: integer, y: integer) is subtype of (x: integer?, y: integer?)
  • (x: integer, y: integer) is not subtype of (p: integer, q: integer), because field names differ
  • (integer, text) is not subtype of (x: integer, y: integer)
  • (x: integer, y: integer) is not subtype of (integer, text)

Classes

Class has a name and zero or more member definitions.

  • When a class with name A is defined, A can be used as a type name in the code after the class definition.
  • Error if there already is a built-in or user-defined type with same name.
  • Class members are: attribute, key, index.

Class syntax

ClassDefinition: "class" Identifier "{" ClassMemberDefinition* "}"

ClassMemberDefinition :
    AttributeDefinition
    KeyDefinition
    IndexDefinition

Example:

class user {
    name: text;
    address: text;
    key name;
    index address;
}

Attributes

Attribute definition may contain a name, type, default value expression and modifiers (e. g. mutable).

Syntax:

AttributeDefinition: "mutable"? FieldDefinition ("=" Expression) ";"

FieldDefinition: Identifier (":" Type)?

  • If type is not specified, same type as the attribute name is taken (built-in or user-defined). Error if there is no such type.
  • Error if there already is another attribute with same name in the same class.
  • If default value expression is specified, the type of the expression must be a subtype of the attribute’s type.
  • Expressions specification will be written later. We can use simplest expressions now for testing: integer literal, string literal, true, false, null, etc.

Examples:

name;            // same as "name: name;", there is a built-in type "name"
address: text;
mutable age: integer;
mutable status: text = 'Unknown';

Keys, indices

Keys and indices consist of one or more fields.

Syntax:

KeyDefinition: "key" FieldDefinition ("," FieldDefinition)* ";"

IndexDefinition: "index" FieldDefinition ("," FieldDefinition)* ";"

Handling of fields

  • Error if same field name is used more than once within one key/index.
  • If there is no attribute with such name, an attribute is added to the class implicitly. The added attribute is not mutable, has no default value.
  • If there is an attribute with such name, the key/index field cannot have a type specified.

No error:

key foo: integer;

Error:

foo: integer;
key foo: integer;

Error if there already is a key/index with same set of fields.

Not an error:

index a;
index a, b;

Error:

index a, b;
index b, a;

It does not matter if a key/index is defined before or after an attribute used in it

Code:

x: integer;
key x;

is equivalent to:

key x;
x: integer;

Same for field type restrictions: does not matter whether it is before or after the attribute definition

No error:

key x: integer;

No error:

x: integer;
key x;

Error:

x: integer;
key x: integer; // ERROR

Error:

key x: integer; // ERROR
x: integer;