3. Data Types
The way LiteScript handles data types is pretty unconventional when comparing with other languages but it is designed that way to make heavy use of simple data types as polymorphically and generalized as possible.
3.1 Primitive Data Types
The different data types are designed to be as primitive as possible to satisfy backwards compatibility with legacy 32 bit era software, which is limiting but should be enough to satisfy LiteScript's purpose of running small scripts.
- The "integer" data type
In LiteScript, the integer data type is implemented as a 32 bit signed integer, meaning that its minimum value is -2,147,483,648 and its minimum value is 2,147,483,647, which is enough for the language's functionalities.
To declare an integer in an expression, all you need to do is provide a regular numeric value without a decimal ('.'), and the interpreter will recognize it as an integer.
012100000000-1234
The integer data type allows all arithmetic operations like addition, subtraction (unary and binary), multiplication, division, modulus, and bit-wise logic (AND, OR, NOT, XOR, LSHIFT, RSHIFT).
LiteScript doesn't natively support boolean values, instead, the most common substitute is to use the integer data type (just like in C), where 0 evaluates to false and any value different from 0 evaluates to true.
- The "real" data type
In LiteScript, the real data type is implemented as a 32 bit floating point number, meaning that the interval it spans is approximately: [-3.4x10^38, -1.176x10^-38] U [1.176x10^-38, 3.4x10^38], as per the IEEE floating point standard.
To declare a real in an expression, all you need to do is provide a numeric value with a decimal ('.'), and the interpreter will recognize it as an real.
0.01.0273000.545789-0.000012
The real data type allows almost all arithmetic operations: addition, subtraction (unary and binary), multiplication, division, and some additional operations unique to this data type, like logarithmation and exponentiation.
- The "string" data type
In LiteScript, the string data type is implemented as a byte string, meaning that it can hold a variable number of ASCII characters (including special characters) as well as hold raw unicode bytes, although it is not natively supported.
To declare a string in an expression, all you need to do is provide a string of characters in between quotes ("), just like any other programming language that supports them. Spaces are also allowed, as the quotes already delimit the string.
"""Hello World!""\r\n""😊"
The string data type only has support for one operation: concatenation with plus signs ('+'), and it can hold a subset of special ASCII characters:
- Null byte character (\0).
- New line character (\n).
- Carriage return character (\r).
- Backslash character (\\).
- Quote character (\").
LiteScript doesn't natively support a primitive character data type, so single characters are represented with strings.
3.2 Containers
Containers are the only supported data structure in LiteScript, and they are implemented as dictionaries that hold ordered key-value pairs, meaning that every regular modification and access is performed in logarithmic time complexity.
A key can be any of the three primitive data types mentioned previously and they can all work in unison, meaning that in a same container we can have keys that are of a different data type, the order of which is as follows: first integers, second reals and lastly strings.
A value can be any of the three primitive data types mentioned previously in addition to containers, therefore allowing containers to be nested inside others as long as their key is of a primitive data type.
To declare an empty container, just simply declare it with a name using the "container" keyword.
container [name]
Containers also hold a number of unique member operations that can be invoked, with their particular LiteScript syntax, with these we can simulate any data structure that we could ever need: stacks, queues, dictionaries, heaps, lists... (with the performance drawback).
3.2.1 Container operations
NOTES:
- All these operations are case-sensitive, fixed-form and are atomic at the instruction level, meaning that they occupy an entire line.
- Containers can also be used as variables.
- The following operations: top, end, psh, pop, deq, num are done in near constant time.
- The following operations: igtk, igtv are done in near linear time.
- The "get" operation
[container] get [key] -> [variable]
Given a specific key, gets the value which that key holds and stores it in the specified variable. Since container accesses are atomic at the instruction/line granularity, it means that we cannot directly reference container values in expressions, they have to be gathered separately, adhering to the philosophy of memory accesses in assembly.
Only variables can be used to retrieve values, expressions are not to be used; however you can use expressions to evaluate keys, as long as it is compacted.
c get 0 -> x ✅c get i+1 -> x ✅c get i + 1 -> x ❌c get i+1 -> x+1 ❌
- The "put" operation
[container] put [key] <- [expression]
Given a specific key, evaluates the expression and puts the corresponding value in the container at the specified key, creating the entry if it doesn't exist.
You can use expressions to evaluate keys and values, as long as the key expression is compacted.
c put 0 <- x ✅c put i+1 <- x-1 ✅c put i+1 <- x - 1 ✅c put i + 1 <- x+1 ❌
- The "del" operation
[container] del [key]
Given a specific key, erases the entry (its key-value pair) associated to that key.
You can use expressions to evaluate keys, compactness is not mandatory.
c del 0 ✅c del i+1 ✅c del i + 1 ✅
- The "top" operation
[container] top -> [variable]
Gets the first ordered value of the container and stores it in the specified variable.
Must specify a variable to retrieve the value, expressions cannot be used.
c top -> x ✅c top -> x+1 ❌c top -> 0 ❌
- The "end" operation
[container] end -> [variable]
Gets the last ordered value of the container and stores it in the specified variable.
Must specify a variable to retrieve the value, expressions cannot be used.
c end -> x ✅c end -> x+1 ❌c end -> 0 ❌
- The "psh" (push) operation
[container] psh <- [expression]
Evaluates the expression and pushes the value at the new last numbered key position, for this to work the container must only hold integer keys.
container cc psh <- x ✅c put 3 <- x+2c psh <- "hey" ✅c put "text" <- xc psh <- 0 ❌
- The "pop" operation
[container] pop
Simply erases the last ordered key-value pair on the container. The container can hold any type of keys for this to work. It triggers an exception if empty.
c pop
- The "deq" (dequeue) operation
[container] deq
Simply erases the first ordered key-value pair on the container. The container can hold any type of keys for this to work. It triggers an exception if empty.
c deq
- The "num" operation
[container] num -> [variable]
Returns the number of key-value pairs stored in the container and stores that value into the specified variable.
Must specify a variable to retrieve the value, expressions cannot be used.
c num -> x ✅c num -> x+1 ❌
- The "igtk" (indexed get key) operation
[container] igtk [expression] -> [variable]
Evaluates the expression and given the resulting value as an index, retrieves the key at that position and stores it in the specified variable. Its use case is to retrieve keys by index in containers that don't have integer valued keys.
Must specify a variable to retrieve the key, expressions cannot be used. The expression that evaluates the index must evaluate to an integer and be in compacted form.
c igtk 0 -> x ✅c igtk 2+1 -> x ✅c igtk 2 + 1 -> x ❌c igtk 0 -> x+1 ❌c igtk 0.1 -> x ❌
- The "igtv" (indexed get value) operation
[container] igtv [expression] -> [variable]
Evaluates the expression and given the resulting value as an index, retrieves the value at that position and stores it in the specified variable. Its use case is to retrieve values by index in containers that don't have integer valued keys.
Must specify a variable to retrieve the value, expressions cannot be used. The expression that evaluates the index must evaluate to an integer and be in compacted form.
c igtv 0 -> x ✅c igtv 2+1 -> x ✅c igtv 2 + 1 -> x ❌c igtv 0 -> x+1 ❌c igtv 0.1 -> x ❌