diff --git a/_selfhost/src/cli.bux b/_selfhost/src/cli.bux index 283364f..15e77c8 100644 --- a/_selfhost/src/cli.bux +++ b/_selfhost/src/cli.bux @@ -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; diff --git a/src/lexer.nim b/src/lexer.nim index 1c81619..17bf6cd 100644 --- a/src/lexer.nim +++ b/src/lexer.nim @@ -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() # ' diff --git a/src_bux/cli.bux b/src_bux/cli.bux index 283364f..15e77c8 100644 --- a/src_bux/cli.bux +++ b/src_bux/cli.bux @@ -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; diff --git a/stdlib/runtime.c b/stdlib/runtime.c index 258d743..d61fff8 100644 --- a/stdlib/runtime.c +++ b/stdlib/runtime.c @@ -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; diff --git a/tests/parser_test.nim b/tests/parser_test.nim index bd7e632..a2f5cea 100644 --- a/tests/parser_test.nim +++ b/tests/parser_test.nim @@ -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") diff --git a/tests/test_ast_fields b/tests/test_ast_fields index 18cdeb4..76fbe63 100755 Binary files a/tests/test_ast_fields and b/tests/test_ast_fields differ diff --git a/tests/test_parse_ast b/tests/test_parse_ast index b23f7a5..850868c 100755 Binary files a/tests/test_parse_ast and b/tests/test_parse_ast differ