Structs, arrays, owned strings, memory model, modules, debug info

- struct with default values; constructors Point(1, 2) / Point(x: 1); field access, auto-deref through pointers
- []T arrays with bounds checks, literals, for-each, push/pop/insert/remove/clear/resize/find/contains/join/slice/reverse/sort
- str carries its length: .len, s[i], +, comparison, sub/find/split/trim/upper/lower/replace/repeat/bytes, str(x), int(s), float(s)
- Memory: values own their memory and are freed at the end of their block; assignment copies, temporaries move;
  parameters are lent and only copied when the function changes them; defer; new()/free() for manual heap memory
- input(), read_file/write_file/file_exists, exit(), fn main([]str args)
- Finch modules: import name, module.fn(), module.Struct
- C structs from headers (fields, fixed arrays, nested); passed and returned by value with the System V x86-64 ABI;
  addr(fn) gives C a callback
- link "file.c" compiles and links your own C code
- -g emits DWARF debug info (gdb/lldb)
- Small C runtime (runtime/finch_rt.c) embedded in the compiler, cached in ~/.cache/finch
- Tests: 58, all clean under valgrind (MEMCHECK=1 tests/run.sh)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-08 18:57:37 +02:00
commit 1dc976bd8f
59 changed files with 4577 additions and 1047 deletions

View file

@ -1,27 +1,45 @@
#!/usr/bin/env bash
# Finch test suite.
# tests/run/*.fn must compile and print exactly what is in the matching .out file
# (files without `fn main` are modules or helpers and are skipped;
# a matching .in file is fed to the program as its input)
# tests/fail/*.fn must fail; the first line is "// expect: <text that must appear in the error>"
# MEMCHECK=1 tests/run.sh also runs every program under valgrind: no leaks, no bad memory access
set -u
cd "$(dirname "$0")"
FINCH=${FINCH:-../build/finch}
pass=0; failed=0
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
for f in run/*.fn; do
grep -q "^fn main" "$f" || continue
want="${f%.fn}.out"
got=$("$FINCH" run "$f" 2>&1)
if [ "$got" == "$(cat "$want")" ]; then
pass=$((pass + 1))
else
input="${f%.fn}.in"
[ -f "$input" ] || input=/dev/null
got=$("$FINCH" run "$f" < "$input" 2>&1)
if [ "$got" != "$(cat "$want")" ]; then
failed=$((failed + 1))
echo "FAIL $f"
diff <(echo "$got") "$want" | head -20
continue
fi
if [ "${MEMCHECK:-0}" = 1 ]; then
exe="$tmp/$(basename "${f%.fn}")"
"$FINCH" build "$f" -o "$exe" >/dev/null 2>&1
if ! valgrind -q --leak-check=full --errors-for-leak-kinds=all --error-exitcode=99 "$exe" < "$input" >/dev/null 2>"$tmp/vg"; then
failed=$((failed + 1))
echo "MEMCHECK FAIL $f"
head -20 "$tmp/vg"
continue
fi
fi
pass=$((pass + 1))
done
for f in fail/*.fn; do
expect=$(head -1 "$f" | sed -n 's|^// expect: ||p')
got=$("$FINCH" run "$f" 2>&1)
got=$("$FINCH" run "$f" < /dev/null 2>&1)
status=$?
if [ $status -ne 0 ] && [[ "$got" == *"$expect"* ]]; then
pass=$((pass + 1))