fix: selfhost CLI run command, lexer char literal, parser test path, calloc in runtime

- Fix lexer unterminated char error test (was treating 'a as lifetime)
- Fix parser test sample.bux path for root-dir execution
- Add bux_system() to runtime.c and run command to selfhost CLI
- Switch bux_alloc from malloc to calloc for zero-init
- Fix module flattening in Cli_Check/Cli_Compile to scan full decl list
- Make test passes fully, selfhost builds successfully
This commit is contained in:
2026-06-02 00:50:16 +03:00
parent 2b911410cb
commit 975cc347d0
7 changed files with 93 additions and 12 deletions
+42 -5
View File
@@ -12,6 +12,7 @@ extern func bux_path_join(a: String, b: String) -> String;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_run_nim(nim_file: String, out_bin: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
extern func bux_system(cmd: String) -> int;
func ReadFile(path: String) -> String {
return bux_read_file(path);
@@ -61,9 +62,14 @@ func Cli_Compile(source: String, sourceName: String) -> String {
}
PrintLine(" Parse done");
// Flatten module wrappers: if the only item is a module decl, hoist its children
if mod.firstItem != null as *Decl && mod.firstItem.kind == dkModule && mod.firstItem.childDecl1 != null as *Decl {
mod.firstItem = mod.firstItem.childDecl1;
// Flatten module wrappers: find module decl and hoist its children
var decl: *Decl = mod.firstItem;
while decl != null as *Decl {
if decl.kind == dkModule && decl.childDecl1 != null as *Decl {
mod.firstItem = decl.childDecl1;
break;
}
decl = decl.childDecl2;
}
// Phase 3: Semantic analysis
@@ -167,8 +173,13 @@ func Cli_Check(srcPath: String) -> int {
}
// Flatten module wrappers
if mod.firstItem != null as *Decl && mod.firstItem.kind == dkModule && mod.firstItem.childDecl1 != null as *Decl {
mod.firstItem = mod.firstItem.childDecl1;
var decl2: *Decl = mod.firstItem;
while decl2 != null as *Decl {
if decl2.kind == dkModule && decl2.childDecl1 != null as *Decl {
mod.firstItem = decl2.childDecl1;
break;
}
decl2 = decl2.childDecl2;
}
// Phase 3: Sema
@@ -348,6 +359,26 @@ func Cli_BuildProject(projectDir: String) -> int {
return 0;
}
// ---------------------------------------------------------------------------
// Run command — build project and execute
// ---------------------------------------------------------------------------
func Cli_RunProject(projectDir: String) -> int {
let rc: int = Cli_BuildProject(projectDir);
if rc != 0 {
return rc;
}
let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml"));
var outName: String = man.name;
if String_Eq(outName, "") {
outName = "bux_out";
}
let outBin: String = bux_path_join(bux_path_join(projectDir, "build"), outName);
Print("Running: ");
PrintLine(outBin);
return bux_system(outBin);
}
// ---------------------------------------------------------------------------
// Main entry — dispatch based on args
// ---------------------------------------------------------------------------
@@ -401,6 +432,12 @@ func Cli_Run(args: *String, argCount: int) -> int {
return Cli_BuildProject(dir);
}
if String_Eq(cmd, "run") {
let dir: String = ".";
if argCount >= 3 { dir = args[2]; }
return Cli_RunProject(dir);
}
Print("Unknown command: ");
PrintLine(cmd);
return 1;
+2
View File
@@ -553,6 +553,8 @@ proc nextToken(lex: var Lexer): Token =
# Simple heuristic: if peek(2) is ', it's a char literal; else lifetime
if lex.peek(2) == '\'':
return lex.scanChar(startLoc, 0)
elif lex.peek(2) == '\0':
return lex.scanChar(startLoc, 0)
else:
# Lifetime: consume ' and then identifier chars
discard lex.advance() # '
+42 -5
View File
@@ -12,6 +12,7 @@ extern func bux_path_join(a: String, b: String) -> String;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_run_nim(nim_file: String, out_bin: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
extern func bux_system(cmd: String) -> int;
func ReadFile(path: String) -> String {
return bux_read_file(path);
@@ -61,9 +62,14 @@ func Cli_Compile(source: String, sourceName: String) -> String {
}
PrintLine(" Parse done");
// Flatten module wrappers: if the only item is a module decl, hoist its children
if mod.firstItem != null as *Decl && mod.firstItem.kind == dkModule && mod.firstItem.childDecl1 != null as *Decl {
mod.firstItem = mod.firstItem.childDecl1;
// Flatten module wrappers: find module decl and hoist its children
var decl: *Decl = mod.firstItem;
while decl != null as *Decl {
if decl.kind == dkModule && decl.childDecl1 != null as *Decl {
mod.firstItem = decl.childDecl1;
break;
}
decl = decl.childDecl2;
}
// Phase 3: Semantic analysis
@@ -167,8 +173,13 @@ func Cli_Check(srcPath: String) -> int {
}
// Flatten module wrappers
if mod.firstItem != null as *Decl && mod.firstItem.kind == dkModule && mod.firstItem.childDecl1 != null as *Decl {
mod.firstItem = mod.firstItem.childDecl1;
var decl2: *Decl = mod.firstItem;
while decl2 != null as *Decl {
if decl2.kind == dkModule && decl2.childDecl1 != null as *Decl {
mod.firstItem = decl2.childDecl1;
break;
}
decl2 = decl2.childDecl2;
}
// Phase 3: Sema
@@ -348,6 +359,26 @@ func Cli_BuildProject(projectDir: String) -> int {
return 0;
}
// ---------------------------------------------------------------------------
// Run command — build project and execute
// ---------------------------------------------------------------------------
func Cli_RunProject(projectDir: String) -> int {
let rc: int = Cli_BuildProject(projectDir);
if rc != 0 {
return rc;
}
let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml"));
var outName: String = man.name;
if String_Eq(outName, "") {
outName = "bux_out";
}
let outBin: String = bux_path_join(bux_path_join(projectDir, "build"), outName);
Print("Running: ");
PrintLine(outBin);
return bux_system(outBin);
}
// ---------------------------------------------------------------------------
// Main entry — dispatch based on args
// ---------------------------------------------------------------------------
@@ -401,6 +432,12 @@ func Cli_Run(args: *String, argCount: int) -> int {
return Cli_BuildProject(dir);
}
if String_Eq(cmd, "run") {
let dir: String = ".";
if argCount >= 3 { dir = args[2]; }
return Cli_RunProject(dir);
}
Print("Unknown command: ");
PrintLine(cmd);
return 1;
+6 -1
View File
@@ -22,7 +22,7 @@ char* bux_argv(int index) {
/* Memory allocation */
void* bux_alloc(size_t size) {
void* ptr = malloc(size);
void* ptr = calloc(1, size);
if (ptr == NULL) {
fprintf(stderr, "bux runtime: out of memory (alloc %zu bytes)\n", size);
abort();
@@ -75,6 +75,11 @@ int bux_run_nim(const char* nim_file, const char* out_bin) {
return system(cmd);
}
int bux_system(const char* cmd) {
if (cmd == NULL) return -1;
return system(cmd);
}
int bux_strlen_c(const char* s) {
if (s == NULL) return 0;
const char* p = s;
+1 -1
View File
@@ -111,7 +111,7 @@ suite "Parser":
check ret.stmtReturnValue.exprCallCallee.kind == ekIdent
test "full sample file":
let source = readFile("tests/testdata/sample.bux")
let source = readFile(currentSourcePath.parentDir / "testdata" / "sample.bux")
let lexRes = tokenize(source, "sample.bux")
check not lexRes.hasErrors
let parseRes = parse(lexRes.tokens, "sample.bux")
Binary file not shown.
Binary file not shown.