Bootstrap: the Finch compiler written in Finch

boot/ is a second Finch compiler written in Finch (lexer, parser, type checker and
an LLVM IR text generator, ~3k lines). Built by the C++ compiler it compiles itself;
stage 2 and stage 3 produce byte-identical IR. It covers the core language: functions,
structs, arrays, str, pointers, loops and modules (no C imports, sized numbers or defer),
copies values like the main compiler and leaves freeing memory to the operating system.

tests/boot.sh checks the fixpoint and runs the test programs it supports.
Also: shell(cmd) built-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-08 19:07:49 +02:00
commit c7ddd4d8b6
8 changed files with 3153 additions and 1 deletions

View file

@ -8,7 +8,7 @@ namespace finch {
bool Codegen::isBuiltin(const std::string &n) {
static const std::set<std::string> names = {"print", "addr", "input", "new", "free", "read_file", "write_file",
"file_exists", "exit"};
"file_exists", "exit", "shell"};
FType t;
return names.count(n) || typeFromName(n, t);
}
@ -91,6 +91,7 @@ Function *Codegen::rt(const std::string &name) {
{"finch_file_exists", {I32, {P}}},
{"finch_read_file", {V, {P, P, P, I}}},
{"finch_write_file", {I32, {P, P}}},
{"finch_shell", {I, {P}}},
};
auto it = sigs.find(name);
if (it == sigs.end()) {
@ -229,6 +230,15 @@ Value_ Codegen::call(const CallExpr &c) {
release(path);
return res;
}
if (n == "shell") {
Value_ cmd = coerce(expr(one("command")), FType::Str, c.args[0]->pos, "the command");
Value *ss = tmpOf(ty(FType::Str));
b.CreateCall(rt("finch_str_copy"), {ss, tmp(cmd.v)}); // a heap copy is always NUL-terminated
Value *r = b.CreateCall(rt("finch_shell"), {ss});
dropAt(ss, FType::Str);
release(cmd);
return {r, FType::I64};
}
if (n == "write_file") {
checkArgs(c.args, c.argNames, 2, p, n);
Value_ path = coerce(expr(*c.args[0]), FType::Str, c.args[0]->pos, "the file name");