Finch/docs/pl/dla-technikow.md
Nexoniarz 0591cd20f3 Finch 1.0.0
A small, fast programming language on LLVM with C-like syntax.

- Lexer, recursive-descent parser, single-pass type checker + LLVM IR codegen, O2 pipeline
- Types: int/float/bool/char/str, sized i8..u64/f32/f64, pointers ptr[T] with addr()/.value/null
- Functions (fn ... -> T), if/else, while, for..in, break/continue, Go-style operator precedence
- Implicit conversions only when lossless; literals adapt to their target type
- Runtime checks for division by zero, MIN/-1 and null .value
- C interop: import "header.h" via libclang, link "lib" with pkg-config, link-error advice
- Human-readable errors with source excerpts
- Test suite (34 tests), examples, docs for beginners/technicians/engineers in English and Polish

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:21:06 +02:00

19 KiB
Raw Blame History

Finch dla techników

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, korzystanie z bibliotek C i rozwiązywanie typowych problemów. Nie opisuje, jak kompilator działa w środku. Do tego służy przewodnik dla inżynierów.

🇬🇧 English version: for-technicians.md


Spis treści

  1. Czym jest Finch
  2. Instalacja
  3. Polecenie finch
  4. Jak wygląda plik Fincha
  5. Zmienne
  6. Typy
  7. Operatory
  8. Sterowanie przebiegiem
  9. Funkcje
  10. Wskaźniki
  11. Biblioteki z C
  12. Błędy
  13. Rozwiązywanie problemów
  14. Struktura projektu i testy
  15. Obecne ograniczenia

1. Czym jest Finch

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 zaplecza kompilatorów, na którym działają clang, Rust i Swift.

Co to daje:

  • Szybkość zbliżoną do C. Nie ma interpretera, maszyny wirtualnej ani garbage collectora.
  • 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.

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ć.


2. Instalacja

Finch działa obecnie na Linuksie x86-64. Testowany był z LLVM 21. Z innymi wersjami LLVM prawdopodobnie się nie skompiluje, bo API C++ LLVM zmienia się między wydaniami.

Opcja A: Nix (zalecana, nic nie instalujesz ręcznie)

git clone https://github.com/Nexoniarz/Finch.git
cd Finch
nix-shell                        # pobiera LLVM, libclang, cmake, ninja, pkg-config
cmake -S . -B build -G Ninja
ninja -C build
./build/finch version            # finch 1.0.0 (LLVM 21.x)

Opcja B: pakiety z twojej dystrybucji

Potrzebujesz: kompilatora C/C++, CMake ≥ 3.20, Ninja (albo Make), plików deweloperskich LLVM 21 i libclang 21, a opcjonalnie pkg-config.

Dystrybucja Pakiety (nazwy mogą się nieco różnić zależnie od wersji)
Debian / Ubuntu build-essential cmake ninja-build pkg-config llvm-21-dev libclang-21-dev
Fedora gcc-c++ cmake ninja-build pkgconf llvm-devel clang-devel
Arch base-devel cmake ninja pkgconf llvm clang

Jeśli CMake nie znajduje LLVM, wskaż mu katalog:

cmake -S . -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-21/lib/cmake/llvm
ninja -C build

Dodanie finch do PATH (opcjonalnie)

sudo cp build/finch /usr/local/bin/
finch version

Podczas pracy Finch potrzebuje działającego kompilatora C (cc). Używa go jako linkera, który skleja skompilowany kod w gotowy program. Inny kompilator wskażesz zmienną środowiskową CC.


3. Polecenie finch

finch run   <plik.fn>              skompiluj i od razu uruchom
finch build <plik.fn> [-o nazwa]   skompiluj do programu (domyślna nazwa: nazwa pliku)
finch ir    <plik.fn>              pokaż LLVM IR (dla ciekawskich)
finch version                      pokaż wersję

opcje:
  -l <lib>   dolinkuj bibliotekę C, to samo co  link "lib"  w pliku
  -O0        bez optymalizacji

Przykłady:

finch run hello.fn                 # wypisuje wynik
finch build gra.fn -o mojagra      # tworzy ./mojagra
./mojagra
finch run okno.fn -l glfw          # z biblioteką C

finch run przekazuje dalej kod wyjścia twojego programu. Jeśli program się wysypie, powie jak (np. the program crashed: Segmentation fault).


4. Jak wygląda plik Fincha

// komentarz zaczyna się od dwóch ukośników
/* albo zajmuje
   kilka linijek */

import "math.h"          // nagłówki C (opcjonalne), zawsze na górze
link "m"                 // biblioteki C do dolinkowania (opcjonalne)

fn main() {              // tu zaczyna się program
    print("Cześć")
}

Zasady, które warto znać:

  • Jedna instrukcja na linijkę. Średników nie ma. Długie wyrażenie możesz złamać po operatorze (a +⏎ b) albo w dowolnym miejscu wewnątrz nawiasów ( ).
  • 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) {.
  • Funkcje mogą być w pliku w dowolnej kolejności.
  • main to fn main() albo fn main() -> int (zwraca kod wyjścia).

5. Zmienne

x := 5               // nowa zmienna, typ zgadnięty (int)
int y = 10           // nowa zmienna, typ podany jawnie
int z                // bez wartości: startuje od zera ("" dla str, null dla wskaźników)

x = 7                // przypisanie
x += 1               // także -= *= /= %=
  • Nazwę można zadeklarować raz w funkcji. Finch nie pozwala użyć tej samej nazwy ponownie w bloku 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.
  • 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.

Słowa kluczowe: fn return if else while for in break continue true false null import link


6. Typy

Typy na co dzień

Typ Przechowuje Przykład
int liczba całkowita, 64-bitowa (to samo co i64) 42, -7, 0xFF, 1_000_000
float liczba z przecinkiem, 64-bitowa (to samo co f64) 3.14
bool true albo false
char jeden znak wielkości bajtu 'A', '\n'
str tekst (napis w stylu C) "cześć\tświecie"

Znaki specjalne w tekstach: \n nowa linia, \t tabulator, \r, \0, \\, \", \'.

Typy z rozmiarem (do plików, sprzętu, bibliotek C)

Ze znakiem Zakres Bez znaku Zakres
i8 −128 … 127 u8 0 … 255
i16 −32 768 … 32 767 u16 0 … 65 535
i32 ok. ±2,1 mld u32 0 … ok. 4,29 mld
i64 ok. ±9,2 × 10¹⁸ u64 0 … ok. 1,8 × 10¹⁹

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.

Zamiana typów

Finch zamienia typy sam tylko wtedy, gdy nic nie może zginąć:

Z Na Automatycznie?
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) ✅
dowolna liczba całkowita float / f32 ✅
f32 f64 ✅
null dowolny wskaźnik ✅
dowolny wskaźnik, str ptr ✅
ptr dowolny ptr[T] ✅
wszystko inne ❌ napisz to jawnie: typ(wartość)

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). To samo dotyczy stałych z nagłówków C.

Jawna zamiana wygląda jak wywołanie funkcji o nazwie typu:

int(3.99)      // 3     (obcina część ułamkową)
int(-3.99)     // -3
float(7) / 2   // 3.5
u8(300)        // 44    (zostawia najniższe 8 bitów)
i8(200)        // -56
int('A')       // 65
char(66)       // 'B'
bool(0)        // false (każda liczba różna od zera to true)
ptr(tekst)     // str jako wskaźnik bez typu
str(p)         // wskaźnik jako str

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. Jeśli potrzebujesz więcej miejsca, najpierw zamień typ: int(x) + 10.


7. Operatory

Według priorytetu (wyższy wiąże mocniej)

Priorytet Operatory Znaczenie
5 * / % << >> & mnożenie, dzielenie, reszta, przesunięcie bitów w lewo/prawo, bitowe AND
4 + - | ^ dodawanie, odejmowanie, bitowe OR, bitowe XOR
3 == != < <= > >= porównania
2 && logiczne I
1 || logiczne LUB

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 flagi & 4 != 0 znaczy (flagi & 4) != 0. W C to samo po cichu znaczy co innego.

Uwagi:

  • / na liczbach całkowitych zaokrągla w stronę zera: 7 / 2 to 3, -7 / 2 to -3. % ma znak lewej strony.
  • 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.
  • Tekst (str) można porównywać przez == i != (porównuje zawartość). < i > na tekstach nie działają.
  • Wskaźniki porównuje się przez == i !=, także z null.
  • Operatory bitowe wymagają liczb całkowitych. Dla bool używaj && i ||.
  • >> na liczbach ze znakiem zachowuje znak (-16 >> 2 to -4). Na liczbach bez znaku wypełnia zerami.

8. Sterowanie przebiegiem

if x > 10 {
    ...
} else if x > 0 {
    ...
} else {
    ...
}

while licznik > 0 {
    licznik -= 1
}

for i in 0..10 {          // i = 0, 1, …, 9  (koniec nie jest wliczany)
    if i == 3 { continue }
    if i == 8 { break }
}
  • 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. i jest typu int i nie można go zmieniać wewnątrz pętli.
  • 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.

9. Funkcje

fn nazwa(typ1 param1, typ2 param2) -> typ_wyniku {
    ...
    return wartosc
}

fn bez_wyniku(str wiadomosc) {    // bez "->": nic nie zwraca
    print(wiadomosc)
}
  • Parametry są kopiami. Zmiana parametru nie zmienia zmiennej u wywołującego. Do tego służą wskaźniki (niżej).
  • Funkcja z -> typ musi zwrócić wartość na każdej ścieżce. Finch to sprawdza.
  • 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).

10. Wskaźniki

Wskaźnik przechowuje adres wartości, czyli miejsce w pamięci, gdzie ona leży. Finch zamiast * i & z C używa słów:

C Finch Znaczenie
int *p ptr[int] p p wskazuje na int
&x addr(x) adres x
*p p.value wartość, na którą wskazuje p
NULL null wskazuje donikąd
void * ptr wskazuje na coś nieznanego typu
fn podwoj(ptr[int] p) {
    p.value = p.value * 2
}

fn main() {
    x := 21
    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.

Użycie .value na wskaźniku null zatrzymuje program 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, bo zmienna wtedy już nie istnieje. Finch jeszcze tego nie sprawdza.


11. Biblioteki z C

Prawie każda biblioteka na Linuksie ma interfejs w C: grafika, dźwięk, sieć, bazy danych. Finch może z nich korzystać bezpośrednio.

Krok 1: zaimportuj nagłówek

import "stdio.h"
import "math.h"
import "GLFW/glfw3.h"     // ścieżka w systemowych katalogach nagłówków
import "mojalib.h"        // nagłówek leżący obok twojego pliku .fn

Finch czyta nagłówek przez libclang (parser C z clanga) i udostępnia:

  • funkcje, także te ze zmienną liczbą argumentów, jak printf,
  • wartości enum,
  • proste stałe #define, które są pojedynczą liczbą, np. M_PI, EOF, GL_COLOR_BUFFER_BIT,
  • zmienne globalne, np. stdout i stderr.

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:

link "glfw"              // używa libglfw.so
link "GL"                // używa libGL.so

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:

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"

Jak typy C wyglądają w Finchu

C Finch
char, signed char, unsigned char char, i8, u8
short, int, long, long long i16, i32, i64, i64 (według faktycznego rozmiaru)
unsigned … u16, u32, u64
float, double f32, f64
_Bool / bool bool
enum odpowiadająca liczba całkowita ze znakiem
char *, const char * str
int *, double *, … ptr[i32], ptr[f64], …
void *, struct X *, FILE *, wskaźniki na funkcje ptr

Czyli int z C to i32 w Finchu. Liczby wpisane w kod dopasowują się same (abs(-5) działa), ale zmienną typu int z Fincha trzeba zamienić: abs(i32(x)).

Przykład

import "stdio.h"
import "math.h"
import "stdlib.h"

fn main() {
    printf("%d + %d = %d\n", 2, 3, 5)
    print(sqrt(2), M_PI, RAND_MAX)

    ptr[i32] n = malloc(4)       // poproś C o 4 bajty pamięci
    n.value = 7
    print(n.value)
    free(n)                      // i oddaj je

    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. examples/llvm.fn pokazuje, że Finch potrafi nawet sterować samym LLVM przez jego API w C.

Co jeszcze nie działa

  • Funkcje C, które przyjmują lub zwracają struct przez wartość, np. div(). Finch mówi o tym wprost, jeśli spróbujesz. Funkcje przyjmujące wskaźnik na strukturę działają.
  • Makra-funkcje (#define MAX(a,b) ...) i makra, które nie są pojedynczą liczbą.
  • long double, liczby 128-bitowe.
  • Odczytywanie pól struktury C.

12. Błędy

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
   12 |     wynik = "wysoki"
      |             ^

Format: plik:linijka:kolumna: error: wyjaśnienie, a pod spodem linijka ze znakiem ^ pod miejscem błędu. Komunikaty są po angielsku.

Błędy w trakcie działania

Niektóre problemy widać dopiero, gdy program działa. Zamiast niezdefiniowanego zachowania (jak w C) Finch zatrzymuje się z czytelnym komunikatem na wyjściu błędów i kodem wyjścia 1:

Komunikat Przyczyna
runtime error: division by zero / albo % przez zmienną równą 0
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

13. Rozwiązywanie problemów

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.
… 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 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.
no C compiler found to link with Zainstaluj gcc lub clang albo ustaw CC.
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.
unexpected character 'ż' Polska litera w nazwie. W nazwach używaj tylko a–z.
CMake: libclang not found Zainstaluj pakiet deweloperski libclang albo uruchom cmake w nix-shell.
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.

14. Struktura projektu i testy

Finch/
├── src/            kompilator (C++)
├── examples/       przykładowe programy (hello, tour, types, c_import, window, llvm)
├── tests/
│   ├── run/        programy + dokładny wynik, który muszą wypisać (.out)
│   ├── fail/       programy, które muszą się nie udać, z oczekiwanym błędem w 1. linijce
│   └── run.sh      skrypt uruchamiający testy
├── docs/           ta dokumentacja (en, pl)
├── shell.nix       środowisko deweloperskie Nix
└── CMakeLists.txt

Testy uruchomisz po zbudowaniu:

tests/run.sh             # → 34 passed, 0 failed

15. Obecne ograniczenia

Finch 1.0 to solidny rdzeń, a nie skończony język. Jeszcze nie ma:

  • struct, tablic, czytania z klawiatury (input()), sklejania tekstów ("a" + "b"). Na razie użyj do tego funkcji z C (scanf, snprintf, malloc…).
  • Automatycznego zarządzania pamięcią (w planach: zwalnianie na końcu bloku i defer).
  • Modułów Fincha (na razie jeden program = jeden plik .fn).
  • Platform innych niż Linux x86-64.

Plan rozwoju jest w README.