extern variables and object-like macro consts

This commit is contained in:
2026-06-17 21:52:05 +02:00
parent f5605fd3ec
commit 19e9fbdd4b
33 changed files with 3136 additions and 116 deletions
+61 -1
View File
@@ -15,6 +15,18 @@ import "core:fmt"
import vmem "core:mem/virtual"
import "core:os"
import "core:os/os2"
import "core:strings"
// write_escaped_c_string writes value into builder with `\` and `"` escaped so
// it is safe to embed inside a C string literal (e.g. an `#include "..."`).
write_escaped_c_string :: proc(builder: ^strings.Builder, value: string) {
for b in transmute([]byte)value {
if b == '\\' || b == '"' {
strings.write_byte(builder, '\\')
}
strings.write_byte(builder, b)
}
}
compile_package :: proc(
input_path, output_path: string,
@@ -69,6 +81,54 @@ compile_package :: proc(
fmt.eprintln("failed to load root package directory:", input_path)
return 2
}
// Generated C trampolines (for `static inline` imports) must be compiled and
// linked with the program. Write them out and add the source as a link input
// before the loader's arena (which owns the trampoline strings) is freed.
trampoline_path := ""
effective_link_arguments := link_arguments
owns_arguments := false
// Register the path defer first so it runs last (LIFO): the augmented
// argument slice, whose Input value aliases trampoline_path, is freed before
// the path string it points at.
defer if len(trampoline_path) > 0 {
_ = os.remove(trampoline_path)
delete(trampoline_path)
}
defer if owns_arguments {
delete(effective_link_arguments)
}
if len(ast_module.c_trampolines) > 0 {
builder := strings.builder_make()
defer strings.builder_destroy(&builder)
seen_headers: map[string]bool
defer delete(seen_headers)
for trampoline in ast_module.c_trampolines {
if seen_headers[trampoline.header] {
continue
}
seen_headers[trampoline.header] = true
strings.write_string(&builder, "#include \"")
write_escaped_c_string(&builder, trampoline.header)
strings.write_string(&builder, "\"\n")
}
for trampoline in ast_module.c_trampolines {
strings.write_string(&builder, trampoline.source)
}
// Owned (stable allocator); freed in the path defer above after backend
// compilation, which is the only consumer, has run.
trampoline_path = fmt.aprintf("%s.brolang-trampolines-%d.c", output_path, os2.get_pid())
if err := os.write_entire_file_or_err(trampoline_path, transmute([]byte)strings.to_string(builder)); err != nil {
fmt.eprintln("failed to write C trampoline source:", err)
return 2
}
augmented := make([]linker.Argument, len(link_arguments)+1)
copy(augmented, link_arguments)
augmented[len(link_arguments)] = linker.Argument{kind=.Input, value=trampoline_path}
effective_link_arguments = augmented
owns_arguments = true
}
vmem.arena_free_all(&lexer_arena)
hir_module := checker.check(&ast_module, &diagnostics, &symbols, selected, vmem.arena_allocator(&checker_arena))
vmem.arena_free_all(&parser_arena)
@@ -87,7 +147,7 @@ compile_package :: proc(
}
source.print_all(&diagnostics)
if !backend.compile(llvm_path, output_path, link_arguments, selected, c_options) {
if !backend.compile(llvm_path, output_path, effective_link_arguments, selected, c_options) {
return 2
}
if len(diagnostics.items) > 0 {