Finch/boot/main.fch
Nexoniarz 8497251dc5 OpenGL and Vulkan examples; ptr(int)/int(ptr); trailing commas in calls; drop .fn hint
- examples/opengl.fch: OpenGL 3.3 core, shaders, VAO/VBO, a spinning triangle (checked offscreen)
- examples/vulkan.fch: instance, GPUs with properties/memory/queues, logical device and queue
- ptr(n) and int(p) conversions for C APIs that pass offsets as pointers
- .ptr works on string literals; a comma after the last call argument is allowed
- a missing header included by an imported header is now named in the error
- the old .fn extension hint is gone (Finch is new, there are no old files)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:37:34 +02:00

116 lines
2.8 KiB
Text

// finch-boot: the Finch compiler written in Finch.
//
// finch-boot build <file.fch> [-o name] compile a program
// finch-boot ir <file.fch> print the LLVM IR
//
// It reads the program and the modules it imports, checks it, writes LLVM IR as text
// and lets clang turn that into a program, linked with Finch's C runtime.
import lexer
import ast
import parser
import gen
fn dirOf(str path) -> str {
i := path.len - 1
while i >= 0 && path[i] != '/' {
i -= 1
}
if i < 0 {
return "."
}
return path.sub(0, i)
}
fn stem(str path) -> str {
name := path.sub(dirOf(path).len + 1, path.len)
if dirOf(path) == "." && !path.starts_with("./") {
name = path
}
dot := name.find(".")
if dot > 0 {
return name.sub(0, dot)
}
return name
}
struct Loader {
[]ast.Program progs
[]str files
[]str loaded
}
fn load(ptr[Loader] l, str path, str module) {
if !file_exists(path) {
if module.len > 0 {
print("error: can't find the module '" + module + "' (looked for " + path + ")")
} else {
print("error: can't open '" + path + "'")
}
exit(1)
}
src := read_file(path)
file := l.files.len
l.files.push(path)
prog := parser.parse(lexer.lex(src, path), file, path)
prog.module = module
prog.path = path
imports := prog.imports
l.progs.push(prog)
for name in imports {
if !l.loaded.contains(name) {
l.loaded.push(name)
load(l, dirOf(path) + "/" + name + ".fch", name)
}
}
}
fn usage() {
print("finch-boot - the Finch compiler written in Finch")
print("usage: finch-boot build <file.fch> [-o name]")
print(" finch-boot ir <file.fch>")
exit(1)
}
fn main([]str args) -> int {
if args.len < 3 {
usage()
}
cmd := args[1]
file := args[2]
out := stem(file)
if args.len == 5 && args[3] == "-o" {
out = args[4]
} else if args.len != 3 {
usage()
}
if cmd != "build" && cmd != "ir" {
usage()
}
loader := Loader()
load(addr(loader), file, "")
ir := gen.generate(loader.progs, loader.files)
if cmd == "ir" {
print(ir)
return 0
}
// the runtime is Finch's C file; FINCH_RT can point somewhere else
rt := dirOf(args[0]) + "/../runtime/finch_rt.c"
if file_exists("runtime/finch_rt.c") {
rt = "runtime/finch_rt.c"
}
if !file_exists(rt) {
print("error: can't find the Finch runtime (runtime/finch_rt.c)")
return 1
}
llPath := out + ".ll"
write_file(llPath, ir)
code := shell("clang -O2 -w '" + llPath + "' '" + rt + "' -o '" + out + "' -lm")
shell("rm -f '" + llPath + "'")
if code != 0 {
print("error: clang failed")
return 1
}
return 0
}