diff --git a/README.md b/README.md index 80001d9..88fa784 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # FromZero / Language -[README in Russian](./README.ru.md) +[README in Russian](./README.ru.md) · [Documentation](./docs/en/index.md) **FromZero/Language** is a package consisting of a JIT compiler and a virtual machine for the FZL language. @@ -24,14 +24,6 @@ At the moment, the language has the following capabilities: Working examples of code can be found in `exl/` directory. -## File extensions - -Standard file extensions: - -- `.fzs` - source code -- `.fzb` - compiled binary file - - ## Building 1. Clone the repository: `git clone https://git.samsonium.dev/samsonium/fz-lang.git` diff --git a/README.ru.md b/README.ru.md index 875cdb5..57a9d3d 100644 --- a/README.ru.md +++ b/README.ru.md @@ -1,6 +1,6 @@ # FromZero / Language -[README in English](./README.md) +[README in English](./README.md) · [Документация](./docs/ru/index.md) **FromZero/Language** - пакет из JIT-компилятора и виртуальной машины для языка FZL. @@ -24,14 +24,6 @@ Примеры кода лежат в директории `exl/`. -## Расширения файлов - -Стандартными для языка являются два расширения файлов: - -- `.fzs` - исходный код -- `.fzb` - скомпилированный бинарный файл - - ## Инструкции по сборке 1. Склонировать проект: `git clone https://git.samsonium.dev/samsonium/fz-lang.git` diff --git a/docs/en/book/00_introduction.md b/docs/en/book/00_introduction.md new file mode 100644 index 0000000..a710eef --- /dev/null +++ b/docs/en/book/00_introduction.md @@ -0,0 +1,34 @@ +# Introduction + +Welcome to this short book on the basics of FZLang, and thank you for giving this programming language a try. + +First, let's talk about what FZLang is and how this book is organized. + +## What is FZLang? + +FZLang is a programming language with its own toolchain, which includes a compiler and a virtual machine. The compiler checks and compiles source code into a portable file, and the virtual machine runs programs written in FZLang. + +This project originally began as a way to learn about building compilers. I (Samsonium) wanted to explore a few questions: + +- Why is there a meaningful difference between a compiler and an interpreter? +- How does a program called a compiler understand what I want it to do? +- How does execution of a compiled program work? And how does the process of translating from human-readable code to machine instructions happen? + +I've been programming for more than ten years, but had never taken the time to find out! + +So this project became a place to experiment and learn more about the foundations of the tools programmers use every day. Along the way, the language began to take shape and develop a philosophy of its own. + +Today, I would describe that philosophy as: “Be as convenient as a tool, while always trying to remain predictable.” I imagine every language community wants something similar for its language, but this principle has helped me make choices at important points in the project's development. + +### How FZLang handles memory + +The question first came up when I introduced stacks to the virtual machine: “A garbage collector, or something else?” Learning how garbage collection works would certainly have been interesting. However, having chosen Java as my first language, I was familiar with its “weight” and runtime behavior. Of course, its garbage collector deserves some of the credit for that. + +So instead of a GC, I decided to explore RAII. This led to a simple model for how objects move through a program: when an object is passed as an argument, it is “borrowed” into a nested environment; when returned, it is moved into the parent environment. This approach avoids a GC and, most importantly, gives objects a deterministic lifetime—and therefore more predictable memory use, as far as possible. + +In short, FZLang uses scope-based RAII. + +--- + +Next: [Getting started](./01_getting-started.md) + diff --git a/docs/en/book/01_getting-started.md b/docs/en/book/01_getting-started.md new file mode 100644 index 0000000..b7009c1 --- /dev/null +++ b/docs/en/book/01_getting-started.md @@ -0,0 +1,44 @@ +# Getting started + +To get started with FZLang, you need two executable files on your computer: + +- `fzc` — the compiler +- `fz` — the virtual machine + +To compile a program, run `fzc` and pass it the paths to your source files. If compilation succeeds, run the compiled program with `fz`, passing the path to the compiled file. If you do not specify an output path for the compiler, it saves the compiled program as `a.fzb`. + +## Compiler options + +To see the available compiler options, run `fzc --help` (or `fzc -h`). The help currently looks like this: + +``` +Usage: fzc [flags] +Available flags: + -h --help Print help + -v --verbose Display compilation info (tokens, AST, consts table, etc) + -V --version Print compiler version + -o --out Set compiled file name +``` + +The `-h` (`--help`) flag prints the same help text. The `-v` / `--verbose` flags print detailed compilation information, including: + +- The tokens that were read; +- The syntax tree that was built; +- The values stored in the constants table; +- The opcodes written to the program. + +The `-V` / `--version` flags print the compiler version. The `-o` / `--out` flags let you choose the output file path. + +## Virtual machine options + +The virtual machine has two options: `--help` (`-h`) and `-d`. + +- `--help` (or `-h`) prints the help text; +- `-d ` prints the opcodes with their indices. Use this flag to see how the VM will read your program. + +--- + +Next: [Basics and types](./02_basics-and-types.md) + +Previous: [Introduction](./00_introduction.md) + diff --git a/docs/en/book/02_basics-and-types.md b/docs/en/book/02_basics-and-types.md new file mode 100644 index 0000000..61e54f9 --- /dev/null +++ b/docs/en/book/02_basics-and-types.md @@ -0,0 +1,161 @@ +# Basics and types + +In our first program, we asked FZLang to print some text. That's a good start: a program isn't mysterious; it's a set of instructions that the computer carries out one after another. Now let's see how those instructions work, how to store data, and how to ask a program to make a decision. + +Don't worry if some words—such as “type” or “expression”—sound unfamiliar for now. We'll unpack them with small examples. Try changing the values as you read: seeing what changes is one of the best ways to understand what a program does. + +## A first program: instructions and comments + +The built-in `printline` function writes the value you pass to it to standard output: + +```fz +printline("Hello, FZLang!"); +``` + +Here we call `printline` and pass it text inside parentheses. The quotation marks show where the text begins and ends. The semicolon marks the end of the instruction. Try replacing the greeting with your own—the program will print the text you gave it. + +Code doesn't have to contain only instructions. You can add notes for yourself and other readers, called comments. A single-line comment starts with `//`; the program ignores everything from those characters to the end of the line: + +```fz +// First, calculate a result, then print it. +printline(2 + 2); +``` + +A comment doesn't affect how the program runs. It helps explain why the code is there, especially when you come back to it later. + +## Variables: giving values names + +Imagine that a program needs to use the same value more than once. You could write it out every time, but it's more convenient to save it under a name. That's what variables are: named places to keep values. + +To declare a variable, write its type, its name, and, if you like, an initial value: + +```fz +int count = 10; +str greeting = "Hello there!"; +bool ready = true; +``` + +These lines create three variables: `count` stores a number, `greeting` stores text, and `ready` stores a yes-or-no value. A type helps the language understand what kind of value we're working with. For example, numbers can be added, while text is useful for messages. + +You can also declare a variable first and assign a value to it later: + +```fz +u64 total; +total = 15; +printline(total); +``` + +Once a variable has been declared, you can use its name to read its value or assign it a new one. For example, `count = count + 1;` means: take the current value of `count`, add one, and store the result back in `count`. If it was 10, it will be 11 afterward. + +### Numbers: integers and their sizes + +FZLang has signed and unsigned integer types. Signed numbers can be negative; unsigned types are for non-negative values. For example, a temperature below zero fits a signed type, while a visitor count can use an unsigned type: + +```fz +int temperature = -5; +uint visitors = 42; +``` + +For more precise choices, there are fixed-width types: `i8`, `i16`, `i32`, `i64` and their unsigned counterparts `u8`, `u16`, `u32`, `u64`. The number after `i` or `u` tells you the type's width in bits. `int` and `uint` are the size of a machine word. + +```fz +i16 offset = -120; +u8 level = 7; +``` + +For now, it's enough to remember that `int` works well for ordinary integers. You can choose an exact width when it matters. Numeric types are compatible with one another under the language's rules, but you still state a variable's type when declaring it. + +### Boolean values and text + +The `bool` type has just two values: `true` and `false`. It's useful for storing the answer to a question—for example, whether a door is open or a program is ready to continue. + +```fz +bool is_open = true; +printline(is_open); +``` + +Text uses the `str` type, short for “string”: + +```fz +str language = "FZLang"; +printline(language); +``` + +The type reference also lists `char`, but it isn't currently used by the language. We'll leave classes and object values for later sections; for now, numbers, text, and boolean values are plenty to get comfortable with. + +## Expressions: asking the program to calculate + +An expression is a piece of code that can be evaluated to produce a value. The simple expression `2 + 2` is one example. Expressions can be stored in variables or passed to functions: + +```fz +int result = 2 + 2 * 2; +printline(result); +``` + +The program evaluates the expression, stores the result in `result`, and then prints it. Just as in ordinary arithmetic, multiplication happens before addition, so the result is 6. To change the order, use parentheses: `(2 + 2) * 2`. + +Numbers support addition `+`, subtraction `-`, multiplication `*`, division `/`, remainder `%`, and exponentiation `^`. A unary minus lets you write a negative number, such as `-5`. + +Comparisons let us ask questions about values. `==` and `!=` check whether values are equal or unequal; `<`, `<=`, `>` and `>=` compare numbers. A comparison produces a boolean value, either `true` or `false`: + +```fz +bool enough = result >= 4 && result != 0; +printline(enough); +``` + +Here `enough` is true only if `result` is at least 4 and is not zero. The `&&` operator means “and,” `||` means “or,” and `!` flips a boolean value. The [Operators](../reference/operators.md) reference lists all operations and their limitations. Note that comparing objects with `==` or `!=` checks whether the variables refer to the same object, not whether the objects contain the same values. + +## Blocks and conditions: choosing what happens next + +Sometimes a program needs to do different things in different situations. The `if` condition lets it choose a path. It checks a boolean expression and runs the matching branch: + +```fz +bool is_ready = true; + +if (is_ready) +{ + printline("We can continue"); +} +else +{ + printline("Let's wait"); +} +``` + +If `is_ready` is `true`, the program prints the first message; otherwise, it prints the second. You can leave out `else` when there's nothing to do in the other case. If there are more than two possibilities, add another condition with `else if`. + +Curly braces `{}` group instructions into a block. You've already seen one in the condition example; functions use blocks too. Blocks make it clear which instructions belong together. An `if` condition must have type `bool`, such as a comparison result or a boolean variable. + +## Functions: putting useful actions together + +When you want to use the same sequence of actions in different places, you can put it in a function. A function receives values through parameters, runs instructions, and can return a result. + +```fz +fn sum(int left, int right) int +{ + return left + right; +} + +int result = sum(2, 3); +printline(result); +``` + +A declaration starts with `fn`, followed by the function name, its parameters, and its result type. Here, `sum` takes two `int` values, adds them, and returns an `int`. The `return` keyword sends the result back to the code that called the function. That's why `sum(2, 3)` can be used as an expression—for example, to initialize a variable. + +## What to remember + +You've now met some of the basic building blocks of a program: instructions, comments, variables, types, expressions, conditions, and functions. There's no need to memorize every operator right away; you can return to the reference as you need it. + +- A variable has a name and a type, and stores a value. +- For your first programs, you'll use numbers (`int`, `uint`, and fixed-width types), boolean values (`bool`), and strings (`str`). +- Expressions calculate new values, and comparisons help a program make decisions. +- `if` conditions and `{}` blocks control which instructions the program runs. +- Functions group actions and can return a result. +- Instructions usually end with a semicolon, and `printline` helps you see what your program did. + +--- + +Next: [Type system](../reference/types_system.md) + +Previous: [Getting started](./01_getting-started.md) + diff --git a/docs/en/book/index.md b/docs/en/book/index.md new file mode 100644 index 0000000..58ff43d --- /dev/null +++ b/docs/en/book/index.md @@ -0,0 +1,8 @@ +# The FZLang book + +A gentle introduction to FZLang, from getting set up to writing programs with variables, types, and functions. + +- [Introduction](./00_introduction.md) +- [Getting started](./01_getting-started.md) +- [Basics and types](./02_basics-and-types.md) + diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..109a37c --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,12 @@ +# FZLang documentation + +Choose a section to explore the FZLang documentation. + +- [The book](./book/index.md) — a guided introduction to the language. +- [Reference](./reference/index.md) — types, operators, grammar, and runtime diagnostics. +- [Internals](./internals/index.md) — virtual machine implementation details. +- [Standard library](./stdlib/index.md) — built-in functions. +- [Language philosophy](./philosopy.md) + +[Документация на русском языке](../ru/index.md) + diff --git a/docs/en/internals/index.md b/docs/en/internals/index.md new file mode 100644 index 0000000..5ab3659 --- /dev/null +++ b/docs/en/internals/index.md @@ -0,0 +1,8 @@ +# Internals + +Implementation details of the FZLang virtual machine and compiled programs. + +- [Object layout](./object_layout.md) +- [Opcodes](./opcodes.md) +- [Virtual machine stacks](./stacks.md) + diff --git a/docs/en/internals/object_layout.md b/docs/en/internals/object_layout.md new file mode 100644 index 0000000..4a509c8 --- /dev/null +++ b/docs/en/internals/object_layout.md @@ -0,0 +1,4 @@ +# Object layout + +This page is a placeholder for documentation of the in-memory layout of FZLang objects. + diff --git a/docs/en/internals/opcodes.md b/docs/en/internals/opcodes.md new file mode 100644 index 0000000..bce2022 --- /dev/null +++ b/docs/en/internals/opcodes.md @@ -0,0 +1,118 @@ +# Opcodes + +FZLang programs run on the virtual machine included in the FZLang toolchain, which is built alongside the compiler. The FZC compiler can emit the following opcodes, which the VM understands and executes. + +## Loading data and working with the stack + +Codes: `0xF00001`..`0xF00004` + +| Number | Name | Purpose | Argument | +| ------ | ---- | ------- | -------- | +| 0xF00001 | FZL_OPCODE_LOADCONST | Push constant `N` onto the stack | `N` — constant index | +| 0xF00002 | FZL_OPCODE_CALL_BUILTIN | Call built-in function `N` | `N` — function index | +| 0xF00003 | FZL_OPCODE_PUSHNUM | Push number `N` onto the stack | `N` — value | +| 0xF00004 | FZL_OPCODE_PUSHBOOL | Push boolean `B` onto the stack | `B` — value | + +## Context and environment + +Codes: `0xF10001`..`0xF10005` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xF10001 | FZL_OPCODE_MK_ENVTABLE | Create a new environment | +| 0xF10002 | FZL_OPCODE_RM_ENVTABLE | Destroy an environment | +| 0xF10003 | FZL_OPCODE_STORE | Create a name in the environment from the value at the top of the stack | +| 0xF10004 | FZL_OPCODE_LOOKUP_ENV | Take a name from the stack, look it up in the environment, and push its value | +| 0xF10005 | FZL_OPCODE_WRITE | Take a name and value from the stack and write the value to the environment | + +## Control flow + +Codes: `0xF20001`..`0xF20002` + +| Number | Name | Purpose | Argument | +| ------ | ---- | ------- | -------- | +| 0xF20001 | FZL_OPCODE_JMP | Jump to address `IP` | `IP` — target address | +| 0xF20002 | FZL_OPCODE_JZ | Jump to address `IP` if the value at the top of the stack is zero | `IP` — target address | + +## Function calls + +Codes: `0xF30001`..`0xF30002` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xF30001 | FZL_OPCODE_CALL | Push the current address onto the call stack and jump to the start of a function | +| 0xF30002 | FZL_OPCODE_RET | Pop the last address from the call stack and jump to it | + +## Objects + +Codes: `0xFA0001`..`0xFA0005` + +| Number | Name | Purpose | Argument | +| ------ | ---- | ------- | -------- | +| 0xFA0001 | FZL_OPCODE_ALLOC_OBJECT | Allocate an object for `N` values and push it onto the stack | `N` — number of values | +| 0xFA0002 | FZL_OPCODE_GET_FIELD | Pop an object reference from the stack and get field `N` | `N` — field index | +| 0xFA0003 | FZL_OPCODE_SET_FIELD | Pop an object reference and a value, then write the value to field `N` | `N` — field index | +| 0xFA0004 | FZL_OPCODE_ESCAPE_OBJECT | Move an object (including its children) to the parent environment | none | + +## Arithmetic operations + +Codes: `0xA00001`..`0xA00007` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xA00001 | FZL_OPCODE_ADD | Pop two numbers, add them, and push the result | +| 0xA00002 | FZL_OPCODE_SUB | Pop two numbers, subtract the first from the second, and push the result | +| 0xA00003 | FZL_OPCODE_MUL | Pop two numbers, multiply them, and push the result | +| 0xA00004 | FZL_OPCODE_DIV | Pop two numbers, divide the first by the second, and push the result | +| 0xA00005 | FZL_OPCODE_POW | Pop two numbers, raise the second to the power of the first, and push the result | +| 0xA00006 | FZL_OPCODE_MOD | Pop two numbers and push the remainder of the second divided by the first | +| 0xA00007 | FZL_OPCODE_NEG | Pop a number, negate it, and push it back onto the stack | + +## Numeric comparisons + +Codes: `0xB00001`..`0xB00006` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xB00001 | FZL_OPCODE_CMP_EQ | Pop two numbers, compare them for equality, and push the result | +| 0xB00002 | FZL_OPCODE_CMP_NEQ | Pop two numbers, compare them for inequality, and push the result | +| 0xB00003 | FZL_OPCODE_CMP_LT | Pop two numbers, check whether the second is less than the first, and push the result | +| 0xB00004 | FZL_OPCODE_CMP_LTEQ | Pop two numbers, check whether the second is less than or equal to the first, and push the result | +| 0xB00005 | FZL_OPCODE_CMP_GT | Pop two numbers, check whether the second is greater than the first, and push the result | +| 0xB00006 | FZL_OPCODE_CMP_GTEQ | Pop two numbers, check whether the second is greater than or equal to the first, and push the result | + +## String comparisons + +Codes: `0xB10001`..`0xB10002` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xB10001 | FZL_OPCODE_CMP_EQ_STR | Pop two strings, compare them for equality, and push the result | +| 0xB10002 | FZL_OPCODE_CMP_NEQ_STR | Pop two strings, compare them for inequality, and push the result | + +## Object comparisons + +Codes: `0xB20001`..`0xB20002` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xB20001 | FZL_OPCODE_CMP_EQ_OBJ | Pop two objects, compare them for equality, and push the result | +| 0xB20002 | FZL_OPCODE_CMP_NEQ_OBJ | Pop two objects, compare them for inequality, and push the result | + +## Binary logical operations + +Codes: `0xC00001`..`0xC00002` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xC00001 | FZL_OPCODE_AND | Pop two values and push their logical AND | +| 0xC00002 | FZL_OPCODE_OR | Pop two values and push their logical OR | + +## Unary logical operations + +Code: `0xD00001` + +| Number | Name | Purpose | +| ------ | ---- | ------- | +| 0xD00001 | FZL_OPCODE_NOT | Pop a value and push its inverted boolean result | + diff --git a/docs/en/internals/stacks.md b/docs/en/internals/stacks.md new file mode 100644 index 0000000..aab72b6 --- /dev/null +++ b/docs/en/internals/stacks.md @@ -0,0 +1,92 @@ +# Virtual machine stacks + +While running, the FZLang virtual machine relies on three stacks: the data stack, the environment stack, and the call stack. Each stack is initialized as an array of 256 values and then zeroed. All three share the same basic property: the most recently added value is at the top and is the first one removed, decrementing the stack-top pointer. + +## Data stack + +The data stack is an array of 256 `fzl_value_t` elements. The `fzl_value_t` structure is a container for values used while a program runs. + +```c +typedef struct +{ + fzl_type_t type; + + union + { + void *obj; + + struct + { + const char *value; + size_t length; + } s; + + char c; + bool b; + + int8_t i8; + uint8_t u8; + + int16_t i16; + uint16_t u16; + + int32_t i32; + uint32_t u32; + + int64_t i64; + uint64_t u64; + + intptr_t i; + uintptr_t u; + } as; +} fzl_value_t; +``` + +The VM uses this stack to store and retrieve intermediate values while the program runs. The stack should be empty when execution finishes. + +## Environment stack + +The environment stack tracks the lexical environment in which evaluation and instructions are currently taking place. It is an array of 256 `fzl_vm_env_t` elements. + +```c +typedef struct +{ + size_t size; + size_t remainder; + + fzl_vm_env_entry_t *table; + + fzl_vm_object_t **owned_objects; + size_t owned_objects_remainder; + size_t owned_objects_count; +} fzl_vm_env_t; +``` + +Each stack element stores a table of local names (`fzl_vm_env_entry_t`) and the objects owned by the current environment. A local name is a container that wraps an `fzl_value_t` and gives it a name: + +```c +typedef struct +{ + char *name; + fzl_value_t value; +} fzl_vm_env_entry_t; +``` + +An object is an array of its fields. The number of fields is set when the object is allocated by the `FZL_OPCODE_ALLOC_OBJECT N` opcode (see the [opcode reference](./opcodes.md) for details). + +```c +typedef struct +{ + fzl_value_t *values; + size_t values_count; +} fzl_vm_object_t; +``` + +An environment is created or removed by the `FZL_OPCODE_MK_ENVTABLE` and `FZL_OPCODE_RM_ENVTABLE` opcodes, respectively. If the compiler sees a value of type `FZL_TYPE_OBJECT` being returned from a function, it inserts `FZL_OPCODE_ESCAPE_OBJECT` before `FZL_OPCODE_RET` and `FZL_OPCODE_RM_ENVTABLE`. This moves ownership of the object to the parent environment. + +## Call stack + +The call stack stores instruction addresses when entering a function through the `FZL_OPCODE_CALL` opcode. `FZL_OPCODE_RET` pops an address from the call stack and jumps to it. + +The stack itself is an array of 256 `size_t` elements. + diff --git a/docs/en/philosopy.md b/docs/en/philosopy.md new file mode 100644 index 0000000..be9d3a2 --- /dev/null +++ b/docs/en/philosopy.md @@ -0,0 +1,24 @@ +# Language philosophy + +FZLang has two guiding goals: “be convenient as a tool, while always trying to remain predictable.” + +Here are some of the ways the language tries to follow that philosophy. + +## Memory and resources + +In environments with a garbage collector (GC), programmers don't know exactly when the collector will run. An object may remain in memory for seconds after a method has finished. And when cleanup does happen, the runtime may pause briefly—something that can matter in audio, games, networking, or embedded systems. + +FZLang uses scope-based RAII, a model in which memory use is deterministic. Every object belongs to a scope, and when that scope is destroyed, the object is destroyed with it. This makes response times more consistent and predictable. + +Scope-based RAII also helps manage resources such as sockets and files. Resource cleanup follows the same clear rule: when a scope ends, everything attached to it is cleaned up. + +Finally, scope-based RAII avoids the runtime overhead of a full garbage collector. A GC needs mechanisms such as memory maps, background threads, and read and write barriers. FZLang's virtual machine is small, and memory is released as instructions are processed. + +## Syntax + +FZLang deliberately keeps some parts of its syntax explicit. For example, methods of a class instance must use `this`, and declarations require types. These are strict parts of the language. + +On the other hand, a function declaration can omit a return type when it doesn't return a value; writing `void` every time would be unnecessary. + +In this way, FZLang aims to balance syntax that keeps behavior predictable with simplifications that are genuinely useful. + diff --git a/docs/en/reference/errors_print.md b/docs/en/reference/errors_print.md new file mode 100644 index 0000000..940550a --- /dev/null +++ b/docs/en/reference/errors_print.md @@ -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 `(/)`. 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 +``` + diff --git a/docs/en/reference/grammar.ebnf b/docs/en/reference/grammar.ebnf new file mode 100644 index 0000000..d072d81 --- /dev/null +++ b/docs/en/reference/grammar.ebnf @@ -0,0 +1,61 @@ +/* FZL (FromZero Language) syntax */ +/* Version 1 */ +/* Authors: */ +/* - Samsonium (https://git.samsonium.dev/Samsonium) */ + + ::= <_> + + + ::= + | + | + | + | + | + +<_> ::= " "* + ::= " "+ + + ::= <_> ";" <_> + ::= "{" <_> * "}" <_> + ::= "return" ( )? <_> ";" <_> + + ::= "class" <_> "{" <_> ( | )* "}" <_> + ::= "fn" <_> "(" <_> ( (<_> "," <_> )*)? ")" <_> + ::= + + ::= "if" <_> "(" <_> <_> ")" <_> (<_> "else" )? + +/* Assign (lowest expression priority, right-associative) */ + ::= + ::= (<_> "=" <_> )? + ::= ("new" ) | + ::= (<_> "||" <_> )* + ::= (<_> "&&" <_> )* + ::= (<_> <_> )* + ::= "==" | "!=" | "<=" | ">=" | "<" | ">" + + ::= (<_> ("+" | "-") <_> )* + ::= (<_> ("*" | "/" | "%") <_> )* + ::= (<_> "^" <_> )? + ::= (("!" | "-" | "+") <_>)* + +/* Postfix: chain of calls and dots */ + ::= ( | )* + ::= <_> "(" <_> ( (<_> "," <_> )*)? <_> ")" + ::= <_> "." <_> + + ::= + | + | "(" <_> <_> ")" + | + + ::= + + ::= "int" | "uint" | "bool" | "char" | "str" + | "i8" | "i16" | "i32" | "i64" + | "u8" | "u16" | "u32" | "u64" + + ::= | | "true" | "false" | "null" + ::= ([A-z] | "_") | ([A-z] | [0-9] | "_")* + ::= ('"' [^"]* '"') | ("'" [^']* "'") + diff --git a/docs/en/reference/grammar_ebnf.md b/docs/en/reference/grammar_ebnf.md new file mode 100644 index 0000000..b4739cd --- /dev/null +++ b/docs/en/reference/grammar_ebnf.md @@ -0,0 +1,4 @@ +# Grammar (EBNF) + +The formal grammar is maintained in the [grammar.ebnf](./grammar.ebnf) file. + diff --git a/docs/en/reference/index.md b/docs/en/reference/index.md new file mode 100644 index 0000000..506095f --- /dev/null +++ b/docs/en/reference/index.md @@ -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) + diff --git a/docs/en/reference/operators.md b/docs/en/reference/operators.md new file mode 100644 index 0000000..284f78f --- /dev/null +++ b/docs/en/reference/operators.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 | + diff --git a/docs/en/reference/types_system.md b/docs/en/reference/types_system.md new file mode 100644 index 0000000..2a6c25b --- /dev/null +++ b/docs/en/reference/types_system.md @@ -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 ``, 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) + diff --git a/docs/en/stdlib/builtins.md b/docs/en/stdlib/builtins.md new file mode 100644 index 0000000..4754dcf --- /dev/null +++ b/docs/en/stdlib/builtins.md @@ -0,0 +1,20 @@ +# Built-in functions + +FZLang provides built-in functions that are available in every program. + +## Exit: `exit` + +``` +fn exit(uint code) +``` + +Takes an unsigned integer argument and exits the program with the specified code. + +## Print to stdout: `printline` + +``` +fn printline(value) +``` + +Accepts any value and writes it to standard output. Primitive values are printed directly according to their types. Objects are printed as `obj<...>`, where `...` is a list of their field values. Each field is printed with its type and value in the format `T`, where `T` is the type name and `V` is the current value. + diff --git a/docs/en/stdlib/index.md b/docs/en/stdlib/index.md new file mode 100644 index 0000000..2f6d44d --- /dev/null +++ b/docs/en/stdlib/index.md @@ -0,0 +1,6 @@ +# Standard library + +Documentation for functions available to FZLang programs without additional imports. + +- [Built-in functions](./builtins.md) + diff --git a/docs/ru/book/index.md b/docs/ru/book/index.md new file mode 100644 index 0000000..6505fae --- /dev/null +++ b/docs/ru/book/index.md @@ -0,0 +1,8 @@ +# Книга по FZLang + +Знакомство с FZLang: от подготовки к работе до программ с переменными, типами и функциями. + +- [Вступление](./00_introduction.md) +- [Начало работы](./01_getting-started.md) +- [Основы и типы](./02_basics-and-types.md) + diff --git a/docs/ru/index.md b/docs/ru/index.md new file mode 100644 index 0000000..925c253 --- /dev/null +++ b/docs/ru/index.md @@ -0,0 +1,12 @@ +# Документация FZLang + +Выберите раздел документации: + +- [Книга](./book/index.md) — последовательное знакомство с языком. +- [Справочник](./reference/index.md) — типы, операторы, грамматика и диагностика выполнения. +- [Внутреннее устройство](./internals/index.md) — подробности о виртуальной машине. +- [Стандартная библиотека](./stdlib/index.md) — встроенные функции. +- [Философия языка](./philosopy.md) + +[Read the documentation in English](../en/index.md) + diff --git a/docs/ru/internals/index.md b/docs/ru/internals/index.md new file mode 100644 index 0000000..76957c5 --- /dev/null +++ b/docs/ru/internals/index.md @@ -0,0 +1,8 @@ +# Внутреннее устройство + +Материалы об устройстве виртуальной машины FZLang и скомпилированных программ. + +- [Размещение объектов](./object_layout.md) +- [Опкоды](./opcodes.md) +- [Стеки виртуальной машины](./stacks.md) + diff --git a/docs/ru/reference/index.md b/docs/ru/reference/index.md new file mode 100644 index 0000000..129a7b8 --- /dev/null +++ b/docs/ru/reference/index.md @@ -0,0 +1,9 @@ +# Справочник + +Справочные материалы по языку и среде выполнения FZLang. + +- [Операторы](./operators.md) +- [Система типов](./types_system.md) +- [Грамматика (EBNF)](./grammar_ebnf.md) +- [Вывод ошибок](./errors_print.md) + diff --git a/docs/ru/stdlib/index.md b/docs/ru/stdlib/index.md new file mode 100644 index 0000000..730e656 --- /dev/null +++ b/docs/ru/stdlib/index.md @@ -0,0 +1,6 @@ +# Стандартная библиотека + +Описание функций, доступных в программах FZLang без дополнительных подключений. + +- [Встроенные функции](./builtins.md) +