Docs for Finch 2.0: beginners, technicians, engineers (en, pl); READMEs; raylib example

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-08 19:20:46 +02:00
commit 3e55da2645
13 changed files with 2590 additions and 2140 deletions

View file

@ -25,19 +25,24 @@ fn main() {
## Why Finch ## Why Finch
- **Simple.** No semicolons, no headers, no preprocessor. Each thing has one way to do it. - **Simple.** No semicolons, no headers, no preprocessor. Each thing has one way to do it.
- **Fast.** It compiles through LLVM's `-O2` pipeline to native code, with no runtime or garbage collector. - **Fast.** It compiles through LLVM's `-O2` pipeline to native code, with no garbage collector.
Recursive `fib(40)` runs in about 0.19 s (`clang -O2` on the same C: about 0.23 s). Recursive `fib(40)` runs in about 0.19 s (`clang -O2` on the same C: about 0.23 s); bounds-checked
- **Safer than C, where it is cheap.** No silent narrowing and no signed/unsigned mixing. Division by zero array code matches C.
and null `.value` stop with a clear runtime error instead of undefined behavior. Operator precedence - **Memory without the pain.** Lists, text and structs are values: assigning copies, and the end of a
that does not bite (`x & 1 == 0` means what it says). block frees them. There is no `free` to forget, no use-after-free, and every test is clean under valgrind.
- **Safer than C, where it is cheap.** No silent narrowing and no signed/unsigned mixing. Out-of-range
indexes, division by zero and null pointers stop with a clear runtime error instead of undefined behavior.
Operator precedence that does not bite (`x & 1 == 0` means what it says).
- **Errors that talk like a person:** - **Errors that talk like a person:**
``` ```
game.fn:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert) game.fn:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert)
3 | i32 y = x 3 | i32 y = x
| ^ | ^
``` ```
- **Any C library, directly.** `import "GLFW/glfw3.h"` + `link "glfw"`, and you can call it. - **Any C library, directly.** `import "raylib.h"` + `link "raylib"`, and you can call it, structs by value
Headers are read by libclang, so there is nothing to declare by hand. and callbacks included. Headers are read by libclang, so there is nothing to declare by hand.
- **Written in itself, too.** `boot/` is a Finch compiler written in Finch that compiles itself to a
byte-identical result.
## Quick start ## Quick start
@ -52,7 +57,8 @@ ninja -C build
./build/finch run examples/hello.fn ./build/finch run examples/hello.fn
./build/finch build examples/tour.fn -o tour && ./tour ./build/finch build examples/tour.fn -o tour && ./tour
tests/run.sh # 34 passed, 0 failed tests/run.sh # 58 passed, 0 failed
tests/boot.sh # the compiler written in Finch builds itself
``` ```
## Documentation ## Documentation
@ -70,14 +76,17 @@ Pick the guide that fits you:
| | | | | |
|---|---| |---|---|
| Variables | `x := 5` (inferred) or `int x = 5` | | Variables | `x := 5` (inferred) or `int x = 5` |
| Types | `int` `float` `bool` `char` `str`, sized `i8`…`i64` `u8`…`u64` `f32` `f64`, pointers `ptr[T]` / `ptr` | | Types | `int` `float` `bool` `char` `str`, sized `i8`…`i64` `u8`…`u64` `f32` `f64`, arrays `[]T`, pointers `ptr[T]` |
| Functions | `fn add(int a, int b) -> int { return a + b }` | | Functions | `fn add(int a, int b) -> int { return a + b }` |
| Control | `if` / `else if` / `else`, `while`, `for i in 0..10`, `break`, `continue` | | Structs | `struct Point { … }` with one field per line (`int x`, `int y = 0`); create with `Point(1, 2)` or `Point(x: 1)` |
| Pointers | `p := addr(x)`, `p.value = 5`, `null` | | Arrays | `nums := [1, 2, 3]`, `nums.push(4)`, `nums[0]`, `nums.len`, `for n in nums { }` |
| Conversions | `int(3.9)`, `u8(x)`, `float(n)` (implicit only when nothing is lost) | | Text | `"a" + "b"`, `str(42)`, `int("42")`, `s.split(",")`, `s.upper()`, `s[0]` |
| Operators | `+ - * / %`, `& \| ^ << >> ~`, `== != < <= > >=`, `&& \|\| !` | | Control | `if` / `else if` / `else`, `while`, `for i in 0..10`, `break`, `continue`, `defer` |
| C interop | `import "stdio.h"`, `link "glfw"` | | Memory | automatic for arrays, text and structs; `new(...)` / `free(...)` for your own heap structures |
| Output | `print(a, b, c)` prints anything, separated by spaces | | Modules | `import shapes` → `shapes.area(b)` |
| C interop | `import "stdio.h"`, `link "glfw"`, `link "mine.c"`, `addr(fn)` for callbacks |
| I/O | `print(...)`, `input("? ")`, `read_file`, `write_file`, `shell` |
| Tools | `finch run/build/ir`, `-g` for gdb, `-O0` |
## Examples ## Examples
@ -85,32 +94,42 @@ Pick the guide that fits you:
|---|---| |---|---|
| [`examples/hello.fn`](examples/hello.fn) | the smallest program | | [`examples/hello.fn`](examples/hello.fn) | the smallest program |
| [`examples/tour.fn`](examples/tour.fn) | variables, functions, conditions, loops | | [`examples/tour.fn`](examples/tour.fn) | variables, functions, conditions, loops |
| [`examples/guess.fn`](examples/guess.fn) | a guessing game: input, loops, C's `rand` |
| [`examples/todo.fn`](examples/todo.fn) | structs, arrays, strings and files |
| [`examples/structs.fn`](examples/structs.fn) | structs, arrays of structs, a linked list with `new`/`free` |
| [`examples/types.fn`](examples/types.fn) | sized numbers and pointers | | [`examples/types.fn`](examples/types.fn) | sized numbers and pointers |
| [`examples/c_import.fn`](examples/c_import.fn) | `printf`, `math.h`, `malloc`/`free`, `stderr` | | [`examples/c_import.fn`](examples/c_import.fn) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
| [`examples/window.fn`](examples/window.fn) | a GLFW + OpenGL window | | [`examples/window.fn`](examples/window.fn) | a GLFW + OpenGL window |
| [`examples/raylib.fn`](examples/raylib.fn) | raylib with C structs by value |
| [`examples/llvm.fn`](examples/llvm.fn) | Finch building LLVM IR through the LLVM-C API | | [`examples/llvm.fn`](examples/llvm.fn) | Finch building LLVM IR through the LLVM-C API |
| [`boot/`](boot/) | the Finch compiler, written in Finch |
## Roadmap ## Roadmap
- [x] Lexer, parser, LLVM codegen, O2 optimization - [x] Lexer, parser, LLVM codegen, O2 optimization
- [x] Types incl. sized integers, pointers, conversions; functions; `if`/`while`/`for` - [x] Types incl. sized integers, pointers, conversions; functions; `if`/`while`/`for`
- [x] Readable compile errors; runtime checks (division, null) - [x] Readable compile errors; runtime checks (division, null, bounds)
- [x] Bitwise operators with sane precedence - [x] Bitwise operators with sane precedence
- [x] C headers through libclang; `link` with pkg-config; link-error advice - [x] C headers through libclang; `link` with pkg-config; link-error advice
- [ ] `struct` (and C structs by value) - [x] `struct`, including C structs by value (System V ABI) and callbacks
- [ ] Arrays/slices with `.len` and bounds checks - [x] Arrays with `.len`, bounds checks and methods; `for x in list`
- [ ] Memory: automatic free at the end of the owning block, `defer`, manual `free` when wanted - [x] Memory: values freed at the end of their block, `defer`, `new`/`free`
- [ ] Strings: `+`, `len`, `str(x)`; `input()` - [x] Strings: `+`, `.len`, indexing, methods, `str(x)`, `int(s)`; `input()`; files
- [ ] Finch modules (`import math`) - [x] Finch modules (`import name`)
- [ ] Debug info (DWARF) - [x] Debug info (`-g`)
- [ ] Bootstrap: the Finch compiler written in Finch - [x] Bootstrap: a Finch compiler written in Finch that builds itself
- [ ] Methods on structs, maps, `match`
- [ ] Error values instead of stopping the program
- [ ] More platforms (ARM64, macOS)
## Layout ## Layout
``` ```
src/ compiler: lexer, parser, AST, codegen, C import, driver (C++17, ~2.4k lines) src/ compiler: lexer, parser, AST, codegen, C import + ABI, driver (C++17, ~4.5k lines)
runtime/ the small C runtime linked into every program
boot/ the Finch compiler written in Finch (~3k lines)
examples/ example programs examples/ example programs
tests/ run/ (golden output), fail/ (expected errors), run.sh tests/ run/ (golden output), fail/ (expected errors), run.sh, boot.sh
docs/ en/ and pl/ guides for three audiences docs/ en/ and pl/ guides for three audiences
``` ```

View file

@ -25,10 +25,13 @@ fn main() {
## Dlaczego Finch ## Dlaczego Finch
- **Prosty.** Bez średników, bez plików nagłówkowych, bez preprocesora. Każdą rzecz robi się na jeden sposób. - **Prosty.** Bez średników, bez plików nagłówkowych, bez preprocesora. Każdą rzecz robi się na jeden sposób.
- **Szybki.** Kompiluje się przez potok `-O2` z LLVM do kodu natywnego, bez środowiska uruchomieniowego i garbage collectora. - **Szybki.** Kompiluje się przez potok `-O2` z LLVM do kodu natywnego, bez garbage collectora.
Rekurencyjne `fib(40)` trwa ok. 0,19 s (`clang -O2` na tym samym kodzie C: ok. 0,23 s). Rekurencyjne `fib(40)` trwa ok. 0,19 s (`clang -O2` na tym samym kodzie C: ok. 0,23 s), a kod na tablicach
z kontrolą zakresu dorównuje C.
- **Pamięć bez bólu.** Listy, teksty i struktury są wartościami: przypisanie kopiuje, a koniec bloku je zwalnia.
Nie ma `free`, o którym można zapomnieć, nie ma użycia po zwolnieniu, a każdy test jest czysty pod valgrindem.
- **Bezpieczniejszy niż C tam, gdzie to tanie.** Nie ma cichego zwężania typów ani mieszania liczb ze znakiem - **Bezpieczniejszy niż C tam, gdzie to tanie.** Nie ma cichego zwężania typów ani mieszania liczb ze znakiem
i bez znaku. Dzielenie przez zero i `.value` na null zatrzymują program czytelnym błędem, zamiast i bez znaku. Indeks poza zakresem, dzielenie przez zero i null zatrzymują program czytelnym błędem, zamiast
powodować niezdefiniowane zachowanie. Priorytety operatorów nie gryzą (`x & 1 == 0` znaczy to, co widać). powodować niezdefiniowane zachowanie. Priorytety operatorów nie gryzą (`x & 1 == 0` znaczy to, co widać).
- **Błędy mówią po ludzku:** - **Błędy mówią po ludzku:**
``` ```
@ -36,8 +39,10 @@ fn main() {
3 | i32 y = x 3 | i32 y = x
| ^ | ^
``` ```
- **Każda biblioteka C, bezpośrednio.** Piszesz `import "GLFW/glfw3.h"` i `link "glfw"`, i możesz z niej korzystać. - **Każda biblioteka C, bezpośrednio.** Piszesz `import "raylib.h"` i `link "raylib"`, i możesz z niej korzystać,
Nagłówki czyta libclang, więc nie trzeba niczego deklarować ręcznie. także ze struktur przekazywanych przez wartość i z callbacków. Nagłówki czyta libclang, więc nie trzeba niczego deklarować ręcznie.
- **Napisany także w sobie.** `boot/` to kompilator Fincha napisany w Finchu, który kompiluje sam siebie
do identycznego co do bajtu wyniku.
## Szybki start ## Szybki start
@ -52,7 +57,8 @@ ninja -C build
./build/finch run examples/hello.fn ./build/finch run examples/hello.fn
./build/finch build examples/tour.fn -o tour && ./tour ./build/finch build examples/tour.fn -o tour && ./tour
tests/run.sh # 34 passed, 0 failed tests/run.sh # 58 passed, 0 failed
tests/boot.sh # kompilator napisany w Finchu buduje sam siebie
``` ```
## Dokumentacja ## Dokumentacja
@ -70,14 +76,17 @@ Wybierz przewodnik dla siebie:
| | | | | |
|---|---| |---|---|
| Zmienne | `x := 5` (typ zgadnięty) albo `int x = 5` | | Zmienne | `x := 5` (typ zgadnięty) albo `int x = 5` |
| Typy | `int` `float` `bool` `char` `str`, z rozmiarem `i8`…`i64` `u8`…`u64` `f32` `f64`, wskaźniki `ptr[T]` / `ptr` | | Typy | `int` `float` `bool` `char` `str`, z rozmiarem `i8`…`i64` `u8`…`u64` `f32` `f64`, tablice `[]T`, wskaźniki `ptr[T]` |
| Funkcje | `fn add(int a, int b) -> int { return a + b }` | | Funkcje | `fn add(int a, int b) -> int { return a + b }` |
| Sterowanie | `if` / `else if` / `else`, `while`, `for i in 0..10`, `break`, `continue` | | Struktury | `struct Punkt { … }` z jednym polem na linijkę (`int x`, `int y = 0`); tworzenie przez `Punkt(1, 2)` albo `Punkt(x: 1)` |
| Wskaźniki | `p := addr(x)`, `p.value = 5`, `null` | | Tablice | `liczby := [1, 2, 3]`, `liczby.push(4)`, `liczby[0]`, `liczby.len`, `for n in liczby { }` |
| Konwersje | `int(3.9)`, `u8(x)`, `float(n)` (niejawne tylko wtedy, gdy nic nie ginie) | | Tekst | `"a" + "b"`, `str(42)`, `int("42")`, `s.split(",")`, `s.upper()`, `s[0]` |
| Operatory | `+ - * / %`, `& \| ^ << >> ~`, `== != < <= > >=`, `&& \|\| !` | | Sterowanie | `if` / `else if` / `else`, `while`, `for i in 0..10`, `break`, `continue`, `defer` |
| Współpraca z C | `import "stdio.h"`, `link "glfw"` | | Pamięć | automatyczna dla tablic, tekstów i struktur; `new(...)` / `free(...)` dla własnych struktur na stercie |
| Wypisywanie | `print(a, b, c)` wypisuje wszystko, oddzielając spacjami | | Moduły | `import ksztalty` → `ksztalty.pole(p)` |
| Współpraca z C | `import "stdio.h"`, `link "glfw"`, `link "moje.c"`, `addr(fn)` dla callbacków |
| Wejście/wyjście | `print(...)`, `input("? ")`, `read_file`, `write_file`, `shell` |
| Narzędzia | `finch run/build/ir`, `-g` dla gdb, `-O0` |
## Przykłady ## Przykłady
@ -85,32 +94,42 @@ Wybierz przewodnik dla siebie:
|---|---| |---|---|
| [`examples/hello.fn`](examples/hello.fn) | najmniejszy program | | [`examples/hello.fn`](examples/hello.fn) | najmniejszy program |
| [`examples/tour.fn`](examples/tour.fn) | zmienne, funkcje, warunki, pętle | | [`examples/tour.fn`](examples/tour.fn) | zmienne, funkcje, warunki, pętle |
| [`examples/guess.fn`](examples/guess.fn) | gra w zgadywanie: wejście, pętle, `rand` z C |
| [`examples/todo.fn`](examples/todo.fn) | struktury, tablice, teksty i pliki |
| [`examples/structs.fn`](examples/structs.fn) | struktury, tablice struktur, lista wiązana z `new`/`free` |
| [`examples/types.fn`](examples/types.fn) | liczby z rozmiarem i wskaźniki | | [`examples/types.fn`](examples/types.fn) | liczby z rozmiarem i wskaźniki |
| [`examples/c_import.fn`](examples/c_import.fn) | `printf`, `math.h`, `malloc`/`free`, `stderr` | | [`examples/c_import.fn`](examples/c_import.fn) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
| [`examples/window.fn`](examples/window.fn) | okno GLFW + OpenGL | | [`examples/window.fn`](examples/window.fn) | okno GLFW + OpenGL |
| [`examples/raylib.fn`](examples/raylib.fn) | raylib ze strukturami C przez wartość |
| [`examples/llvm.fn`](examples/llvm.fn) | Finch budujący LLVM IR przez API LLVM-C | | [`examples/llvm.fn`](examples/llvm.fn) | Finch budujący LLVM IR przez API LLVM-C |
| [`boot/`](boot/) | kompilator Fincha napisany w Finchu |
## Plan rozwoju ## Plan rozwoju
- [x] Lekser, parser, generowanie kodu LLVM, optymalizacja O2 - [x] Lekser, parser, generowanie kodu LLVM, optymalizacja O2
- [x] Typy, w tym liczby z rozmiarem, wskaźniki, konwersje; funkcje; `if`/`while`/`for` - [x] Typy, w tym liczby z rozmiarem, wskaźniki, konwersje; funkcje; `if`/`while`/`for`
- [x] Czytelne błędy kompilacji; kontrole w czasie działania (dzielenie, null) - [x] Czytelne błędy kompilacji; kontrole w czasie działania (dzielenie, null, zakres)
- [x] Operatory bitowe z rozsądnymi priorytetami - [x] Operatory bitowe z rozsądnymi priorytetami
- [x] Nagłówki C przez libclang; `link` z pkg-config; porady przy błędach linkowania - [x] Nagłówki C przez libclang; `link` z pkg-config; porady przy błędach linkowania
- [ ] `struct` (i struktury C przez wartość) - [x] `struct`, także struktury C przez wartość (ABI System V) i callbacki
- [ ] Tablice/wycinki z `.len` i kontrolą zakresu - [x] Tablice z `.len`, kontrolą zakresu i metodami; `for x in lista`
- [ ] Pamięć: automatyczne zwalnianie na końcu bloku, `defer`, ręczne `free` dla chętnych - [x] Pamięć: zwalnianie na końcu bloku, `defer`, `new`/`free`
- [ ] Teksty: `+`, `len`, `str(x)`; `input()` - [x] Teksty: `+`, `.len`, indeksowanie, metody, `str(x)`, `int(s)`; `input()`; pliki
- [ ] Moduły Fincha (`import math`) - [x] Moduły Fincha (`import nazwa`)
- [ ] Informacje dla debuggera (DWARF) - [x] Informacje dla debuggera (`-g`)
- [ ] Bootstrap: kompilator Fincha napisany w Finchu - [x] Bootstrap: kompilator Fincha napisany w Finchu, który buduje sam siebie
- [ ] Metody w strukturach, mapy, `match`
- [ ] Błędy jako wartości zamiast zatrzymywania programu
- [ ] Więcej platform (ARM64, macOS)
## Struktura ## Struktura
``` ```
src/ kompilator: lekser, parser, AST, codegen, import C, sterownik (C++17, ok. 2,4 tys. linii) src/ kompilator: lekser, parser, AST, codegen, import C + ABI, sterownik (C++17, ok. 4,5 tys. linii)
runtime/ mały runtime w C dołączany do każdego programu
boot/ kompilator Fincha napisany w Finchu (ok. 3 tys. linii)
examples/ przykładowe programy examples/ przykładowe programy
tests/ run/ (wzorcowe wyjście), fail/ (oczekiwane błędy), run.sh tests/ run/ (wzorcowe wyjście), fail/ (oczekiwane błędy), run.sh, boot.sh
docs/ przewodniki en/ i pl/ dla trzech grup odbiorców docs/ przewodniki en/ i pl/ dla trzech grup odbiorców
``` ```

View file

@ -20,10 +20,15 @@ Take it slowly, one section at a time, and type every example yourself: that is
8. [Making decisions: if](#8-making-decisions-if) 8. [Making decisions: if](#8-making-decisions-if)
9. [Repeating things: loops](#9-repeating-things-loops) 9. [Repeating things: loops](#9-repeating-things-loops)
10. [Your own commands: functions](#10-your-own-commands-functions) 10. [Your own commands: functions](#10-your-own-commands-functions)
11. [When something goes wrong](#11-when-something-goes-wrong) 11. [Lists: many values in one box](#11-lists-many-values-in-one-box)
12. [Small projects to try](#12-small-projects-to-try) 12. [Playing with text](#12-playing-with-text)
13. [Cheat sheet](#13-cheat-sheet) 13. [Asking the user](#13-asking-the-user)
14. [Words you will hear](#14-words-you-will-hear) 14. [Your own kinds of values: structs](#14-your-own-kinds-of-values-structs)
15. [Saving to a file](#15-saving-to-a-file)
16. [When something goes wrong](#16-when-something-goes-wrong)
17. [Small projects to try](#17-small-projects-to-try)
18. [Cheat sheet](#18-cheat-sheet)
19. [Words you will hear](#19-words-you-will-hear)
--- ---
@ -72,7 +77,7 @@ To check it works, type:
./build/finch version ./build/finch version
``` ```
You should see something like `finch 1.0.0`. You should see something like `finch 2.0.0`.
--- ---
@ -441,7 +446,200 @@ It does not matter where in the file you put your functions, above or below `mai
--- ---
## 11. When something goes wrong ## 11. Lists: many values in one box
Sometimes one box is not enough. A shopping list has many things on it.
A **list** (programmers also say **array**) keeps many values in order:
```c
fn main() {
fruits := ["apple", "banana", "cherry"]
print(fruits)
print("I have", fruits.len, "fruits")
print("The first one is", fruits[0])
}
```
```
["apple", "banana", "cherry"]
I have 3 fruits
The first one is apple
```
- Square brackets `[ ]` make a list. Commas separate the values.
- `.len` tells you how many things are in the list.
- `fruits[0]` is the **first** thing. Counting starts at **0**, so `fruits[1]` is the second, and `fruits[2]` the third.
If you ask for a place that doesn't exist, like `fruits[10]`, the program stops and tells you:
`index 10 is out of range (the length is 3)`.
### Adding and taking away
```c
fn main() {
[]int scores // an empty list of whole numbers
scores.push(10) // put 10 at the end
scores.push(25)
scores.push(7)
print(scores) // [10, 25, 7]
last := scores.pop() // take the last one out
print(last, scores) // 7 [10, 25]
scores.sort()
print(scores)
}
```
### Going through a list
`for ... in` visits every value, one by one:
```c
fn main() {
total := 0
for n in [3, 5, 2] {
total += n
}
print("Total:", total) // Total: 10
}
```
Lists can do more: `contains`, `find`, `insert`, `remove`, `sort`, `reverse`, `join`.
The [guide for technicians](for-technicians.md#arrays) lists them all.
---
## 12. Playing with text
Text can be glued together with `+`:
```c
fn main() {
name := "Ola"
greeting := "Hello, " + name + "!"
print(greeting)
}
```
To glue a number to text, turn the number into text first with `str(...)`:
```c
age := 10
print("Age: " + str(age))
```
Text has handy tools too:
```c
fn main() {
word := "Finch"
print(word.len) // 5 letters
print(word[0]) // F (the first letter)
print(word.upper()) // FINCH
print(word.lower()) // finch
print(word.contains("in")) // true
for letter in word {
print(letter) // F, i, n, c, h, one per line
}
}
```
---
## 13. Asking the user
`input` shows a question and waits until the person types an answer and presses Enter:
```c
fn main() {
name := input("What's your name? ")
print("Nice to meet you,", name)
}
```
The answer is always **text**. If you need a number, turn it into one with `int(...)`:
```c
fn main() {
answer := input("How old are you? ")
age := int(answer)
print("Next year you will be", age + 1)
}
```
If the person types something that isn't a number, the program stops with a message
like `can't turn "abc" into int`.
---
## 14. Your own kinds of values: structs
Imagine a card in a library: each card has a title, an author and a year.
A **struct** lets you make your own kind of value with named parts:
```c
struct Book {
str title
str author
int year
}
fn main() {
b := Book(title: "The Hobbit", author: "Tolkien", year: 1937)
print(b.title, "by", b.author)
b.year = 1938
print(b)
}
```
```
The Hobbit by Tolkien
Book(title: "The Hobbit", author: "Tolkien", year: 1938)
```
- `struct Book { ... }` describes what every book has. Put it **outside** `main`.
- `Book(title: ..., author: ..., year: ...)` makes one book.
- `b.title` reads one part. `b.year = 1938` changes it.
You can give parts a starting value. Then you don't have to fill them in:
```c
struct Player {
str name
int lives = 3
}
fn main() {
p := Player(name: "Ola")
print(p.lives) // 3
}
```
And you can keep many of them in a list: `[]Book shelf`, then `shelf.push(b)`.
---
## 15. Saving to a file
Programs forget everything when they end. To remember things, save them to a file:
```c
fn main() {
write_file("note.txt", "Buy milk")
print(read_file("note.txt"))
}
```
- `write_file(name, text)` saves the text in a file (and replaces what was there).
- `read_file(name)` gives you the file's text back.
- `file_exists(name)` tells you if the file is there (`true` or `false`).
The file appears in the folder where you ran the program.
---
## 16. When something goes wrong
Everyone makes mistakes when programming, even experts, every day. Everyone makes mistakes when programming, even experts, every day.
Finch tries hard to explain what went wrong in plain words. Example: Finch tries hard to explain what went wrong in plain words. Example:
@ -479,10 +677,13 @@ Here we simply misspelled `age`.
| `this '{' is never closed` | A `}` is missing | Add the `}` | | `this '{' is never closed` | A `}` is missing | Add the `}` |
| `a condition must be bool` | `if` needs a yes/no question | Compare something: `if x > 0` | | `a condition must be bool` | `if` needs a yes/no question | Compare something: `if x > 0` |
| `runtime error: division by zero` | You divided by 0 while the program ran | Check the number before dividing | | `runtime error: division by zero` | You divided by 0 while the program ran | Check the number before dividing |
| `index 5 is out of range (the length is 3)` | You asked a list for a place it doesn't have | Remember counting starts at 0; check `.len` |
| `can't turn "abc" into int` | `int(...)` got text that isn't a number | Check what the person typed |
| `can't use '+' on str and int` | Text plus a number | Use `str(...)` on the number |
--- ---
## 12. Small projects to try ## 17. Small projects to try
Try writing each one yourself first. Look at the solution only if you get stuck. Try writing each one yourself first. Look at the solution only if you get stuck.
@ -589,9 +790,60 @@ fn main() {
``` ```
</details> </details>
### Project 6: Guess the number
The computer picks a secret number. The player guesses until they find it,
and the computer says "higher" or "lower" each time.
<details><summary>Solution</summary>
```c
fn main() {
secret := 42
tries := 0
while true {
guess := int(input("Your guess: "))
tries += 1
if guess < secret {
print("Higher!")
} else if guess > secret {
print("Lower!")
} else {
print("Yes! You needed", tries, "tries")
return
}
}
}
```
`return` inside `main` ends the program. (A version with a random number is in `examples/guess.fn`.)
</details>
### Project 7: Class average
Ask for 3 grades, keep them in a list, and show the list and the average.
<details><summary>Solution</summary>
```c
fn main() {
[]int grades
for i in 0..3 {
grades.push(int(input("Grade: ")))
}
sum := 0
for g in grades {
sum += g
}
print("Grades:", grades)
print("Average:", float(sum) / grades.len)
}
```
</details>
--- ---
## 13. Cheat sheet ## 18. Cheat sheet
```c ```c
// a comment // a comment
@ -624,11 +876,21 @@ fn main() { // the program starts here
fn add(int a, int b) -> int { // your own command fn add(int a, int b) -> int { // your own command
return a + b return a + b
} }
struct Pet { // your own kind of value
str name
int age = 1
}
// lists: nums := [1, 2, 3] nums.push(4) nums[0] nums.len for n in nums { }
// text: "a" + "b" str(42) int("42") s.len s.upper() s.contains("x")
// ask: name := input("Name? ")
// files: write_file("f.txt", text) read_file("f.txt")
``` ```
--- ---
## 14. Words you will hear ## 19. Words you will hear
| Word | Meaning | | Word | Meaning |
|---|---| |---|---|
@ -641,6 +903,10 @@ fn add(int a, int b) -> int { // your own command
| **function** | A named group of steps you can use again | | **function** | A named group of steps you can use again |
| **loop** | Steps that repeat | | **loop** | Steps that repeat |
| **condition** | A yes/no question, like `x > 5` | | **condition** | A yes/no question, like `x > 5` |
| **list / array** | Many values kept in order, like `[1, 2, 3]` |
| **index** | The position of a value in a list, starting at 0 |
| **struct** | Your own kind of value, made of named parts |
| **input** | What the person types into the program |
| **error** | A message saying something is wrong, and where | | **error** | A message saying something is wrong, and where |
| **bug** | A mistake in a program | | **bug** | A mistake in a program |

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
# Finch for Technicians # Finch for Technicians
**For people who know their way around a computer, but little or no programming.** **For people who know their way around a computer, but little or no programming.**
This guide covers installing Finch, using the `finch` tool, the whole language, using This guide covers installing Finch, using the `finch` tool, the whole language, memory,
C libraries, and fixing common problems. It does not cover how the compiler works inside. modules, C libraries, debugging and fixing common problems. It does not cover how the
For that, see the [guide for engineers](for-engineers.md). compiler works inside. For that, see the [guide for engineers](for-engineers.md).
> 🇵🇱 Polska wersja: [dla-technikow.md](../pl/dla-technikow.md) > 🇵🇱 Polska wersja: [dla-technikow.md](../pl/dla-technikow.md)
@ -20,12 +20,19 @@ For that, see the [guide for engineers](for-engineers.md).
7. [Operators](#7-operators) 7. [Operators](#7-operators)
8. [Control flow](#8-control-flow) 8. [Control flow](#8-control-flow)
9. [Functions](#9-functions) 9. [Functions](#9-functions)
10. [Pointers](#10-pointers) 10. [Arrays](#10-arrays)
11. [Using C libraries](#11-using-c-libraries) 11. [Text (str)](#11-text-str)
12. [Errors](#12-errors) 12. [Structs](#12-structs)
13. [Troubleshooting](#13-troubleshooting) 13. [Memory: who frees what](#13-memory-who-frees-what)
14. [Project layout and tests](#14-project-layout-and-tests) 14. [Pointers](#14-pointers)
15. [Current limits](#15-current-limits) 15. [Built-in functions](#15-built-in-functions)
16. [Modules](#16-modules)
17. [Using C libraries](#17-using-c-libraries)
18. [Errors](#18-errors)
19. [Debugging](#19-debugging)
20. [Troubleshooting](#20-troubleshooting)
21. [Project layout and tests](#21-project-layout-and-tests)
22. [Current limits](#22-current-limits)
--- ---
@ -37,9 +44,13 @@ file into a real program (a native executable, like the ones made from C), using
What that gives you: What that gives you:
- **Speed close to C.** There is no interpreter, virtual machine or garbage collector. - **Speed close to C.** No interpreter, virtual machine or garbage collector. Array bounds
checks and other safety checks cost almost nothing after optimization.
- **Small, standalone programs** that you can copy and run. - **Small, standalone programs** that you can copy and run.
- **Direct access to C libraries.** `import "stdio.h"` and call anything in it. - **Memory handled for you.** Lists and text are freed automatically at the end of the block
that owns them, with no garbage collector involved.
- **Direct access to C libraries.** `import "stdio.h"` and call anything in it, even functions
that take structs.
The design rules: one way to do each thing, C-like syntax without C's traps, and error The design rules: one way to do each thing, C-like syntax without C's traps, and error
messages that tell you how to fix the problem. messages that tell you how to fix the problem.
@ -56,10 +67,10 @@ Other LLVM versions will likely fail to compile, because LLVM's C++ API changes
```sh ```sh
git clone https://github.com/Nexoniarz/Finch.git git clone https://github.com/Nexoniarz/Finch.git
cd Finch cd Finch
nix-shell # downloads LLVM, libclang, cmake, ninja, pkg-config nix-shell # downloads LLVM, libclang, clang, cmake, ninja, pkg-config
cmake -S . -B build -G Ninja cmake -S . -B build -G Ninja
ninja -C build ninja -C build
./build/finch version # finch 1.0.0 (LLVM 21.x) ./build/finch version # finch 2.0.0 (LLVM 21.x)
``` ```
### Option B: your distribution's packages ### Option B: your distribution's packages
@ -73,7 +84,7 @@ libclang 21 development files, and optionally `pkg-config`.
| Fedora | `gcc-c++ cmake ninja-build pkgconf llvm-devel clang-devel` | | Fedora | `gcc-c++ cmake ninja-build pkgconf llvm-devel clang-devel` |
| Arch | `base-devel cmake ninja pkgconf llvm clang` | | Arch | `base-devel cmake ninja pkgconf llvm clang` |
If CMake cannot find LLVM, point it there: These have not been tested; if CMake cannot find LLVM, point it there:
```sh ```sh
cmake -S . -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-21/lib/cmake/llvm cmake -S . -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-21/lib/cmake/llvm
@ -87,21 +98,22 @@ sudo cp build/finch /usr/local/bin/
finch version finch version
``` ```
Finch needs a working C compiler (`cc`) at run time. It uses it as the **linker** that At run time Finch needs a C compiler (`cc`). It is used to link programs, and once to build
turns compiled code into a program. Set the `CC` environment variable to use a different one. Finch's small runtime library, which is kept in `~/.cache/finch`. Set `CC` to use a different one.
--- ---
## 3. The finch command ## 3. The finch command
``` ```
finch run <file.fn> compile and run right away finch run <file.fn> [args...] compile and run right away
finch build <file.fn> [-o name] compile into a program (default name: the file's name) finch build <file.fn> [-o name] compile into a program (default name: the file's name)
finch ir <file.fn> print the LLVM IR (for the curious) finch ir <file.fn> print the LLVM IR (for the curious)
finch version show the version finch version show the version
options: options:
-l <lib> link a C library, same as link "lib" in the file -l <lib> link a C library, same as link "lib" in the file
-g add debug info (for gdb / lldb)
-O0 skip optimizations -O0 skip optimizations
``` ```
@ -109,13 +121,13 @@ Examples:
```sh ```sh
finch run hello.fn # prints the output finch run hello.fn # prints the output
finch run tool.fn input.txt -v # arguments after the file go to the program
finch build game.fn -o mygame # creates ./mygame finch build game.fn -o mygame # creates ./mygame
./mygame finch build game.fn -g -O0 # a build for the debugger
finch run window.fn -l glfw # with a C library
``` ```
`finch run` passes on your program's exit code. If the program crashes, it tells you `finch run` passes on your program's exit code. If the program crashes, it says how
how (for example `the program crashed: Segmentation fault`). (for example `the program crashed: Segmentation fault`).
--- ---
@ -126,8 +138,14 @@ how (for example `the program crashed: Segmentation fault`).
/* or span /* or span
several lines */ several lines */
import "math.h" // C headers (optional), always at the top import "math.h" // C headers (optional), at the top
link "m" // C libraries to link (optional) import shapes // Finch modules (optional): shapes.fn next to this file
link "m" // C libraries or C files to link (optional)
struct Point { // your own types
int x
int y
}
fn main() { // the program starts here fn main() { // the program starts here
print("Hello") print("Hello")
@ -136,13 +154,14 @@ fn main() { // the program starts here
Rules worth knowing: Rules worth knowing:
- **One statement per line.** There are no semicolons. To split a long expression, - **One statement per line.** There are no semicolons. To split a long expression, break the line
break the line **after** an operator (`a +⏎ b`) or anywhere inside brackets `( )`. **after** an operator (`a +⏎ b`) or anywhere inside brackets `( )` or `[ ]`.
- **Curly braces are always required** after `if`, `else`, `for`, `while`, and the opening - **Curly braces are always required** after `if`, `else`, `for`, `while`, and the opening `{`
`{` goes on the same line. goes on the same line.
- **No brackets around conditions:** `if x > 5 {`, not `if (x > 5) {`. - **No brackets around conditions:** `if x > 5 {`, not `if (x > 5) {`.
- Functions can be in any order in the file. - Functions and structs can be in any order in the file.
- `main` is either `fn main()` or `fn main() -> int` (returns the exit code). - `main` is `fn main()`, `fn main() -> int` (returns the exit code) or `fn main([]str args)`
(gets the command-line arguments; `args[0]` is the program's own path).
--- ---
@ -151,7 +170,8 @@ Rules worth knowing:
```c ```c
x := 5 // new variable, type inferred (int) x := 5 // new variable, type inferred (int)
int y = 10 // new variable, type written explicitly int y = 10 // new variable, type written explicitly
int z // no value: starts at zero ("" for str, null for pointers) int z // no value: starts at zero ("" for str, [] for arrays, null for pointers)
Point p // a struct with no value: zero, or the struct's default values
x = 7 // assign x = 7 // assign
x += 1 // also -= *= /= %= x += 1 // also -= *= /= %=
@ -160,10 +180,10 @@ x += 1 // also -= *= /= %=
- A name can be declared **once** in a function. Finch does not allow reusing a name in an - A name can be declared **once** in a function. Finch does not allow reusing a name in an
inner block (no "shadowing"), because that is a common source of confusion. inner block (no "shadowing"), because that is a common source of confusion.
- Variables exist from their declaration to the end of the `{ }` block they are in. - Variables exist from their declaration to the end of the `{ }` block they are in.
- Names: ASCII letters, digits and `_`, not starting with a digit. Non-ASCII letters are allowed only inside strings. Type names (`int`, `u8`, …) and - Names: ASCII letters, digits and `_`, not starting with a digit. Non-ASCII letters are allowed only inside strings.
keywords are reserved. Type names (`int`, `u8`, …), built-in function names and keywords are reserved.
**Keywords:** `fn return if else while for in break continue true false null import link` **Keywords:** `fn return if else while for in break continue true false null import link struct defer`
--- ---
@ -177,7 +197,9 @@ x += 1 // also -= *= /= %=
| `float` | decimal number, 64-bit (same as `f64`) | `3.14` | | `float` | decimal number, 64-bit (same as `f64`) | `3.14` |
| `bool` | `true` or `false` | | | `bool` | `true` or `false` | |
| `char` | one byte-sized character | `'A'`, `'\n'` | | `char` | one byte-sized character | `'A'`, `'\n'` |
| `str` | text (a C string) | `"hello\tworld"` | | `str` | text | `"hello\tworld"` |
| `[]T` | a list (array) of `T` | `[1, 2, 3]` |
| your structs | named groups of values | `Point(1, 2)` |
String and char escapes: `\n` new line, `\t` tab, `\r`, `\0`, `\\`, `\"`, `\'`. String and char escapes: `\n` new line, `\t` tab, `\r`, `\0`, `\\`, `\"`, `\'`.
@ -192,11 +214,6 @@ String and char escapes: `\n` new line, `\t` tab, `\r`, `\0`, `\\`, `\"`, `\'`.
`f32` is a 32-bit decimal number (about 7 significant digits), `f64` a 64-bit one (about 15–16). `f32` is a 32-bit decimal number (about 7 significant digits), `f64` a 64-bit one (about 15–16).
### Pointers
`ptr[T]` points to a value of type `T`. Plain `ptr` points to "something" with unknown
type, like `void*` in C. See [Pointers](#10-pointers).
### Conversions ### Conversions
Finch converts automatically **only when nothing can be lost**: Finch converts automatically **only when nothing can be lost**:
@ -208,32 +225,30 @@ Finch converts automatically **only when nothing can be lost**:
| any whole number | `float` / `f32` | ✅ | | any whole number | `float` / `f32` | ✅ |
| `f32` | `f64` | ✅ | | `f32` | `f64` | ✅ |
| `null` | any pointer | ✅ | | `null` | any pointer | ✅ |
| any pointer, `str` | `ptr` | ✅ | | any pointer | `ptr`, and `ptr` to any pointer | ✅ |
| `ptr` | any `ptr[T]` | ✅ |
| anything else | | ❌ write it: `type(value)` | | anything else | | ❌ write it: `type(value)` |
**Number literals adapt.** A number written in the code fits into any type that can hold it: **Number literals adapt.** A number written in the code fits into any type that can hold it:
`u8 b = 200` works, `u8 b = 300` is an error (`the number 300 doesn't fit in u8`). `u8 b = 200` works, `u8 b = 300` is an error. The same applies to constants imported from C headers.
The same applies to constants imported from C headers.
**Explicit conversions** use the type name like a function: **Explicit conversions** use the type name like a function:
```c ```c
int(3.99) // 3 (cuts off the fraction) int(3.99) // 3 (cuts off the fraction)
int(-3.99) // -3 float(7) / 2 // 3.5
float(7) / 2 // 3.5 u8(300) // 44 (keeps the lowest 8 bits)
u8(300) // 44 (keeps the lowest 8 bits) int('A') // 65
i8(200) // -56 char(66) // 'B'
int('A') // 65 bool(0) // false (any non-zero number is true)
char(66) // 'B' str(42) // "42" (also floats, bools, chars; str([]u8) turns bytes into text)
bool(0) // false (any non-zero number is true) int("42") // 42 (stops the program if the text isn't a number)
ptr(text) // a str as an untyped pointer float("2.5") // 2.5
str(p) // a pointer as a str ptr(p) // a typed pointer as an untyped one
str(p) // a C char pointer as text
``` ```
**Fixed-size numbers wrap around**, like real hardware: a `u8` holding 255 plus 1 becomes 0. **Fixed-size numbers wrap around**, like real hardware: a `u8` holding 255 plus 1 becomes 0.
Calculations happen in the type of the values involved, so `u8(250) + 10` is `4`, not `260`. Calculations happen in the type of the values involved, so `u8(250) + 10` is `4`, not `260`.
Convert first if you need more room: `int(x) + 10`.
--- ---
@ -244,7 +259,7 @@ Convert first if you need more room: `int(x) + 10`.
| Priority | Operators | Meaning | | Priority | Operators | Meaning |
|---|---|---| |---|---|---|
| 5 | `*` `/` `%` `<<` `>>` `&` | multiply, divide, remainder, shift left/right, bitwise AND | | 5 | `*` `/` `%` `<<` `>>` `&` | multiply, divide, remainder, shift left/right, bitwise AND |
| 4 | `+` `-` `\|` `^` | add, subtract, bitwise OR, bitwise XOR | | 4 | `+` `-` `\|` `^` | add (and join text), subtract, bitwise OR, bitwise XOR |
| 3 | `==` `!=` `<` `<=` `>` `>=` | comparisons | | 3 | `==` `!=` `<` `<=` `>` `>=` | comparisons |
| 2 | `&&` | logical AND | | 2 | `&&` | logical AND |
| 1 | `\|\|` | logical OR | | 1 | `\|\|` | logical OR |
@ -252,18 +267,16 @@ Convert first if you need more room: `int(x) + 10`.
Unary (in front of a value): `-x` negate, `!x` logical NOT, `~x` flip all bits. Unary (in front of a value): `-x` negate, `!x` logical NOT, `~x` flip all bits.
> **Different from C, on purpose:** bit operators bind tighter than comparisons, so > **Different from C, on purpose:** bit operators bind tighter than comparisons, so
> `flags & 4 != 0` means `(flags & 4) != 0`. In C it silently means something else. > `flags & 4 != 0` means `(flags & 4) != 0`.
Notes: Notes:
- `/` on whole numbers rounds toward zero: `7 / 2` is `3`, `-7 / 2` is `-3`. `%` takes the sign of the left side. - `/` on whole numbers rounds toward zero: `7 / 2` is `3`. `%` takes the sign of the left side.
- Dividing by zero **stops the program** with `runtime error: division by zero` and the line number. - Dividing by zero **stops the program** with `runtime error: division by zero` and the line number.
If the zero is written in the code, it is a compile error instead.
- `&&` and `||` only evaluate the right side if needed. - `&&` and `||` only evaluate the right side if needed.
- Text (`str`) can be compared with `==` and `!=` (it compares the contents). `<` and `>` do not work on text. - Text: `+` joins, `==` `!=` compare contents, `<` `>` compare alphabetically (byte by byte).
- Pointers can be compared with `==` and `!=`, including against `null`. - Pointers can be compared with `==` and `!=`, including against `null`. A `str` is never `null`.
- Bit operators need whole numbers. For `bool` use `&&` and `||`. - Arrays and structs can't be compared with `==`; compare their parts.
- `>>` on signed numbers keeps the sign (`-16 >> 2` is `-4`). On unsigned numbers it fills with zeros.
--- ---
@ -286,14 +299,32 @@ for i in 0..10 { // i = 0, 1, …, 9 (the end is not included)
if i == 3 { continue } if i == 3 { continue }
if i == 8 { break } if i == 8 { break }
} }
for name in names { // every element of an array (or every char of a str)
print(name)
}
``` ```
- A condition must be a `bool`. `if x {` with a number is an error; write `if x != 0 {`. - A condition must be a `bool`. `if x {` with a number is an error; write `if x != 0 {`.
- In `for i in a..b`, both ends are whole numbers evaluated **once** before the loop starts. - In `for i in a..b`, both ends are evaluated **once**. `i` is an `int` and cannot be changed in the loop.
`i` is an `int` and cannot be changed inside the loop. - In `for x in list`, `x` cannot be changed either. To change elements, loop over the indexes:
`for i in 0..list.len { list[i] = ... }`. Adding to the list inside the loop is fine.
- `while true { ... }` loops until a `break` or `return`. - `while true { ... }` loops until a `break` or `return`.
- Code after `return`, `break` or `continue` in the same block is an error, because it can never run. - Code after `return`, `break` or `continue` in the same block is an error, because it can never run.
### defer
`defer` runs a statement when the current block ends, however it ends: normally, by `return`,
`break` or `continue`. Several defers run in reverse order. Handy for cleanup:
```c
f := fopen("data.txt", "r")
defer fclose(f)
// ... use f; it is closed when the block ends, on every path
```
A deferred statement can't contain `return`, `break` or `continue`.
--- ---
## 9. Functions ## 9. Functions
@ -309,32 +340,129 @@ fn no_result(str message) { // no "->": returns nothing
} }
``` ```
- Parameters are copies. Changing a parameter does not change the caller's variable. - Arguments behave like copies: changing a parameter never changes the caller's variable.
Use a pointer for that (see below). (Finch only really copies when needed: a function that doesn't change an array gets it for free.)
To change the caller's variable, pass a pointer: see [Pointers](#14-pointers).
- A function with `-> type` must return a value on every path. Finch checks this. - A function with `-> type` must return a value on every path. Finch checks this.
- A function cannot be defined inside another function. - A function cannot be defined inside another function.
- Built-in names you cannot reuse: `print`, `addr`, and all type names.
### Built-in functions
| Function | What it does |
|---|---|
| `print(a, b, ...)` | Prints all values separated by spaces, then a new line. Works with every type. |
| `addr(x)` | Gives a pointer to variable `x`. |
| `int(x)`, `u8(x)`, `float(x)`, … | Converts types (see [Conversions](#conversions)). |
--- ---
## 10. Pointers ## 10. Arrays
A pointer holds the **address** of a value: where it lives in memory. Finch uses words ```c
instead of C's `*` and `&`: nums := [5, 3, 8] // an array of int
[]str names // an empty array of str
grid := [[1, 2], [3, 4]] // arrays of arrays
[]f32 xs = [1, 2.5] // the type decides what the numbers become
```
| Operation | Meaning |
|---|---|
| `a.len` | number of elements |
| `a[i]` | element `i` (from 0); out of range stops the program with a clear message |
| `a[i] = x` | change an element |
| `a.push(x)` | add at the end |
| `a.pop()` | remove the last one and give it back |
| `a.insert(i, x)` | put `x` at position `i`, moving the rest |
| `a.remove(i)` | remove position `i` and give it back |
| `a.clear()` | remove everything |
| `a.resize(n)` | make it `n` long (new elements are zero) |
| `a.contains(x)`, `a.find(x)` | is `x` there? its index, or -1 (numbers, chars, bools, str) |
| `a.sort()`, `a.reverse()` | in place (numbers, chars, str) |
| `a.slice(s, e)` | a new array with elements `s` … `e-1` |
| `a.join(sep)` | for `[]str`: one text with `sep` between the parts |
| `a.ptr` | the address of the first element (for C) |
| `print(a)` | prints `[1, 2, 3]` |
**Assigning copies:** after `b := a`, changing `b` does not change `a`.
---
## 11. Text (str)
```c
s := "Hello"
s += ", world" // join
print(s.len, s[0]) // 12 H (.len counts bytes; ą, ż, … take 2)
s[0] = 'J' // change a character
```
| Method | Gives back |
|---|---|
| `s.sub(a, b)` | the part from `a` to `b-1` |
| `s.find(t)` | where `t` starts, or -1 |
| `s.contains(t)`, `s.starts_with(t)`, `s.ends_with(t)` | `bool` |
| `s.split(sep)` | `[]str`; `split("")` gives single characters |
| `s.trim()` | without spaces/newlines at both ends |
| `s.upper()`, `s.lower()` | A–Z / a–z changed (ASCII letters) |
| `s.replace(a, b)` | every `a` replaced by `b` |
| `s.repeat(n)` | `s` n times |
| `s.bytes()` | `[]u8` with the bytes |
| `s.ptr` | a `ptr[char]` for C (valid while `s` lives) |
`str` keeps its length, so `.len` is instant, and it is always NUL-terminated, so passing it
to C as `char*` works.
---
## 12. Structs
```c
struct Player {
str name
int lives = 3 // a default value
[]int scores
Point pos // structs inside structs
}
p := Player(name: "Ola") // by name: missing fields get their default (or zero)
q := Point(1, 2) // in order: then every field must be given
p.lives -= 1
p.scores.push(10)
p.pos.x = 5
print(p) // Player(name: "Ola", lives: 2, scores: [10], pos: Point(x: 5, y: 0))
```
- Assigning or passing a struct copies it, together with its arrays and text.
- A struct can't contain itself directly; use `ptr[Node]` or `[]Node` for that field (trees, lists).
- Structs have no methods. Write functions that take the struct: `fn heal(Player p) -> Player`.
---
## 13. Memory: who frees what
You never free arrays, text or structs. Finch does it for you, at a predictable moment:
**when the block that owns the value ends** (`}`), or earlier on `return`, `break`, `continue`.
- A variable **owns** its value. Assigning copies it, so two variables never share one list.
- A value produced by a function call or an expression is **moved** where it's stored: no copy.
- `return list` moves the variable out of the function: no copy.
- Function parameters are **lent**: the function gets the caller's value without a copy. If the
function changes the parameter, Finch makes it a private copy first. You never notice except in speed.
For memory you manage yourself (linked lists, trees, sharing between structures):
```c
node := new(Node(value: 1)) // put a value on the heap, get a ptr[Node]
defer free(node) // free(...) gives it back (and frees its arrays and text)
```
Memory from C (`malloc`, library objects) is freed with C's functions, as in C.
---
## 14. Pointers
A pointer holds the **address** of a value. Finch uses words instead of C's `*` and `&`:
| C | Finch | Meaning | | C | Finch | Meaning |
|---|---|---| |---|---|---|
| `int *p` | `ptr[int] p` | p points to an int | | `int *p` | `ptr[int] p` | p points to an int |
| `&x` | `addr(x)` | the address of x | | `&x` | `addr(x)` | the address of x (also `addr(a[i])`, `addr(s.field)`) |
| `*p` | `p.value` | the value p points to | | `*p` | `p.value` | the value p points to |
| `p->field` | `p.field` | a field of the struct p points to |
| `p[i]` | `p[i]` | C-style indexing (no bounds check) |
| `NULL` | `null` | points nowhere | | `NULL` | `null` | points nowhere |
| `void *` | `ptr` | points to something of unknown type | | `void *` | `ptr` | points to something of unknown type |
@ -343,38 +471,74 @@ fn double(ptr[int] p) {
p.value = p.value * 2 p.value = p.value * 2
} }
fn main() { x := 21
x := 21 double(addr(x)) // x is 42
double(addr(x))
print(x) // 42
ptr[int] nothing = null
if nothing == null {
print("empty")
}
}
``` ```
Pointers can point to pointers: `ptr[ptr[int]]`, then `pp.value.value`. If a struct has its own field called `value`, then `p.value` on a pointer to it means that field.
Using `.value` on a `null` pointer **stops the program** with Using `.value`, `.field` or `[i]` on a `null` pointer **stops the program** with a clear message.
`runtime error: used .value on a null pointer` instead of crashing randomly.
⚠️ A pointer to a variable must not be used after the function that owns the variable ⚠️ A pointer to a variable must not be used after the variable's block has ended. Finch does not check this.
has returned. The variable no longer exists then. Finch does not check this yet.
--- ---
## 11. Using C libraries ## 15. Built-in functions
Almost every library on Linux has a C interface: graphics, sound, networking, databases. | Function | What it does |
Finch can use them directly. |---|---|
| `print(a, b, ...)` | Prints all values separated by spaces, then a new line. Works with every type, including arrays and structs. |
| `input()`, `input("question")` | Reads one line typed by the user (without the newline). Empty text at end of input. |
| `read_file(path)` | The whole file as `str`. Stops the program with a clear message if it can't be read. |
| `write_file(path, text)` | Writes (replaces) the file; gives back `true` if it worked. |
| `file_exists(path)` | `true` / `false` |
| `shell(command)` | Runs a shell command, gives back its exit code. |
| `exit(code)` | Ends the program right away. |
| `addr(x)` | A pointer to `x`; `addr(function)` gives C a function pointer (callback). |
| `new(value)`, `free(p)` | Manual heap memory (see [Memory](#13-memory-who-frees-what)). |
| `int(x)`, `str(x)`, `u8(x)`, … | Conversions (see [Conversions](#conversions)). |
---
## 16. Modules
Split a program into files. `import shapes` loads `shapes.fn` from the same folder (or from a
folder listed in the `FINCH_PATH` environment variable, separated by `:`).
```c
// shapes.fn
struct Box {
float w
float h
}
fn area(Box b) -> float {
return b.w * b.h
}
```
```c
// main.fn
import shapes
fn main() {
b := shapes.Box(2, 3)
print(shapes.area(b))
shapes.Box other // a module's type in a declaration
}
```
- Everything in a module is used with the module's name in front: `shapes.area`, `shapes.Box`.
- A module doesn't have `main`. Modules can import other modules.
---
## 17. Using C libraries
### Step 1: import the header ### Step 1: import the header
```c ```c
import "stdio.h" import "stdio.h"
import "math.h"
import "GLFW/glfw3.h" // a path inside the system include folders import "GLFW/glfw3.h" // a path inside the system include folders
import "mylib.h" // a header next to your .fn file import "mylib.h" // a header next to your .fn file
``` ```
@ -382,33 +546,23 @@ import "mylib.h" // a header next to your .fn file
Finch reads the header with **libclang** (clang's C parser) and makes these usable: Finch reads the header with **libclang** (clang's C parser) and makes these usable:
- **functions**, including ones with a variable number of arguments, like `printf`, - **functions**, including ones with a variable number of arguments, like `printf`,
- **`enum` values**, - **structs**, by value and by pointer (`Vector2`, `SDL_Rect`, `CXCursor`, …), with their fields,
- **simple `#define` constants** that are a single number, like `M_PI`, `EOF`, `GL_COLOR_BUFFER_BIT`, - **`enum` values** and **simple `#define` constants** (`M_PI`, `GL_COLOR_BUFFER_BIT`),
- **global variables**, like `stdout` and `stderr`. - **global variables**, like `stdout` and `stderr`.
### Step 2: link the library ### Step 2: link the library
A header only *describes* functions. Their actual code is in a library file (`libNAME.so`).
Tell Finch which library to use, at the top of the file:
```c ```c
link "glfw" // uses libglfw.so link "glfw" // uses libglfw.so (pkg-config is asked first, if installed)
link "GL" // uses libGL.so link "helpers.c" // compile and link your own C file (next to the .fn file)
link "prebuilt.a" // or an object file / static library
``` ```
Write just the name: for `libglfw.so` that is `glfw`. If `pkg-config` knows the name, Finch uses
its settings (paths and extra libraries). Otherwise it passes `-lNAME` to the linker.
The C standard library and the math library (`libm`) are always linked.
On the command line, `-l NAME` does the same as `link "NAME"`.
If you forget the `link` line, Finch tells you exactly what is missing: If you forget the `link` line, Finch tells you exactly what is missing:
``` ```
error: glfwInit, glfwCreateWindow, glfwMakeContextCurrent and 5 more come from "GLFW/glfw3.h", but its library isn't linked error: glfwInit, glfwCreateWindow, glfwMakeContextCurrent and 5 more come from "GLFW/glfw3.h", but its library isn't linked
the header only says the functions exist; their code lives in a library. ...
add this at the top of your file (with the library's real name):
link "glfw" link "glfw"
``` ```
@ -422,52 +576,46 @@ error: glfwInit, glfwCreateWindow, glfwMakeContextCurrent and 5 more come from "
| `float`, `double` | `f32`, `f64` | | `float`, `double` | `f32`, `f64` |
| `_Bool` / `bool` | `bool` | | `_Bool` / `bool` | `bool` |
| `enum` | the matching signed integer | | `enum` | the matching signed integer |
| `char *`, `const char *` | `str` | | `char *` as a **parameter or result** | `str` |
| `int *`, `double *`, … | `ptr[i32]`, `ptr[f64]`, … | | `char *` inside a struct or behind a pointer | `ptr[char]` (use `str(p)` to read it) |
| `void *`, `struct X *`, `FILE *`, function pointers | `ptr` | | `struct X` by value | `X`, a struct with the same fields |
| `struct X *`, `int *`, … | `ptr[X]`, `ptr[i32]`, … |
| `int arr[16]` inside a struct | `[16]i32`: index it and use `.len` |
| `void *`, function pointers, incomplete structs | `ptr` |
So C's `int` is Finch's `i32`. Literals adapt automatically (`abs(-5)` works), ### Example: structs and callbacks
but a Finch `int` variable must be converted: `abs(i32(x))`.
### Example
```c ```c
import "stdio.h" import "raylib.h"
import "math.h" link "raylib"
import "stdlib.h"
fn main() { fn main() {
printf("%d + %d = %d\n", 2, 3, 5) InitWindow(800, 450, "Finch")
print(sqrt(2), M_PI, RAND_MAX) while !WindowShouldClose() {
BeginDrawing()
ptr[i32] n = malloc(4) // ask C for 4 bytes of memory ClearBackground(Color(r: 30, g: 30, b: 40, a: 255))
n.value = 7 DrawCircleV(Vector2(400, 225), 50, Color(255, 136, 0, 255))
print(n.value) EndDrawing()
free(n) // and give them back }
CloseWindow()
fprintf(stderr, "this goes to the error output\n")
} }
``` ```
A complete graphics example is in `examples/window.fn`: a GLFW + OpenGL window that changes color. To give C a function to call back (like `qsort`'s comparator), use `addr(myFunction)`.
`examples/llvm.fn` shows that Finch can even drive LLVM itself through its C API. Its parameters must be C types (numbers, pointers, C structs).
### What does not work yet ### What does not work yet
- C functions that take or return a **`struct` by value**, such as `div()`. Finch says so - C **unions** by value, and structs with **bit fields** by value (pointers to them work).
clearly if you try. Functions taking a *pointer* to a struct are fine. - **Function-like macros** and macros that are not a single number.
- **Function-like macros** (`#define MAX(a,b) ...`) and macros that are not a single number.
- `long double`, 128-bit integers. - `long double`, 128-bit integers.
- Reading the **fields** of a C struct.
--- ---
## 12. Errors ## 18. Errors
### Compile errors ### Compile errors
Finch stops at the first problem and shows where it is:
``` ```
game.fn:12:9: error: 'score' must be int, but this is str game.fn:12:9: error: 'score' must be int, but this is str
12 | score = "high" 12 | score = "high"
@ -478,64 +626,87 @@ Format: `file:line:column: error: explanation`, then the line with a `^` under t
### Runtime errors ### Runtime errors
Some problems can only be seen while the program runs. Instead of undefined behavior Instead of undefined behavior (as in C), Finch stops with a message and exit code 1:
(as in C), Finch stops with a clear message on the error output and exit code 1:
| Message | Cause | | Message | Cause |
|---|---| |---|---|
| `runtime error: division by zero` | `/` or `%` by a variable that was 0 | | `runtime error: division by zero` | `/` or `%` by a variable that was 0 |
| `runtime error: division overflows …` | the smallest signed number divided by −1 | | `runtime error: division overflows …` | the smallest signed number divided by −1 |
| `runtime error: used .value on a null pointer` | reading or writing `p.value` while `p` is `null` | | `runtime error: index 5 is out of range (the length is 3)` | `a[5]` on a shorter array or str |
| `runtime error: pop() on an empty array` | |
| `runtime error: used .value on a null pointer` | also `.field` and `[i]` through `null` |
| `runtime error: can't turn "x" into int` | `int(...)` / `float(...)` on text that isn't a number |
| `runtime error: can't read the file "…"` | `read_file` on a missing or unreadable file |
--- ---
## 13. Troubleshooting ## 19. Debugging
```sh
finch build game.fn -g -O0 -o game
gdb ./game
(gdb) break game.fn:12
(gdb) run
(gdb) bt # where are we, through which functions
(gdb) info locals # all variables
(gdb) print player.lives
```
`-g` adds debug info (lines, functions, variables, struct fields). `-O0` keeps every variable
visible; without it the optimizer may remove some.
---
## 20. Troubleshooting
| Problem | Solution | | Problem | Solution |
|---|---| |---|---|
| `can't find the C header 'x.h'` | The library's development files are missing. Install them (on Debian: `libx-dev`; on Nix: add to `shell.nix`). For your own header, put it next to the `.fn` file. | | `can't find the C header 'x.h'` | The library's development files are missing. Install them (Debian: `libx-dev`; Nix: add to `shell.nix`). |
| `… come from "x.h", but its library isn't linked` | Add `link "name"` at the top of the file. | | `… come from "x.h", but its library isn't linked` | Add `link "name"` at the top of the file. |
| `the library 'x' wasn't found` | The library is not installed, or the name is wrong. On Nix, add it to `shell.nix` and run inside `nix-shell`. | | `the library 'x' wasn't found` | Not installed, or a wrong name. On Nix, add it to `shell.nix` and run inside `nix-shell`. |
| `the C function 'f' can't be used from Finch yet` | It uses a struct by value, or another unsupported type. Look for a variant that takes pointers. | | `the C function 'f' can't be used from Finch yet` | It uses a union, bit fields or another unsupported type by value. Look for a variant that takes pointers. |
| `no C compiler found to link with` | Install gcc or clang, or set `CC`. | | `can't find the module 'x'` | Put `x.fn` next to the importing file, or set `FINCH_PATH`. |
| The program runs, but a C library is not found at start | It was linked from a path the system does not search. Run it inside the same `nix-shell`, or install the library system-wide. | | `couldn't build the Finch runtime` | No working C compiler. Install gcc or clang, or set `CC`. |
| CMake: `libclang not found` | Install the libclang dev package, or run `cmake` inside `nix-shell`. | | A library is not found when the program starts | It was linked from a folder the system doesn't search. Run inside the same `nix-shell`, or install it system-wide. |
| Build fails with LLVM API errors | You have a different LLVM version than 21. | | `clang` inside `nix-shell` can't find `stdio.h` | You have an old `shell.nix`: `llvmPackages.clang` must be listed before `llvmPackages.libclang`. |
| A program never ends | A loop condition never becomes false. Press Ctrl+C. | | A program never ends | A loop condition never becomes false. Press Ctrl+C. |
--- ---
## 14. Project layout and tests ## 21. Project layout and tests
``` ```
Finch/ Finch/
├── src/ the compiler (C++) ├── src/ the compiler (C++)
├── examples/ example programs (hello, tour, types, c_import, window, llvm) ├── runtime/ finch_rt.c: the small runtime (text, arrays, input, files)
├── boot/ the Finch compiler written in Finch (see the engineers' guide)
├── examples/ hello, tour, types, structs, todo, guess, c_import, window, raylib, llvm
├── tests/ ├── tests/
│ ├── run/ programs + the exact output they must print (.out) │ ├── run/ programs + the exact output they must print (.out), input (.in)
│ ├── fail/ programs that must fail, with the expected error in line 1 │ ├── fail/ programs that must fail, with the expected error in line 1
│ └── run.sh the test runner │ ├── run.sh the test runner (MEMCHECK=1 also checks memory with valgrind)
│ └── boot.sh builds the self-hosted compiler with itself and compares
├── docs/ this documentation (en, pl) ├── docs/ this documentation (en, pl)
├── shell.nix the Nix development environment ├── shell.nix the Nix development environment
└── CMakeLists.txt └── CMakeLists.txt
``` ```
Run the tests after building:
```sh ```sh
tests/run.sh # → 34 passed, 0 failed tests/run.sh # → 58 passed, 0 failed
MEMCHECK=1 tests/run.sh # the same under valgrind: no leaks, no bad memory access
tests/boot.sh # the self-hosting check (needs clang)
``` ```
--- ---
## 15. Current limits ## 22. Current limits
Finch 1.0 is a solid core, not a finished language. Not there yet: Finch 2.0 is a complete small language, but not a finished one. Not there yet:
- `struct`, arrays, reading keyboard input (`input()`), string building (`"a" + "b"`). - Methods on structs, generics, maps/dictionaries, `match`. (Use arrays of structs and functions.)
For now, use C functions for these (`scanf`, `snprintf`, `malloc`…). - Error values: errors in `int("x")` or `read_file` stop the program. Check first (`file_exists`).
- Automatic memory management (planned: memory freed at the end of the block, plus `defer`). - Unicode-aware text: `.len`, `s[i]` and `upper()` work on bytes / ASCII.
- Finch modules (one program = one `.fn` file for now). - Threads.
- Platforms other than Linux x86-64. - Platforms other than Linux x86-64.
See the [roadmap in the README](../../README.md#roadmap). See the [roadmap in the README](../../README.md#roadmap).

File diff suppressed because it is too large Load diff

View file

@ -20,10 +20,15 @@ Czytaj powoli, po jednym rozdziale, i przepisuj każdy przykład samodzielnie. T
8. [Podejmowanie decyzji: if](#8-podejmowanie-decyzji-if) 8. [Podejmowanie decyzji: if](#8-podejmowanie-decyzji-if)
9. [Powtarzanie: pętle](#9-powtarzanie-pętle) 9. [Powtarzanie: pętle](#9-powtarzanie-pętle)
10. [Własne polecenia: funkcje](#10-własne-polecenia-funkcje) 10. [Własne polecenia: funkcje](#10-własne-polecenia-funkcje)
11. [Kiedy coś pójdzie nie tak](#11-kiedy-coś-pójdzie-nie-tak) 11. [Listy: wiele wartości w jednym pudełku](#11-listy-wiele-wartości-w-jednym-pudełku)
12. [Małe projekty do zrobienia](#12-małe-projekty-do-zrobienia) 12. [Zabawa tekstem](#12-zabawa-tekstem)
13. [Ściąga](#13-ściąga) 13. [Pytanie użytkownika](#13-pytanie-użytkownika)
14. [Słowniczek](#14-słowniczek) 14. [Własne rodzaje wartości: struktury](#14-własne-rodzaje-wartości-struktury)
15. [Zapisywanie do pliku](#15-zapisywanie-do-pliku)
16. [Kiedy coś pójdzie nie tak](#16-kiedy-coś-pójdzie-nie-tak)
17. [Małe projekty do zrobienia](#17-małe-projekty-do-zrobienia)
18. [Ściąga](#18-ściąga)
19. [Słowniczek](#19-słowniczek)
--- ---
@ -72,7 +77,7 @@ Jeśli ostatnia linijka skończy się bez słowa `error`, Finch jest gotowy.
./build/finch version ./build/finch version
``` ```
Powinieneś zobaczyć coś w stylu `finch 1.0.0`. Powinieneś zobaczyć coś w stylu `finch 2.0.0`.
--- ---
@ -446,7 +451,202 @@ Nie ma znaczenia, gdzie w pliku umieścisz swoje funkcje, nad czy pod `main`.
--- ---
## 11. Kiedy coś pójdzie nie tak ## 11. Listy: wiele wartości w jednym pudełku
Czasem jedno pudełko nie wystarczy. Na liście zakupów jest wiele rzeczy.
**Lista** (programiści mówią też **tablica**) trzyma wiele wartości po kolei:
```c
fn main() {
owoce := ["jabłko", "banan", "wiśnia"]
print(owoce)
print("Mam", owoce.len, "owoce")
print("Pierwszy to", owoce[0])
}
```
```
["jabłko", "banan", "wiśnia"]
Mam 3 owoce
Pierwszy to jabłko
```
- Nawiasy kwadratowe `[ ]` tworzą listę. Wartości oddzielamy przecinkami.
- `.len` mówi, ile rzeczy jest na liście.
- `owoce[0]` to **pierwsza** rzecz. Liczymy od **0**, więc `owoce[1]` to druga, a `owoce[2]` trzecia.
Jeśli zapytasz o miejsce, którego nie ma, np. `owoce[10]`, program się zatrzyma i powie:
`index 10 is out of range (the length is 3)`, czyli „indeks 10 jest poza zakresem (długość to 3)”.
### Dodawanie i zabieranie
```c
fn main() {
[]int wyniki // pusta lista liczb całkowitych
wyniki.push(10) // dołóż 10 na koniec
wyniki.push(25)
wyniki.push(7)
print(wyniki) // [10, 25, 7]
ostatni := wyniki.pop() // zabierz ostatni
print(ostatni, wyniki) // 7 [10, 25]
wyniki.sort() // posortuj
print(wyniki)
}
```
### Przechodzenie po liście
`for ... in` odwiedza każdą wartość po kolei:
```c
fn main() {
suma := 0
for n in [3, 5, 2] {
suma += n
}
print("Suma:", suma) // Suma: 10
}
```
Listy umieją więcej: `contains` (czy zawiera), `find` (znajdź), `insert` (wstaw), `remove` (usuń),
`sort` (sortuj), `reverse` (odwróć), `join` (połącz). Wszystkie opisuje [przewodnik dla techników](dla-technikow.md#tablice).
---
## 12. Zabawa tekstem
Teksty sklejasz znakiem `+`:
```c
fn main() {
imie := "Ola"
powitanie := "Cześć, " + imie + "!"
print(powitanie)
}
```
Żeby dokleić liczbę do tekstu, najpierw zamień ją na tekst przez `str(...)`:
```c
wiek := 10
print("Wiek: " + str(wiek))
```
Teksty mają też przydatne narzędzia:
```c
fn main() {
slowo := "Finch"
print(slowo.len) // 5 liter
print(slowo[0]) // F (pierwsza litera)
print(slowo.upper()) // FINCH (wielkimi literami)
print(slowo.lower()) // finch (małymi literami)
print(slowo.contains("in")) // true (czy zawiera "in")
for litera in slowo {
print(litera) // F, i, n, c, h, każda w osobnej linijce
}
}
```
⚠️ `.len` liczy bajty, a polskie litery (ą, ę, ż…) zajmują po 2 bajty. `"żółw".len` to 7, a nie 4.
---
## 13. Pytanie użytkownika
`input` pokazuje pytanie i czeka, aż ktoś wpisze odpowiedź i naciśnie Enter:
```c
fn main() {
imie := input("Jak masz na imię? ")
print("Miło cię poznać,", imie)
}
```
Odpowiedź jest zawsze **tekstem**. Jeśli potrzebujesz liczby, zamień ją przez `int(...)`:
```c
fn main() {
odpowiedz := input("Ile masz lat? ")
wiek := int(odpowiedz)
print("Za rok będziesz mieć", wiek + 1)
}
```
Jeśli ktoś wpisze coś, co nie jest liczbą, program zatrzyma się z komunikatem
w stylu `can't turn "abc" into int` („nie da się zamienić "abc" na int”).
---
## 14. Własne rodzaje wartości: struktury
Wyobraź sobie kartę w bibliotece: każda karta ma tytuł, autora i rok.
**Struktura** (`struct`) pozwala zrobić własny rodzaj wartości z nazwanymi częściami:
```c
struct Ksiazka {
str tytul
str autor
int rok
}
fn main() {
k := Ksiazka(tytul: "Hobbit", autor: "Tolkien", rok: 1937)
print(k.tytul, "napisał", k.autor)
k.rok = 1938
print(k)
}
```
```
Hobbit napisał Tolkien
Ksiazka(tytul: "Hobbit", autor: "Tolkien", rok: 1938)
```
- `struct Ksiazka { ... }` opisuje, co ma każda książka. Pisz to **poza** `main`.
- `Ksiazka(tytul: ..., autor: ..., rok: ...)` tworzy jedną książkę.
- `k.tytul` odczytuje jedną część, a `k.rok = 1938` ją zmienia.
Częściom możesz nadać wartość początkową. Wtedy nie trzeba ich podawać:
```c
struct Gracz {
str imie
int zycia = 3
}
fn main() {
g := Gracz(imie: "Ola")
print(g.zycia) // 3
}
```
Możesz też trzymać wiele struktur na liście: `[]Ksiazka polka`, a potem `polka.push(k)`.
---
## 15. Zapisywanie do pliku
Program zapomina wszystko, kiedy się kończy. Żeby coś zapamiętać, zapisz to do pliku:
```c
fn main() {
write_file("notatka.txt", "Kupić mleko")
print(read_file("notatka.txt"))
}
```
- `write_file(nazwa, tekst)` zapisuje tekst w pliku (i zastępuje to, co tam było).
- `read_file(nazwa)` oddaje tekst z pliku.
- `file_exists(nazwa)` mówi, czy plik istnieje (`true` albo `false`).
Plik pojawi się w folderze, w którym uruchomiłeś program.
---
## 16. Kiedy coś pójdzie nie tak
Każdy się myli przy programowaniu, nawet eksperci, codziennie. Każdy się myli przy programowaniu, nawet eksperci, codziennie.
Finch stara się tłumaczyć, co poszło nie tak, prostymi słowami (na razie po angielsku). Przykład: Finch stara się tłumaczyć, co poszło nie tak, prostymi słowami (na razie po angielsku). Przykład:
@ -485,10 +685,13 @@ Tutaj po prostu źle napisaliśmy `wiek`. Komunikat znaczy: „nie ma zmiennej o
| `a condition must be bool` | `if` potrzebuje pytania tak/nie | Porównaj coś: `if x > 0` | | `a condition must be bool` | `if` potrzebuje pytania tak/nie | Porównaj coś: `if x > 0` |
| `unexpected character 'ż'` | Polska litera w nazwie | Zmień na zwykłą literę: `zolw` | | `unexpected character 'ż'` | Polska litera w nazwie | Zmień na zwykłą literę: `zolw` |
| `runtime error: division by zero` | Program podzielił przez 0 w trakcie działania | Sprawdź liczbę przed dzieleniem | | `runtime error: division by zero` | Program podzielił przez 0 w trakcie działania | Sprawdź liczbę przed dzieleniem |
| `index 5 is out of range (the length is 3)` | Zapytałeś listę o miejsce, którego nie ma | Pamiętaj, że liczymy od 0; sprawdź `.len` |
| `can't turn "abc" into int` | `int(...)` dostał tekst, który nie jest liczbą | Sprawdź, co wpisał użytkownik |
| `can't use '+' on str and int` | Tekst plus liczba | Zamień liczbę na tekst przez `str(...)` |
--- ---
## 12. Małe projekty do zrobienia ## 17. Małe projekty do zrobienia
Spróbuj najpierw napisać każdy sam. Do rozwiązania zajrzyj dopiero, gdy utkniesz. Spróbuj najpierw napisać każdy sam. Do rozwiązania zajrzyj dopiero, gdy utkniesz.
@ -595,9 +798,59 @@ fn main() {
``` ```
</details> </details>
### Projekt 6: Zgadnij liczbę
Komputer ma tajną liczbę. Gracz zgaduje, aż trafi, a komputer za każdym razem mówi „więcej” albo „mniej”.
<details><summary>Rozwiązanie</summary>
```c
fn main() {
tajna := 42
proby := 0
while true {
strzal := int(input("Twój strzał: "))
proby += 1
if strzal < tajna {
print("Więcej!")
} else if strzal > tajna {
print("Mniej!")
} else {
print("Tak! Potrzebowałeś", proby, "prób")
return
}
}
}
```
`return` w `main` kończy program. (Wersja z losową liczbą jest w `examples/guess.fn`.)
</details>
### Projekt 7: Średnia ocen
Zapytaj o 3 oceny, zapisz je na liście, a potem pokaż listę i średnią.
<details><summary>Rozwiązanie</summary>
```c
fn main() {
[]int oceny
for i in 0..3 {
oceny.push(int(input("Ocena: ")))
}
suma := 0
for o in oceny {
suma += o
}
print("Oceny:", oceny)
print("Średnia:", float(suma) / oceny.len)
}
```
</details>
--- ---
## 13. Ściąga ## 18. Ściąga
```c ```c
// komentarz // komentarz
@ -630,11 +883,21 @@ fn main() { // tu zaczyna się program
fn dodaj(int a, int b) -> int { // własne polecenie fn dodaj(int a, int b) -> int { // własne polecenie
return a + b return a + b
} }
struct Zwierzak { // własny rodzaj wartości
str imie
int wiek = 1
}
// listy: liczby := [1, 2, 3] liczby.push(4) liczby[0] liczby.len for n in liczby { }
// tekst: "a" + "b" str(42) int("42") s.len s.upper() s.contains("x")
// pytanie: imie := input("Imię? ")
// pliki: write_file("p.txt", tekst) read_file("p.txt")
``` ```
--- ---
## 14. Słowniczek ## 19. Słowniczek
| Słowo | Znaczenie | | Słowo | Znaczenie |
|---|---| |---|---|
@ -647,6 +910,10 @@ fn dodaj(int a, int b) -> int { // własne polecenie
| **funkcja** | Nazwana grupa kroków, której można używać wiele razy | | **funkcja** | Nazwana grupa kroków, której można używać wiele razy |
| **pętla** | Kroki, które się powtarzają | | **pętla** | Kroki, które się powtarzają |
| **warunek** | Pytanie tak/nie, np. `x > 5` | | **warunek** | Pytanie tak/nie, np. `x > 5` |
| **lista / tablica** | Wiele wartości po kolei, np. `[1, 2, 3]` |
| **indeks** | Pozycja wartości na liście, liczona od 0 |
| **struktura** (struct) | Własny rodzaj wartości złożony z nazwanych części |
| **wejście** (input) | To, co użytkownik wpisuje do programu |
| **błąd** (error) | Komunikat, że coś jest nie tak, i gdzie | | **błąd** (error) | Komunikat, że coś jest nie tak, i gdzie |
| **bug** | Pomyłka w programie | | **bug** | Pomyłka w programie |
@ -662,6 +929,10 @@ fn dodaj(int a, int b) -> int { // własne polecenie
| `return` | zwróć (oddaj) | | `return` | zwróć (oddaj) |
| `true` / `false` | prawda / fałsz | | `true` / `false` | prawda / fałsz |
| `fn` (od *function*) | funkcja | | `fn` (od *function*) | funkcja |
| `struct` | struktura |
| `push` / `pop` | dołóż / zabierz |
| `input` | wejście (pytanie) |
| `len` (od *length*) | długość |
| `error` | błąd | | `error` | błąd |
--- ---

View file

@ -1,9 +1,9 @@
# Finch dla techników # Finch dla techników
**Dla osób, które dobrze znają komputery, ale programować nie umieją albo umieją niewiele.** **Dla osób, które dobrze znają komputery, ale programować nie umieją albo umieją niewiele.**
Ten przewodnik opisuje instalację Fincha, obsługę polecenia `finch`, cały język, Ten przewodnik opisuje instalację Fincha, obsługę polecenia `finch`, cały język, pamięć,
korzystanie z bibliotek C i rozwiązywanie typowych problemów. Nie opisuje, jak kompilator moduły, biblioteki C, debugowanie i rozwiązywanie typowych problemów. Nie opisuje, jak
działa w środku. Do tego służy [przewodnik dla inżynierów](dla-inzynierow.md). kompilator działa w środku. Do tego służy [przewodnik dla inżynierów](dla-inzynierow.md).
> 🇬🇧 English version: [for-technicians.md](../en/for-technicians.md) > 🇬🇧 English version: [for-technicians.md](../en/for-technicians.md)
@ -20,26 +20,37 @@ działa w środku. Do tego służy [przewodnik dla inżynierów](dla-inzynierow.
7. [Operatory](#7-operatory) 7. [Operatory](#7-operatory)
8. [Sterowanie przebiegiem](#8-sterowanie-przebiegiem) 8. [Sterowanie przebiegiem](#8-sterowanie-przebiegiem)
9. [Funkcje](#9-funkcje) 9. [Funkcje](#9-funkcje)
10. [Wskaźniki](#10-wskaźniki) 10. [Tablice](#10-tablice)
11. [Biblioteki z C](#11-biblioteki-z-c) 11. [Tekst (str)](#11-tekst-str)
12. [Błędy](#12-błędy) 12. [Struktury](#12-struktury)
13. [Rozwiązywanie problemów](#13-rozwiązywanie-problemów) 13. [Pamięć: kto co zwalnia](#13-pamięć-kto-co-zwalnia)
14. [Struktura projektu i testy](#14-struktura-projektu-i-testy) 14. [Wskaźniki](#14-wskaźniki)
15. [Obecne ograniczenia](#15-obecne-ograniczenia) 15. [Funkcje wbudowane](#15-funkcje-wbudowane)
16. [Moduły](#16-moduły)
17. [Biblioteki z C](#17-biblioteki-z-c)
18. [Błędy](#18-błędy)
19. [Debugowanie](#19-debugowanie)
20. [Rozwiązywanie problemów](#20-rozwiązywanie-problemów)
21. [Struktura projektu i testy](#21-struktura-projektu-i-testy)
22. [Obecne ograniczenia](#22-obecne-ograniczenia)
--- ---
## 1. Czym jest Finch ## 1. Czym jest Finch
Finch to język **kompilowany**. Narzędzie `finch` tłumaczy plik `.fn` na prawdziwy program Finch to język **kompilowany**. Narzędzie `finch` tłumaczy plik `.fn` na prawdziwy program
(natywny plik wykonywalny, taki sam jak te z C). Korzysta przy tym z **LLVM**, tego samego (natywny plik wykonywalny, taki jak te z C). Korzysta przy tym z **LLVM**, tego samego zaplecza
zaplecza kompilatorów, na którym działają clang, Rust i Swift. kompilatorów, na którym działają clang, Rust i Swift.
Co to daje: Co to daje:
- **Szybkość zbliżoną do C.** Nie ma interpretera, maszyny wirtualnej ani garbage collectora. - **Szybkość zbliżoną do C.** Nie ma interpretera, maszyny wirtualnej ani garbage collectora.
Kontrola zakresu tablic i inne zabezpieczenia po optymalizacji prawie nic nie kosztują.
- **Małe, samodzielne programy**, które można skopiować i uruchomić. - **Małe, samodzielne programy**, które można skopiować i uruchomić.
- **Bezpośredni dostęp do bibliotek C.** Piszesz `import "stdio.h"` i wołasz, co chcesz. - **Pamięć obsługiwana za ciebie.** Listy i teksty są zwalniane automatycznie na końcu bloku,
do którego należą, bez garbage collectora.
- **Bezpośredni dostęp do bibliotek C.** Piszesz `import "stdio.h"` i wołasz, co chcesz,
nawet funkcje przyjmujące struktury.
Zasady projektu: jeden sposób na każdą rzecz, składnia podobna do C bez jej pułapek Zasady projektu: jeden sposób na każdą rzecz, składnia podobna do C bez jej pułapek
i komunikaty błędów, które mówią, jak problem naprawić. i komunikaty błędów, które mówią, jak problem naprawić.
@ -56,10 +67,10 @@ Z innymi wersjami LLVM prawdopodobnie się nie skompiluje, bo API C++ LLVM zmien
```sh ```sh
git clone https://github.com/Nexoniarz/Finch.git git clone https://github.com/Nexoniarz/Finch.git
cd Finch cd Finch
nix-shell # pobiera LLVM, libclang, cmake, ninja, pkg-config nix-shell # pobiera LLVM, libclang, clang, cmake, ninja, pkg-config
cmake -S . -B build -G Ninja cmake -S . -B build -G Ninja
ninja -C build ninja -C build
./build/finch version # finch 1.0.0 (LLVM 21.x) ./build/finch version # finch 2.0.0 (LLVM 21.x)
``` ```
### Opcja B: pakiety z twojej dystrybucji ### Opcja B: pakiety z twojej dystrybucji
@ -73,7 +84,7 @@ i libclang 21, a opcjonalnie `pkg-config`.
| Fedora | `gcc-c++ cmake ninja-build pkgconf llvm-devel clang-devel` | | Fedora | `gcc-c++ cmake ninja-build pkgconf llvm-devel clang-devel` |
| Arch | `base-devel cmake ninja pkgconf llvm clang` | | Arch | `base-devel cmake ninja pkgconf llvm clang` |
Jeśli CMake nie znajduje LLVM, wskaż mu katalog: Ta opcja nie była testowana. Jeśli CMake nie znajduje LLVM, wskaż mu katalog:
```sh ```sh
cmake -S . -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-21/lib/cmake/llvm cmake -S . -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-21/lib/cmake/llvm
@ -87,21 +98,23 @@ sudo cp build/finch /usr/local/bin/
finch version finch version
``` ```
Podczas pracy Finch potrzebuje działającego kompilatora C (`cc`). Używa go jako **linkera**, Podczas pracy Finch potrzebuje kompilatora C (`cc`). Używa go do linkowania programów i jednorazowo
który skleja skompilowany kod w gotowy program. Inny kompilator wskażesz zmienną środowiskową `CC`. do zbudowania swojej małej biblioteki uruchomieniowej, którą trzyma w `~/.cache/finch`.
Inny kompilator wskażesz zmienną `CC`.
--- ---
## 3. Polecenie finch ## 3. Polecenie finch
``` ```
finch run <plik.fn> skompiluj i od razu uruchom finch run <plik.fn> [argumenty...] skompiluj i od razu uruchom
finch build <plik.fn> [-o nazwa] skompiluj do programu (domyślna nazwa: nazwa pliku) finch build <plik.fn> [-o nazwa] skompiluj do programu (domyślna nazwa: nazwa pliku)
finch ir <plik.fn> pokaż LLVM IR (dla ciekawskich) finch ir <plik.fn> pokaż LLVM IR (dla ciekawskich)
finch version pokaż wersję finch version pokaż wersję
opcje: opcje:
-l <lib> dolinkuj bibliotekę C, to samo co link "lib" w pliku -l <lib> dolinkuj bibliotekę C, to samo co link "lib" w pliku
-g dodaj informacje dla debuggera (gdb / lldb)
-O0 bez optymalizacji -O0 bez optymalizacji
``` ```
@ -109,13 +122,13 @@ Przykłady:
```sh ```sh
finch run hello.fn # wypisuje wynik finch run hello.fn # wypisuje wynik
finch run narzedzie.fn dane.txt -v # argumenty po nazwie pliku trafiają do programu
finch build gra.fn -o mojagra # tworzy ./mojagra finch build gra.fn -o mojagra # tworzy ./mojagra
./mojagra finch build gra.fn -g -O0 # wersja dla debuggera
finch run okno.fn -l glfw # z biblioteką C
``` ```
`finch run` przekazuje dalej kod wyjścia twojego programu. Jeśli program się wysypie, `finch run` przekazuje dalej kod wyjścia programu. Jeśli program się wysypie, powie jak
powie jak (np. `the program crashed: Segmentation fault`). (np. `the program crashed: Segmentation fault`).
--- ---
@ -126,8 +139,14 @@ powie jak (np. `the program crashed: Segmentation fault`).
/* albo zajmuje /* albo zajmuje
kilka linijek */ kilka linijek */
import "math.h" // nagłówki C (opcjonalne), zawsze na górze import "math.h" // nagłówki C (opcjonalne), na górze
link "m" // biblioteki C do dolinkowania (opcjonalne) import ksztalty // moduły Fincha (opcjonalne): ksztalty.fn obok tego pliku
link "m" // biblioteki albo pliki C do dolinkowania (opcjonalne)
struct Punkt { // własne typy
int x
int y
}
fn main() { // tu zaczyna się program fn main() { // tu zaczyna się program
print("Cześć") print("Cześć")
@ -136,12 +155,13 @@ fn main() { // tu zaczyna się program
Zasady, które warto znać: Zasady, które warto znać:
- **Jedna instrukcja na linijkę.** Średników nie ma. Długie wyrażenie możesz złamać - **Jedna instrukcja na linijkę.** Średników nie ma. Długie wyrażenie możesz złamać **po** operatorze
**po** operatorze (`a +⏎ b`) albo w dowolnym miejscu wewnątrz nawiasów `( )`. (`a +⏎ b`) albo w dowolnym miejscu wewnątrz nawiasów `( )` lub `[ ]`.
- **Klamry są zawsze obowiązkowe** po `if`, `else`, `for`, `while`, a otwierająca `{` stoi w tej samej linijce. - **Klamry są zawsze obowiązkowe** po `if`, `else`, `for`, `while`, a otwierająca `{` stoi w tej samej linijce.
- **Bez nawiasów wokół warunków:** `if x > 5 {`, a nie `if (x > 5) {`. - **Bez nawiasów wokół warunków:** `if x > 5 {`, a nie `if (x > 5) {`.
- Funkcje mogą być w pliku w dowolnej kolejności. - Funkcje i struktury mogą być w pliku w dowolnej kolejności.
- `main` to `fn main()` albo `fn main() -> int` (zwraca kod wyjścia). - `main` to `fn main()`, `fn main() -> int` (zwraca kod wyjścia) albo `fn main([]str args)`
(dostaje argumenty z linii poleceń; `args[0]` to ścieżka samego programu).
--- ---
@ -150,19 +170,20 @@ Zasady, które warto znać:
```c ```c
x := 5 // nowa zmienna, typ zgadnięty (int) x := 5 // nowa zmienna, typ zgadnięty (int)
int y = 10 // nowa zmienna, typ podany jawnie int y = 10 // nowa zmienna, typ podany jawnie
int z // bez wartości: startuje od zera ("" dla str, null dla wskaźników) int z // bez wartości: startuje od zera ("" dla str, [] dla tablic, null dla wskaźników)
Punkt p // struktura bez wartości: zera albo wartości domyślne struktury
x = 7 // przypisanie x = 7 // przypisanie
x += 1 // także -= *= /= %= x += 1 // także -= *= /= %=
``` ```
- Nazwę można zadeklarować **raz** w funkcji. Finch nie pozwala użyć tej samej nazwy ponownie - Nazwę można zadeklarować **raz** w funkcji. Finch nie pozwala użyć jej ponownie w bloku
w bloku wewnętrznym (nie ma „przesłaniania”), bo to częste źródło pomyłek. wewnętrznym (nie ma „przesłaniania”), bo to częste źródło pomyłek.
- Zmienna istnieje od deklaracji do końca bloku `{ }`, w którym powstała. - Zmienna istnieje od deklaracji do końca bloku `{ }`, w którym powstała.
- Nazwy: litery ASCII, cyfry i `_`, bez cyfry na początku. Polskie litery tylko w tekstach w cudzysłowie. - Nazwy: litery ASCII, cyfry i `_`, bez cyfry na początku. Polskie litery tylko w tekstach w cudzysłowie.
Nazwy typów (`int`, `u8`, …) i słowa kluczowe są zarezerwowane. Nazwy typów (`int`, `u8`, …), funkcji wbudowanych i słowa kluczowe są zarezerwowane.
**Słowa kluczowe:** `fn return if else while for in break continue true false null import link` **Słowa kluczowe:** `fn return if else while for in break continue true false null import link struct defer`
--- ---
@ -176,7 +197,9 @@ x += 1 // także -= *= /= %=
| `float` | liczba z przecinkiem, 64-bitowa (to samo co `f64`) | `3.14` | | `float` | liczba z przecinkiem, 64-bitowa (to samo co `f64`) | `3.14` |
| `bool` | `true` albo `false` | | | `bool` | `true` albo `false` | |
| `char` | jeden znak wielkości bajtu | `'A'`, `'\n'` | | `char` | jeden znak wielkości bajtu | `'A'`, `'\n'` |
| `str` | tekst (napis w stylu C) | `"cześć\tświecie"` | | `str` | tekst | `"cześć\tświecie"` |
| `[]T` | lista (tablica) elementów `T` | `[1, 2, 3]` |
| twoje struktury | nazwane grupy wartości | `Punkt(1, 2)` |
Znaki specjalne w tekstach: `\n` nowa linia, `\t` tabulator, `\r`, `\0`, `\\`, `\"`, `\'`. Znaki specjalne w tekstach: `\n` nowa linia, `\t` tabulator, `\r`, `\0`, `\\`, `\"`, `\'`.
@ -191,48 +214,41 @@ Znaki specjalne w tekstach: `\n` nowa linia, `\t` tabulator, `\r`, `\0`, `\\`, `
`f32` to 32-bitowa liczba z przecinkiem (ok. 7 cyfr znaczących), `f64` to 64-bitowa (ok. 15–16). `f32` to 32-bitowa liczba z przecinkiem (ok. 7 cyfr znaczących), `f64` to 64-bitowa (ok. 15–16).
### Wskaźniki
`ptr[T]` wskazuje na wartość typu `T`. Samo `ptr` wskazuje na „coś” nieznanego typu,
jak `void*` w C. Szczegóły w rozdziale [Wskaźniki](#10-wskaźniki).
### Zamiana typów ### Zamiana typów
Finch zamienia typy sam **tylko wtedy, gdy nic nie może zginąć**: Finch zamienia typy sam **tylko wtedy, gdy nic nie może zginąć**:
| Z | Na | Automatycznie? | | Z | Na | Automatycznie? |
|---|---|---| |---|---|---|
| mniejsza liczba całkowita | większa o tej samej „znakowości” (`i8`→`i32`, `u8`→`u64`) | ✅ | | mniejsza liczba całkowita | większa o tej samej znakowości (`i8`→`i32`, `u8`→`u64`) | ✅ |
| bez znaku | ściśle większa ze znakiem (`u8`→`i16`, `u32`→`i64`) | ✅ | | bez znaku | ściśle większa ze znakiem (`u8`→`i16`, `u32`→`i64`) | ✅ |
| dowolna liczba całkowita | `float` / `f32` | ✅ | | dowolna liczba całkowita | `float` / `f32` | ✅ |
| `f32` | `f64` | ✅ | | `f32` | `f64` | ✅ |
| `null` | dowolny wskaźnik | ✅ | | `null` | dowolny wskaźnik | ✅ |
| dowolny wskaźnik, `str` | `ptr` | ✅ | | dowolny wskaźnik | `ptr`, a `ptr` na dowolny wskaźnik | ✅ |
| `ptr` | dowolny `ptr[T]` | ✅ |
| wszystko inne | | ❌ napisz to jawnie: `typ(wartość)` | | wszystko inne | | ❌ napisz to jawnie: `typ(wartość)` |
**Liczby wpisane w kod się dopasowują.** Liczba napisana wprost w kodzie pasuje do każdego typu, **Liczby wpisane w kod się dopasowują.** Liczba napisana wprost w kodzie pasuje do każdego typu,
który ją pomieści: `u8 b = 200` działa, a `u8 b = 300` to błąd (`the number 300 doesn't fit in u8`). który ją pomieści: `u8 b = 200` działa, a `u8 b = 300` to błąd. To samo dotyczy stałych z nagłówków C.
To samo dotyczy stałych z nagłówków C.
**Jawna zamiana** wygląda jak wywołanie funkcji o nazwie typu: **Jawna zamiana** wygląda jak wywołanie funkcji o nazwie typu:
```c ```c
int(3.99) // 3 (obcina część ułamkową) int(3.99) // 3 (obcina część ułamkową)
int(-3.99) // -3 float(7) / 2 // 3.5
float(7) / 2 // 3.5 u8(300) // 44 (zostawia najniższe 8 bitów)
u8(300) // 44 (zostawia najniższe 8 bitów) int('A') // 65
i8(200) // -56 char(66) // 'B'
int('A') // 65 bool(0) // false (każda liczba różna od zera to true)
char(66) // 'B' str(42) // "42" (też float, bool, char; str([]u8) zamienia bajty na tekst)
bool(0) // false (każda liczba różna od zera to true) int("42") // 42 (zatrzymuje program, jeśli tekst nie jest liczbą)
ptr(tekst) // str jako wskaźnik bez typu float("2.5") // 2.5
str(p) // wskaźnik jako str ptr(p) // wskaźnik z typem jako wskaźnik bez typu
str(p) // wskaźnik na znaki z C jako tekst
``` ```
**Liczby o stałym rozmiarze się „zawijają”**, jak w prawdziwym sprzęcie: `u8` równe 255 plus 1 daje 0. **Liczby o stałym rozmiarze się „zawijają”**, jak w prawdziwym sprzęcie: `u8` równe 255 plus 1 daje 0.
Działania liczą się w typie użytych wartości, więc `u8(250) + 10` daje `4`, a nie `260`. Działania liczą się w typie użytych wartości, więc `u8(250) + 10` daje `4`, a nie `260`.
Jeśli potrzebujesz więcej miejsca, najpierw zamień typ: `int(x) + 10`.
--- ---
@ -242,8 +258,8 @@ Jeśli potrzebujesz więcej miejsca, najpierw zamień typ: `int(x) + 10`.
| Priorytet | Operatory | Znaczenie | | Priorytet | Operatory | Znaczenie |
|---|---|---| |---|---|---|
| 5 | `*` `/` `%` `<<` `>>` `&` | mnożenie, dzielenie, reszta, przesunięcie bitów w lewo/prawo, bitowe AND | | 5 | `*` `/` `%` `<<` `>>` `&` | mnożenie, dzielenie, reszta, przesunięcia bitów, bitowe AND |
| 4 | `+` `-` `\|` `^` | dodawanie, odejmowanie, bitowe OR, bitowe XOR | | 4 | `+` `-` `\|` `^` | dodawanie (i sklejanie tekstu), odejmowanie, bitowe OR, bitowe XOR |
| 3 | `==` `!=` `<` `<=` `>` `>=` | porównania | | 3 | `==` `!=` `<` `<=` `>` `>=` | porównania |
| 2 | `&&` | logiczne I | | 2 | `&&` | logiczne I |
| 1 | `\|\|` | logiczne LUB | | 1 | `\|\|` | logiczne LUB |
@ -251,18 +267,16 @@ Jeśli potrzebujesz więcej miejsca, najpierw zamień typ: `int(x) + 10`.
Jednoargumentowe (przed wartością): `-x` zmiana znaku, `!x` logiczne NIE, `~x` odwrócenie wszystkich bitów. Jednoargumentowe (przed wartością): `-x` zmiana znaku, `!x` logiczne NIE, `~x` odwrócenie wszystkich bitów.
> **Celowo inaczej niż w C:** operatory bitowe wiążą mocniej niż porównania, więc > **Celowo inaczej niż w C:** operatory bitowe wiążą mocniej niż porównania, więc
> `flagi & 4 != 0` znaczy `(flagi & 4) != 0`. W C to samo po cichu znaczy co innego. > `flagi & 4 != 0` znaczy `(flagi & 4) != 0`.
Uwagi: Uwagi:
- `/` na liczbach całkowitych zaokrągla w stronę zera: `7 / 2` to `3`, `-7 / 2` to `-3`. `%` ma znak lewej strony. - `/` na liczbach całkowitych zaokrągla w stronę zera: `7 / 2` to `3`. `%` ma znak lewej strony.
- Dzielenie przez zero **zatrzymuje program** komunikatem `runtime error: division by zero` z numerem linijki. - Dzielenie przez zero **zatrzymuje program** komunikatem `runtime error: division by zero` z numerem linijki.
Jeśli zero jest wpisane wprost w kod, jest to błąd już przy kompilacji.
- `&&` i `||` liczą prawą stronę tylko wtedy, gdy to potrzebne. - `&&` i `||` liczą prawą stronę tylko wtedy, gdy to potrzebne.
- Tekst (`str`) można porównywać przez `==` i `!=` (porównuje zawartość). `<` i `>` na tekstach nie działają. - Tekst: `+` skleja, `==` `!=` porównują treść, `<` `>` porównują alfabetycznie (bajt po bajcie).
- Wskaźniki porównuje się przez `==` i `!=`, także z `null`. - Wskaźniki porównuje się przez `==` i `!=`, także z `null`. `str` nigdy nie jest `null`.
- Operatory bitowe wymagają liczb całkowitych. Dla `bool` używaj `&&` i `||`. - Tablic i struktur nie porównasz przez `==`; porównuj ich części.
- `>>` na liczbach ze znakiem zachowuje znak (`-16 >> 2` to `-4`). Na liczbach bez znaku wypełnia zerami.
--- ---
@ -285,14 +299,33 @@ for i in 0..10 { // i = 0, 1, …, 9 (koniec nie jest wliczany)
if i == 3 { continue } if i == 3 { continue }
if i == 8 { break } if i == 8 { break }
} }
for imie in imiona { // każdy element tablicy (albo każdy znak str)
print(imie)
}
``` ```
- Warunek musi być typu `bool`. `if x {` z liczbą to błąd; napisz `if x != 0 {`. - Warunek musi być typu `bool`. `if x {` z liczbą to błąd; napisz `if x != 0 {`.
- W `for i in a..b` oba końce są liczbami całkowitymi obliczanymi **raz**, przed startem pętli. - W `for i in a..b` oba końce są liczone **raz**. `i` jest typu `int` i nie można go zmieniać w pętli.
`i` jest typu `int` i nie można go zmieniać wewnątrz pętli. - W `for x in lista` zmiennej `x` też nie zmienisz. Żeby zmieniać elementy, przechodź po indeksach:
`for i in 0..lista.len { lista[i] = ... }`. Dodawanie do listy w trakcie pętli jest dozwolone.
- `while true { ... }` kręci się do `break` albo `return`. - `while true { ... }` kręci się do `break` albo `return`.
- Kod po `return`, `break` lub `continue` w tym samym bloku to błąd, bo nigdy się nie wykona. - Kod po `return`, `break` lub `continue` w tym samym bloku to błąd, bo nigdy się nie wykona.
### defer
`defer` uruchamia instrukcję, gdy bieżący blok się kończy, niezależnie od tego, jak się kończy:
normalnie, przez `return`, `break` lub `continue`. Kilka `defer` wykonuje się w odwrotnej kolejności.
Przydaje się do sprzątania:
```c
f := fopen("dane.txt", "r")
defer fclose(f)
// ... używaj f; zostanie zamknięty na końcu bloku, na każdej ścieżce
```
Instrukcja w `defer` nie może zawierać `return`, `break` ani `continue`.
--- ---
## 9. Funkcje ## 9. Funkcje
@ -308,32 +341,129 @@ fn bez_wyniku(str wiadomosc) { // bez "->": nic nie zwraca
} }
``` ```
- Parametry są kopiami. Zmiana parametru nie zmienia zmiennej u wywołującego. - Argumenty zachowują się jak kopie: zmiana parametru nigdy nie zmienia zmiennej u wywołującego.
Do tego służą wskaźniki (niżej). (Finch kopiuje naprawdę tylko wtedy, gdy trzeba: funkcja, która nie zmienia tablicy, dostaje ją za darmo.)
Żeby zmienić zmienną wywołującego, przekaż wskaźnik, zobacz [Wskaźniki](#14-wskaźniki).
- Funkcja z `-> typ` musi zwrócić wartość na każdej ścieżce. Finch to sprawdza. - Funkcja z `-> typ` musi zwrócić wartość na każdej ścieżce. Finch to sprawdza.
- Funkcji nie można definiować wewnątrz innej funkcji. - Funkcji nie można definiować wewnątrz innej funkcji.
- Wbudowane nazwy, których nie możesz użyć: `print`, `addr` i wszystkie nazwy typów.
### Funkcje wbudowane
| Funkcja | Co robi |
|---|---|
| `print(a, b, ...)` | Wypisuje wszystkie wartości oddzielone spacjami i przechodzi do nowej linii. Działa z każdym typem. |
| `addr(x)` | Daje wskaźnik na zmienną `x`. |
| `int(x)`, `u8(x)`, `float(x)`, … | Zamienia typy (zobacz [Zamiana typów](#zamiana-typów)). |
--- ---
## 10. Wskaźniki ## 10. Tablice
Wskaźnik przechowuje **adres** wartości, czyli miejsce w pamięci, gdzie ona leży. Finch zamiast ```c
`*` i `&` z C używa słów: liczby := [5, 3, 8] // tablica int
[]str imiona // pusta tablica str
siatka := [[1, 2], [3, 4]] // tablice tablic
[]f32 xs = [1, 2.5] // typ decyduje, czym staną się liczby
```
| Operacja | Znaczenie |
|---|---|
| `a.len` | liczba elementów |
| `a[i]` | element `i` (od 0); poza zakresem program zatrzymuje się z czytelnym komunikatem |
| `a[i] = x` | zmiana elementu |
| `a.push(x)` | dodaj na końcu |
| `a.pop()` | zabierz ostatni i go oddaj |
| `a.insert(i, x)` | wstaw `x` na pozycję `i`, przesuwając resztę |
| `a.remove(i)` | usuń pozycję `i` i ją oddaj |
| `a.clear()` | usuń wszystko |
| `a.resize(n)` | zmień długość na `n` (nowe elementy to zera) |
| `a.contains(x)`, `a.find(x)` | czy `x` jest w środku? jego indeks albo -1 (liczby, znaki, bool, str) |
| `a.sort()`, `a.reverse()` | w miejscu (liczby, znaki, str) |
| `a.slice(s, e)` | nowa tablica z elementami `s` … `e-1` |
| `a.join(sep)` | dla `[]str`: jeden tekst z `sep` między częściami |
| `a.ptr` | adres pierwszego elementu (dla C) |
| `print(a)` | wypisuje `[1, 2, 3]` |
**Przypisanie kopiuje:** po `b := a` zmiana `b` nie zmienia `a`.
---
## 11. Tekst (str)
```c
s := "Cześć"
s += ", świecie" // sklejanie
print(s.len, s[0]) // .len liczy bajty; ą, ś, ż… zajmują po 2
s[0] = 'D' // zmiana znaku
```
| Metoda | Oddaje |
|---|---|
| `s.sub(a, b)` | fragment od `a` do `b-1` |
| `s.find(t)` | gdzie zaczyna się `t`, albo -1 |
| `s.contains(t)`, `s.starts_with(t)`, `s.ends_with(t)` | `bool` |
| `s.split(sep)` | `[]str`; `split("")` daje pojedyncze znaki |
| `s.trim()` | bez spacji i nowych linii na obu końcach |
| `s.upper()`, `s.lower()` | zmienione litery A–Z / a–z (tylko ASCII) |
| `s.replace(a, b)` | każde `a` zamienione na `b` |
| `s.repeat(n)` | `s` n razy |
| `s.bytes()` | `[]u8` z bajtami |
| `s.ptr` | `ptr[char]` dla C (ważny, dopóki żyje `s`) |
`str` pamięta swoją długość, więc `.len` działa natychmiast. Zawsze kończy się zerem, więc
przekazanie go do C jako `char*` działa.
---
## 12. Struktury
```c
struct Gracz {
str imie
int zycia = 3 // wartość domyślna
[]int wyniki
Punkt pozycja // struktury w strukturach
}
g := Gracz(imie: "Ola") // po nazwie: brakujące pola dostają domyślne (albo zero)
q := Punkt(1, 2) // po kolei: wtedy trzeba podać każde pole
g.zycia -= 1
g.wyniki.push(10)
g.pozycja.x = 5
print(g) // Gracz(imie: "Ola", zycia: 2, wyniki: [10], pozycja: Punkt(x: 5, y: 0))
```
- Przypisanie albo przekazanie struktury ją kopiuje, razem z jej tablicami i tekstami.
- Struktura nie może zawierać samej siebie wprost; dla takiego pola użyj `ptr[Wezel]` albo `[]Wezel` (drzewa, listy).
- Struktury nie mają metod. Pisz funkcje, które je przyjmują: `fn wylecz(Gracz g) -> Gracz`.
---
## 13. Pamięć: kto co zwalnia
Tablic, tekstów i struktur nie zwalniasz nigdy. Finch robi to za ciebie, w przewidywalnym momencie:
**gdy kończy się blok, do którego wartość należy** (`}`), albo wcześniej przy `return`, `break`, `continue`.
- Zmienna **jest właścicielem** swojej wartości. Przypisanie kopiuje, więc dwie zmienne nigdy nie dzielą jednej listy.
- Wartość z wywołania funkcji albo wyrażenia jest **przenoszona** tam, gdzie ją zapisujesz, bez kopiowania.
- `return lista` przenosi zmienną poza funkcję, bez kopiowania.
- Parametry funkcji są **pożyczane**: funkcja dostaje wartość wywołującego bez kopii. Jeśli ją zmienia,
Finch najpierw robi jej prywatną kopię. Zauważysz to najwyżej po szybkości.
Do pamięci zarządzanej samodzielnie (listy wiązane, drzewa, dzielenie między strukturami):
```c
wezel := new(Wezel(wartosc: 1)) // wstaw wartość na stertę, dostajesz ptr[Wezel]
defer free(wezel) // free(...) ją oddaje (razem z jej tablicami i tekstami)
```
Pamięć z C (`malloc`, obiekty bibliotek) zwalniasz funkcjami z C, jak w C.
---
## 14. Wskaźniki
Wskaźnik przechowuje **adres** wartości. Finch zamiast `*` i `&` z C używa słów:
| C | Finch | Znaczenie | | C | Finch | Znaczenie |
|---|---|---| |---|---|---|
| `int *p` | `ptr[int] p` | p wskazuje na int | | `int *p` | `ptr[int] p` | p wskazuje na int |
| `&x` | `addr(x)` | adres x | | `&x` | `addr(x)` | adres x (także `addr(a[i])`, `addr(s.pole)`) |
| `*p` | `p.value` | wartość, na którą wskazuje p | | `*p` | `p.value` | wartość, na którą wskazuje p |
| `p->pole` | `p.pole` | pole struktury, na którą wskazuje p |
| `p[i]` | `p[i]` | indeksowanie jak w C (bez kontroli zakresu) |
| `NULL` | `null` | wskazuje donikąd | | `NULL` | `null` | wskazuje donikąd |
| `void *` | `ptr` | wskazuje na coś nieznanego typu | | `void *` | `ptr` | wskazuje na coś nieznanego typu |
@ -342,72 +472,98 @@ fn podwoj(ptr[int] p) {
p.value = p.value * 2 p.value = p.value * 2
} }
fn main() { x := 21
x := 21 podwoj(addr(x)) // x to 42
podwoj(addr(x))
print(x) // 42
ptr[int] nic = null
if nic == null {
print("pusty")
}
}
``` ```
Wskaźnik może wskazywać na wskaźnik: `ptr[ptr[int]]`, a potem `pp.value.value`. Jeśli struktura ma własne pole o nazwie `value`, to `p.value` na wskaźniku do niej oznacza to pole.
Użycie `.value` na wskaźniku `null` **zatrzymuje program** komunikatem Użycie `.value`, `.pole` albo `[i]` na wskaźniku `null` **zatrzymuje program** z czytelnym komunikatem.
`runtime error: used .value on a null pointer`, zamiast losowo go wysypać.
⚠️ Wskaźnika na zmienną nie wolno używać po zakończeniu funkcji, do której ta zmienna należała, ⚠️ Wskaźnika na zmienną nie wolno używać po zakończeniu jej bloku. Finch tego nie sprawdza.
bo zmienna wtedy już nie istnieje. Finch jeszcze tego nie sprawdza.
--- ---
## 11. Biblioteki z C ## 15. Funkcje wbudowane
Prawie każda biblioteka na Linuksie ma interfejs w C: grafika, dźwięk, sieć, bazy danych. | Funkcja | Co robi |
Finch może z nich korzystać bezpośrednio. |---|---|
| `print(a, b, ...)` | Wypisuje wszystkie wartości oddzielone spacjami i przechodzi do nowej linii. Działa z każdym typem, także z tablicami i strukturami. |
| `input()`, `input("pytanie")` | Czyta jedną linijkę wpisaną przez użytkownika (bez znaku nowej linii). Na końcu wejścia daje pusty tekst. |
| `read_file(sciezka)` | Cały plik jako `str`. Jeśli nie da się go przeczytać, zatrzymuje program z czytelnym komunikatem. |
| `write_file(sciezka, tekst)` | Zapisuje (zastępuje) plik; oddaje `true`, jeśli się udało. |
| `file_exists(sciezka)` | `true` / `false` |
| `shell(polecenie)` | Uruchamia polecenie powłoki i oddaje jego kod wyjścia. |
| `exit(kod)` | Natychmiast kończy program. |
| `addr(x)` | Wskaźnik na `x`; `addr(funkcja)` daje C wskaźnik na funkcję (callback). |
| `new(wartosc)`, `free(p)` | Ręczna pamięć na stercie (zobacz [Pamięć](#13-pamięć-kto-co-zwalnia)). |
| `int(x)`, `str(x)`, `u8(x)`, … | Zamiany typów (zobacz [Zamiana typów](#zamiana-typów)). |
---
## 16. Moduły
Program możesz podzielić na pliki. `import ksztalty` wczytuje `ksztalty.fn` z tego samego folderu
(albo z folderu wymienionego w zmiennej środowiskowej `FINCH_PATH`, rozdzielonych `:`).
```c
// ksztalty.fn
struct Pudlo {
float w
float h
}
fn pole(Pudlo p) -> float {
return p.w * p.h
}
```
```c
// main.fn
import ksztalty
fn main() {
p := ksztalty.Pudlo(2, 3)
print(ksztalty.pole(p))
ksztalty.Pudlo inne // typ z modułu w deklaracji
}
```
- Wszystkiego z modułu używasz z jego nazwą z przodu: `ksztalty.pole`, `ksztalty.Pudlo`.
- Moduł nie ma `main`. Moduły mogą importować inne moduły.
---
## 17. Biblioteki z C
### Krok 1: zaimportuj nagłówek ### Krok 1: zaimportuj nagłówek
```c ```c
import "stdio.h" import "stdio.h"
import "math.h"
import "GLFW/glfw3.h" // ścieżka w systemowych katalogach nagłówków import "GLFW/glfw3.h" // ścieżka w systemowych katalogach nagłówków
import "mojalib.h" // nagłówek leżący obok twojego pliku .fn import "mojalib.h" // nagłówek obok twojego pliku .fn
``` ```
Finch czyta nagłówek przez **libclang** (parser C z clanga) i udostępnia: Finch czyta nagłówek przez **libclang** (parser C z clanga) i udostępnia:
- **funkcje**, także te ze zmienną liczbą argumentów, jak `printf`, - **funkcje**, także te ze zmienną liczbą argumentów, jak `printf`,
- **wartości `enum`**, - **struktury**, przez wartość i przez wskaźnik (`Vector2`, `SDL_Rect`, `CXCursor`, …), razem z polami,
- **proste stałe `#define`**, które są pojedynczą liczbą, np. `M_PI`, `EOF`, `GL_COLOR_BUFFER_BIT`, - **wartości `enum`** i **proste stałe `#define`** (`M_PI`, `GL_COLOR_BUFFER_BIT`),
- **zmienne globalne**, np. `stdout` i `stderr`. - **zmienne globalne**, np. `stdout` i `stderr`.
### Krok 2: dolinkuj bibliotekę ### Krok 2: dolinkuj bibliotekę
Nagłówek tylko *opisuje* funkcje. Ich właściwy kod jest w pliku biblioteki (`libNAZWA.so`).
Powiedz Finchowi, której biblioteki użyć, na górze pliku:
```c ```c
link "glfw" // używa libglfw.so link "glfw" // używa libglfw.so (najpierw pyta pkg-config, jeśli jest)
link "GL" // używa libGL.so link "pomocnicze.c" // skompiluj i dołącz własny plik C (obok pliku .fn)
link "gotowe.a" // albo plik obiektowy / bibliotekę statyczną
``` ```
Wpisz samą nazwę: dla `libglfw.so` to `glfw`. Jeśli `pkg-config` zna tę nazwę, Finch użyje jego
ustawień (ścieżek i dodatkowych bibliotek). W przeciwnym razie przekaże linkerowi `-lNAZWA`.
Biblioteka standardowa C i matematyczna (`libm`) są dolinkowane zawsze.
Na linii poleceń `-l NAZWA` robi to samo co `link "NAZWA"`.
Jeśli zapomnisz linijki `link`, Finch powie dokładnie, czego brakuje: Jeśli zapomnisz linijki `link`, Finch powie dokładnie, czego brakuje:
``` ```
error: glfwInit, glfwCreateWindow, glfwMakeContextCurrent and 5 more come from "GLFW/glfw3.h", but its library isn't linked error: glfwInit, glfwCreateWindow, glfwMakeContextCurrent and 5 more come from "GLFW/glfw3.h", but its library isn't linked
the header only says the functions exist; their code lives in a library. ...
add this at the top of your file (with the library's real name):
link "glfw" link "glfw"
``` ```
@ -421,122 +577,138 @@ error: glfwInit, glfwCreateWindow, glfwMakeContextCurrent and 5 more come from "
| `float`, `double` | `f32`, `f64` | | `float`, `double` | `f32`, `f64` |
| `_Bool` / `bool` | `bool` | | `_Bool` / `bool` | `bool` |
| `enum` | odpowiadająca liczba całkowita ze znakiem | | `enum` | odpowiadająca liczba całkowita ze znakiem |
| `char *`, `const char *` | `str` | | `char *` jako **parametr albo wynik** | `str` |
| `int *`, `double *`, … | `ptr[i32]`, `ptr[f64]`, … | | `char *` w strukturze albo za wskaźnikiem | `ptr[char]` (czytasz przez `str(p)`) |
| `void *`, `struct X *`, `FILE *`, wskaźniki na funkcje | `ptr` | | `struct X` przez wartość | `X`, struktura z tymi samymi polami |
| `struct X *`, `int *`, … | `ptr[X]`, `ptr[i32]`, … |
| `int arr[16]` w strukturze | `[16]i32`: indeksujesz i używasz `.len` |
| `void *`, wskaźniki na funkcje, niekompletne struktury | `ptr` |
Czyli `int` z C to `i32` w Finchu. Liczby wpisane w kod dopasowują się same (`abs(-5)` działa), ### Przykład: struktury i raylib
ale zmienną typu `int` z Fincha trzeba zamienić: `abs(i32(x))`.
### Przykład
```c ```c
import "stdio.h" import "raylib.h"
import "math.h" link "raylib"
import "stdlib.h"
fn main() { fn main() {
printf("%d + %d = %d\n", 2, 3, 5) InitWindow(800, 450, "Finch")
print(sqrt(2), M_PI, RAND_MAX) while !WindowShouldClose() {
BeginDrawing()
ptr[i32] n = malloc(4) // poproś C o 4 bajty pamięci ClearBackground(Color(r: 30, g: 30, b: 40, a: 255))
n.value = 7 DrawCircleV(Vector2(400, 225), 50, Color(255, 136, 0, 255))
print(n.value) EndDrawing()
free(n) // i oddaj je }
CloseWindow()
fprintf(stderr, "to idzie na wyjście błędów\n")
} }
``` ```
Pełny przykład z grafiką jest w `examples/window.fn`: okno GLFW + OpenGL, które zmienia kolor. Żeby dać C funkcję do wywołania zwrotnego (np. porównywarkę dla `qsort`), użyj `addr(mojaFunkcja)`.
`examples/llvm.fn` pokazuje, że Finch potrafi nawet sterować samym LLVM przez jego API w C. Jej parametry muszą być typami C (liczby, wskaźniki, struktury C).
### Co jeszcze nie działa ### Co jeszcze nie działa
- Funkcje C, które przyjmują lub zwracają **`struct` przez wartość**, np. `div()`. Finch mówi o tym - **Unie** C przez wartość i struktury z **polami bitowymi** przez wartość (wskaźniki do nich działają).
wprost, jeśli spróbujesz. Funkcje przyjmujące *wskaźnik* na strukturę działają. - **Makra-funkcje** i makra, które nie są pojedynczą liczbą.
- **Makra-funkcje** (`#define MAX(a,b) ...`) i makra, które nie są pojedynczą liczbą.
- `long double`, liczby 128-bitowe. - `long double`, liczby 128-bitowe.
- Odczytywanie **pól** struktury C.
--- ---
## 12. Błędy ## 18. Błędy
### Błędy kompilacji ### Błędy kompilacji
Finch zatrzymuje się na pierwszym problemie i pokazuje, gdzie jest:
``` ```
gra.fn:12:9: error: 'wynik' must be int, but this is str gra.fn:12:9: error: 'wynik' must be int, but this is str
12 | wynik = "wysoki" 12 | wynik = "wysoki"
| ^ | ^
``` ```
Format: `plik:linijka:kolumna: error: wyjaśnienie`, a pod spodem linijka ze znakiem `^` pod miejscem błędu. Format: `plik:linijka:kolumna: error: wyjaśnienie`, a pod spodem linijka ze znakiem `^`.
Komunikaty są po angielsku. Komunikaty są po angielsku.
### Błędy w trakcie działania ### Błędy w trakcie działania
Niektóre problemy widać dopiero, gdy program działa. Zamiast niezdefiniowanego zachowania Zamiast niezdefiniowanego zachowania (jak w C) Finch zatrzymuje się z komunikatem i kodem wyjścia 1:
(jak w C) Finch zatrzymuje się z czytelnym komunikatem na wyjściu błędów i kodem wyjścia 1:
| Komunikat | Przyczyna | | Komunikat | Przyczyna |
|---|---| |---|---|
| `runtime error: division by zero` | `/` albo `%` przez zmienną równą 0 | | `runtime error: division by zero` | `/` albo `%` przez zmienną równą 0 |
| `runtime error: division overflows …` | najmniejsza liczba ze znakiem podzielona przez −1 | | `runtime error: division overflows …` | najmniejsza liczba ze znakiem podzielona przez −1 |
| `runtime error: used .value on a null pointer` | odczyt lub zapis `p.value`, gdy `p` jest `null` | | `runtime error: index 5 is out of range (the length is 3)` | `a[5]` na krótszej tablicy albo tekście |
| `runtime error: pop() on an empty array` | |
| `runtime error: used .value on a null pointer` | także `.pole` i `[i]` przez `null` |
| `runtime error: can't turn "x" into int` | `int(...)` / `float(...)` na tekście, który nie jest liczbą |
| `runtime error: can't read the file "…"` | `read_file` na brakującym albo nieczytelnym pliku |
--- ---
## 13. Rozwiązywanie problemów ## 19. Debugowanie
```sh
finch build gra.fn -g -O0 -o gra
gdb ./gra
(gdb) break gra.fn:12
(gdb) run
(gdb) bt # gdzie jesteśmy, przez które funkcje
(gdb) info locals # wszystkie zmienne
(gdb) print gracz.zycia
```
`-g` dodaje informacje dla debuggera (linijki, funkcje, zmienne, pola struktur). `-O0` zostawia wszystkie
zmienne widoczne; bez niego optymalizator może część z nich usunąć.
---
## 20. Rozwiązywanie problemów
| Problem | Rozwiązanie | | Problem | Rozwiązanie |
|---|---| |---|---|
| `can't find the C header 'x.h'` | Brakuje plików deweloperskich biblioteki. Zainstaluj je (Debian: `libx-dev`; Nix: dopisz do `shell.nix`). Własny nagłówek połóż obok pliku `.fn`. | | `can't find the C header 'x.h'` | Brakuje plików deweloperskich biblioteki. Zainstaluj je (Debian: `libx-dev`; Nix: dopisz do `shell.nix`). |
| `… come from "x.h", but its library isn't linked` | Dopisz `link "nazwa"` na górze pliku. | | `… come from "x.h", but its library isn't linked` | Dopisz `link "nazwa"` na górze pliku. |
| `the library 'x' wasn't found` | Biblioteka nie jest zainstalowana albo nazwa jest zła. Na Niksie dopisz ją do `shell.nix` i pracuj wewnątrz `nix-shell`. | | `the library 'x' wasn't found` | Nie jest zainstalowana albo nazwa jest zła. Na Niksie dopisz ją do `shell.nix` i pracuj w `nix-shell`. |
| `the C function 'f' can't be used from Finch yet` | Używa struktury przez wartość albo innego nieobsługiwanego typu. Poszukaj wariantu, który przyjmuje wskaźniki. | | `the C function 'f' can't be used from Finch yet` | Używa unii, pól bitowych albo innego nieobsługiwanego typu przez wartość. Poszukaj wariantu ze wskaźnikami. |
| `no C compiler found to link with` | Zainstaluj gcc lub clang albo ustaw `CC`. | | `can't find the module 'x'` | Połóż `x.fn` obok importującego pliku albo ustaw `FINCH_PATH`. |
| Program się kompiluje, ale przy starcie nie znajduje biblioteki C | Został zlinkowany ze ścieżki, której system nie przeszukuje. Uruchamiaj go w tym samym `nix-shell` albo zainstaluj bibliotekę systemowo. | | `couldn't build the Finch runtime` | Brak działającego kompilatora C. Zainstaluj gcc lub clang albo ustaw `CC`. |
| `unexpected character 'ż'` | Polska litera w nazwie. W nazwach używaj tylko a–z. | | Biblioteka nie znajduje się przy starcie programu | Zlinkowano ją z folderu, którego system nie przeszukuje. Uruchamiaj w tym samym `nix-shell` albo zainstaluj ją systemowo. |
| CMake: `libclang not found` | Zainstaluj pakiet deweloperski libclang albo uruchom `cmake` w `nix-shell`. | | `clang` w `nix-shell` nie widzi `stdio.h` | Masz stary `shell.nix`: `llvmPackages.clang` musi być przed `llvmPackages.libclang`. |
| Budowanie kończy się błędami API LLVM | Masz inną wersję LLVM niż 21. |
| Program nigdy się nie kończy | Warunek pętli nigdy nie staje się fałszywy. Naciśnij Ctrl+C. | | Program nigdy się nie kończy | Warunek pętli nigdy nie staje się fałszywy. Naciśnij Ctrl+C. |
--- ---
## 14. Struktura projektu i testy ## 21. Struktura projektu i testy
``` ```
Finch/ Finch/
├── src/ kompilator (C++) ├── src/ kompilator (C++)
├── examples/ przykładowe programy (hello, tour, types, c_import, window, llvm) ├── runtime/ finch_rt.c: mała biblioteka uruchomieniowa (teksty, tablice, wejście, pliki)
├── boot/ kompilator Fincha napisany w Finchu (zobacz przewodnik dla inżynierów)
├── examples/ hello, tour, types, structs, todo, guess, c_import, window, raylib, llvm
├── tests/ ├── tests/
│ ├── run/ programy + dokładny wynik, który muszą wypisać (.out) │ ├── run/ programy + dokładny wynik (.out) i wejście (.in)
│ ├── fail/ programy, które muszą się nie udać, z oczekiwanym błędem w 1. linijce │ ├── fail/ programy, które muszą się nie udać, z oczekiwanym błędem w 1. linijce
│ └── run.sh skrypt uruchamiający testy │ ├── run.sh uruchamia testy (MEMCHECK=1 sprawdza też pamięć valgrindem)
│ └── boot.sh buduje kompilator samohostujący nim samym i porównuje
├── docs/ ta dokumentacja (en, pl) ├── docs/ ta dokumentacja (en, pl)
├── shell.nix środowisko deweloperskie Nix ├── shell.nix środowisko deweloperskie Nix
└── CMakeLists.txt └── CMakeLists.txt
``` ```
Testy uruchomisz po zbudowaniu:
```sh ```sh
tests/run.sh # → 34 passed, 0 failed tests/run.sh # → 58 passed, 0 failed
MEMCHECK=1 tests/run.sh # to samo pod valgrindem: bez wycieków i złych dostępów do pamięci
tests/boot.sh # sprawdzenie samohostowania (wymaga clanga)
``` ```
--- ---
## 15. Obecne ograniczenia ## 22. Obecne ograniczenia
Finch 1.0 to solidny rdzeń, a nie skończony język. Jeszcze nie ma: Finch 2.0 to kompletny mały język, ale nie skończony. Jeszcze nie ma:
- `struct`, tablic, czytania z klawiatury (`input()`), sklejania tekstów (`"a" + "b"`). - Metod w strukturach, typów generycznych, słowników (map), `match`. (Używaj tablic struktur i funkcji.)
Na razie użyj do tego funkcji z C (`scanf`, `snprintf`, `malloc`…). - Błędów jako wartości: błąd w `int("x")` albo `read_file` zatrzymuje program. Sprawdzaj wcześniej (`file_exists`).
- Automatycznego zarządzania pamięcią (w planach: zwalnianie na końcu bloku i `defer`). - Tekstu świadomego Unicode: `.len`, `s[i]` i `upper()` działają na bajtach / ASCII.
- Modułów Fincha (na razie jeden program = jeden plik `.fn`). - Wątków.
- Platform innych niż Linux x86-64. - Platform innych niż Linux x86-64.
Plan rozwoju jest w [README](../../README.pl.md#plan-rozwoju). Plan rozwoju jest w [README](../../README.pl.md#plan-rozwoju).

15
examples/raylib.fn Normal file
View file

@ -0,0 +1,15 @@
// A raylib window with a circle: C structs (Color, Vector2) passed by value.
// Needs raylib (in shell.nix).
import "raylib.h"
link "raylib"
fn main() {
InitWindow(800, 450, "Finch")
while !WindowShouldClose() {
BeginDrawing()
ClearBackground(Color(r: 30, g: 30, b: 40, a: 255))
DrawCircleV(Vector2(400, 225), 50, Color(255, 136, 0, 255))
EndDrawing()
}
CloseWindow()
}

1
notatka.txt Normal file
View file

@ -0,0 +1 @@
Kupić mleko

1
note.txt Normal file
View file

@ -0,0 +1 @@
Buy milk

View file

@ -10,8 +10,9 @@ pkgs.mkShell {
ninja ninja
pkg-config # finds the right flags for `link "..."` pkg-config # finds the right flags for `link "..."`
# for examples/window.fn # for examples/window.fn and examples/raylib.fn
glfw glfw
libGL libGL
raylib
]; ];
} }

View file

@ -230,7 +230,8 @@ Value *Codegen::own(const Value_ &v) {
} }
void Codegen::release(const Value_ &v) { void Codegen::release(const Value_ &v) {
if (v.fresh && v.v && owning(v.type)) dropValue(v.v, v.type); // constants (string literals, empty arrays) own no memory: nothing to free
if (v.fresh && v.v && !isa<Constant>(v.v) && owning(v.type)) dropValue(v.v, v.type);
} }
Value *Codegen::copyValue(Value *v, const FType &t) { Value *Codegen::copyValue(Value *v, const FType &t) {