Updated the spec
This commit is contained in:
parent
4ed6708e60
commit
6ee8e90f40
291
README.md
291
README.md
@ -8,7 +8,7 @@
|
|||||||
- [Specification](#table-of-contents)
|
- [Specification](#table-of-contents)
|
||||||
|
|
||||||
## What?
|
## What?
|
||||||
Omega is a language intended for scripting purposes. Omega is reads `.omega` files which contain Omega-code (specified below), which will then be parsed into bytecode, which can be run instantly, or later through the interpreter.
|
Omega is a language intended for scripting purposes. Omega compiler `omegac` reads `.og` files which contain Omega-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 `.ogc` files, which function as compiled Omega, which can then be run with the Omega-interpreter `omega`. If stated, the `omegac` compiler can contain the `omega` interpreter with it aswell, and in such case, can run the parsed bytecode right away with a `--run` -flag.
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
The original version of Omega 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.
|
The original version of Omega 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.
|
||||||
@ -31,7 +31,6 @@ Table of contents for the Omega spec
|
|||||||
- [General syntax](#general-syntax)
|
- [General syntax](#general-syntax)
|
||||||
- [Expressions](#expressions)
|
- [Expressions](#expressions)
|
||||||
- [Scopes](#scopes)
|
- [Scopes](#scopes)
|
||||||
- [Conditions](#conditions)
|
|
||||||
- [Values](#values)
|
- [Values](#values)
|
||||||
- [Keywords](#keywords)
|
- [Keywords](#keywords)
|
||||||
|
|
||||||
@ -67,36 +66,76 @@ Function calls are also very similar to other languages. If there esists a funct
|
|||||||
## Operators
|
## Operators
|
||||||
Operators are a number of individual and combined symbols which together form meanings which are interpreted in a special way.
|
Operators are a number of individual and combined symbols which together form meanings which are interpreted in a special way.
|
||||||
|
|
||||||
- `+`
|
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:
|
||||||
- Plus-operator. Combines the two values surrounding it.
|
- [Logical operators](#logical-operators)
|
||||||
- Examples:
|
- [Arithmetic operators](#arithmetic-operators)
|
||||||
- `2 + 3` returns `5`
|
- [Assignment operators](#assignment-operators)
|
||||||
- `"Hello" + "!"` returns `"Hello!"`
|
|
||||||
- `-`
|
### Unary and binary operators
|
||||||
- Minus-operator. Removes the second value from the first.
|
The difference between unary and binary operators is that unary operators require one value and binary operators require two values.
|
||||||
- Examples:
|
|
||||||
- `2 - 3` returns `-1`
|
Unary operators have such examples as:
|
||||||
- `*`
|
- `!` NOT operator, converts `!true` into `false` etc.
|
||||||
- Multiplication-operator. Multiplies the surrounding values.
|
- `-` minus operator, negates the next value `-5` etc.
|
||||||
- Examples:
|
|
||||||
- `2 * 3` returns `6`
|
Binary operators have such examples as:
|
||||||
- `/`
|
- `&&` AND operator, checks weather both sides of the operator are `true`.
|
||||||
- Division-operator. Divides the first value with the second.
|
- `+` plus-operator, adds both sides of the operator together.
|
||||||
- Examples:
|
|
||||||
- `6 / 3` returns `2`
|
### Logical operators
|
||||||
- `2.0 / 3` returns `0.666..`
|
These are the operators ofthen called as "conditions" and most commonly used in if-statements and such.
|
||||||
- `%`
|
|
||||||
- Modulo-operator. Retrns the remainder from the division of the first value by the second.
|
- `&&` AND binary operator. Checks weather both sides of the operator are `true`
|
||||||
- Examples:
|
- `true && true` returns true
|
||||||
- `2 % 3` returns `2`
|
- `false && true` return false
|
||||||
- `6 % 3` returns `0`
|
- `||` OR binary operator. Checks weather either side of the operator is `true`
|
||||||
- `10 % 3` returns `1`
|
- `true || true` returns true
|
||||||
- `++`
|
- `true || false` return true
|
||||||
- Addition-operator. Special operator, which adds one to the value before it.
|
- `^` XOR binary operator. Checks weather only one side of the operator is true.
|
||||||
- Examples:
|
- `true ^ true` return false
|
||||||
- `1++` returns 2
|
- `true ^ false` returns true
|
||||||
- `2++` returns 3
|
- `false ^ false` returns false
|
||||||
- Some operators are listed under [Conditions](#conditions)
|
- `==` Equals binary operator. Checks weather both sides of the operator are the same.
|
||||||
|
- `"not" == "test"` returns false
|
||||||
|
- `3 == 3` returns true
|
||||||
|
- `!=` Not equals binary operator. Checks weather both sides of the operator are **not** the same.
|
||||||
|
- `"not" != "test"` returns true
|
||||||
|
- `3 == 3` returns false
|
||||||
|
- `!` Not unary operator. Negates the value associated with it.
|
||||||
|
- `!true` returns false
|
||||||
|
- `!(true ^ true)` returns true
|
||||||
|
|
||||||
|
### Arithmetic operators
|
||||||
|
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.
|
||||||
|
|
||||||
## Scopes
|
## Scopes
|
||||||
Scopes are areas of code surrounded by brackets `{}`. E.g.
|
Scopes are areas of code surrounded by brackets `{}`. E.g.
|
||||||
@ -111,28 +150,6 @@ variable = this_is_scoped; // Exception! Cannot access inner-scope.
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Conditions
|
|
||||||
Conditions are a special kind of [expression](#expressions), which will return a `boolean` [value](#values).
|
|
||||||
|
|
||||||
Conditions may be simply `true` or `false`, or tests like `var`, where `var` is a `boolean`-type variable, or `name == "test"`, where `name` is a `string`-type variable.
|
|
||||||
|
|
||||||
Conditions can also be set as [values](#values) for variables, since they explicitly return a `boolean` value.
|
|
||||||
|
|
||||||
Operators for conditions are
|
|
||||||
- `==` for equals.
|
|
||||||
- `!=` for not equals.
|
|
||||||
- `||` for or.
|
|
||||||
- `&&` for and.
|
|
||||||
- `^` for xor.
|
|
||||||
|
|
||||||
Any of these can be mixed with any of them, and can be wrapped in parenthesis to priorisize which must be tested first. Examples:
|
|
||||||
```
|
|
||||||
let first = true; // true
|
|
||||||
let second = first == false; // false
|
|
||||||
let third = second && first; // false
|
|
||||||
let fourth = "otus" == "test" || (first ^ second); // true
|
|
||||||
```
|
|
||||||
|
|
||||||
## Values
|
## Values
|
||||||
There are a number of values you can assign to your variables, as of Omega 1.0, only primitive values are possible. Such types are:
|
There are a number of values you can assign to your variables, as of Omega 1.0, only primitive values are possible. Such types are:
|
||||||
- `string`, a basic piece of text, defined as followes: `"String here"`.
|
- `string`, a basic piece of text, defined as followes: `"String here"`.
|
||||||
@ -143,14 +160,33 @@ There are a number of values you can assign to your variables, as of Omega 1.0,
|
|||||||
- `f32` (or usually float), a basic 32-bit float value, such as `1.5` or `6.32`.
|
- `f32` (or usually float), a basic 32-bit float value, such as `1.5` or `6.32`.
|
||||||
- `f64` (or usually double), a basic 64-bit float value, such as `1.5` or `6.32`.
|
- `f64` (or usually double), a basic 64-bit float value, such as `1.5` or `6.32`.
|
||||||
- `boolean`, contains value of `true` or `false`. This also includes [conditions](#conditions).
|
- `boolean`, contains value of `true` or `false`. This also includes [conditions](#conditions).
|
||||||
|
- `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.
|
||||||
|
|
||||||
|
Values in Omega are strongly typed, meaning combining two different types cannot be combined, unless they are successfully cast.
|
||||||
|
|
||||||
|
### Conditions
|
||||||
|
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.
|
||||||
|
|
||||||
## Keywords
|
## Keywords
|
||||||
The following keywords are specified to execute an action.
|
The following keywords are specified to execute an action.
|
||||||
|
|
||||||
- [`let`](#let) initializes a new variable.
|
- [`let`](#let) initializes a new variable.
|
||||||
|
- [`if`](#if) enters the scope once if the condition is met.
|
||||||
|
- [`else`](#else)
|
||||||
- [`def`](#def) defines a new function.
|
- [`def`](#def) defines a new function.
|
||||||
- [`while`](#while) initializes a scope which will be ran while a condition applies, which is specified after the `while`.
|
- [`while`](#while) functions like `if`, but enters the scope as long as the condition is met.
|
||||||
- [`for`](#for) initializes a scope which will be ran a number of times specified after the `for`.
|
- [`for`](#for) initializes a scope which will be ran a number of times specified after the `for`.
|
||||||
|
- [`break`](#break)
|
||||||
|
- [`continue`](#continue)
|
||||||
|
- [`unwrap`](#unwrap)
|
||||||
|
- [`some`](#some)
|
||||||
|
- [`exists`](#exists)
|
||||||
|
- [`empty`](#empty)
|
||||||
|
- [`as`](#as)
|
||||||
- [All operators](#operators) are also somewhat considered as keywords.
|
- [All operators](#operators) are also somewhat considered as keywords.
|
||||||
|
|
||||||
#### `let`
|
#### `let`
|
||||||
@ -167,9 +203,46 @@ five = 3; // (without let-keyword) Re-sets five to 3
|
|||||||
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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).
|
||||||
- After the value of the variable, the `let`-expression **must** end in a semicolon `;`.
|
- After the value of the variable, the `let`-expression **must** end in a semicolon `;`.
|
||||||
|
|
||||||
|
#### `if`
|
||||||
|
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.
|
||||||
|
|
||||||
#### `def`
|
#### `def`
|
||||||
Defines a new function or method as follows:
|
Defines a new function or method as follows:
|
||||||
```
|
```
|
||||||
@ -185,39 +258,40 @@ def third_function(param1: i32, param2: string) {
|
|||||||
// Code
|
// Code
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- The initialization of the function/method **must** begin with `def`.
|
- The signature of the function/method **must** begin with `def`.
|
||||||
- After `def` there must be a number of whitespace, after which the name of the defined function must follow.
|
- After `def` there must be a number of whitespace, after which the name of the defined function must follow.
|
||||||
- Immediately after the name of the function, there must be an opening parenthesis `(`.
|
- Immediately after the name of the function, there must be an opening parenthesis `(`.
|
||||||
- After the opening bracket there may be parameters listed.
|
- After the opening bracket there may be parameters listed.
|
||||||
- Format of the parameters follows the [`let`](#let) format, without the `let`-keyword.
|
- Format of the parameters follows the [`let`](#let) format, without the `let`-keyword.
|
||||||
- There **must** also be a type-definition.
|
- There **must** also be a type-definition.
|
||||||
- There cannot be any default values. (no equals`=`-sign)
|
- There cannot be any default values. (no [assignment operators](#assignment-operators))
|
||||||
- The parameters are divided by a comma`,`, after which there may be any number of whitespace.
|
- The parameters are divided by a comma`,`, after which there may be any number of whitespace.
|
||||||
- After the list of parameters there **must** be a closing bracket `)`.
|
- After the list of parameters there **must** be a closing bracket `)`.
|
||||||
- Between the parenthesis and the parameter-lists, there may be any number of whitespace.
|
- Between the parenthesis and the parameter-lists, there may be any number of whitespace.
|
||||||
- After the parenthesis and any number of whitespace, there must be a [scope definition](#scopes).
|
- After the parenthesis and any number of whitespace, there must be the [function body](#scopes).
|
||||||
|
|
||||||
#### `while`
|
#### `while`
|
||||||
Defines a loop which will be as long as the [condition](#conditions) defined after it is met.
|
Defines a loop which will be as long as the [condition](#conditions) defined after it is met.
|
||||||
```
|
```
|
||||||
while (true) {
|
while true {
|
||||||
// Runs infinitely.
|
// Runs infinitely.
|
||||||
}
|
}
|
||||||
|
|
||||||
while (false) {
|
while false {
|
||||||
// Never enters this loop.
|
// Never enters this loop.
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true == false) {
|
while true == false {
|
||||||
// Also never enters this loop.
|
// Also never enters this loop.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- To specify a while, the line **must** begin with a `while`.
|
- To specify a while, the line **must** begin with a `while`.
|
||||||
- After the `while`, there can be a number of whitespace, after which there **must** be parenthesis, within which (like parameters are in `def`), there must be a `boolean` [value](#values) or otherwise known as a [condition](#conditions).
|
- 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 parenthesis there must be a [scope definition](#scopes).
|
- After the value there must be a [scope definition](#scopes).
|
||||||
|
|
||||||
#### `for`
|
#### `for`
|
||||||
|
**TODO: Decide weather to use C-style or Rust-style for-loops**
|
||||||
Defines a loop very similar to while, but which parameters inside the parenthesis consists of three parts separated by semicolons`;`.
|
Defines a loop very similar to while, but which parameters inside the parenthesis consists of three parts separated by semicolons`;`.
|
||||||
```
|
```
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
@ -228,4 +302,93 @@ for (let i = 0; i < 10; i++) {
|
|||||||
- 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 weather or not the scope inside the loop will be accessed.
|
- 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 weather or not the scope inside the loop will be accessed.
|
||||||
- The second part (`i < 10` in this example) is the condition defining weather the loop-scope will be accessed or not.
|
- The second part (`i < 10` in this example) is the condition defining weather the loop-scope will be accessed or not.
|
||||||
- The third part (`i++` in this example) is the step-expression, which will be executed after each execution of the loop-scope.
|
- The third part (`i++` in this example) is the step-expression, which will be executed after each execution of the loop-scope.
|
||||||
|
- Another difference to while where parenthesis are **not** necessary, in `for`, te parenthesis around these three parts **are** necessary.
|
||||||
- Otherwise `for` is identical to [`while`](#while)
|
- Otherwise `for` is identical to [`while`](#while)
|
||||||
|
|
||||||
|
#### `break`
|
||||||
|
Break is a simple keyword used to break a loop immediately.
|
||||||
|
```
|
||||||
|
while (true) {
|
||||||
|
break; // The loop only enters once, then leaves.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The break must end with a semicolon`;`.
|
||||||
|
- If there is no loop and break is called, an exception occurs.
|
||||||
|
|
||||||
|
#### `continue`
|
||||||
|
Continue is a simple keyword to skip the rest of the loop's body.
|
||||||
|
```
|
||||||
|
while (true) {
|
||||||
|
continue;
|
||||||
|
print("Hello!"); // This code is never reached.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The continue must end with a semicolon`;`.
|
||||||
|
- If there is no loop and continue is called, an exception occurs.
|
||||||
|
|
||||||
|
#### `unwrap`
|
||||||
|
Unwrap is a keyword used to unwrap an optional variable.
|
||||||
|
```
|
||||||
|
let optional: i32? = some_number as i32;
|
||||||
|
let number: i32 = unwrap optional;
|
||||||
|
```
|
||||||
|
|
||||||
|
- unwrap must be followed by an optional variable. If an un-optional variable is given to unwrap, an exception occurs.
|
||||||
|
- there must be a space between `unwrap` and the optional value.
|
||||||
|
|
||||||
|
#### `some`
|
||||||
|
Some is a keyword used to wrap a variable to create an optional variable.
|
||||||
|
```
|
||||||
|
let number: i32 = some_number;
|
||||||
|
let optional: i32? = some number;
|
||||||
|
```
|
||||||
|
|
||||||
|
- Some must be followed by a variable. The variable is then wrapped into an optional and the optional is returned.
|
||||||
|
- There must be a space between the `some` and the variable.
|
||||||
|
|
||||||
|
#### `exists`
|
||||||
|
Exists is a keyword that is used to check weather an optional variable contains a value or not.
|
||||||
|
```
|
||||||
|
let optional: i32? = something();
|
||||||
|
if (optional exists) {
|
||||||
|
// optional contains a value
|
||||||
|
} else {
|
||||||
|
// optional is empty.
|
||||||
|
}
|
||||||
|
|
||||||
|
let op_exists: boolean = optional exists;
|
||||||
|
```
|
||||||
|
- Unlike `unwrap` and `some`, `exists` must follow the optional value. Exists-keyword then returns the result as a boolean.
|
||||||
|
- There must be a space between the optional and `exists`
|
||||||
|
- If `exists` is after a non-optional variable, an exeption occurs.
|
||||||
|
|
||||||
|
#### `empty`
|
||||||
|
Empty is the keyword used to set an optional as empty (to not contain a value).
|
||||||
|
```
|
||||||
|
let optional: i32? = empty;
|
||||||
|
if (optional exists) {
|
||||||
|
// Never executed
|
||||||
|
} else {
|
||||||
|
// Executed, since optional is empty
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
```
|
||||||
|
let long: i64 = 5;
|
||||||
|
let int_opt: i32? = long as i32;
|
||||||
|
let int = 0;
|
||||||
|
if (int_opt exists) {
|
||||||
|
int = unwrap int_opt;
|
||||||
|
} else {
|
||||||
|
// Cast failed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Before `as` there must be a variable, or a value, and after there must be the type which the value is attempted to be cast as.
|
||||||
|
Loading…
Reference in New Issue
Block a user