Reid is a language intended for scripting purposes. Reid compiler `reidc` reads `.reid` files which contain Reid-code (specified below), which will then be parsed into bytecode. The parsed bytecode then, unless otherwise stated (via a `--no-output` compile-flag), will produce `.reidc` files, which function as compiled Reid, which can then be run with the Reid-interpreter `reid`. If stated, the `reidc` compiler can contain the `reid` interpreter with it aswell, and in such case, can run the parsed bytecode right away with a `--run` -flag.
**But why "Reid"?**
Reid is a letter in the old nordic alphabet that means "ride" or "journey", so the meaning was quite fitting (this langauge is quite a "wild ride"), and the name was pretty cool, and the letter made a cool and easy to make logo.
The original version of Reid was written in TypeScript for a NodeJS server for a dungeons and dragons system client. The new server is being written in Rust, so the language must be re-written. To make the process easier, here is the specifications (and technically the documentation) for the re-visited version of the language. To the same repository I will be creating the actual Rust implementation of this language too.
Reid is created by [Teascade][teascade], original version being written with TypeScript in 2016, and new specification and Rust implementation written in 2017.
The general syntax of Reid is fairly similar to that of [TypeScript][typescript] or [Rust][rust]. The syntax is a mix of [keywords](#keywords) and [expressions](#expressions) displayed such as in the [examples](#examples).
Expressions are a set of [values](#values), [function calls](#function-calls) and [operators](#operators) that Reid interprets and returns a new [value](#values) out of.
Function calls are also very similar to other languages. If there esists a function called `function_one`, it can be called with `function_one();`. If there exists a function called `function_two` which requires two i32's as arguments, it can be called as `function_two(5, 2);` where 5 and 2 are example [integer values](#values).
There a few types of operators, and all of these types are [either unary of binary operators](#unary-and-binary-operators), although some of them are valid as both:
- [Logical operators](#logical-operators)
- [Arithmetic operators](#arithmetic-operators)
- [Assignment operators](#assignment-operators)
### Unary and binary operators
The difference between unary and binary operators is that unary operators require one value and binary operators require two values.
Unary operators have such examples as:
-`!` NOT operator, converts `!true` into `false` etc.
-`-` minus operator, negates the next value `-5` etc.
Arithmetic operators are operators used to do math calculations such as addition or multiplication. any integer or float -based types can be used together.
-`+` Plus binary and unary operator. As a binary operator combines both sides of the operator, as an unary operator simply returns the associated value.
-`+ 3` returns 3
-`2 + 3` returns 5
-`-` Minus binary and unary operator. As binary operator subtracts the latter side of the operator from the first, as an unary operator simply returns the negated associated value.
-`- 3` returns -3
-`2 - 3` returns -1
-`*` Multiplication binary operator. Returns the value of the both sides multiplied.
-`2 * 3` returns 6
-`5 * 5` returns 25
-`/` Division binary operator. Returns the division of the first value with the second.
-`2.0 / 3` returns 0.666..
-`6 / 2` returns 3
-`%` Modulo binary operator. Returns the remainder of the division between the two values.
-`2 % 3` returns 2
-`6 % 2` returns 0
### Assignment operators
Assignment operators are a special kind of operator used assign values to variables.
The most basic type of assignment operator being of course `=`.
For example:
-`test_var = 3` sets the value of test_var to 3
`=` can be combined with _any_ of the binary arithmetic operators however to create other kinds of assignment operators which work like so:
-`test_var += 3` would be the same as `test_var = test_var + 3`
-`test_var *= 3` would be the same as `test_var = test_var * 3`
If you however try to use these kinds of assignment operators on variabes which have not been initialized with a value yet, an exception will occur.
As is visible in the example, variables defined in the scope are no longer accessible outside the scope. Scopes exist in their individual "environments", where they can access the variables in their upper scopes, but not inner scopes.
Parenthesis`()` can be added to surround any [operators](#operators), [expressions](#expressions), [values](#values) or [keywords](#keywords) to guide on what order and how the code should be run.
For example:
-`(2 + 3) * 5` = `5 * 5` = `25`
-`!(true ^ true)`
-`(print("test"))` Here parenthesis won't do much through
-`2 + (3)` Here parenthesis are somewhat useless aswell.
-`T?` is an optional type. Any type can be an optional type, but the optional type must be checked with [`var?`](#operators)-operator before it can be accessed via [`unwrap`](#unwrap), or the execution will crash.
-`T[]` is a primitive array-type. **warning: this part is heavily work-in-progress**
- New arrays may be created as such `T[len]()`, where T is the type of the array, and len is the size of it.
- For example: `i32[4]()` would create an `i32`-array with 4 slots.
- Slots in an array are accessible with the standard `array[i]` syntax.
For the sake of glossary, conditions can simply be `true` or `false`, but in all cases where "conditions" are said, [logical operators](#logical-operators) also apply.
When using numbers directly (like `5`, `32` or `753`), if their type cannot be deducted easily (via parameter type or variable type), the number's type will default to `i32`, then `i64`, then `i16`, then `f32` and finally `f64`.
If you however use numbers with decimals like `5.0`, `32.2` or `73.1`, their type will default to `f32` and then `f64`.
If it is necessary to specify the type of the number (ie. for [function overloading](#function-overloading)), you can simply add the type immediately after the number, e.g. `5i32`, `32f32`, or `12i16`.
There are some special (mostly arithmetic) cases where different languages might act one way or another, so here are a list of those cases and how Reid handles them:
This also causes a runtime exception, as can be deducted.
**Integer under-/overflow**
Trying to assign a number larger or smaller than the byte-limit of the type allows (ie. larger than `2147483647` for `i32` or smaller than `-2147483647`), will cause a runtime exception.
[Function overloading][overloading] in Reid is possible, meaning you can create functions that have the same name as other already existing functions, but the parameter count and/or parameter types must differ.
ie. you could have two functions called `test`, both of which require a parameter `param`, but the other function's `param` is a string type, and the other is `i32`, like so:
```
def test(param: string) {
// Code
}
def test(param: i32) {
// Code
}
```
When calling overloaded functions though, keep in mind that if you have e.g. `test(param: i32)` and `test(param: i64)`, when calling it by `test(5)`, the first overload will be called, since `5` defaults to `i32` (See [using numbers](#using-numbers) under [values](#values)). To call the `i64` version, you need to specify the type by calling `test(5i64)`
- Initialization of new variable **must** contain `let`, but re-definition of an existing variable, **cannot** start with `let`.
- The name of the variable being defined must follow the `let` after whitespace.
- After the name of the variable, there _may_ be a definition of the type of the variable, but it is not necassary. When re-defining a value of a variable, there **cannot** be a re-definition of the type.
- If there is no type-definition, an initializing value must be set.
- type-definition's form is as follows: `: T`, and it cannot be preceded by whitespace. between the colon and the `T` there may be whitespace.
- After whitespace, there may be (or must be, if no type-definition is given), an equals`=`-sign, after which there must be more whitespace, after which the [value](#value) of the variable is given. This is simply an [assignment operator](#assignment-operators).
Defines an if-statement, which will, if the condition is met, enter the scope defined _after_ the if.
```
if true {
// Executed code
}
if "test" == "not true" {
// Not executed code
}
```
- begins with an `if`, after which there must be some whitespace. After the whitespace there must be [a condition](#conditions).
- After the condition there must be some whitespace, after which there is the [scope definition](#scopes)
#### `else`
Defines an else-statement, which must proceed after the if-statement's scope. if the if statement's condition was not met, else will be entered. An if-statement can be added immediately after the `else`, to chain them up.
```
if false {
// Not executed code
} else {
// Executed code
}
if false {
// Not executed code
} else if "test1" == "test2" {
// Also not executed code
} else {
// Executed code
}
```
- The else keyword must follow immediately after the if-statement's body (as seen in the example). Only whitespace is allowed in the middle.
- After the else, there can be a new if-statement, without a body for the else itself.
- If there is no if-statement following the else, there must be a body for the else itself, which will then be executed if the preceding if-statement was not executed.
- After the `while`, there can be a number of whitespace, after which there must be a `boolean` [value](#values) or otherwise known as a [condition](#conditions).
- After the value there must be a [scope definition](#scopes).
- The first part (`let i = 0` in this example) is the beginning-expression. It can be any expression, and it will be executed as the loop begins whether or not the scope inside the loop will be accessed.
- The second part (`i <10`inthisexample)istheconditiondefiningwhethertheloop-scopewillbeaccessedornot.
- Like `true` or `false`, empty is used as a value, except it can only be used in an [assignment operator](#assignment-operators)
- Trying to set a non-optional value as empty will cause an exception.
#### `as`
As is the keyword used when you need to cast a variable to another. It will return the casted result as an optional which will be empty if the cast failed.