Commit graph Finch/boot/gen.fch
Author SHA1 Message Date
Nexoniarz
8c3b5b15b9 -g: debug info, in the compiler written in Finch
DWARF as LLVM metadata: a compile unit, a subprogram per function, a location on every call, a variable
for each local and parameter with types a debugger can open (structs with their fields, arrays and text
as ptr/len/cap, maps, enums with their variants, pointers, fixed arrays). Methods show self.

Use it with -O0, as the docs say: the optimizer inlines the program into main, which has no debug info.
boot.sh builds ten programs with -g -O0 and checks they still behave. 51 passed.
2026-10-25 21:08:13 +01:00
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
Nexoniarz
ea6d2a57e5 a standard library in Finch: path, time, proc, json
import json   import path   import time   import proc  -- found in std/ after the program's own files,
so a program's own json.fch always wins.

path: join, base, dir, ext, stem, parts, clean, is_abs.
time: now, millis, sleep, date, local, format, days_in_month (clock_gettime, strftime through C import).
proc: run, output (an error with the command's output when it fails), env, pid, cwd.
json: parse (errors with line and column), stringify, pretty, new_*, get/at/set/push/has/names and
str_or/int_or/float_or/bool_or; \u escapes with surrogate pairs.

The C importer now leaves a plain (non-const) char * parameter a ptr[char], a buffer C writes into;
only const char * is text going in. str(ptr) and str([]u8) are in the self-hosted compiler.

boot.sh: 40 passed, 0 failed.
2026-10-25 20:58:26 +01:00
Nexoniarz
04eb0cce55 generics (in the compiler written in Finch)
fn max[T](T a, T b) -> T      struct Stack[T] { []T items }      fn Stack[T].push(T x)

The type is worked out from the arguments (max(3, 4), first([1, 2])) and written out
where it cannot be: max[float](1, 2.5), Stack[int](), Pair[str, int](...). Each use makes
its own copy, so a generic costs nothing at run time. Several parameters
(Pair[A, B]), nesting (Stack[Pair[int, str]]), methods, and printing (Stack[int](items: ...))
work. An error inside a generic says which version it was building.

boot.sh: 37 passed, 0 failed (stage 2 and 3 identical).
2026-10-25 20:53:13 +01:00
Nexoniarz
609ceffe58 enums and match (in the compiler written in Finch)
enum Color { Red, Green, Blue }   enum Status { Ok = 200, NotFound = 404 }

Color.Red, Color(n) (checked), int(c), == and != only, printed by name, usable as
map keys and struct fields, with methods (fn Color.next()).

match value { Red, Green -> ... Blue -> ... else -> ... } as a statement, or as an
expression that gives a value. Works on enums, whole numbers, chars, text and bools;
a match on an enum has to handle every variant or end with else, and says which are
missing. A bare variant name (Red) is enough inside a match on an enum.

Only the self-hosted compiler has them; tests/new holds their tests, tests/run stays what
the C++ compiler also runs. boot.sh: 31 passed.
2026-10-25 20:49:04 +01:00
Nexoniarz
04d5486477 boot: structs by value (System V), addr(fn) callbacks -- the compiler builds itself
C structs travel as C passes them: up to 16 bytes in INTEGER/SSE registers per eightbyte,
bigger ones byval and through sret, out-of-registers structs on the stack. addr(fn) makes a
C-callable thunk that rebuilds the arguments. With those, the self-hosted compiler can
compile boot/cimport.fch, which calls libclang with CXCursor and CXString by value and gives
it callbacks -- so it compiles itself.

boot.sh: stage 2 and stage 3 are identical (57904 lines of IR); 22 passed, 0 failed,
0 skipped. Only x86-64 System V so far; Windows and ARM64 conventions are still in C++.
2026-10-25 19:47:20 +01:00
Nexoniarz
13ef63a586 boot: C import (import "x.h", link, C functions, constants, globals, .ptr, ptr(x))
The self-hosted compiler reads headers with boot/cimport.fch and compiles calls to C
functions (variadics, char* as str, C's default promotions), enum and #define
constants, extern variables like stdout, C structs laid out as C laid them (padding
and fixed arrays, by pointer), .ptr of a str or array, ptr(x) and int(ptr). link
"lib" asks pkg-config, link "x.c" compiles the file along.

cimport and c_pointers pass. Not yet: structs by value (the calling convention) and
addr(fn) for C callbacks, which is also what stops it building itself.

boot.sh now says so when stage 2 can't be made and runs the tests on stage 1.
2026-10-25 19:44:58 +01:00
Nexoniarz
083d66c82b boot: maps, for k, v in, for i, x in
map[K]V, [:] and ["a": 1] literals, m[k] reads and writes (m[k] += 1 starts from
zero), has/get/remove/clear/keys/values, .len, copy and printing, and a loop over
a map that stops with an error when it adds keys. for i, x in list gives the
index too.

boot.sh: 17 passed, 0 failed, 5 skipped (all five import C).
2026-10-25 19:13:46 +01:00
Nexoniarz
a5f417100d boot: errors as values (-> T!, error(), try, or, fallible builtins and main)
A function that can fail returns { ok, value, message }. return error(...) makes
the failure; try passes it on after running the defers; 'or <value>' and
'or { ... err ... }' handle it. int(text), float(text), read_file, write_file
and delete_file report failure to an enclosing or/try instead of stopping the
program. fn main() -> ! prints the error and exits with 1.

boot.sh: 15 passed, 0 failed, 7 skipped.
2026-10-25 19:13:46 +01:00
Nexoniarz
84693aa655 boot: defer, str.bytes()
defer runs when its scope ends and before return, break and continue leave it,
newest first, and sees only the variables declared before it. Not allowed
inside a defer: defer, return, break, continue.

boot.sh: 14 passed, 0 failed, 8 skipped.
2026-10-25 19:13:46 +01:00
Nexoniarz
051af1a04e boot: methods (fn T.m, self by address, mutation analysis), str() of any value
Methods are functions whose first parameter is the receiver's address; a method
that changes self can't be called on a temporary or a read-only value, and
calling one on a variable keeps a parameter from being borrowed. An argument
that lives inside the receiver is copied (c.merge(c)). str(x) and print of
arrays and structs go through one formatter, as in the C++ compiler.

boot.sh: 12 passed, 0 failed, 10 skipped.
2026-10-25 19:13:46 +01:00
Nexoniarz
873b52565d boot: sized numbers (i8..u64, f32), conversions, unsigned arithmetic
The self-hosted compiler now has the numeric types the C++ one has: int and
float stay the i64 and f64 defaults, the others lower to their LLVM sizes;
literals adapt to the other operand if they fit, smaller types widen into
bigger ones, signed and unsigned mixing is refused, division and shifts and
comparisons pick their signedness, and u8(x), f32(x), bool(x) convert.

boot.sh: 11 passed, 0 failed, 10 skipped (was 9 / 12).
2026-10-25 19:13:46 +01:00
Nexoniarz
84cbbb3e16 Windows support (wip): target layer, Microsoft x64 ABI, portable runtime, CI
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:46:51 +02:00
Nexoniarz
f897fb3140 Rename the Finch file extension from .fn to .fch
.fn is already used by other tools (e.g. Fennel). Modules are now looked up as name.fch;
a leftover name.fn gets a clear message telling you to rename it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:27:42 +02:00
Renamed from boot/gen.fn (Browse further)