mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 08:54:25 +00:00
Finch 2.4.0: methods, maps, errors as values; str() of any value; macOS and ARM64 docs; Kate support
- fn Struct.method(...) with self by reference, also on C structs
- map[K]V: insertion-ordered hash maps, literals, m[k] += 1, has/get/remove/keys/values
- -> T! / -> !, return error(...), or <value>, or { err }, try; fallible int/float/read_file/write_file/delete_file
- for i, x in list / for k, v in map
- print and str() share one text formatter
- LSP: method and map completion, method signatures; grammar and snippets for VS Code and Kate
- tests: 74 (methods, errors, maps + 11 failure cases); docs at all three levels, en and pl
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fd86eb885c
commit
b872be3176
55 changed files with 3400 additions and 336 deletions
|
|
@ -38,9 +38,9 @@ Język z perspektywy użytkownika opisuje [przewodnik dla techników](dla-techni
|
|||
20. [Semantyka a C](#20-semantyka-a-c)
|
||||
21. [Spis funkcji](#21-spis-funkcji)
|
||||
22. [Rozbudowa kompilatora](#22-rozbudowa-kompilatora)
|
||||
23. [Platformy i Windows](#23-platformy-i-windows)
|
||||
23. [Platformy: Windows, macOS, ARM64](#23-platformy-windows-macos-arm64)
|
||||
24. [Serwer języka](#24-serwer-języka)
|
||||
25. [Rozszerzenie VS Code](#25-rozszerzenie-vs-code)
|
||||
25. [Integracje z edytorami](#25-integracje-z-edytorami)
|
||||
26. [Znane ograniczenia](#26-znane-ograniczenia)
|
||||
|
||||
---
|
||||
|
|
@ -90,20 +90,24 @@ Decyzje, które kształtują resztę:
|
|||
|
||||
| Plik | Linie | Odpowiedzialność |
|
||||
|---|---|---|
|
||||
| `src/error.h` | 40 | `g_files` (wszystkie źródła), `failAt(file, line, col, msg)`, `fail(line, col, msg)` |
|
||||
| `src/error.h` | 50 | `g_files` (wszystkie źródła), `failAt(file, line, col, msg)`, `fail(line, col, msg)`, `FinchError` |
|
||||
| `src/lexer.h/.cpp` | 250 | `Tok`, `Token`, `lex(indeksPliku)`, `tokName()` |
|
||||
| `src/ast.h` | 310 | `Type` (z Array, Fixed, Struct, Named), wszystkie węzły `Expr`/`Stmt`, `FnDecl`, `StructDecl`, `Import`, `Link`, `Program` |
|
||||
| `src/parser.h/.cpp` | 540 | Parser zstępujący `Parser`, `typeFromName()` |
|
||||
| `src/cimport.h/.cpp` | 420 | Import przez libclang: funkcje, struktury C (pola, przesunięcia), stałe, zmienne globalne |
|
||||
| `src/ast.h` | 360 | `Type` (z Array, Map, Fixed, Struct, Named), wszystkie węzły `Expr`/`Stmt`, `FnDecl`, `StructDecl`, `Import`, `Link`, `Program` |
|
||||
| `src/parser.h/.cpp` | 610 | Parser zstępujący `Parser`, `typeFromName()` |
|
||||
| `src/loader.h/.cpp` | 90 | Wczytywanie plików i modułów (`Loader`), `FINCH_PATH` |
|
||||
| `src/target.h/.cpp` | 210 | `g_target`: triple, kompilator C, procesy, ścieżki; inicjalizacja backendów |
|
||||
| `src/cimport.h/.cpp` | 430 | Import przez libclang: funkcje, struktury C (pola, przesunięcia), stałe, zmienne globalne |
|
||||
| `src/codegen.h` | 15 | `generate()` |
|
||||
| `src/codegen_impl.h` | 265 | Klasa `Codegen` i jej struktury pomocnicze, wspólne dla czterech plików poniżej |
|
||||
| `src/codegen.cpp` | 1090 | Program, deklaracje, zasięgi i sprzątanie, instrukcje, wyrażenia, miejsca (`ref`), operatory, analiza modyfikacji |
|
||||
| `src/codegen_types.cpp` | 500 | Typy LLVM, rozwiązywanie typów, układ struktur, konwersje, własność (helpery kopiowania/zwalniania), wypisywanie, typy DWARF |
|
||||
| `src/codegen_builtins.cpp` | 710 | Wywołania, konstruktory, funkcje wbudowane, konwersje, metody tablic i tekstów, deklaracje runtime'u, panic |
|
||||
| `src/abi.cpp` | 310 | Klasyfikacja System V x86-64, wywołania C ze strukturami przez wartość, wrappery wywoływalne z C |
|
||||
| `src/main.cpp` | 390 | Sterownik: CLI, ładowanie, maszyna docelowa, O2, emisja, cache runtime'u, linkowanie, porady przy błędach linkera, `run` |
|
||||
| `runtime/finch_rt.c` | 440 | Biblioteka uruchomieniowa (§12) |
|
||||
| `boot/*.fch` | 3080 | Kompilator samohostujący (§17) |
|
||||
| `src/codegen_impl.h` | 310 | Klasa `Codegen` i jej struktury pomocnicze, wspólne dla plików poniżej |
|
||||
| `src/codegen.cpp` | 1460 | Program, deklaracje (funkcje, metody), zasięgi i sprzątanie, instrukcje, wyrażenia, miejsca (`ref`), operatory, błędy jako wartości, analiza modyfikacji |
|
||||
| `src/codegen_types.cpp` | 580 | Typy LLVM, rozwiązywanie typów, układ struktur, konwersje, własność (helpery kopiowania/zwalniania), zamiana na tekst, typy dla debuggera |
|
||||
| `src/codegen_builtins.cpp` | 890 | Wywołania, metody, konstruktory, funkcje wbudowane, konwersje, metody tablic i tekstów, deklaracje runtime'u, panic |
|
||||
| `src/codegen_map.cpp` | 240 | Mapy: indeksowanie, literały, metody, przechodzenie |
|
||||
| `src/abi.cpp` | 370 | Konwencje wywołań C (System V x86-64, Microsoft x64, AAPCS64), wywołania C ze strukturami przez wartość, wrappery wywoływalne z C |
|
||||
| `src/lsp.cpp`, `src/index.h`, `src/builtins_doc.h` | 700 | Serwer języka (§24) |
|
||||
| `src/main.cpp` | 350 | Sterownik: CLI, maszyna docelowa, O2, emisja, cache runtime'u, linkowanie, porady przy błędach linkera, `run` |
|
||||
| `runtime/finch_rt.c` | 740 | Biblioteka uruchomieniowa (§12) |
|
||||
| `boot/*.fch` | 3100 | Kompilator samohostujący (§17) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -120,7 +124,8 @@ struct Token { Tok kind; std::string text; int line, col; bool newlineBefore; };
|
|||
- **Pozycje:** od 1; `col` liczy znaki (bajty kontynuacji UTF-8 jej nie zwiększają), więc `^` trafia
|
||||
pod `"Błąd"`. Zabłąkany znak spoza ASCII jest pokazywany w całości z podpowiedzią, że nazwy są tylko ASCII.
|
||||
- **Identyfikatory / słowa kluczowe:** `[A-Za-z_][A-Za-z0-9_]*`. Słowa kluczowe:
|
||||
`fn return if else while for in break continue true false null import link struct defer`.
|
||||
`fn return if else while for in break continue true false null import link struct defer or try`.
|
||||
(`self` i `map` to zwykłe identyfikatory ze znaczeniem zależnym od miejsca: w metodzie, przed `[`.)
|
||||
**Nazwy typów nie są słowami kluczowymi**: `int`, `u8`, `ptr`… to `Ident`, rozpoznawane przez parser
|
||||
za pomocą `typeFromName()`, więc `u8(x)` jest zwykłym wywołaniem, a lekser nie zależy od listy typów.
|
||||
- **Liczby:** dziesiętne albo `0x` szesnastkowe z separatorami `_`. Bez ósemkowych (`010` to 10).
|
||||
|
|
@ -142,27 +147,31 @@ program = { import | link | struct | function } ;
|
|||
import = "import" ( STRING | IDENT ) ; (* "x.h" = nagłówek C, nazwa = moduł Fincha *)
|
||||
link = "link" STRING ; (* "glfw" | "plik.c" | "plik.o" | "lib.a" *)
|
||||
struct = "struct" IDENT "{" { type IDENT [ "=" expr ] NOWA_LINIA } "}" ;
|
||||
function = "fn" IDENT "(" [ param { "," param } ] ")" [ "->" type ] block ;
|
||||
function = "fn" IDENT [ "." IDENT ] "(" [ param { "," param } ] ")" [ "->" ( type [ "!" ] | "!" ) ] block ;
|
||||
(* fn Punkt.przesun: metoda; "!": może się nie udać *)
|
||||
param = type IDENT ;
|
||||
type = "[" "]" type | "ptr" [ "[" type "]" ] | TYPENAME | IDENT [ "." IDENT ] ;
|
||||
type = "[" "]" type | "map" "[" type "]" type | "ptr" [ "[" type "]" ]
|
||||
| TYPENAME | IDENT [ "." IDENT ] ;
|
||||
|
||||
block = "{" { statement TERMINATOR } "}" ;
|
||||
statement = if | while | for | return | "break" | "continue" | "defer" statement | block
|
||||
| type IDENT [ "=" expr ] (* deklaracja, zobacz atDeclaration *)
|
||||
| IDENT ":=" expr
|
||||
| target assignop expr (* cel: zmienna, pole, element, p.value *)
|
||||
| call | method ;
|
||||
for = "for" IDENT "in" expr ( ".." expr block | block ) ; (* zakres | po elementach *)
|
||||
| call | method | try | orelse ;
|
||||
for = "for" IDENT [ "," IDENT ] "in" expr ( ".." expr block | block ) ; (* zakres | po elementach *)
|
||||
assignop = "=" | "+=" | "-=" | "*=" | "/=" | "%=" ;
|
||||
|
||||
expr = or ; or = and { "||" and } ; and = cmp { "&&" cmp } ;
|
||||
expr = or [ "or" ( block | expr ) ] ; (* f(x) or 0, f(x) or { ... }: najniższy, od prawej *)
|
||||
or = and { "||" and } ; and = cmp { "&&" cmp } ;
|
||||
cmp = add { ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) add } ;
|
||||
add = mul { ( "+" | "-" | "|" | "^" ) mul } ;
|
||||
mul = unary { ( "*" | "/" | "%" | "<<" | ">>" | "&" ) unary } ;
|
||||
unary = ( "-" | "!" | "~" ) unary | postfix ;
|
||||
unary = ( "-" | "!" | "~" ) unary | "try" postfix | postfix ;
|
||||
postfix = primary { "." IDENT [ args ] | "[" expr "]" } ;
|
||||
primary = INT | FLOAT | STRING | CHAR | "true" | "false" | "null"
|
||||
| IDENT [ args ] | "(" expr ")" | "[" [ expr { "," expr } [","] ] "]" ;
|
||||
| IDENT [ args ] | "(" expr ")" | "[" [ expr { "," expr } [","] ] "]"
|
||||
| "[" ":" "]" | "[" expr ":" expr { "," expr ":" expr } [","] "]" ; (* mapy *)
|
||||
args = "(" [ arg { "," arg } [","] ] ")" ; arg = [ IDENT ":" ] expr ;
|
||||
```
|
||||
|
||||
|
|
@ -202,11 +211,16 @@ Wszystkie węzły są w `src/ast.h`: małe hierarchie klas z jawnym `kind`, rozg
|
|||
| `Member` | `MemberExpr` | `obj`, `field` (`.x`, `.len`, `.ptr`, `.value`) |
|
||||
| `Index` | `IndexExpr` | `obj`, `index` |
|
||||
| `ArrayLit` | `ArrayLitExpr` | `elems` |
|
||||
| `Method` | `MethodExpr` | `obj`, `name`, `args`, `argNames`; także `modul.fch(...)` |
|
||||
| `Method` | `MethodExpr` | `obj`, `name`, `args`, `argNames`; także `modul.fn(...)` i `p.metoda(...)` |
|
||||
| `MapLit` | `MapLitExpr` | `keys`, `values` (`[:]` nie ma żadnych) |
|
||||
| `Try` | `TryExpr` | `call` |
|
||||
| `OrElse` | `OrElseExpr` | `call` oraz `fallback` (wyrażenie) albo `block` |
|
||||
|
||||
| StmtKind | Węzeł |
|
||||
|---|---|
|
||||
| `Block` `VarDecl` `Assign` `Expr` `If` `While` `For` `ForEach` `Return` `Break` `Continue` `Defer` | jak w nazwie; `ForEachStmt` ma `var`, `list`, `body`; `DeferStmt` trzyma jedną instrukcję |
|
||||
| `Block` `VarDecl` `Assign` `Expr` `If` `While` `For` `ForEach` `Return` `Break` `Continue` `Defer` | jak w nazwie; `ForEachStmt` ma `var`, `var2` (`for i, x` / `for k, v`), `list`, `body`; `DeferStmt` trzyma jedną instrukcję |
|
||||
|
||||
`FnDecl` ma `recv` (struktura metody, w innych przypadkach `""`) i `fallible` (`-> T!`).
|
||||
|
||||
`math.sqrt(x)` parsuje się jako `MethodExpr` na `VarExpr("math")`; to, czy `math` jest modułem, czy zmienną,
|
||||
rozstrzyga się przy generowaniu kodu. Wywołania nazywają funkcję, zamiast trzymać wyrażenie: Finch nie ma
|
||||
|
|
@ -220,9 +234,10 @@ Nie ma węzła „load”: to, czy `x` znaczy adres, czy wartość, zależy od k
|
|||
```cpp
|
||||
struct Type {
|
||||
enum Kind { Void, Bool, Char, I8, I16, I32, I64, U8, U16, U32, U64, F32, F64,
|
||||
Str, Ptr, Null, Array, Fixed, Struct, Named };
|
||||
Str, Ptr, Null, Array, Map, Fixed, Struct, Named };
|
||||
Kind kind;
|
||||
std::shared_ptr<Type> elem; // Ptr (null = `ptr` bez typu), Array, Fixed
|
||||
std::shared_ptr<Type> elem; // Ptr (null = `ptr` bez typu), Array, Fixed; Map: wartość
|
||||
std::shared_ptr<Type> key; // Map
|
||||
long long count; // Fixed
|
||||
std::string name, module; // Named (jak napisano) / Struct (rozwiązany)
|
||||
StructInfo *info; // Struct
|
||||
|
|
@ -302,9 +317,12 @@ nazwy typów (konwersja) → funkcja bieżącego modułu → struktura bieżące
|
|||
| `char` `i8` `u8` / `i16` `u16` / `i32` `u32` / `int` `u64` | `i8` / `i16` / `i32` / `i64` |
|
||||
| `f32` / `float` | `float` / `double` |
|
||||
| `str`, `[]T` | `{ ptr, i64, i64 }` (wskaźnik, długość, pojemność) |
|
||||
| `map[K]V` | `{ ptr, i64, i64, i64, ptr, i64 }` (entries, len, used, cap, index, icap; §10.12) |
|
||||
| `ptr`, `ptr[T]`, `null` | `ptr` (nieprzezroczysty) |
|
||||
| `[N]T` | `[N x T]` |
|
||||
| struktura Fincha | nazwany typ struct, naturalny układ |
|
||||
| `self` metody | `ptr` (pierwszy parametr) |
|
||||
| wynik `-> T!` | `{ i1 ok, T value, str error }` (`i8` w miejscu `T` dla `-> !`) |
|
||||
| struktura C | nazwany **upakowany** (packed) struct z jawnymi bajtami wypełnienia (§13.2) |
|
||||
|
||||
Znakowość istnieje tylko w typie Fincha i wybiera `sdiv/udiv`, `ashr/lshr`, `icmp s*/u*`,
|
||||
|
|
@ -380,6 +398,8 @@ każda decyzja zapada statycznie.
|
|||
| wartość tymczasowa, której nikt nie trzyma (`print(a + b)`, `f(g())`, instrukcja-wyrażenie) | **`release(v)`**: zwalniana zaraz po użyciu |
|
||||
| `a.pop()`, `a.remove(i)` | element jest przenoszony na zewnątrz (fresh) |
|
||||
| `for x in lista` | `x` pożycza kolejne elementy; jeśli ciało może zmieniać `lista`, elementy są kopiowane |
|
||||
| wywołanie metody `v.m()` | `self` **to jest** `v` (przekazywany jest jego adres): bez kopii; zmiany widzi wywołujący |
|
||||
| mapy | są właścicielami kluczy i wartości tak jak tablice elementów; `m[k] = v` przenosi/kopiuje `v` i zwalnia starą wartość |
|
||||
|
||||
Literał tekstowy to wartość fresh z `cap = 0`: przeniesienie nic nie kosztuje, zwalnianie jest pomijane
|
||||
(`release()` ignoruje stałe), a zapis do niego (`s[i] = c`) najpierw robi kopię na stercie (`finch_str_own`).
|
||||
|
|
@ -394,6 +414,11 @@ parametry są pożyczane (`Fn::borrowParam`), zmieniane kopiowane raz na wejści
|
|||
i zachowawcza: wszystko, co *może* zmienić parametr, powoduje kopię, co zawsze jest poprawne.
|
||||
Ta sama analiza decyduje, czy `for x in lista` musi kopiować elementy.
|
||||
|
||||
Metody biorą w tym udział: `declareFns()` najpierw ustala, które metody zmieniają `self`
|
||||
(`mutates("self", ciało)`), powtarzając, aż nic się nie zmieni, bo wywołanie zmieniającej metody na `self`
|
||||
też zmienia `self`. Ich nazwy trafiają do `mutatingMethods`, które `mutatesExpr()` traktuje jak `push`:
|
||||
`p.przesun(1)` zmienia `p`. Dopiero potem liczone jest `borrowParam` dla każdej funkcji i metody.
|
||||
|
||||
### 9.3 Kopiowanie i zwalnianie
|
||||
|
||||
`dropAt(addr, T)` / `copyAt(dst, src, T)` działają na adresach:
|
||||
|
|
@ -513,7 +538,10 @@ wartości (dla `str +=`).
|
|||
### 10.4 Funkcje i `main`
|
||||
|
||||
Funkcje są `internal` z naturalną sygnaturą LLVM (agregaty przez wartość). Parametry posiadające
|
||||
pamięć są kopiowane na wejściu tylko wtedy, gdy `borrowParam` jest fałszywe. `fn main` to wewnętrzna
|
||||
pamięć są kopiowane na wejściu tylko wtedy, gdy `borrowParam` jest fałszywe. Metody (§10.10) dostają adres
|
||||
`self` jako dodatkowy pierwszy parametr; funkcje, które mogą się nie udać (§10.11), zwracają `{ i1, T, str }`.
|
||||
`fn main() -> !` / `-> int!` sprawia, że wrapper przy porażce wypisuje `error: <komunikat>` na stderr
|
||||
i kończy z kodem 1. `fn main` to wewnętrzna
|
||||
`finch.main`; osobna zewnętrzna `i32 main(i32, ptr)` (`defineMainWrapper`) buduje `[]str args` przez
|
||||
`finch_args`, jeśli trzeba, woła ją, zwalnia argumenty i zwraca kod wyjścia. Funkcja, która dochodzi do
|
||||
swojego końca, dostaje sprzątanie zasięgów i `ret void` albo błąd `can reach its end without returning T`.
|
||||
|
|
@ -543,10 +571,13 @@ zasięgami wywołującego, albo zero). Wywołania C opisuje §13.
|
|||
|
||||
### 10.8 Wypisywanie
|
||||
|
||||
`print` emituje `printf` dla każdego argumentu z formatem wybranym wg typu (`%lld`, `%llu`, `%g`, `%c`,
|
||||
`%.*s` dla `str` z jego długością, `%p`), oddzielając spacjami, a na końcu `\n`. Tablice, tablice o stałym
|
||||
rozmiarze i struktury wołają generowane helpery `finch.print.<klucz>`, które wypisują `[1, 2]` /
|
||||
`Punkt(x: 1, y: 2)`, biorąc teksty i znaki w środku w cudzysłów.
|
||||
`print` emituje `printf` dla każdego argumentu skalarnego z formatem wybranym wg typu (`%lld`, `%llu`,
|
||||
`%g`, `%c`, `%.*s` dla `str` z jego długością), oddzielając spacjami, a na końcu `\n`. Wszystko inne
|
||||
(wskaźniki, tablice, tablice o stałym rozmiarze, mapy, struktury) jest najpierw **zamieniane na tekst**
|
||||
w buforze `str` na stercie przez `emitFormat()` i wypisywane przez `finch_buf_print`. Typy złożone dostają
|
||||
generowany helper `finch.format.<klucz>(buf, wartość*)`, który dopisuje `[1, 2]` / `{"a": 1}` /
|
||||
`Punkt(x: 1, y: 2)`, biorąc teksty i znaki w środku w cudzysłów. `str(x)` tablicy, mapy albo struktury
|
||||
używa tego samego helpera, więc `str(x)` to zawsze to, co pokazuje `print(x)`.
|
||||
|
||||
### 10.9 Metody tablic i tekstów
|
||||
|
||||
|
|
@ -556,6 +587,66 @@ Tablice: `push` (`finch_arr_reserve` + zapis + len++), `pop`/`remove` (przeniesi
|
|||
`reverse` (pętla zamian), `sort` (`qsort` z libc z generowaną porównywarką `finch.cmp.<klucz>`), `join` (runtime).
|
||||
Metody zmieniające tablicę wymagają adresu (zmienna/pole/element).
|
||||
Teksty: `sub find contains starts_with ends_with split trim upper lower replace repeat bytes`, wszystkie w runtime.
|
||||
`for i, x in lista` dodaje obok elementu zmienną-indeks typu `int` tylko do odczytu.
|
||||
|
||||
### 10.10 Metody
|
||||
|
||||
`fn Punkt.przesun(int dx)` jest deklarowana jak funkcja `finch.[modul.]Punkt.przesun` z dodatkowym
|
||||
pierwszym parametrem `ptr` i trzymana w `Codegen::methods[StructInfo*]` (więc metody idą za strukturą do
|
||||
każdego modułu, który jej używa, a struktury z C też mogą mieć metody). W `define()` `self` to `Var`, którego
|
||||
`slot` **jest tym argumentem-wskaźnikiem**: `self.x` to GEP od struktury samego wywołującego, a `self = …`
|
||||
zapisuje wartość wywołującego. Nigdy nie ma kopii.
|
||||
|
||||
Przy wywołaniu `obj.m(args)` (`structMethod()`) `ref(obj)` daje adres, gdy `obj` jest miejscem (zmienna,
|
||||
pole, element, `m[klucz]`), wczytany wskaźnik dla `ptr[T]` (sprawdzany na null) albo tymczasowe miejsce na
|
||||
stosie dla wartości (`Punkt(1, 2).dlugosc()`), zwalniane po wywołaniu. Z analizy modyfikacji wynikają dwie
|
||||
reguły bezpieczeństwa: metody zmieniającej `self` nie można wywołać na zmiennej pętli (tylko do odczytu),
|
||||
a jeśli taka metoda dostaje argument żyjący wewnątrz odbiorcy (`c.merge(c)`, `p.add(p.items)`), argument
|
||||
jest najpierw kopiowany, żeby `self` nie mógł zwolnić pamięci, na którą argument wciąż wskazuje.
|
||||
|
||||
### 10.11 Błędy jako wartości
|
||||
|
||||
Funkcja, która może się nie udać (`-> T!`), zwraca `{ i1 ok, T value, str error }`; przy porażce `value`
|
||||
jest zerem, więc wywołujący nigdy nie musi go zwalniać. `return v` opakowuje `{1, v, zero}` (`retValue()`);
|
||||
`return error(msg)` liczy komunikat, wykonuje sprzątanie i zwraca `{0, zero, msg}` (`retError()`).
|
||||
|
||||
Wywołanie takiej funkcji jest dozwolone tylko jako argument `or` / `try`. `fallible(wywołanie)` ustawia
|
||||
`fallibleTarget` na wyrażenie wywołania; `callFinch()` (i funkcje wbudowane, które mogą się nie udać)
|
||||
porównują z nim swoje `CallExpr`/`MethodExpr` i jeśli pasuje, zostawiają `fallibleFailed` (`i1`)
|
||||
i `fallibleMsg` (`str`) dla wywołującego; jeśli wywołanie takiej funkcji nie pasuje, to błąd kompilacji
|
||||
*'f' can fail, so say what happens then*. Zagnieżdżone obsługi zapisują i przywracają te trzy pola.
|
||||
|
||||
| Forma | Tłumaczenie |
|
||||
|---|---|
|
||||
| `f(x) or zapasowa` | `br failed, or.failed, or.ok`; blok porażki zwalnia komunikat i liczy wartość zapasową (tylko tam); φ łączy obie wartości |
|
||||
| `f(x) or { … }` | blok porażki deklaruje `err` (właściciel komunikatu) i wykonuje blok. Jeśli wartość jest używana, blok musi kończyć się skokiem; jako instrukcja może przejść dalej, a ścieżka sukcesu zwalnia nieużytą wartość |
|
||||
| `try f(x)` | blok porażki wykonuje `emitCleanups(0)` i zwraca `{0, zero, msg}` z bieżącej funkcji, która też musi móc się nie udać |
|
||||
|
||||
Funkcje wbudowane z trybem porażki sprawdzają `&wywołanie == fallibleTarget` i przełączają się na
|
||||
`finch_str_try_int`, `finch_str_try_float`, `finch_try_read_file`, `finch_try_write_file`,
|
||||
`finch_try_delete_file`, które zwracają 1 / 0 i wypełniają `str` z błędem zamiast robić panic. Koszt przy
|
||||
sukcesie to jedna przewidywalna gałąź; nic nie jest alokowane, dopóki nie zdarzy się porażka.
|
||||
|
||||
### 10.12 Mapy
|
||||
|
||||
Runtime (§12) trzyma tablicę haszującą zachowującą kolejność wstawiania, w stylu `dict` z CPythona:
|
||||
tablica `entries` z miejscami `{ i64 hash, K key, V value }` w kolejności wstawiania (hash 0 = usunięte)
|
||||
oraz `index` z `icap` miejscami (potęga dwójki, ≥ 2 × cap) z sondowaniem liniowym (−1 puste, −2 usunięte,
|
||||
w innym razie numer wpisu). Runtime tylko haszuje (FNV-1a + 64-bitowe mieszanie; klucze tekstowe po treści,
|
||||
pozostałe po bajtach), znajduje i umieszcza wpisy; kompilator zapisuje, kopiuje i zwalnia klucze i wartości,
|
||||
dokładnie tak jak elementy tablic.
|
||||
|
||||
| Kod | Tłumaczenie |
|
||||
|---|---|
|
||||
| odczyt `m[k]` | `finch_map_find`; −1 → `finch_map_missing` (noreturn, z kluczem jako tekst); w innym razie miejsce `entry.value` |
|
||||
| zapis `m[k]` (`=`, `+=`, metoda zmieniająca, `addr`) | `mapSlot()`: `finch_map_slot` zwraca wpis, dodając go, jeśli go nie ma; nowy wpis dostaje kopię klucza i wartość domyślną typu |
|
||||
| `["a": 1, …]` | wyzerowana mapa + `mapSlot` dla każdego wpisu (pierwszy wpis ustala typy; liczbowe wartości są wszystkie `float`, jeśli któraś ma kropkę) |
|
||||
| `has`, `get`, `remove`, `clear`, `keys`, `values` | `codegen_map.cpp`; wartość domyślna `get` jest liczona tylko wtedy, gdy klucza brak |
|
||||
| `for k, v in m` | pętla po `0..used` pomijająca usunięte miejsca, wczytująca `entries` i `used` w każdym obrocie |
|
||||
| kopia / zwolnienie / tekst | helpery jak dla tablic: `finch_map_clone_raw`, a potem głębokie kopie posiadających kluczy i wartości; zwolnienie każdego żywego wpisu, potem `finch_map_free` |
|
||||
|
||||
Usunięcie oznacza miejsce (i wpis w indeksie) jako usunięte; miejsca są upychane, gdy tablica miałaby
|
||||
urosnąć, a co najmniej ćwierć z nich jest usunięta. Usunięcie ostatniego klucza resetuje tablicę.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -573,7 +664,9 @@ udowodnić (stałe indeksy w zakresie, kontrole wyciągnięte z pętli).
|
|||
| indeks tablicy/tekstu/tablicy stałej poza zakresem (`icmp uge`, więc także ujemne) | `boundsCheck()` |
|
||||
| `.value`, `.pole`, `[i]` przez null | `member()`, `index()` |
|
||||
| `pop()` na pustej, `slice()`/`sub()` poza zakresem | metody, runtime |
|
||||
| zły tekst w `int(s)`/`float(s)`, nieczytelny plik w `read_file` | runtime |
|
||||
| zły tekst w `int(s)`/`float(s)`, nieczytelny plik w `read_file` (bez `or` / `try`) | runtime |
|
||||
| odczyt `m[klucz]` z brakującym kluczem | `mapIndex()` |
|
||||
| metoda wywołana przez `null` w `ptr[T]` | `structMethod()` |
|
||||
|
||||
Sito na 20 milionów elementów z kontrolą zakresu przy każdym dostępie działa tak szybko jak to samo w C
|
||||
(ok. 0,09 s): kontrole są wyciągane z pętli albo zwijane.
|
||||
|
|
@ -599,8 +692,11 @@ Funkcje przyjmują i zwracają te struktury **przez wskaźnik**, więc ich ABI j
|
|||
struktur): budowanie tekstów (`concat`, `from_int/uint/float/char/bool`, `sub`, `trim`, `upper`, `lower`,
|
||||
`replace`, `split`, `join`, `repeat`, `from_bytes`), porównania (`eq`, `cmp`, `find`, `starts`, `ends`),
|
||||
własność (`copy`, `own`, `drop`, `from_c`), tablice (`reserve`, `make`, `resize`, `clone_raw`, `free`,
|
||||
`insert_gap`, `remove_gap`), `args`, `input`, pliki, `shell` i panic. Brak pamięci wypisuje
|
||||
`out of memory` i kończy proces.
|
||||
`insert_gap`, `remove_gap`), mapy (`map_find`, `map_slot`, `map_remove_at`, `map_clear`, `map_free`,
|
||||
`map_clone_raw`, `map_missing`; §10.12), bufory tekstu dla `print`/`str` (`buf_add`, `buf_int`, …,
|
||||
`buf_print`), warianty mogące się nie udać (`str_try_int`, `str_try_float`, `try_read_file`,
|
||||
`try_write_file`, `try_delete_file`, `report_error`), `args`, `input`, pliki, `shell` i panic. Brak pamięci
|
||||
wypisuje `out of memory` i kończy proces.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -759,7 +855,7 @@ wynik z oczekiwanym. Kompilacja samego siebie zajmuje kompilatorowi samohostują
|
|||
- libclang: `find_path(clang-c/Index.h)` + `find_library(clang)`.
|
||||
- `runtime/finch_rt.c` jest wczytywany przy konfiguracji do `build/rt_source.inc` jako surowy literał
|
||||
napisowy; `CMAKE_CONFIGURE_DEPENDS` ponawia konfigurację, gdy plik się zmieni.
|
||||
- `FINCH_VERSION` z `project(VERSION 2.3.0)`. C++17, `-Wall -Wextra` (MSVC: `/W3`), bez ostrzeżeń.
|
||||
- `FINCH_VERSION` z `project(VERSION 2.4.0)`. C++17, `-Wall -Wextra` (MSVC: `/W3`), bez ostrzeżeń.
|
||||
- `shell.nix` wymienia `llvmPackages.clang` przed `llvmPackages.libclang`: ten drugi dostarcza też
|
||||
„gołego” `clang`, który nie widzi nagłówków systemowych.
|
||||
|
||||
|
|
@ -769,9 +865,12 @@ wynik z oczekiwanym. Kompilacja samego siebie zajmuje kompilatorowi samohostują
|
|||
|
||||
- `tests/run.sh`: każdy `tests/run/*.fch` z `fn main` musi wypisać dokładnie swój `.out` (stdin z `.in`, jeśli
|
||||
jest); pliki bez `main` to moduły albo pliki pomocnicze. Każdy `tests/fail/*.fch` musi się nie udać z tekstem
|
||||
z linijki `// expect:`. Obecnie **59 passed, 0 failed**.
|
||||
z linijki `// expect:`. Obecnie **74 passed, 0 failed**.
|
||||
- `MEMCHECK=1 tests/run.sh`: to samo plus valgrind na każdym programie (bez wycieków i złych dostępów).
|
||||
- `tests/boot.sh`: punkt stały samohostowania i przebieg na podzbiorze (§17).
|
||||
- `tests/cross.sh windows|arm64`: każdy test zbudowany z `--target` i uruchomiony w Wine / QEMU.
|
||||
- `python3 tests/lsp_test.py`: serwer języka, sterowany jak przez edytor.
|
||||
- CI (`.github/workflows/ci.yml`) uruchamia to wszystko na Linuksie x86-64, Linuksie ARM64, macOS ARM64 i Windowsie.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -860,11 +959,14 @@ wynik z oczekiwanym. Kompilacja samego siebie zajmuje kompilatorowi samohostują
|
|||
| Funkcja | Robi |
|
||||
|---|---|
|
||||
| `call` / `method` | rozwiązuje wywołanie / metodę albo wywołanie z modułu |
|
||||
| `callFinch` / `construct` / `callC` | funkcja Fincha / konstruktor struktury / funkcja C |
|
||||
| `callFinch` / `construct` / `callC` | funkcja lub metoda Fincha / konstruktor struktury / funkcja C |
|
||||
| `structMethod` | `obj.m(...)`: adres odbiorcy, kontrole bezpieczeństwa, wywołanie |
|
||||
| `print`, `addrOf`, `convert`, `arrayMethod` | funkcje wbudowane |
|
||||
| `fallible`, `tryExpr`, `orElse`, `retValue`, `retError` (`codegen.cpp`) | błędy jako wartości (§10.11) |
|
||||
| `mapIndex`, `mapLit`, `mapMethod`, `mapSlot`, `forEachEntry` (`codegen_map.cpp`) | mapy (§10.12) |
|
||||
| `rt` / `libc` | deklaruje funkcję runtime'u / libc |
|
||||
| `fileName`, `panicIf`, `boundsCheck`, `checkDivisor` | kontrole w czasie działania |
|
||||
| `classify`, `makePlan`, `declareC`, `emitCCall`, `cThunk` | klasyfikacja System V, plan rejestrów, deklaracja, wywołanie, wrapper dla callbacków |
|
||||
| `classify`, `makePlan`, `declareC`, `emitCCall`, `cThunk` | klasyfikacja ABI (System V, Microsoft x64, AAPCS64), plan rejestrów, deklaracja, wywołanie, wrapper dla callbacków |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -889,12 +991,16 @@ przed wyskokiem z zasięgów.
|
|||
|
||||
---
|
||||
|
||||
## 23. Platformy i Windows
|
||||
## 23. Platformy: Windows, macOS, ARM64
|
||||
|
||||
`src/target.h` trzyma `g_target`: triple LLVM, flagi `windows` / `msvc` / `cross`, kompilator C (`cc`)
|
||||
i rozszerzenie plików wykonywalnych. `setTarget()` wybiera kompilator C: `FINCH_CC`, potem `CC` (nie przy
|
||||
kompilacji krzyżowej), potem `x86_64-w64-mingw32-gcc` dla `--target windows` z Linuksa, `clang` na
|
||||
Windowsie, `cc` na Uniksie. Przez tę warstwę idzie wszystko, co zależy od platformy:
|
||||
`src/target.h` trzyma `g_target`: triple LLVM, flagi `windows` / `msvc` / `darwin` / `cross`, kompilator C
|
||||
(`cc`) i rozszerzenie plików wykonywalnych. `setTarget()` zamienia `windows`, `linux`, `arm64`, `macos`
|
||||
(albo pełny triple) na triple i wybiera kompilator C: `FINCH_CC`, potem `CC` (nie przy kompilacji
|
||||
krzyżowej), potem `x86_64-w64-mingw32-gcc` / `aarch64-linux-gnu-gcc` dla Windowsa / Linuksa ARM64 z innego
|
||||
systemu, `clang --target=<triple>` dla pozostałych celów krzyżowych, `clang` na Windowsie, `cc` na Uniksie.
|
||||
`initTargets()` rejestruje backendy X86 i AArch64 (te, które ma dany LLVM; CMake definiuje
|
||||
`FINCH_TARGET_X86` / `FINCH_TARGET_AARCH64` i linkuje ich biblioteki). Przez tę warstwę idzie wszystko,
|
||||
co zależy od platformy:
|
||||
|
||||
- **Nagłówki:** libclang parsuje z `--target=<triple>` (więc `long` ma 32 bity na Windowsie i używane są
|
||||
właściwe nagłówki), a na Windowsie z `-D_USE_MATH_DEFINES`. Katalogi nagłówków pochodzą z `<cc> -E -v`.
|
||||
|
|
@ -905,7 +1011,12 @@ Windowsie, `cc` na Uniksie. Przez tę warstwę idzie wszystko, co zależy od pla
|
|||
- **Linkowanie:** bez `-lm` przy MSVC; nazwy `.exe`; klucz cache runtime'u obejmuje triple i kompilator C.
|
||||
Komunikaty linkera MSVC (`LNK2019 unresolved external symbol`, `LNK1104`/`LNK1181 cannot open file`) są
|
||||
tłumaczone tak jak komunikaty GNU ld.
|
||||
- **Uruchamianie:** program dla Windowsa zbudowany na Linuksie uruchamia się przez `wine`.
|
||||
- **Uruchamianie:** `runPrefix()`: program dla Windowsa zbudowany na Linuksie uruchamia się przez `wine`,
|
||||
program dla Linuksa ARM64 na innym procesorze przez `qemu-aarch64`.
|
||||
- **macOS:** `<cc> -E -v` wypisuje wpisy `(framework directory)`; zamieniają się w `-iframework` dla
|
||||
libclang, więc `import "OpenGL/gl.h"` działa. `link "Cocoa.framework"` daje `-framework Cocoa`. Komunikaty
|
||||
linkera Apple (`Undefined symbols for architecture arm64: "_f"`, `ld: library 'x' not found`,
|
||||
`framework 'X' not found`) są tłumaczone jak pozostałe, bez wiodącego `_` z Mach-O.
|
||||
- **Informacje dla debuggera:** CodeView dla celów MSVC, DWARF w pozostałych.
|
||||
- **Runtime:** na Windowsie nie ma `getline` ani `sys/wait.h`; `stdin`/`stdout`/`stderr` są tam makrami,
|
||||
więc `finch_std_stream(i)` udostępnia je Finchowi, gdy nagłówek nie ma dla nich zmiennej `extern`.
|
||||
|
|
@ -920,10 +1031,30 @@ kopii (bez `byval`: funkcja dostaje zwykły wskaźnik). Wyniki 1/2/4/8-bajtowe w
|
|||
wszystko inne przez ukryty wskaźnik `sret`. Każdy argument zajmuje jedno miejsce, więc nie ma liczenia
|
||||
rejestrów. `cThunk()` robi to samo w drugą stronę dla callbacków.
|
||||
|
||||
`tests/windows.sh` buduje każdy test z `--target windows`, uruchamia go w Wine i porównuje wynik: przechodzą
|
||||
`tests/cross.sh windows` buduje każdy test z `--target windows`, uruchamia go w Wine i porównuje wynik: przechodzą
|
||||
wszystkie, łącznie z `c_structs` (który wtedy sprawdza reguły Microsoftu na bibliotece C zbudowanej MinGW).
|
||||
CI dodatkowo buduje `finch.exe` przez MSVC na Windows Server i uruchamia tam `tests/run.sh`.
|
||||
|
||||
### AAPCS64 (Linux ARM64 i Apple)
|
||||
|
||||
`classify()` odwzorowuje to, jak clang tłumaczy wywołania na AArch64:
|
||||
|
||||
- **Jednorodny agregat zmiennoprzecinkowy** (1–4 elementy, wszystkie `float` albo wszystkie `double`,
|
||||
zagnieżdżone struktury i tablice spłaszczone) idzie w rejestrach wektorowych: przekazywany jako
|
||||
`[n x float]` / `[n x double]`, zwracany jako `{ float, … }`. Na Linuksie argument dostaje też
|
||||
`alignstack(8)`, tak jak u clanga; Apple tego nie robi.
|
||||
- Każda inna struktura do 16 bajtów idzie w rejestrach ogólnych: `i64` (≤ 8 bajtów) albo `[2 x i64]`
|
||||
(`i128`, jeśli jej wyrównanie to 16); zwracana jest jako liczba całkowita dokładnie jej rozmiaru albo `[2 x i64]`.
|
||||
- Większe struktury są **pośrednie (Indirect)**: kopia na stosie wywołującego, przekazana przez adres
|
||||
(bez `byval`); zwracane przez `sret`.
|
||||
- Nie ma liczenia rejestrów: każdą część rozmieszcza backend.
|
||||
- Zwykły `char` w C jest **bez znaku** na Linuksie ARM64, a ze znakiem na Apple i x86, co ma znaczenie przy
|
||||
rozszerzaniu argumentów i wyników typu `char` (`charIsSigned()`).
|
||||
- Apple przekazuje zmienną część `printf(...)` przez stos; LLVM robi to na podstawie `...` w typie funkcji.
|
||||
|
||||
`tests/cross.sh arm64` uruchamia cały zestaw testów (łącznie z testem ABI struktur na bibliotece C
|
||||
zbudowanej GCC) w QEMU, a CI robi to natywnie na Linuksie ARM64 i Apple Silicon.
|
||||
|
||||
### Budowanie finch.exe
|
||||
|
||||
CI używa Visual Studio 2022, Ninja i oficjalnego pakietu `clang+llvm-21.x-x86_64-pc-windows-msvc`
|
||||
|
|
@ -970,7 +1101,19 @@ i podpowiedziach parametrów (i odwzorowany w ściągach).
|
|||
|
||||
---
|
||||
|
||||
## 25. Rozszerzenie VS Code
|
||||
## 25. Integracje z edytorami
|
||||
|
||||
### Kate
|
||||
|
||||
`editors/kate/finch.xml` to definicja KSyntaxHighlighting (silnika Kate, KWrite i KDevelop): listy słów
|
||||
kluczowych zgodne z `builtins_doc.h`, kontekst dla `fn Typ.metoda`, teksty ze znakami specjalnymi oraz
|
||||
regiony zwijania dla `{ }` i komentarzy blokowych. `lspclient.json` to wpis dla klienta LSP w Kate
|
||||
(`highlightingModeRegex: ^Finch$`); `install.sh` kopiuje definicję i dołącza wpis do
|
||||
`~/.config/kate/lspclient/settings.json`. Kate wysyła więcej zapytań niż VS Code (`documentHighlight`,
|
||||
`foldingRange`, …); serwer odpowiada na każde nieobsługiwane zapytanie błędem `MethodNotFound`, więc żaden
|
||||
klient nie czeka.
|
||||
|
||||
### VS Code
|
||||
|
||||
`editors/vscode` to zwykłe rozszerzenie w JavaScripcie: `extension.js` uruchamia `finch lsp` przez
|
||||
`vscode-languageclient` (ścieżka z ustawienia `finch.path`) i dodaje **Run** / **Build** jako zadania
|
||||
|
|
@ -982,10 +1125,15 @@ szablony. CI pakuje je przez `vsce` do `finch-lang.vsix`.
|
|||
|
||||
## 26. Znane ograniczenia
|
||||
|
||||
- Brak metod w strukturach, typów generycznych, map, `match`, domknięć i błędów jako wartości (`int("x")`, `read_file` robią panic).
|
||||
- Brak typów generycznych, interfejsów, `match`, domknięć i enumów pisanych w Finchu.
|
||||
- Teksty to bajty: `.len`, `s[i]`, `upper()` nie znają Unicode.
|
||||
- Zmiana kluczy mapy wewnątrz `for` po tej samej mapie nie jest wykrywana (jest bezpieczna dla pamięci,
|
||||
ale wpisy mogą zostać pominięte albo odwiedzone dwa razy).
|
||||
- Porażka `try` / `or` w środku wyrażenia nie zwalnia świeżych wartości tymczasowych policzonych wcześniej
|
||||
w tym wyrażeniu (mały wyciek, tylko na ścieżce porażki).
|
||||
- Wiszące wskaźniki (`addr` zmiennej, która zniknęła, użycie po `free`) nie są wykrywane.
|
||||
- Wielkość przesunięć i konwersje float→int nie są sprawdzane (poison w LLVM, jak w C).
|
||||
- C: unie i struktury z polami bitowymi przez wartość, makra-funkcje, `long double`.
|
||||
- Dwa systemy: Linux i Windows, oba x86-64 (ABI System V i Microsoft x64). Jeszcze bez macOS i ARM.
|
||||
- Kompilator samohostujący nie ma importu C, liczb z rozmiarem ani `defer` i nie zwalnia pamięci.
|
||||
- Cele: x86-64 i ARM64 (Linux, macOS; Windows na x86-64). Bez Windowsa na ARM64, systemów 32-bitowych i WebAssembly.
|
||||
- Kompilator samohostujący nie ma importu C, liczb z rozmiarem, `defer`, map, metod ani błędów jako
|
||||
wartości i nie zwalnia pamięci.
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ Jeśli ostatnia linijka skończy się bez słowa `error`, Finch jest gotowy.
|
|||
./build/finch version
|
||||
```
|
||||
|
||||
Powinieneś zobaczyć coś w stylu `finch 2.3.0`.
|
||||
Powinieneś zobaczyć coś w stylu `finch 2.4.0`.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -510,8 +510,68 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Jeśli chcesz też wiedzieć, *w którym miejscu* listy jesteś, daj `for` dwie nazwy: pierwsza liczy
|
||||
0, 1, 2…, druga to wartość:
|
||||
|
||||
```c
|
||||
fn main() {
|
||||
for i, owoc in ["jabłko", "banan", "wiśnia"] {
|
||||
print(i + 1, owoc)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
1 jabłko
|
||||
2 banan
|
||||
3 wiśnia
|
||||
```
|
||||
|
||||
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).
|
||||
`sort` (sortuj), `reverse` (odwróć), `join` (połącz). Wszystkie opisuje [przewodnik dla techników](dla-technikow.md#10-tablice-i-mapy).
|
||||
|
||||
### Szukanie po nazwie: mapy
|
||||
|
||||
Lista znajduje rzeczy po miejscu: `owoce[0]`. Czasem chcesz znaleźć coś po **nazwie**, tak jak
|
||||
słowo w słowniku albo numer w książce telefonicznej. Do tego służy **mapa** (`map`): każdy
|
||||
**klucz** (nazwa) ma swoją **wartość**:
|
||||
|
||||
```c
|
||||
fn main() {
|
||||
telefon := ["Ola": "555-1234", "Jan": "555-9876"]
|
||||
print(telefon["Ola"]) // 555-1234
|
||||
|
||||
telefon["Ewa"] = "555-0000" // nowa osoba
|
||||
print(telefon.len) // 3
|
||||
|
||||
if telefon.has("Tomek") {
|
||||
print("Tomek jest")
|
||||
} else {
|
||||
print("nie ma Tomka")
|
||||
}
|
||||
|
||||
for imie, numer in telefon { // każde imię z jego numerem
|
||||
print(imie, numer)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `["klucz": wartość, ...]` tworzy mapę. Klucze to zwykle tekst, ale mogą być też liczby.
|
||||
- `telefon["Ola"]` daje wartość dla klucza. Pytanie o klucz, którego nie ma, zatrzymuje program,
|
||||
więc najpierw sprawdź `telefon.has("Tomek")` (czy ma) albo użyj `telefon.get("Tomek", "nie wiem")`.
|
||||
- `for imie, numer in telefon` odwiedza każdy klucz z wartością, w kolejności dodawania.
|
||||
|
||||
Mapy świetnie nadają się do **liczenia**. Nowy klucz zaczyna od 0, więc `+= 1` po prostu działa:
|
||||
|
||||
```c
|
||||
fn main() {
|
||||
map[str]int glosy // pusta mapa: klucze to tekst, wartości to liczby
|
||||
for g in ["kot", "pies", "kot", "kot", "rybka"] {
|
||||
glosy[g] += 1
|
||||
}
|
||||
print(glosy) // {"kot": 3, "pies": 1, "rybka": 1}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -625,6 +685,36 @@ fn main() {
|
|||
|
||||
Możesz też trzymać wiele struktur na liście: `[]Ksiazka polka`, a potem `polka.push(k)`.
|
||||
|
||||
### Polecenia twojej struktury: metody
|
||||
|
||||
Struktura może mieć własne polecenia (**metody**). Napisz nazwę struktury, kropkę i nazwę polecenia.
|
||||
W środku `self` (ang. „sam”) to struktura, na której użyto polecenia:
|
||||
|
||||
```c
|
||||
struct Gracz {
|
||||
str imie
|
||||
int zycia = 3
|
||||
}
|
||||
|
||||
fn Gracz.trafiony() {
|
||||
self.zycia -= 1
|
||||
print(self.imie, "ma jeszcze", self.zycia, "życia")
|
||||
}
|
||||
|
||||
fn Gracz.zyje() -> bool {
|
||||
return self.zycia > 0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
g := Gracz(imie: "Ola")
|
||||
g.trafiony() // Ola ma jeszcze 2 życia
|
||||
g.trafiony() // Ola ma jeszcze 1 życia
|
||||
print(g.zyje()) // true
|
||||
}
|
||||
```
|
||||
|
||||
`g.trafiony()` zmienia samo `g`, więc po dwóch trafieniach `g.zycia` naprawdę wynosi 1.
|
||||
|
||||
---
|
||||
|
||||
## 15. Zapisywanie do pliku
|
||||
|
|
@ -688,6 +778,57 @@ Tutaj po prostu źle napisaliśmy `wiek`. Komunikat znaczy: „nie ma zmiennej o
|
|||
| `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(...)` |
|
||||
| `'f' can fail, so say what happens then` | Użyłeś polecenia, które może się nie udać, i nie napisałeś, co wtedy | Dopisz `or ...` (niżej) |
|
||||
|
||||
### Rzeczy, które mogą się nie udać: `or` i `try`
|
||||
|
||||
Niektóre rzeczy mogą pójść źle *w trakcie działania programu*: ktoś wpisze `abc` zamiast liczby
|
||||
albo pliku nie ma. Zamiast zatrzymywać program, możesz powiedzieć, co wtedy zrobić, słowem **`or`** („albo”):
|
||||
|
||||
```c
|
||||
fn main() {
|
||||
wiek := int(input("Ile masz lat? ")) or 0 // to nie liczba? weź 0
|
||||
print("Za rok będziesz mieć", wiek + 1)
|
||||
|
||||
notatka := read_file("notatka.txt") or "(jeszcze brak notatki)"
|
||||
print(notatka)
|
||||
}
|
||||
```
|
||||
|
||||
Po `or` może też być blok. W środku `err` to komunikat, co poszło nie tak:
|
||||
|
||||
```c
|
||||
fn main() {
|
||||
while true {
|
||||
n := int(input("Podaj liczbę: ")) or {
|
||||
print("To nie jest liczba, spróbuj jeszcze raz")
|
||||
continue
|
||||
}
|
||||
print("Wybrałeś", n)
|
||||
break
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Twoje własne funkcje też mogą się nie udać. Dopisz `!` po typie wyniku i użyj `return error("...")`:
|
||||
|
||||
```c
|
||||
fn bezpieczne_dzielenie(int a, int b) -> int! {
|
||||
if b == 0 {
|
||||
return error("nie można dzielić przez zero")
|
||||
}
|
||||
return a / b
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print(bezpieczne_dzielenie(10, 2) or -1) // 5
|
||||
print(bezpieczne_dzielenie(10, 0) or -1) // -1
|
||||
}
|
||||
```
|
||||
|
||||
Finch nie pozwoli ci zapomnieć: wywołanie `bezpieczne_dzielenie(10, 0)` bez `or` to błąd, który
|
||||
przypomina, żeby to obsłużyć. (Można też przekazać problem dalej przez `try`; wyjaśnia to
|
||||
[przewodnik dla techników](dla-technikow.md#błędy-jako-wartości).)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -889,10 +1030,13 @@ struct Zwierzak { // własny rodzaj wartości
|
|||
int wiek = 1
|
||||
}
|
||||
|
||||
// listy: liczby := [1, 2, 3] liczby.push(4) liczby[0] liczby.len for n in liczby { }
|
||||
// listy: liczby := [1, 2, 3] liczby.push(4) liczby[0] liczby.len for n in liczby { } for i, n in liczby { }
|
||||
// mapy: wiek := ["Ola": 9] wiek["Jan"] = 10 wiek["Ola"] wiek.has("Tomek") for imie, lata in wiek { }
|
||||
// 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")
|
||||
// metody: fn Zwierzak.urosnij() { self.wiek += 1 } z.urosnij()
|
||||
// porażki: n := int(tekst) or 0 x := read_file("p.txt") or { print(err) ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -913,6 +1057,8 @@ struct Zwierzak { // własny rodzaj wartości
|
|||
| **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 |
|
||||
| **metoda** | Polecenie należące do struktury, np. `g.trafiony()` |
|
||||
| **mapa** (map) | Wartości znajdowane po kluczu (nazwie), jak w słowniku |
|
||||
| **wejście** (input) | To, co użytkownik wpisuje do programu |
|
||||
| **błąd** (error) | Komunikat, że coś jest nie tak, i gdzie |
|
||||
| **bug** | Pomyłka w programie |
|
||||
|
|
@ -934,6 +1080,11 @@ struct Zwierzak { // własny rodzaj wartości
|
|||
| `input` | wejście (pytanie) |
|
||||
| `len` (od *length*) | długość |
|
||||
| `error` | błąd |
|
||||
| `or` | albo (co zrobić, gdy się nie uda) |
|
||||
| `try` | spróbuj (a jak się nie uda, przekaż błąd dalej) |
|
||||
| `self` | sam (struktura, na której użyto metody) |
|
||||
| `map` | mapa (słownik) |
|
||||
| `has` / `get` | ma / weź |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -20,16 +20,16 @@ kompilator działa w środku. Do tego służy [przewodnik dla inżynierów](dla-
|
|||
7. [Operatory](#7-operatory)
|
||||
8. [Sterowanie przebiegiem](#8-sterowanie-przebiegiem)
|
||||
9. [Funkcje](#9-funkcje)
|
||||
10. [Tablice](#10-tablice)
|
||||
10. [Tablice i mapy](#10-tablice-i-mapy)
|
||||
11. [Tekst (str)](#11-tekst-str)
|
||||
12. [Struktury](#12-struktury)
|
||||
12. [Struktury i metody](#12-struktury-i-metody)
|
||||
13. [Pamięć: kto co zwalnia](#13-pamięć-kto-co-zwalnia)
|
||||
14. [Wskaźniki](#14-wskaźniki)
|
||||
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. [Edytor: VS Code](#19-edytor-vs-code)
|
||||
19. [Edytory: VS Code, Kate i inne](#19-edytory-vs-code-kate-i-inne)
|
||||
20. [Debugowanie](#20-debugowanie)
|
||||
21. [Rozwiązywanie problemów](#21-rozwiązywanie-problemów)
|
||||
22. [Struktura projektu i testy](#22-struktura-projektu-i-testy)
|
||||
|
|
@ -60,7 +60,9 @@ i komunikaty błędów, które mówią, jak problem naprawić.
|
|||
|
||||
## 2. Instalacja
|
||||
|
||||
Finch działa na **Linuksie i Windowsie, x86-64**, i jest budowany z **LLVM 21**.
|
||||
Finch działa na **Linuksie** (x86-64 i ARM64), **macOS** (Apple Silicon i Intel) i **Windowsie** (x86-64),
|
||||
i jest budowany z **LLVM 21**. Każde [wydanie](https://github.com/Nexoniarz/Finch/releases) ma gotowy
|
||||
`finch` dla Windowsa, Linuksa x86-64, Linuksa ARM64 i macOS ARM64.
|
||||
|
||||
### Windows
|
||||
|
||||
|
|
@ -75,8 +77,30 @@ Finch działa na **Linuksie i Windowsie, x86-64**, i jest budowany z **LLVM 21**
|
|||
Programy to zwykłe pliki `.exe`. Żeby zbudować Fincha ze źródeł na Windowsie, idź za `.github/workflows/ci.yml`
|
||||
(Visual Studio 2022 + pakiet `clang+llvm-21.x-x86_64-pc-windows-msvc` z wydań LLVM).
|
||||
|
||||
### macOS
|
||||
|
||||
```sh
|
||||
brew install llvm@21 # LLVM dla Fincha; programy linkuje clang od Apple
|
||||
```
|
||||
|
||||
Potem rozpakuj **`finch-macos-arm64.tar.gz`** z wydań (korzysta z `llvm@21` z Homebrew)
|
||||
albo zbuduj go sam (narzędzia wiersza poleceń Xcode: `xcode-select --install`):
|
||||
|
||||
```sh
|
||||
git clone https://github.com/Nexoniarz/Finch.git && cd Finch
|
||||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR="$(brew --prefix llvm@21)/lib/cmake/llvm"
|
||||
ninja -C build
|
||||
./build/finch version
|
||||
```
|
||||
|
||||
Frameworki Apple linkuje się przez `link "Cocoa.framework"` (`-framework Cocoa`), a ich nagłówki importuje
|
||||
się normalnie: `import "OpenGL/gl.h"`.
|
||||
|
||||
### Linux
|
||||
|
||||
Gotowe `finch-linux-x64.tar.gz` / `finch-linux-arm64.tar.gz` potrzebują bibliotek LLVM 21
|
||||
(Debian/Ubuntu: `libllvm21 libclang1-21` z [apt.llvm.org](https://apt.llvm.org)) i kompilatora C (`cc`).
|
||||
Samodzielne budowanie wygląda tak samo na x86-64 i ARM64 (Raspberry Pi 4/5, Graviton, …).
|
||||
Z innymi wersjami LLVM Finch prawdopodobnie się nie skompiluje, bo API C++ LLVM zmienia się między wydaniami.
|
||||
|
||||
### Opcja A: Nix (zalecana, nic nie instalujesz ręcznie)
|
||||
|
|
@ -87,7 +111,7 @@ cd Finch
|
|||
nix-shell # pobiera LLVM, libclang, clang, cmake, ninja, pkg-config
|
||||
cmake -S . -B build -G Ninja
|
||||
ninja -C build
|
||||
./build/finch version # finch 2.3.0 (LLVM 21.x)
|
||||
./build/finch version # finch 2.4.0 (LLVM 21.x)
|
||||
```
|
||||
|
||||
### Opcja B: pakiety z twojej dystrybucji
|
||||
|
|
@ -108,16 +132,27 @@ cmake -S . -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-21/lib/cmake/llvm
|
|||
ninja -C build
|
||||
```
|
||||
|
||||
### Budowanie programów na Windowsa pod Linuksem
|
||||
### Budowanie na inny system
|
||||
|
||||
Z zainstalowanym kompilatorem krzyżowym MinGW-w64 (`x86_64-w64-mingw32-gcc`; na Niksie
|
||||
`pkgsCross.mingwW64.buildPackages.gcc`):
|
||||
`--target` buduje program na inny system. Do linkowania Finch potrzebuje kompilatora C dla tego systemu:
|
||||
|
||||
| `--target` | Buduje | Kompilator C | `finch run` używa |
|
||||
|---|---|---|---|
|
||||
| `windows` | `.exe` dla Windowsa | `x86_64-w64-mingw32-gcc` (MinGW-w64) | Wine |
|
||||
| `arm64` | program dla Linuksa ARM64 | `aarch64-linux-gnu-gcc` | `qemu-aarch64` |
|
||||
| `linux` | program dla Linuksa x86-64 | `clang --target=…` | |
|
||||
| `macos` | program dla macOS (na Macu: dla drugiego procesora) | `clang --target=…` | |
|
||||
| dowolny triple LLVM | np. `aarch64-unknown-linux-gnu` | `clang --target=<triple>` | |
|
||||
|
||||
```sh
|
||||
finch build gra.fch --target windows # gra.exe
|
||||
finch run gra.fch --target windows # uruchamia ją w Wine, jeśli jest zainstalowane
|
||||
finch run gra.fch --target arm64 # buduje na ARM64 i uruchamia w QEMU
|
||||
FINCH_CC=aarch64-unknown-linux-gnu-gcc finch build narzedzie.fch --target arm64 # inna nazwa kompilatora
|
||||
```
|
||||
|
||||
Na Niksie te kompilatory to `pkgsCross.mingwW64.buildPackages.gcc` i
|
||||
`pkgsCross.aarch64-multiplatform.buildPackages.gcc`.
|
||||
|
||||
### Dodanie `finch` do PATH (opcjonalnie)
|
||||
|
||||
```sh
|
||||
|
|
@ -137,12 +172,14 @@ Inny kompilator wskażesz zmienną `CC`.
|
|||
finch run <plik.fch> [argumenty...] skompiluj i od razu uruchom
|
||||
finch build <plik.fch> [-o nazwa] skompiluj do programu (domyślna nazwa: nazwa pliku)
|
||||
finch ir <plik.fch> pokaż LLVM IR (dla ciekawskich)
|
||||
finch lsp serwer języka, dla edytorów
|
||||
finch version pokaż wersję
|
||||
|
||||
opcje:
|
||||
-l <lib> dolinkuj bibliotekę C, to samo co link "lib" w pliku
|
||||
-g dodaj informacje dla debuggera (gdb / lldb)
|
||||
-O0 bez optymalizacji
|
||||
-l <lib> dolinkuj bibliotekę C, to samo co link "lib" w pliku
|
||||
--target <nazwa> zbuduj na inny system: windows, linux, arm64, macos albo triple LLVM
|
||||
-g dodaj informacje dla debuggera (gdb / lldb / Visual Studio)
|
||||
-O0 bez optymalizacji
|
||||
```
|
||||
|
||||
Przykłady:
|
||||
|
|
@ -226,6 +263,7 @@ x += 1 // także -= *= /= %=
|
|||
| `char` | jeden znak wielkości bajtu | `'A'`, `'\n'` |
|
||||
| `str` | tekst | `"cześć\tświecie"` |
|
||||
| `[]T` | lista (tablica) elementów `T` | `[1, 2, 3]` |
|
||||
| `map[K]V` | wartości typu `V` znajdowane po kluczu typu `K` | `["a": 1, "b": 2]` |
|
||||
| twoje struktury | nazwane grupy wartości | `Punkt(1, 2)` |
|
||||
|
||||
Znaki specjalne w tekstach: `\n` nowa linia, `\t` tabulator, `\r`, `\0`, `\\`, `\"`, `\'`.
|
||||
|
|
@ -332,6 +370,14 @@ for i in 0..10 { // i = 0, 1, …, 9 (koniec nie jest wliczany)
|
|||
for imie in imiona { // każdy element tablicy (albo każdy znak str)
|
||||
print(imie)
|
||||
}
|
||||
|
||||
for i, imie in imiona { // razem z indeksem: 0, 1, 2, ...
|
||||
print(i, imie)
|
||||
}
|
||||
|
||||
for klucz, wartosc in wiek { // każdy wpis mapy, w kolejności dodawania kluczy
|
||||
print(klucz, wartosc) // (for klucz in wiek: same klucze)
|
||||
}
|
||||
```
|
||||
|
||||
- Warunek musi być typu `bool`. `if x {` z liczbą to błąd; napisz `if x != 0 {`.
|
||||
|
|
@ -375,11 +421,13 @@ fn bez_wyniku(str wiadomosc) { // bez "->": nic nie zwraca
|
|||
Ż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.
|
||||
- Funkcji nie można definiować wewnątrz innej funkcji.
|
||||
- Funkcję, która może się nie udać, zapisuje się `-> int!` (albo `-> !` bez wyniku); zobacz
|
||||
[Błędy jako wartości](#błędy-jako-wartości).
|
||||
- Długie wywołania możesz rozbić na kilka linijek wewnątrz nawiasów; przecinek po ostatnim argumencie jest dozwolony.
|
||||
|
||||
---
|
||||
|
||||
## 10. Tablice
|
||||
## 10. Tablice i mapy
|
||||
|
||||
```c
|
||||
liczby := [5, 3, 8] // tablica int
|
||||
|
|
@ -408,6 +456,45 @@ siatka := [[1, 2], [3, 4]] // tablice tablic
|
|||
|
||||
**Przypisanie kopiuje:** po `b := a` zmiana `b` nie zmienia `a`.
|
||||
|
||||
### Mapy
|
||||
|
||||
`map[K]V` znajduje wartości po kluczu. Kluczem może być liczba całkowita, `char`, `bool` albo `str`;
|
||||
wartością cokolwiek (tablice, struktury, inne mapy). Przechodzenie pętlą idzie w kolejności, w jakiej
|
||||
klucze zostały dodane po raz pierwszy.
|
||||
|
||||
```c
|
||||
wiek := ["anna": 31, "bob": 25] // literał mapy: map[str]int
|
||||
map[str][]str grupy // pusta mapa
|
||||
map[int]str nazwy = [:] // [:] to pusta mapa tam, gdzie typ jest znany
|
||||
```
|
||||
|
||||
| Operacja | Znaczenie |
|
||||
|---|---|
|
||||
| `m[k]` | wartość dla `k`; brak klucza zatrzymuje program komunikatem `the key "k" is not in the map` |
|
||||
| `m[k] = v` | dodaj albo zastąp |
|
||||
| `m[k] += 1`, `m[k].push(x)`, `m[k].pole = …` | zmiana brakującego klucza najpierw go dodaje, z wartością domyślną typu (0, `""`, `[]`, …) |
|
||||
| `m.len` | liczba kluczy |
|
||||
| `m.has(k)` | czy `k` jest w mapie? |
|
||||
| `m.get(k, domyslna)` | wartość albo `domyslna` (liczona tylko w razie potrzeby) |
|
||||
| `m.remove(k)` | usuwa `k`; `true`, jeśli był |
|
||||
| `m.clear()` | usuwa wszystko |
|
||||
| `m.keys()`, `m.values()` | nowe tablice, w kolejności dodawania |
|
||||
| `print(m)`, `str(m)` | `{"anna": 31, "bob": 25}` |
|
||||
|
||||
```c
|
||||
map[str]int ile
|
||||
for slowo in tekst.split(" ") {
|
||||
ile[slowo] += 1 // liczenie: nowe słowo zaczyna od 0
|
||||
}
|
||||
for slowo, n in ile {
|
||||
print(slowo, n)
|
||||
}
|
||||
```
|
||||
|
||||
Wyszukiwanie używa haszowania (średnio stały czas). Tak jak tablice, mapy są właścicielami kluczy
|
||||
i wartości, kopiują się przy przypisaniu i zwalniają na końcu bloku. Nie dodawaj ani nie usuwaj kluczy
|
||||
mapy wewnątrz `for` po tej samej mapie; zbierz je do tablicy i zmień mapę po pętli.
|
||||
|
||||
---
|
||||
|
||||
## 11. Tekst (str)
|
||||
|
|
@ -437,7 +524,7 @@ przekazanie go do C jako `char*` działa.
|
|||
|
||||
---
|
||||
|
||||
## 12. Struktury
|
||||
## 12. Struktury i metody
|
||||
|
||||
```c
|
||||
struct Gracz {
|
||||
|
|
@ -457,7 +544,34 @@ print(g) // Gracz(imie: "Ola", zycia: 2, wyniki: [10], pozycja: Punkt(x: 5,
|
|||
|
||||
- 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`.
|
||||
|
||||
### Metody
|
||||
|
||||
Metoda to funkcja należąca do struktury: `fn Struktura.nazwa(...)`. W środku `self` to struktura,
|
||||
na której ją wywołano, i to jest sama wartość wywołującego, nie kopia: zmiany w `self` zostają.
|
||||
|
||||
```c
|
||||
fn Gracz.wylecz(int ile) {
|
||||
self.zycia += ile
|
||||
}
|
||||
|
||||
fn Gracz.zyje() -> bool {
|
||||
return self.zycia > 0
|
||||
}
|
||||
|
||||
g := Gracz(imie: "Ola")
|
||||
g.wylecz(2) // g.zycia wynosi teraz 5
|
||||
if g.zyje() { ... }
|
||||
|
||||
druzyna[0].wylecz(1) // na elemencie tablicy, polu, m[klucz], przez ptr[Gracz] ...
|
||||
```
|
||||
|
||||
- Metoda jest w tym samym pliku (module) co jej struktura i jest dostępna wszędzie, gdzie ta struktura.
|
||||
- Metody można też dopisywać do **struktur z nagłówków C**: `fn Vector2.length() -> f32 { ... }`.
|
||||
- Metoda nie może nazywać się tak jak pole.
|
||||
- Wywołanie metody zmieniającej `self` na zmiennej pętli (`for g in druzyna { g.wylecz(1) }`) to błąd,
|
||||
bo zmienna pętli jest tylko do odczytu; użyj `for i in 0..druzyna.len { druzyna[i].wylecz(1) }`.
|
||||
- Tak jak funkcje, metody mogą się nie udać (`-> !`); zobacz [Błędy jako wartości](#błędy-jako-wartości).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -520,14 +634,16 @@ Użycie `.value`, `.pole` albo `[i]` na wskaźniku `null` **zatrzymuje program**
|
|||
|---|---|
|
||||
| `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. |
|
||||
| `read_file(sciezka)` | Cały plik jako `str`. Jeśli nie da się go przeczytać, zatrzymuje program z czytelnym komunikatem, chyba że obsłużysz to: `read_file(p) or ...`. |
|
||||
| `write_file(sciezka, tekst)` | Zapisuje (zastępuje) plik; oddaje `true`, jeśli się udało. Z `or` / `try` porażka niesie przyczynę. |
|
||||
| `file_exists(sciezka)` | `true` / `false` |
|
||||
| `delete_file(sciezka)` | Usuwa plik; `true`, jeśli się udało (albo przyczyna, z `or` / `try`). |
|
||||
| `error(komunikat)` | Porażka, którą zwraca funkcja mogąca się nie udać: `return error("...")`. |
|
||||
| `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)). |
|
||||
| `int(x)`, `str(x)`, `u8(x)`, … | Zamiany typów (zobacz [Zamiana typów](#zamiana-typów)). `str(x)` działa na każdym typie i daje ten sam tekst, co `print`. `int(tekst) or 0` obsługuje tekst, który nie jest liczbą. |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -669,6 +785,54 @@ Oba działają bezpośrednio; `examples/opengl.fch` i `examples/vulkan.fch` to k
|
|||
|
||||
## 18. Błędy
|
||||
|
||||
### Błędy jako wartości
|
||||
|
||||
Niektóre porażki są normalne: użytkownik wpisze `abc`, pliku nie ma. Finch obsługuje je bez wyjątków
|
||||
i bez ukrytych skoków. Funkcja, która może się nie udać, mówi to znakiem `!` po typie wyniku i kończy się
|
||||
porażką przez `return error("komunikat")`:
|
||||
|
||||
```c
|
||||
fn port_z_tekstu(str tekst) -> int! {
|
||||
n := int(tekst) or { return error("'" + tekst + "' to nie liczba") }
|
||||
if n < 1 || n > 65535 {
|
||||
return error("port " + str(n) + " jest poza zakresem")
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
fn zapisz(str sciezka, str tekst) -> ! { // może się nie udać, nic nie oddaje
|
||||
try write_file(sciezka, tekst)
|
||||
}
|
||||
```
|
||||
|
||||
Wywołujący **musi** obsłużyć porażkę, na jeden z trzech sposobów (zapomnienie to błąd kompilacji):
|
||||
|
||||
| Kod | W razie porażki |
|
||||
|---|---|
|
||||
| `port := port_z_tekstu(s) or 8080` | użyj tej wartości (liczonej tylko przy porażce; może to być kolejne wywołanie z `or`) |
|
||||
| `port := port_z_tekstu(s) or { print(err); return }` | wykonaj blok; `err` to komunikat (`str`). Blok musi wyjść (`return`, `break`, `continue`, `exit`), chyba że wynik nie jest używany. |
|
||||
| `port := try port_z_tekstu(s)` | zakończ bieżącą funkcję tym samym błędem (musi też móc się nie udać) |
|
||||
|
||||
```c
|
||||
fn wczytaj(str sciezka) -> Konfig! {
|
||||
tekst := try read_file(sciezka)
|
||||
port := try port_z_tekstu(tekst.trim())
|
||||
return Konfig(port: port)
|
||||
}
|
||||
|
||||
fn main() -> ! { // main też może się nie udać: błąd zostanie wypisany, a kod wyjścia to 1
|
||||
konfig := try wczytaj("app.conf")
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Funkcje wbudowane, które mogą się nie udać, działają tak samo: `int(tekst)`, `float(tekst)`, `read_file`,
|
||||
`write_file`, `delete_file`. Bez `or` / `try` zachowują się jak wcześniej (`int("x")` zatrzymuje program,
|
||||
`write_file` oddaje `bool`).
|
||||
|
||||
Pod spodem funkcja, która może się nie udać, zwraca wartość z flagą i komunikatem: bez alokacji przy
|
||||
sukcesie, bez zwijania stosu, a porażka kosztuje tyle, co zbudowanie komunikatu.
|
||||
|
||||
### Błędy kompilacji
|
||||
|
||||
```
|
||||
|
|
@ -692,11 +856,31 @@ Zamiast niezdefiniowanego zachowania (jak w C) Finch zatrzymuje się z komunikat
|
|||
| `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 |
|
||||
| `runtime error: can't read the file "…"` | `read_file` na brakującym albo nieczytelnym pliku (obsłuż to przez `or`) |
|
||||
| `runtime error: the key "x" is not in the map …` | `m["x"]`, gdy mapa nie ma takiego klucza (użyj `.has` albo `.get`) |
|
||||
| `runtime error: called .f() on a null pointer` | metoda wywołana przez `null` w `ptr[T]` |
|
||||
|
||||
---
|
||||
|
||||
## 19. Edytor: VS Code
|
||||
## 19. Edytory: VS Code, Kate i inne
|
||||
|
||||
Wszystkie funkcje edytorów pochodzą z jednego serwera języka, `finch lsp`, który używa prawdziwego
|
||||
kompilatora: błędy na bieżąco, typy i opisy po najechaniu myszą, idź do definicji, podpowiedzi (pola,
|
||||
metody, metody map i tablic po `.`, zawartość modułów, nazwy z nagłówków C), konspekt pliku i podpowiedzi
|
||||
parametrów.
|
||||
|
||||
### Kate (oraz KWrite, KDevelop)
|
||||
|
||||
```sh
|
||||
editors/kate/install.sh
|
||||
```
|
||||
|
||||
Instaluje podświetlanie (`~/.local/share/org.kde.syntax-highlighting/syntax/finch.xml`) i dopisuje `finch`
|
||||
do ustawień klienta LSP w Kate, zachowując twoje pozostałe serwery. Uruchom Kate ponownie i włącz wtyczkę
|
||||
**LSP Client**. Żeby uruchamiać programy, dodaj `finch run %f` jako cel we wtyczce **Build & Run**; komunikaty
|
||||
Fincha `plik:linia:kolumna: error:` są wtedy klikalne. Szczegóły: `editors/kate/README.md`.
|
||||
|
||||
### VS Code
|
||||
|
||||
Rozszerzenie **Finch** (`editors/vscode`, dołączane też do każdego wydania jako plik `.vsix`) daje:
|
||||
podświetlanie składni, błędy na bieżąco, podpowiedzi (także pola i metody po `.`), typy po najechaniu
|
||||
|
|
@ -704,8 +888,12 @@ myszą, idź do definicji (F12), konspekt pliku, podpowiedzi parametrów, szablo
|
|||
(`Ctrl+F5`). Błędy z **Finch: Build This File** trafiają do panelu Problems.
|
||||
|
||||
Instalacja: Rozszerzenia → `…` → *Install from VSIX…* → wybierz `finch-lang-*.vsix`. Rozszerzenie uruchamia
|
||||
`finch` z PATH; jeśli jest gdzie indziej, ustaw **Finch: Path** w ustawieniach. Inne edytory obsługujące
|
||||
Language Server Protocol (Neovim, Helix, Zed, Emacs, Sublime) mogą używać `finch lsp` bezpośrednio.
|
||||
`finch` z PATH; jeśli jest gdzie indziej, ustaw **Finch: Path** w ustawieniach.
|
||||
|
||||
### Inne
|
||||
|
||||
Neovim, Helix, Zed, Emacs, Sublime i każdy inny edytor obsługujący Language Server Protocol:
|
||||
uruchamiaj `finch lsp` dla plików `.fch` (stdin/stdout, bez opcji).
|
||||
|
||||
## 20. Debugowanie
|
||||
|
||||
|
|
@ -738,6 +926,9 @@ CodeView, który czyta debugger Visual Studio i WinDbg.
|
|||
| 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. |
|
||||
| Windows: `finch` nie linkuje (błędy `LNK…` o `libcmt`, `kernel32`) | Zainstaluj Visual Studio Build Tools z *Desktop development with C++*. |
|
||||
| Windows: `finch.exe` się nie uruchamia (brak `libclang.dll`) | Trzymaj `libclang.dll` obok `finch.exe`. |
|
||||
| macOS: `finch` się nie uruchamia (`Library not loaded: …libLLVM…`) | `brew install llvm@21`. |
|
||||
| macOS: `can't find the C header 'stdio.h'` | Zainstaluj narzędzia wiersza poleceń: `xcode-select --install`. |
|
||||
| `--target arm64`: `no C compiler found to link with` | Zainstaluj `aarch64-linux-gnu-gcc` (Debian: `gcc-aarch64-linux-gnu`) albo ustaw `FINCH_CC`. |
|
||||
| `clang` w `nix-shell` nie widzi `stdio.h` | Masz stary `shell.nix`: `llvmPackages.clang` musi być przed `llvmPackages.libclang`. |
|
||||
| Program nigdy się nie kończy | Warunek pętli nigdy nie staje się fałszywy. Naciśnij Ctrl+C. |
|
||||
|
||||
|
|
@ -756,17 +947,18 @@ Finch/
|
|||
│ ├── fail/ programy, które muszą się nie udać, z oczekiwanym błędem w 1. linijce
|
||||
│ ├── run.sh uruchamia testy (MEMCHECK=1 sprawdza też pamięć valgrindem)
|
||||
│ └── boot.sh buduje kompilator samohostujący nim samym i porównuje
|
||||
├── editors/vscode/ rozszerzenie VS Code
|
||||
├── editors/ rozszerzenie VS Code (vscode/), podświetlanie i konfiguracja LSP dla Kate (kate/)
|
||||
├── docs/ ta dokumentacja (en, pl), łącznie ze ściągą
|
||||
├── shell.nix środowisko deweloperskie Nix
|
||||
└── CMakeLists.txt
|
||||
```
|
||||
|
||||
```sh
|
||||
tests/run.sh # → 59 passed, 0 failed
|
||||
tests/run.sh # → 74 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)
|
||||
tests/windows.sh # każdy test zbudowany na Windowsa i uruchomiony w Wine
|
||||
tests/cross.sh windows # każdy test zbudowany na Windowsa i uruchomiony w Wine
|
||||
tests/cross.sh arm64 # każdy test zbudowany na Linuksa ARM64 i uruchomiony w QEMU
|
||||
python3 tests/lsp_test.py # serwer języka
|
||||
```
|
||||
|
||||
|
|
@ -774,12 +966,11 @@ python3 tests/lsp_test.py # serwer języka
|
|||
|
||||
## 23. Obecne ograniczenia
|
||||
|
||||
Finch 2.0 to kompletny mały język, ale nie skończony. Jeszcze nie ma:
|
||||
Jeszcze nie ma:
|
||||
|
||||
- Metod w strukturach, typów generycznych, słowników (map), `match`. (Używaj tablic struktur i funkcji.)
|
||||
- Błędów jako wartości: błąd w `int("x")` albo `read_file` zatrzymuje program. Sprawdzaj wcześniej (`file_exists`).
|
||||
- Typów generycznych (własnego `Lista[T]`), interfejsów, `match`, enumów pisanych w Finchu.
|
||||
- Tekstu świadomego Unicode: `.len`, `s[i]` i `upper()` działają na bajtach / ASCII.
|
||||
- Wątków.
|
||||
- Platform innych niż Linux i Windows na x86-64 (macOS, ARM).
|
||||
- Windowsa na ARM64, systemów 32-bitowych, WebAssembly.
|
||||
|
||||
Plan rozwoju jest w [README](../../README.pl.md#plan-rozwoju).
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Cały Finch na jednej stronie. Wyjaśnienia są w [przewodnikach](../../README.p
|
|||
| `finch lsp` | serwer języka (korzystają z niego edytory) |
|
||||
| `finch version` | wersja |
|
||||
| `-l lib` | dolinkuj bibliotekę C (jak `link "lib"`) |
|
||||
| `--target windows` / `linux` / triple | zbuduj dla innego systemu |
|
||||
| `--target windows` / `linux` / `arm64` / `macos` / triple | zbuduj dla innego systemu |
|
||||
| `-g` | informacje dla debuggera (gdb, lldb, Visual Studio) |
|
||||
| `-O0` | bez optymalizacji |
|
||||
|
||||
|
|
@ -71,6 +71,7 @@ Jedna instrukcja na linijkę, bez średników, `{` w tej samej linijce, bez nawi
|
|||
| `u8 u16 u32 u64` | bez znaku, z rozmiarem |
|
||||
| `f32 f64` | zmiennoprzecinkowe |
|
||||
| `[]T` | tablica: `[1, 2, 3]`, `[]str imiona` |
|
||||
| `map[K]V` | mapa: `["a": 1]`, `map[str]int ile`, `[:]` |
|
||||
| `ptr[T]`, `ptr` | wskaźnik, wskaźnik bez typu |
|
||||
| `Nazwa` / `modul.Nazwa` | struktura |
|
||||
|
||||
|
|
@ -100,6 +101,8 @@ if a > b { … } else if a == b { … } else { … }
|
|||
while x > 0 { … }
|
||||
for i in 0..10 { … } // 0 … 9
|
||||
for el in lista { … } // każdy element (albo znak str)
|
||||
for i, el in lista { … } // razem z indeksem
|
||||
for klucz, wart in m { … } // każdy wpis mapy, w kolejności dodawania
|
||||
break continue return wartosc
|
||||
defer sprzatanie() // uruchomi się na końcu bloku
|
||||
```
|
||||
|
|
@ -121,6 +124,47 @@ Parametry zachowują się jak kopie. Kolejność w pliku nie ma znaczenia. Przec
|
|||
|
||||
---
|
||||
|
||||
## Metody
|
||||
|
||||
```c
|
||||
fn Punkt.przesun(int dx, int dy) { // self to Punkt, na którym wywołano (nie kopia)
|
||||
self.x += dx
|
||||
self.y += dy
|
||||
}
|
||||
fn Punkt.odleglosc() -> float { … }
|
||||
|
||||
p.przesun(1, 2) lista[0].przesun(1, 1) wsk_na_punkt.przesun(0, 1)
|
||||
```
|
||||
|
||||
Także na strukturach z C: `fn Vector2.length() -> f32 { … }`.
|
||||
|
||||
---
|
||||
|
||||
## Błędy jako wartości
|
||||
|
||||
```c
|
||||
fn parsuj(str s) -> int! { // ! = może się nie udać
|
||||
if s.len == 0 {
|
||||
return error("pusty")
|
||||
}
|
||||
return int(s) or { return error("to nie liczba: " + s) }
|
||||
}
|
||||
fn zapisz(str p, str t) -> ! { // może się nie udać, bez wyniku
|
||||
try write_file(p, t)
|
||||
}
|
||||
```
|
||||
|
||||
| Kod | Przy porażce |
|
||||
|---|---|
|
||||
| `x := parsuj(s) or 0` | użyj 0 |
|
||||
| `x := parsuj(s) or { print(err); return }` | wykonaj blok (`err`: komunikat) |
|
||||
| `x := try parsuj(s)` | zakończ porażką też bieżącą funkcję |
|
||||
| `fn main() -> !` | porażka wypisuje `error: …`, kod wyjścia 1 |
|
||||
|
||||
Funkcje wbudowane, które mogą się nie udać: `int(s)`, `float(s)`, `read_file`, `write_file`, `delete_file`.
|
||||
|
||||
---
|
||||
|
||||
## Struktury
|
||||
|
||||
| Kod | Znaczenie |
|
||||
|
|
@ -155,6 +199,23 @@ Parametry zachowują się jak kopie. Kolejność w pliku nie ma znaczenia. Przec
|
|||
|
||||
---
|
||||
|
||||
## Mapy `map[K]V`
|
||||
|
||||
| Kod | Daje |
|
||||
|---|---|
|
||||
| `m := ["a": 1, "b": 2]` | mapa (klucze: liczby, char, bool, str) |
|
||||
| `m[k]` | wartość (brak klucza: program się zatrzymuje) |
|
||||
| `m[k] = v` | dodaj / zastąp |
|
||||
| `m[k] += 1`, `m[k].push(x)` | brakujący klucz zaczyna od wartości domyślnej (0, `[]`, …) |
|
||||
| `m.len` | liczba kluczy |
|
||||
| `m.has(k)` | czy jest |
|
||||
| `m.get(k, domyslna)` | wartość albo domyślna |
|
||||
| `m.remove(k)` | bool: czy był |
|
||||
| `m.clear()` | wyczyść |
|
||||
| `m.keys()`, `m.values()` | tablice, w kolejności dodawania |
|
||||
|
||||
---
|
||||
|
||||
## Tekst `str`
|
||||
|
||||
| Kod | Daje |
|
||||
|
|
@ -189,6 +250,8 @@ Parametry zachowują się jak kopie. Kolejność w pliku nie ma znaczenia. Przec
|
|||
| `char(66)` | `'B'` |
|
||||
| `bool(0)` | `false` |
|
||||
| `str(42)`, `str(1.5)`, `str(true)`, `str('c')` | tekst |
|
||||
| `str([1, 2])`, `str(m)`, `str(punkt)` | ten sam tekst, który pokazuje `print` |
|
||||
| `int("x") or 0`, `float(s) or 0.0` | liczba albo wartość zapasowa |
|
||||
| `str(bajty)` | `[]u8` → tekst |
|
||||
| `str(p)` | `char*` z C → tekst |
|
||||
| `ptr(p)`, `ptr(16)` | wskaźnik bez typu, ze wskaźnika albo liczby |
|
||||
|
|
@ -210,6 +273,7 @@ Automatycznie tylko wtedy, gdy nic nie ginie: mniejsza → większa liczba, dowo
|
|||
| `delete_file(sciezka)` | usuń plik |
|
||||
| `shell(polecenie)` | uruchom polecenie, jego kod wyjścia |
|
||||
| `exit(kod)` | zakończ program od razu |
|
||||
| `error(komunikat)` | `return error("…")` w funkcji `-> T!` |
|
||||
| `addr(x)` | wskaźnik na x; `addr(fn)` = callback dla C |
|
||||
| `new(wartosc)` | wartość na stercie → `ptr[T]` |
|
||||
| `free(p)` | oddaj ją |
|
||||
|
|
@ -281,11 +345,17 @@ ksztalty.Pudlo inne // w deklaracjach
|
|||
| `pop() on an empty array` | `pop()` na pustej tablicy |
|
||||
| `used .value on a null pointer` | `.value` / `.pole` / `[i]` przez `null` |
|
||||
| `can't turn "x" into int` | `int("x")` |
|
||||
| `can't read the file "…"` | `read_file` |
|
||||
| `can't read the file "…"` | `read_file` (bez `or`) |
|
||||
| `the key "x" is not in the map` | `m["x"]` |
|
||||
|
||||
---
|
||||
|
||||
## VS Code (rozszerzenie „Finch”)
|
||||
## Edytory
|
||||
|
||||
Kate: `editors/kate/install.sh`, potem włącz wtyczkę **LSP Client**; uruchamianie przez **Build & Run** → `finch run %f`.
|
||||
Każdy edytor z LSP: `finch lsp`.
|
||||
|
||||
### VS Code (rozszerzenie „Finch”)
|
||||
|
||||
| Klawisz / akcja | Robi |
|
||||
|---|---|
|
||||
|
|
@ -295,7 +365,7 @@ ksztalty.Pudlo inne // w deklaracjach
|
|||
| najechanie myszą | typ / sygnatura / opis |
|
||||
| `Ctrl+Spacja` | podpowiedzi (po `.`: pola, metody, zawartość modułu) |
|
||||
| `Ctrl+Shift+O` | funkcje i struktury w pliku |
|
||||
| `main`, `fn`, `fnr`, `struct`, `for`, `foreach`, `if`, `ife`, `while`, `input` + Tab | szablony |
|
||||
| `main`, `fn`, `fnr`, `fnf`, `method`, `struct`, `for`, `foreach`, `forkv`, `map`, `orb`, `if`, `ife`, `while`, `input` + Tab | szablony |
|
||||
| ustawienie `finch.path` | gdzie jest finch / finch.exe |
|
||||
|
||||
---
|
||||
|
|
@ -314,3 +384,7 @@ ksztalty.Pudlo inne // w deklaracjach
|
|||
| `import` / `link` | zaimportuj / dolinkuj |
|
||||
| `struct` | struktura |
|
||||
| `defer` | odłóż (na koniec bloku) |
|
||||
| `or` | albo (co zrobić przy porażce) |
|
||||
| `try` | spróbuj (porażkę przekaż dalej) |
|
||||
| `self` | sam (w metodzie: struktura, na której ją wywołano) |
|
||||
| `map` | mapa |
|
||||
|
|
|
|||
Loading…
Reference in a new issue