Phase 7.9: multi-file build, module braces, struct init multi-line, else-if fix

This commit is contained in:
2026-05-31 14:25:57 +03:00
parent f4de065160
commit de6e89e3df
33 changed files with 3870 additions and 326 deletions
+72 -33
View File
@@ -141,6 +141,8 @@ Output = "Bin"
printInfo(&"Initialized Bux package '{name}'", useColor)
return 0
proc collectStdlibDecls(stdlibDir: string): seq[Decl]
proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
let useColor = shouldUseColor(opts)
let root = getCurrentDir()
@@ -154,6 +156,8 @@ proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
printError("no src/ directory found", useColor)
return 1
# Phase 1: Parse all .bux files and collect declarations
var allModuleItems: seq[Decl] = @[]
var foundMain = false
for kind, path in walkDir(srcDir):
if kind == pcFile and path.endsWith(".bux"):
@@ -170,15 +174,15 @@ proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
for d in parseRes.diagnostics:
echo &"error: {d.message} at {d.loc}"
return 1
let semaRes = analyze(parseRes.module)
if semaRes.hasErrors:
printError(&"type errors in {path}", useColor)
for d in semaRes.diagnostics:
let sev = if d.severity == sdsError: "error" else: "warning"
echo &"{sev}: {d.message} at {d.loc}"
return 1
if opts.verbose:
printInfo(&"checked {path} ({lexRes.tokens.len} tokens, {parseRes.module.items.len} decls)", useColor)
# Flatten declarations from module wrappers
for decl in parseRes.module.items:
if decl.kind == dkModule:
for sub in decl.declModuleItems:
allModuleItems.add(sub)
else:
allModuleItems.add(decl)
if splitFile(path).name == "Main":
foundMain = true
@@ -186,6 +190,34 @@ proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
printError("no Main.bux found in src/", useColor)
return 1
# Phase 2: Merge with stdlib and check
var stdlibDir = ""
let stdlibSearchPaths = @[
getAppDir() / ".." / "stdlib",
getAppDir() / "stdlib",
getCurrentDir() / "stdlib",
"/home/ziko/z-git/bux/bux/stdlib",
]
for path in stdlibSearchPaths:
if dirExists(path):
stdlibDir = path
break
let stdlibDecls = collectStdlibDecls(stdlibDir)
var mergedItems = stdlibDecls
for decl in allModuleItems:
mergedItems.add(decl)
var unifiedModule = newModule("main")
unifiedModule.items = mergedItems
let semaRes = analyze(unifiedModule)
if semaRes.hasErrors:
printError("type errors in project", useColor)
for d in semaRes.diagnostics:
let sev = if d.severity == sdsError: "error" else: "warning"
echo &"{sev}: {d.message} at {d.loc}"
return 1
if not opts.quiet:
printInfo("check passed", useColor)
return 0
@@ -241,8 +273,8 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
# Collect stdlib declarations once
let stdlibDecls = collectStdlibDecls(stdlibDir)
# Process all .bux files
var allCCode = ""
# Phase 1: Parse all .bux files and collect declarations
var allModuleItems: seq[Decl] = @[]
var foundMain = false
for kind, path in walkDir(srcDir):
if kind == pcFile and path.endsWith(".bux"):
@@ -260,36 +292,43 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
echo &"error: {d.message} at {d.loc}"
return 1
# Merge stdlib declarations into the module (prepend so they are processed first)
var mergedItems = stdlibDecls
# Flatten: extract declarations from module wrappers
for decl in parseRes.module.items:
mergedItems.add(decl)
parseRes.module.items = mergedItems
if decl.kind == dkModule:
for sub in decl.declModuleItems:
allModuleItems.add(sub)
else:
allModuleItems.add(decl)
let (semaRes, semaCtx) = analyzeFull(parseRes.module)
if semaRes.hasErrors:
printError(&"type errors in {path}", useColor)
for d in semaRes.diagnostics:
let sev = if d.severity == sdsError: "error" else: "warning"
echo &"{sev}: {d.message} at {d.loc}"
return 1
# Lower to HIR
let hirModule = lowerModule(parseRes.module, semaCtx)
# Generate C code
var cbe = initCBackend()
let cCode = cbe.emitModule(hirModule)
allCCode.add(cCode)
allCCode.add("\n")
if splitFile(path).name == "Main":
foundMain = true
if not foundMain:
printError("no Main.bux found in src/", useColor)
return 1
# Phase 2: Merge all project declarations with stdlib
var mergedItems = stdlibDecls
for decl in allModuleItems:
mergedItems.add(decl)
# Create unified module
var unifiedModule = newModule("main")
unifiedModule.items = mergedItems
# Phase 3: Sema + HIR + C codegen on unified module
let (semaRes, semaCtx) = analyzeFull(unifiedModule)
if semaRes.hasErrors:
printError("type errors in project", useColor)
for d in semaRes.diagnostics:
let sev = if d.severity == sdsError: "error" else: "warning"
echo &"{sev}: {d.message} at {d.loc}"
return 1
let hirMod = lowerModule(unifiedModule, semaCtx)
var cbe = initCBackend()
var allCCode = cbe.emitModule(hirMod)
# Write C file
let cFile = buildDir / "main.c"
writeFile(cFile, allCCode)
+4
View File
@@ -516,6 +516,8 @@ proc parsePostfix(p: var Parser): Expr =
discard p.advance()
var fields: seq[tuple[name: string, value: Expr]] = @[]
while not p.check(tkRBrace) and not p.isAtEnd:
while p.check(tkNewLine): discard p.advance()
if p.check(tkRBrace) or p.isAtEnd: break
let fieldName = p.expect(tkIdent, "expected field name").text
discard p.expect(tkColon, "expected ':'")
let fieldValue = p.parseExpr()
@@ -737,9 +739,11 @@ proc parseStmt(p: var Parser): Stmt =
let thenBlk = p.parseBlock()
var elseIfs: seq[ElseIf] = @[]
var elseBlk: Block = nil
while p.check(tkNewLine): discard p.advance()
while p.check(tkElse):
let elseLoc = p.currentLoc
discard p.advance()
while p.check(tkNewLine): discard p.advance()
if p.check(tkIf):
discard p.advance()
let elifCond = p.parseExpr()