Finch/docs/en/cheatsheet.md
Nexoniarz dd9cbe7611 export fn and finch build --shared, in the compiler written in Finch; FINCH_PATH, FINCH_RT, FINCH_CC
export fn name(...) is a C function of a library; build --shared makes lib<name>.so with no main and a
finch_module_on_fatal(handler) the host can take runtime errors over with. Ported from the embed work (the
runtime, its test and the docs are that work's; the compiler side is new).

The driver reads FINCH_PATH (more module folders), FINCH_RT (the runtime's file) and FINCH_CC (the clang).
tests/boot.sh also runs tests/shared.sh with the compiler under test. 41 passed.
2026-10-25 21:03:05 +01:00

405 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Finch Cheat Sheet
Everything in Finch on one page. For explanations, see the [guides](../../README.md#documentation).
> 🇵🇱 Polska wersja: [sciaga.md](../pl/sciaga.md)
---
## The `finch` command
| Command | Does |
|---|---|
| `finch run file.fch [args…]` | compile and run (args go to the program) |
| `finch build file.fch [-o name]` | make a program (`name.exe` on Windows) |
| `finch build file.fch --shared [-I dir]` | make a library (`libfile.so`, `file.dll`) that a C program loads; its `export fn`s are its C functions (numbers, bool, char, pointers) |
| `finch ir file.fch` | print the LLVM IR |
| `finch lsp` | the language server (editors use it) |
| `finch version` | version |
| `-l lib` | link a C library (like `link "lib"`) |
| `--target windows` / `linux` / `arm64` / `macos` / triple | build for another system |
| `-g` | debug info (gdb, lldb, Visual Studio) |
| `-O0` | no optimizations |
Environment: `FINCH_PATH` (folders with modules), `FINCH_CC` / `CC` (the C compiler that links).
---
## A file
```c
import "stdio.h" // a C header
import shapes // a Finch module: shapes.fch
link "m" // a C library, or "mine.c" / ".o" / ".a"
struct Point {
int x
int y = 0 // default value
}
fn main() {
print("Hello")
}
```
`main` can be `fn main()`, `fn main() -> int` (exit code) or `fn main([]str args)`.
One statement per line, no semicolons, `{` on the same line, no brackets around conditions.
---
## Variables
| Code | Meaning |
|---|---|
| `x := 5` | new variable, type inferred |
| `int x = 5` | new variable, type written |
| `int x` | zero / `""` / `[]` / `null` / the struct's defaults |
| `x = 7` | assign |
| `x += 1` `-=` `*=` `/=` `%=` | update |
---
## Types
| Type | Example |
|---|---|
| `int` (= `i64`) | `42`, `-7`, `0xFF`, `1_000_000` |
| `float` (= `f64`) | `3.14` |
| `bool` | `true`, `false` |
| `char` | `'A'`, `'\n'` |
| `str` | `"text\t\"quoted\""` |
| `i8 i16 i32 i64` | signed, sized |
| `u8 u16 u32 u64` | unsigned, sized |
| `f32 f64` | floats |
| `[]T` | array: `[1, 2, 3]`, `[]str names` |
| `map[K]V` | map: `["a": 1]`, `map[str]int counts`, `[:]` |
| `ptr[T]`, `ptr` | pointer, untyped pointer |
| `Name` / `module.Name` | a struct |
Escapes: `\n \t \r \0 \\ \" \'`.
---
## Operators
| Priority | Operators |
|---|---|
| 5 | `*` `/` `%` `<<` `>>` `&` |
| 4 | `+` `-` `\|` `^` (`+` also joins text) |
| 3 | `==` `!=` `<` `<=` `>` `>=` |
| 2 | `&&` |
| 1 | `\|\|` |
| unary | `-x` `!x` `~x` |
`x & 1 == 0` means `(x & 1) == 0`.
---
## Control flow
```c
if a > b { … } else if a == b { … } else { … }
while x > 0 { … }
for i in 0..10 { … } // 0 … 9
for item in list { … } // every element (or char of a str)
for i, item in list { … } // with its index
for key, value in m { … } // every map entry, in insertion order
break continue return value
defer cleanup() // runs when the block ends
```
---
## Functions
```c
fn add(int a, int b) -> int {
return a + b
}
fn greet(str name) { // no result
print("Hi", name)
}
```
Parameters behave like copies. Order in the file doesn't matter. A comma after the last argument is allowed.
---
## Methods
```c
fn Point.move(int dx, int dy) { // self is the Point it's called on (not a copy)
self.x += dx
self.y += dy
}
fn Point.dist() -> float { … }
p.move(1, 2) list[0].move(1, 1) ptr_to_point.move(0, 1)
```
Also on structs from C: `fn Vector2.length() -> f32 { … }`.
---
## Errors as values
```c
fn parse(str s) -> int! { // ! = can fail
if s.len == 0 {
return error("empty")
}
return int(s) or { return error("not a number: " + s) }
}
fn save(str p, str t) -> ! { // can fail, no result
try write_file(p, t)
}
```
| Code | On failure |
|---|---|
| `x := parse(s) or 0` | use 0 |
| `x := parse(s) or { print(err); return }` | run the block (`err`: the message) |
| `x := try parse(s)` | fail the current function too |
| `fn main() -> !` | a failure prints `error: …`, exit code 1 |
Fallible built-ins: `int(s)`, `float(s)`, `read_file`, `write_file`, `delete_file`.
---
## Structs
| Code | Meaning |
|---|---|
| `struct P { … }` | define (one field per line, `= default` optional) |
| `P(1, 2)` | create, all fields in order |
| `P(x: 1)` | create by name, the rest default/zero |
| `p.x`, `p.x = 5` | read, change a field |
| `b := a` | a full copy |
---
## Arrays `[]T`
| Code | Gives |
|---|---|
| `a.len` | number of elements |
| `a[i]`, `a[i] = x` | element (checked) |
| `a.push(x)` | add at the end |
| `a.pop()` | remove the last, give it back |
| `a.insert(i, x)` | put at position i |
| `a.remove(i)` | remove position i, give it back |
| `a.clear()` | empty it |
| `a.resize(n)` | length n (new elements are zero) |
| `a.find(x)` | index or -1 |
| `a.contains(x)` | bool |
| `a.sort()` | sort (numbers, chars, str) |
| `a.reverse()` | reverse |
| `a.slice(s, e)` | new array of s … e-1 |
| `a.join(sep)` | `[]str` → one str |
| `a.ptr` | address of the first element (for C) |
---
## Maps `map[K]V`
| Code | Gives |
|---|---|
| `m := ["a": 1, "b": 2]` | a map (keys: numbers, char, bool, str) |
| `m[k]` | value (missing key: the program stops) |
| `m[k] = v` | add / replace |
| `m[k] += 1`, `m[k].push(x)` | a missing key starts at the default (0, `[]`, …) |
| `m.len` | number of keys |
| `m.has(k)` | bool |
| `m.get(k, default)` | value or default |
| `m.remove(k)` | bool: was it there |
| `m.clear()` | empty it |
| `m.keys()`, `m.values()` | arrays, insertion order |
---
## Text `str`
| Code | Gives |
|---|---|
| `s.len` | bytes |
| `s[i]`, `s[i] = 'x'` | char |
| `a + b` | joined text |
| `s.sub(a, b)` | part a … b-1 |
| `s.find(t)` | position or -1 |
| `s.contains(t)` | bool |
| `s.starts_with(t)`, `s.ends_with(t)` | bool |
| `s.split(sep)` | `[]str` |
| `s.trim()` | no spaces at the ends |
| `s.upper()`, `s.lower()` | A–Z / a–z |
| `s.replace(a, b)` | every a → b |
| `s.repeat(n)` | n times |
| `s.bytes()` | `[]u8` |
| `s.ptr` | `ptr[char]` (for C) |
| `==` `!=` `<` `>` | compare |
---
## Conversions
| Code | Result |
|---|---|
| `int(3.9)` | `3` |
| `int("42")` | `42` (stops the program if it isn't a number) |
| `int('A')` | `65` |
| `float(7)`, `float("2.5")` | `7`, `2.5` |
| `u8(300)` | `44` (keeps the low bits) |
| `char(66)` | `'B'` |
| `bool(0)` | `false` |
| `str(42)`, `str(1.5)`, `str(true)`, `str('c')` | text |
| `str([1, 2])`, `str(m)`, `str(point)` | the same text `print` shows |
| `int("x") or 0`, `float(s) or 0.0` | a number, or the fallback |
| `str(bytes)` | `[]u8` → text |
| `str(p)` | C `char*` → text |
| `ptr(p)`, `ptr(16)` | untyped pointer, from a pointer or a number |
| `int(p)` | pointer → address |
Automatic only when nothing is lost: smaller → bigger number, any int → float, `f32` → `f64`.
---
## Built-in functions
| Function | Does |
|---|---|
| `print(a, b, …)` | print, spaces between, newline at the end |
| `input()`, `input("Question? ")` | read a line |
| `read_file(path)` | file → str |
| `write_file(path, text)` | str → file, `true` if it worked |
| `file_exists(path)` | bool |
| `delete_file(path)` | bool |
| `shell(command)` | run a command, its exit code |
| `exit(code)` | stop the program now |
| `error(message)` | `return error("…")` in a `-> T!` function |
| `addr(x)` | pointer to x; `addr(fn)` = C callback |
| `new(value)` | value on the heap → `ptr[T]` |
| `free(p)` | give it back |
---
## Pointers
| Finch | C |
|---|---|
| `ptr[int] p` | `int *p` |
| `addr(x)` | `&x` |
| `p.value` | `*p` |
| `p.field` | `p->field` |
| `p[i]` | `p[i]` |
| `null` | `NULL` |
| `ptr` | `void *` |
---
## Memory
- Arrays, text and structs free themselves at the end of their block. No `free` needed.
- `b := a` copies; a call's result moves; `return x` moves.
- `new(...)` + `free(...)` (often with `defer`) for linked lists, trees, sharing.
- C memory: C's own functions (`malloc` / `free`, `fclose`, …).
---
## Modules
```c
import shapes // shapes.fch next to this file (or in FINCH_PATH)
shapes.area(b) // its functions
shapes.Box(2, 3) // its structs
shapes.Box other // in declarations
```
---
## C
| Code | Meaning |
|---|---|
| `import "x.h"` | functions, structs, enums, `#define` numbers, globals |
| `link "name"` | libname.so / name.lib (pkg-config first) |
| `link "file.c"` | compile your own C file |
| `addr(myFn)` | pass a Finch function as a callback |
| C type | Finch |
|---|---|
| `int` / `long long` / `unsigned` | `i32` / `i64` / `u32` (by size) |
| `float` / `double` | `f32` / `f64` |
| `char *` (parameter, result) | `str` |
| `char *` (field, behind a pointer) | `ptr[char]` |
| `struct X` | `X` |
| `T *` | `ptr[T]` |
| `void *`, handles | `ptr` |
| `T x[N]` (in a struct) | `[N]T` |
---
## Runtime errors (the program stops with file and line)
| Message | Cause |
|---|---|
| `division by zero` | `/` or `%` by 0 |
| `index 5 is out of range (the length is 3)` | `a[5]` |
| `pop() on an empty array` | |
| `used .value on a null pointer` | `.value` / `.field` / `[i]` through `null` |
| `can't turn "x" into int` | `int("x")` |
| `can't read the file "…"` | `read_file` (without `or`) |
| `the key "x" is not in the map` | `m["x"]` |
---
## Editors
Kate: `editors/kate/install.sh`, then enable the **LSP Client** plugin; run with **Build & Run** → `finch run %f`.
Any LSP editor: `finch lsp`.
### VS Code (extension "Finch")
| Key / action | Does |
|---|---|
| ▶ / `Ctrl+F5` | run the file |
| `Finch: Build This File` | build; errors go to Problems |
| `F12` | go to definition |
| hover | type / signature / docs |
| `Ctrl+Space` | completion (after `.`: fields, methods, module members) |
| `Ctrl+Shift+O` | functions and structs in the file |
| `main`, `fn`, `fnr`, `fnf`, `method`, `struct`, `for`, `foreach`, `forkv`, `map`, `orb`, `if`, `ife`, `while`, `input` + Tab | snippets |
| setting `finch.path` | where finch / finch.exe is |
---
## Enums and `match`
```
enum Color { Red, Green, Blue } enum Status { Ok = 200, NotFound = 404 }
c := Color.Green int(c) Color(2) c == Color.Red // == and != only; Color(7) is an error
fn Color.next() -> Color { ... } // methods work
match c { Red -> ... Green, Blue -> { ... } else -> ... } // bare variant names; every variant or else
x := match n { 1, 2 -> "few" else -> "many" } // an expression: every arm the same type
```
## Generics
```
fn max[T](T a, T b) -> T { ... } max(3, 9) max[float](1, 2.5) // worked out, or written out
struct Stack[T] { []T items } fn Stack[T].push(T x) { ... } // the type goes with it:
s := Stack[int]() Stack[int] t Pair[str, int]("age", 31) // never just Stack()
```
## Standard library (`import name`; your own file of that name wins)
| | |
|---|---|
| `path` | `join(a, b)` `base(p)` `dir(p)` `ext(p)` `stem(p)` `parts(p)` `clean(p)` `is_abs(p)` |
| `time` | `now()` `millis()` `sleep(ms)` `date(t)` `local(t)` `format(t, fmt)` `days_in_month(y, m)` |
| `proc` | `run(cmd)` `output(cmd)` (fails with its output) `env(name, fallback)` `pid()` `cwd()` |
| `json` | `parse(text)` `stringify(v)` `pretty(v)` `new_text/number/bool/null/list/object` `v.get(k)` `v.at(i)` `v.set(k, x)` `v.push(x)` `v.has(k)` `v.str_or(d)` `v.int_or(d)` `v.float_or(d)` `v.bool_or(d)` |
## Keywords
`fn return if else while for in break continue true false null import link struct enum match defer or try`
(and `self` inside methods)