feat: translate doc to en, add index files
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# Error output
|
||||
|
||||
When a runtime error occurs, the VM prints its state at the time it stopped. The output includes:
|
||||
|
||||
1. The instruction pointer;
|
||||
2. The data stack;
|
||||
3. The environment stack;
|
||||
4. The call stack.
|
||||
|
||||
## Instruction pointer (IP)
|
||||
|
||||
The instruction pointer is useful for debugging alongside the program's opcode listing. Run the VM with the `--debug` (`-d`) flag and the path to a compiled program to print its opcodes without running it.
|
||||
|
||||
The output looks roughly like this:
|
||||
|
||||
```
|
||||
0000: MK_ENVTABLE
|
||||
0001: JMP IP: 53
|
||||
0003: MK_ENVTABLE
|
||||
0004: LOADCONST #0 ('a')
|
||||
0006: STORE
|
||||
0007: LOADCONST #0 ('a')
|
||||
0009: WRITE
|
||||
0010: MK_ENVTABLE
|
||||
0011: LOADCONST #0 ('a')
|
||||
0013: LOOKUP_ENV
|
||||
0014: GET_FIELD #0
|
||||
...
|
||||
```
|
||||
|
||||
The instruction index helps you find the instruction where the exception occurred. See the [opcode reference](../internals/opcodes.md) for the complete list and descriptions.
|
||||
|
||||
## Data stack
|
||||
|
||||
The data stack is printed as a list of values in the form `[T] V`, where `T` is the type number and `V` is the value. Strings and objects are prefixed with `str: V` and `ref: V`, respectively.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
> Data stack:
|
||||
> [2] str: 'this'
|
||||
```
|
||||
|
||||
Values are printed from top to bottom, so the most recent values appear first.
|
||||
|
||||
## Environment stack
|
||||
|
||||
The environment stack is printed as two lists: objects owned by the environment and names local to it.
|
||||
|
||||
Example environment:
|
||||
|
||||
```
|
||||
> Environments:
|
||||
> [1] 0x55a9d8196a20:
|
||||
> Owned objects (0/2):
|
||||
> Local entries (3/4):
|
||||
> [14] b = 4
|
||||
> [1] a = ref 0x55a9d81969e0
|
||||
> [0] this = 0
|
||||
> [0] 0x55a9d8196930:
|
||||
> Owned objects (1/2):
|
||||
> ref: 0x55a9d81969e0 (entries: 1)
|
||||
> Local entries (1/2):
|
||||
> [1] sum = ref 0x55a9d81969e0
|
||||
```
|
||||
|
||||
`> [N] A` identifies the environment table itself. `N` is the table's nesting level (larger values mean a deeper environment), and `A` is its memory address.
|
||||
|
||||
The first list is `Owned objects`; it shows the number of elements and the allocated array size as `(<count>/<capacity>)`. Objects are listed as `ref: A (entries: N)`, where `A` is the object's memory address and `N` is the number of fields it contains.
|
||||
|
||||
The second list is `Local entries`, with the number of entries and allocated capacity. Each entry has the form `[T] N = V`, where `T` is the type number, `N` is the entry name, and `V` is its value.
|
||||
|
||||
## Call stack
|
||||
|
||||
The call stack contains return addresses. An address is pushed when the `CALL` opcode runs and popped by `RET`.
|
||||
|
||||
```
|
||||
> Call stack:
|
||||
> [0] 53
|
||||
```
|
||||
|
||||
Each entry has the form `[L] IP`, where `L` is the nesting level (larger means deeper) and `IP` is the instruction index to jump to after returning.
|
||||
|
||||
## Complete output
|
||||
|
||||
A complete error report looks like this:
|
||||
|
||||
```
|
||||
===== ERROR =====
|
||||
> Caused on IP: 21
|
||||
|
||||
> Data stack:
|
||||
> [2] str: 'this'
|
||||
|
||||
> Environments:
|
||||
> [1] 0x55a9d8196a20:
|
||||
> Owned objects (0/2):
|
||||
> Local entries (3/4):
|
||||
> [14] b = 4
|
||||
> [1] a = ref 0x55a9d81969e0
|
||||
> [0] this = 0
|
||||
> [0] 0x55a9d8196930:
|
||||
> Owned objects (1/2):
|
||||
> ref: 0x55a9d81969e0 (entries: 1)
|
||||
> Local entries (1/2):
|
||||
> [1] sum = ref 0x55a9d81969e0
|
||||
|
||||
> Call stack:
|
||||
> [0] 53
|
||||
Error: runtime error - expected 2 values in data stack
|
||||
```
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/* FZL (FromZero Language) syntax */
|
||||
/* Version 1 */
|
||||
/* Authors: */
|
||||
/* - Samsonium (https://git.samsonium.dev/Samsonium) */
|
||||
|
||||
<program> ::= <_> <statement>+
|
||||
|
||||
<statement> ::= <class_stmt>
|
||||
| <fn_stmt>
|
||||
| <if_stmt>
|
||||
| <block_stmt>
|
||||
| <ret_stmt>
|
||||
| <expr_stmt>
|
||||
|
||||
<_> ::= " "*
|
||||
<ws> ::= " "+
|
||||
|
||||
<expr_stmt> ::= <expr> <_> ";" <_>
|
||||
<block_stmt> ::= "{" <_> <statement>* "}" <_>
|
||||
<ret_stmt> ::= "return" (<ws> <expr>)? <_> ";" <_>
|
||||
|
||||
<class_stmt> ::= "class" <ws> <name> <_> "{" <_> (<declaration> | <fn_stmt>)* "}" <_>
|
||||
<fn_stmt> ::= "fn" <ws> <name> <_> "(" <_> (<param> (<_> "," <_> <param>)*)? ")" <_> <block_stmt>
|
||||
<param> ::= <type> <ws> <name>
|
||||
|
||||
<if_stmt> ::= "if" <_> "(" <_> <expr> <_> ")" <_> <statement> (<_> "else" <ws> <statement>)?
|
||||
|
||||
/* Assign (lowest expression priority, right-associative) */
|
||||
<expr> ::= <assign_expr>
|
||||
<assign_expr> ::= <new_expr> (<_> "=" <_> <assign_expr>)?
|
||||
<new_expr> ::= ("new" <ws> <name>) | <or_expr>
|
||||
<or_expr> ::= <and_expr> (<_> "||" <_> <and_expr>)*
|
||||
<and_expr> ::= <cond_expr> (<_> "&&" <_> <cond_expr>)*
|
||||
<cond_expr> ::= <term_expr> (<_> <cond_op> <_> <term_expr>)*
|
||||
<cond_op> ::= "==" | "!=" | "<=" | ">=" | "<" | ">"
|
||||
|
||||
<term_expr> ::= <factor_expr> (<_> ("+" | "-") <_> <factor_expr>)*
|
||||
<factor_expr> ::= <power_expr> (<_> ("*" | "/" | "%") <_> <power_expr>)*
|
||||
<power_expr> ::= <unary_expr> (<_> "^" <_> <power_expr>)?
|
||||
<unary_expr> ::= (("!" | "-" | "+") <_>)* <postfix_expr>
|
||||
|
||||
/* Postfix: chain of calls and dots */
|
||||
<postfix_expr> ::= <primary_expr> (<call_suffix> | <member_suffix>)*
|
||||
<call_suffix> ::= <_> "(" <_> (<expr> (<_> "," <_> <expr>)*)? <_> ")"
|
||||
<member_suffix> ::= <_> "." <_> <name>
|
||||
|
||||
<primary_expr> ::= <literal>
|
||||
| <declaration>
|
||||
| "(" <_> <expr> <_> ")"
|
||||
| <name>
|
||||
|
||||
<declaration> ::= <type> <ws> <name>
|
||||
|
||||
<type> ::= "int" | "uint" | "bool" | "char" | "str"
|
||||
| "i8" | "i16" | "i32" | "i64"
|
||||
| "u8" | "u16" | "u32" | "u64"
|
||||
|
||||
<literal> ::= <number> | <string_literal> | "true" | "false" | "null"
|
||||
<name> ::= ([A-z] | "_") | ([A-z] | [0-9] | "_")*
|
||||
<string_literal> ::= ('"' [^"]* '"') | ("'" [^']* "'")
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Grammar (EBNF)
|
||||
|
||||
The formal grammar is maintained in the [grammar.ebnf](./grammar.ebnf) file.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Reference
|
||||
|
||||
Language and runtime reference material.
|
||||
|
||||
- [Operators](./operators.md)
|
||||
- [Type system](./types_system.md)
|
||||
- [Grammar (EBNF)](./grammar_ebnf.md)
|
||||
- [Error output](./errors_print.md)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Operators
|
||||
|
||||
FZLang supports arithmetic, comparison, logical, and unary operations. During execution, a binary operation takes two values from the stack and puts its result back. A unary operation takes one value from the stack and puts its result back.
|
||||
|
||||
## Arithmetic operations
|
||||
|
||||
| Operation | Supported types | Description |
|
||||
| --------- | --------------- | ----------- |
|
||||
| `N + N` | *all numeric types* | Add two operands |
|
||||
| `N - N` | *all numeric types* | Subtract the right operand from the left |
|
||||
| `N * N` | *all numeric types* | Multiply two operands |
|
||||
| `N / N` | *all numeric types* | Divide the left operand by the right |
|
||||
| `N % N` | *all numeric types* | Remainder of the left operand divided by the right |
|
||||
| `N ^ N` | *all numeric types* | Raise the left operand to the power of the right |
|
||||
| `-N` | *all numeric types* | Negate the operand |
|
||||
|
||||
## Comparison operations
|
||||
|
||||
| Operation | Supported types | Description |
|
||||
| --------- | --------------- | ----------- |
|
||||
| `N == N` | *all numeric types*, booleans, objects, strings | Check whether the operands are equal |
|
||||
| `N != N` | *all numeric types*, booleans, objects, strings | Check whether the operands are unequal |
|
||||
| `N > N` | *all numeric types* | Check whether the left operand is greater than the right |
|
||||
| `N >= N` | *all numeric types* | Check whether the left operand is greater than or equal to the right |
|
||||
| `N < N` | *all numeric types* | Check whether the left operand is less than the right |
|
||||
| `N <= N` | *all numeric types* | Check whether the left operand is less than or equal to the right |
|
||||
| `!N` | booleans | Invert the operand |
|
||||
|
||||
> **Important:** Comparing objects with `==` or `!=` compares their addresses, not their values.
|
||||
|
||||
## Logical operations
|
||||
|
||||
| Operation | Supported types | Description |
|
||||
| --------- | --------------- | ----------- |
|
||||
| `N && N` | booleans | Logical **AND** of two operands |
|
||||
| `N || N` | booleans | Logical **OR** of two operands |
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Type system
|
||||
|
||||
FZLang's type system has two foundations: primitives and objects.
|
||||
|
||||
Primitive types include strings, numeric types, booleans, and the internal object type. Objects currently include class instances and the `null` literal.
|
||||
|
||||
## Type table
|
||||
|
||||
| Internal name | Syntax | Description |
|
||||
| ------------- | ------ | ----------- |
|
||||
| FZL_TYPE_NULL | *none* | *currently unused* |
|
||||
| FZL_TYPE_OBJECT | *none* | Class instance or the `null` literal |
|
||||
| FZL_TYPE_STRING | `str` | String |
|
||||
| FZL_TYPE_CHAR | `char` | *currently unused* |
|
||||
| FZL_TYPE_BOOL | `bool` | Boolean |
|
||||
| FZL_TYPE_INT8 | `i8` | 8-bit signed integer |
|
||||
| FZL_TYPE_UINT8 | `u8` | 8-bit unsigned integer |
|
||||
| FZL_TYPE_INT16 | `i16` | 16-bit signed integer |
|
||||
| FZL_TYPE_UINT16 | `u16` | 16-bit unsigned integer |
|
||||
| FZL_TYPE_INT32 | `i32` | 32-bit signed integer |
|
||||
| FZL_TYPE_UINT32 | `u32` | 32-bit unsigned integer |
|
||||
| FZL_TYPE_INT64 | `i64` | 64-bit signed integer |
|
||||
| FZL_TYPE_UINT64 | `u64` | 64-bit unsigned integer |
|
||||
| FZL_TYPE_INT | `int` | Signed integer the size of a machine word |
|
||||
| FZL_TYPE_UINT | `uint` | Unsigned integer the size of a machine word |
|
||||
|
||||
## Compatibility
|
||||
|
||||
Type compatibility is checked using these rules:
|
||||
|
||||
1. If both types are objects (`FZL_TYPE_OBJECT`):
|
||||
1. If either type has the class name `<nullptr>`, the check **passes**.
|
||||
2. If both types have the same class name, the check **passes**.
|
||||
3. Otherwise, the check **fails**.
|
||||
2. If the types are equal, the check **passes**.
|
||||
3. If both types are numeric (`u8`..`uint`, `i8`..`int`), the check **passes**.
|
||||
4. If none of the rules above apply, the check **fails**.
|
||||
|
||||
**Passes** means the types are compatible; **fails** means they are not.
|
||||
|
||||
## Runtime behavior
|
||||
|
||||
Primitive values (that is, values other than `FZL_TYPE_OBJECT`) are used directly. Operations on them use the value stored in the stack slot.
|
||||
|
||||
Object values are stored on the stack as **references to objects**. Valid operations on them include:
|
||||
|
||||
- `FZL_OPCODE_ESCAPE_OBJECT` (move an object to the parent environment)
|
||||
- `FZL_OPCODE_GET_FIELD N` (read field `N` from an object)
|
||||
- `FZL_OPCODE_SET_FIELD N` (write a value to field `N` of an object)
|
||||
|
||||
Reference in New Issue
Block a user