diff --git a/_selfhost/src/ast.bux b/_selfhost/src/ast.bux index 15f8690..60b7a03 100644 --- a/_selfhost/src/ast.bux +++ b/_selfhost/src/ast.bux @@ -244,7 +244,7 @@ struct Decl { retType: *TypeExpr, // Body refBody: *Block, - // Struct fields (up to 8) + // Struct fields (up to 64) fieldCount: int, field0: StructField, field1: StructField, @@ -254,6 +254,62 @@ struct Decl { field5: StructField, field6: StructField, field7: StructField, + field8: StructField, + field9: StructField, + field10: StructField, + field11: StructField, + field12: StructField, + field13: StructField, + field14: StructField, + field15: StructField, + field16: StructField, + field17: StructField, + field18: StructField, + field19: StructField, + field20: StructField, + field21: StructField, + field22: StructField, + field23: StructField, + field24: StructField, + field25: StructField, + field26: StructField, + field27: StructField, + field28: StructField, + field29: StructField, + field30: StructField, + field31: StructField, + field32: StructField, + field33: StructField, + field34: StructField, + field35: StructField, + field36: StructField, + field37: StructField, + field38: StructField, + field39: StructField, + field40: StructField, + field41: StructField, + field42: StructField, + field43: StructField, + field44: StructField, + field45: StructField, + field46: StructField, + field47: StructField, + field48: StructField, + field49: StructField, + field50: StructField, + field51: StructField, + field52: StructField, + field53: StructField, + field54: StructField, + field55: StructField, + field56: StructField, + field57: StructField, + field58: StructField, + field59: StructField, + field60: StructField, + field61: StructField, + field62: StructField, + field63: StructField, // Enum variants (up to 8) variantCount: int, variant0: EnumVariant, diff --git a/_selfhost/src/cli.bux b/_selfhost/src/cli.bux index 9a91efa..283364f 100644 --- a/_selfhost/src/cli.bux +++ b/_selfhost/src/cli.bux @@ -1,5 +1,5 @@ // cli.bux — CLI driver for the Bux self-hosting compiler -// Wires together: Lexer → Parser → Sema → HirLower → CBackend +// Wires together: Lexer → Parser → Sema → HirLower → NimBackend module Cli { extern func PrintLine(s: String); @@ -10,7 +10,7 @@ extern func bux_file_exists(path: String) -> int; extern func bux_dir_exists(path: String) -> int; extern func bux_path_join(a: String, b: String) -> String; extern func bux_mkdir_if_needed(path: String) -> int; -extern func bux_run_cc(c_file: String, out_bin: String, runtime_c: String, io_c: String, math_lib: 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; func ReadFile(path: String) -> String { @@ -38,7 +38,9 @@ func DirExists(path: String) -> bool { func Cli_Compile(source: String, sourceName: String) -> String { // Phase 1: Lex + PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); + PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { PrintLine("Lex errors:"); var i: int = 0; @@ -91,13 +93,13 @@ func Cli_Compile(source: String, sourceName: String) -> String { PrintLine(""); // Phase 5: C code generation - let cCode: String = CBackend_Generate(hirMod); + let nimCode: String = NimBackend_Generate(hirMod); // Cleanup Lexer_Free(lex); Sema_Free(sema); - return cCode; + return nimCode; } // --------------------------------------------------------------------------- @@ -125,7 +127,7 @@ func Cli_Build(srcPath: String, outPath: String) -> int { // Write C output let ok: bool = WriteFile(outPath, cCode); if ok { - Print(" → C code written to "); + Print(" → Nim code written to "); PrintLine(outPath); return 0; } else { @@ -148,11 +150,37 @@ func Cli_Check(srcPath: String) -> int { } PrintLine(" File read OK"); - let cCode: String = Cli_Compile(source, srcPath); - if String_Eq(cCode, "") { + // Phase 1: Lex + PrintLine(" Lexing..."); + let lex: *Lexer = Lexer_Tokenize(source); + PrintLine(" Lex done"); + if Lexer_DiagCount(lex) > 0 { + PrintLine("Lex errors found"); return 1; } + // Phase 2: Parse + let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); + if mod == null as *Module { + PrintLine("Parse failed"); + return 1; + } + + // Flatten module wrappers + if mod.firstItem != null as *Decl && mod.firstItem.kind == dkModule && mod.firstItem.childDecl1 != null as *Decl { + mod.firstItem = mod.firstItem.childDecl1; + } + + // Phase 3: Sema + let sema: *Sema = Sema_Analyze(mod); + if Sema_HasError(sema) { + PrintLine("Sema errors found"); + return 1; + } + + // Phase 4: HIR lowering + let hirMod: *HirModule = HirLower_LowerModule(mod, sema); + PrintLine("Check passed"); return 0; } @@ -163,7 +191,9 @@ func Cli_Check(srcPath: String) -> int { func Cli_CompileSource(source: String, sourceName: String) -> *HirModule { // Phase 1: Lex + PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); + PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { Print("Lex errors in "); PrintLine(sourceName); @@ -286,34 +316,30 @@ func Cli_BuildProject(projectDir: String) -> int { PrintLine("Lowering to HIR..."); let hirMod: *HirModule = HirLower_LowerModule(merged, sema); - // C code generation - PrintLine("Generating C code..."); - let cCode: String = CBackend_Generate(hirMod); + // Nim code generation + PrintLine("Generating Nim code..."); + let nimCode: String = NimBackend_Generate(hirMod); // Create build directory let buildDir: String = bux_path_join(projectDir, "build"); discard bux_mkdir_if_needed(buildDir); - // Write C output - let cFile: String = bux_path_join(buildDir, "main.c"); - if !WriteFile(cFile, cCode) { - PrintLine("Error: cannot write main.c"); + // Write Nim output + let nimFile: String = bux_path_join(buildDir, "main.nim"); + if !WriteFile(nimFile, nimCode) { + PrintLine("Error: cannot write main.nim"); return 1; } - Print("C code written to "); - PrintLine(cFile); + Print("Nim code written to "); + PrintLine(nimFile); - // Find stdlib files - let runtimeC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/runtime.c"); - let ioC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/io.c"); - - // Invoke C compiler - PrintLine("Invoking C compiler..."); + // Invoke Nim compiler + PrintLine("Invoking Nim compiler..."); let outBin: String = bux_path_join(buildDir, "buxc3"); - let rc: int = bux_run_cc(cFile, outBin, runtimeC, ioC, "-lm"); + let rc: int = bux_run_nim(nimFile, outBin); if rc != 0 { - PrintLine("Error: C compilation failed"); + PrintLine("Error: Nim compilation failed"); return 1; } @@ -363,7 +389,7 @@ func Cli_Run(args: *String, argCount: int) -> int { if String_Eq(cmd, "build") { let src: String = "src/Main.bux"; - let out: String = "build/main.c"; + let out: String = "build/main.nim"; if argCount >= 3 { src = args[2]; } if argCount >= 4 { out = args[3]; } return Cli_Build(src, out); diff --git a/_selfhost/src/hir.bux b/_selfhost/src/hir.bux index 1df6e7f..ef21728 100644 --- a/_selfhost/src/hir.bux +++ b/_selfhost/src/hir.bux @@ -39,22 +39,22 @@ const hAwait: int = 30; // --------------------------------------------------------------------------- struct HirNode { - kind: int, - line: uint32, - column: uint32, - typeKind: int, - typeName: String, + kind: int; + line: uint32; + column: uint32; + typeKind: int; + typeName: String; // Common fields (used by multiple kinds) - strValue: String, // var name, callee name, field name, label - intValue: int, // token kind (for lit, unary op, binary op) - boolValue: bool, // range inclusive, isScope + strValue: String; // var name; callee name; field name; label + intValue: int; // token kind (for lit; unary op; binary op) + boolValue: bool; // range inclusive; isScope // Child nodes (up to 3) - child1: *HirNode, // left/operand/condition/base - child2: *HirNode, // right/value/then/body - child3: *HirNode, // else/third + child1: *HirNode; // left/operand/condition/base + child2: *HirNode; // right/value/then/body + child3: *HirNode; // else/third // Extra data pointer (for children arrays, field lists, etc.) - extraData: *void, - extraCount: int, + extraData: *void; + extraCount: int; } // --------------------------------------------------------------------------- @@ -62,24 +62,24 @@ struct HirNode { // --------------------------------------------------------------------------- struct HirParam { - name: String, - typeKind: int, - typeName: String, + name: String; + typeKind: int; + typeName: String; } struct HirFunc { - name: String, - paramCount: int, - param0: HirParam, - param1: HirParam, - param2: HirParam, - param3: HirParam, - param4: HirParam, - param5: HirParam, - retTypeKind: int, - retTypeName: String, - body: *HirNode, - isPublic: bool, + name: String; + paramCount: int; + param0: HirParam; + param1: HirParam; + param2: HirParam; + param3: HirParam; + param4: HirParam; + param5: HirParam; + retTypeKind: int; + retTypeName: String; + body: *HirNode; + isPublic: bool; } // --------------------------------------------------------------------------- @@ -87,12 +87,12 @@ struct HirFunc { // --------------------------------------------------------------------------- struct HirEnumVariant { - name: String, - fieldCount: int, - fieldType0: int, - fieldName0: String, - fieldType1: int, - fieldName1: String, + name: String; + fieldCount: int; + fieldType0: int; + fieldName0: String; + fieldType1: int; + fieldName1: String; } // --------------------------------------------------------------------------- @@ -100,24 +100,31 @@ struct HirEnumVariant { // --------------------------------------------------------------------------- struct HirStructField { - name: String, - typeName: String, + name: String; + typeName: String; } struct HirStruct { - name: String, - fieldCount: int, - fields: *HirStructField, + name: String; + fieldCount: int; + fields: *HirStructField; +} + +struct HirConst { + name: String; + value: int; } struct HirModule { - funcCount: int, - funcs: *HirFunc, - externCount: int, - externFuncs: *HirFunc, - structCount: int, - structs: *HirStruct, - enumCount: int, + funcCount: int; + funcs: *HirFunc; + externCount: int; + externFuncs: *HirFunc; + structCount: int; + structs: *HirStruct; + enumCount: int; + constCount: int; + consts: *HirConst; } // --------------------------------------------------------------------------- diff --git a/_selfhost/src/lexer.bux b/_selfhost/src/lexer.bux index 637e7b7..6bbfb2f 100644 --- a/_selfhost/src/lexer.bux +++ b/_selfhost/src/lexer.bux @@ -46,31 +46,31 @@ const maxTokens: int = 32768; const maxDiags: int = 128; struct LexerDiag { - line: uint32, - column: uint32, - message: String, + line: uint32; + column: uint32; + message: String; } struct Lexer { - source: String, - sourceLen: int, - pos: int, - line: uint32, - column: uint32, - startLine: uint32, - startColumn: uint32, - startPos: int, - tokenCount: int, - tokens: *LexToken, - diagCount: int, - diags: *LexerDiag, + source: String; + sourceLen: int; + pos: int; + line: uint32; + column: uint32; + startLine: uint32; + startColumn: uint32; + startPos: int; + tokenCount: int; + tokens: *LexToken; + diagCount: int; + diags: *LexerDiag; } struct LexToken { - kind: int, - text: String, - line: uint32, - column: uint32, + kind: int; + text: String; + line: uint32; + column: uint32; } // --------------------------------------------------------------------------- @@ -294,6 +294,7 @@ func lexKeywordKind(text: String) -> int { if String_Eq(text, "self") { return tkSelf; } if String_Eq(text, "super") { return tkSuper; } if String_Eq(text, "sizeof") { return tkSizeOf; } + if String_Eq(text, "discard") { return tkDiscard; } if String_Eq(text, "async") { return tkAsync; } if String_Eq(text, "await") { return tkAwait; } if String_Eq(text, "spawn") { return tkSpawn; } diff --git a/_selfhost/src/manifest.bux b/_selfhost/src/manifest.bux index ef130bd..e8cb3a0 100644 --- a/_selfhost/src/manifest.bux +++ b/_selfhost/src/manifest.bux @@ -6,10 +6,10 @@ module Manifest { // Manifest struct // --------------------------------------------------------------------------- struct Manifest { - name: String, - version: String, - pkgType: String, - output: String, + name: String; + version: String; + pkgType: String; + output: String; } // --------------------------------------------------------------------------- diff --git a/_selfhost/src/nim_backend.bux b/_selfhost/src/nim_backend.bux new file mode 100644 index 0000000..8484771 --- /dev/null +++ b/_selfhost/src/nim_backend.bux @@ -0,0 +1,812 @@ +// nim_backend.bux — Nim transpiler backend +// Generates Nim code from the HIR. Much simpler than C backend. + +import Std::String; +import Std::Io::{Print, PrintLine}; + +module NimBackend { + +// --------------------------------------------------------------------------- +// Type → Nim type name +// --------------------------------------------------------------------------- + +func NimBackend_TypeToNim(kind: int) -> String { + if kind == tyVoid { return "void"; } + if kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32 { return "bool"; } + if kind == tyChar8 { return "char"; } + if kind == tyChar16 { return "uint16"; } + if kind == tyChar32 { return "uint32"; } + if kind == tyStr { return "string"; } + if kind == tyInt8 { return "int8"; } + if kind == tyInt16 { return "int16"; } + if kind == tyInt32 { return "int32"; } + if kind == tyInt64 { return "int64"; } + if kind == tyInt { return "int"; } + if kind == tyUInt8 { return "uint8"; } + if kind == tyUInt16 { return "uint16"; } + if kind == tyUInt32 { return "uint32"; } + if kind == tyUInt64 { return "uint64"; } + if kind == tyUInt { return "uint"; } + if kind == tyFloat32 { return "float32"; } + if kind == tyFloat64 { return "float64"; } + if kind == tyPointer { return "pointer"; } + return "int"; +} + +// --------------------------------------------------------------------------- +// Operator → Nim +// --------------------------------------------------------------------------- + +func NimBackend_OpToNim(op: int) -> String { + if op == tkPlus { return "+"; } + if op == tkMinus { return "-"; } + if op == tkStar { return "*"; } + if op == tkSlash { return "/"; } + if op == tkPercent { return "%"; } + if op == tkEq { return "=="; } + if op == tkNe { return "!="; } + if op == tkLt { return "<"; } + if op == tkLe { return "<="; } + if op == tkGt { return ">"; } + if op == tkGe { return ">="; } + if op == tkAmpAmp { return "and"; } + if op == tkPipePipe { return "or"; } + if op == tkBang { return "not"; } + if op == tkAmp { return "and"; } + if op == tkPipe { return "or"; } + if op == tkCaret { return "xor"; } + if op == tkShl { return "shl"; } + if op == tkShr { return "shr"; } + if op == tkAssign { return "="; } + return "?"; +} + +// --------------------------------------------------------------------------- +// Emitter +// --------------------------------------------------------------------------- + +struct NBEmitter { + sb: StringBuilder; + indent: int; +} + +func NBE_Init() -> NBEmitter { + var sb: StringBuilder = StringBuilder_NewCap(4096); + return NBEmitter { sb: sb, indent: 0 }; +} + +func NBE_EmitIndent(nbe: *NBEmitter) { + var i: int = 0; + while i < nbe.indent { + StringBuilder_Append(&nbe.sb, " "); + i = i + 1; + } +} + +func NBE_Emit(nbe: *NBEmitter, text: String) { + NBE_EmitIndent(nbe); + StringBuilder_Append(&nbe.sb, text); +} + +func NBE_EmitLine(nbe: *NBEmitter, text: String) { + NBE_Emit(nbe, text); + StringBuilder_Append(&nbe.sb, "\n"); +} + +func NBE_Build(nbe: *NBEmitter) -> String { + return StringBuilder_Build(&nbe.sb); +} + +func NBE_Append(nbe: *NBEmitter, text: String) { + StringBuilder_Append(&nbe.sb, text); +} + +func NBE_AppendLine(nbe: *NBEmitter, text: String) { + StringBuilder_Append(&nbe.sb, text); + StringBuilder_Append(&nbe.sb, "\n"); +} + +func NBE_AppendChar(nbe: *NBEmitter, c: char8) { + var tmp: *char8 = bux_alloc(2) as *char8; + tmp[0] = c; + tmp[1] = 0 as char8; + StringBuilder_Append(&nbe.sb, tmp); +} + +// --------------------------------------------------------------------------- +// Expression emission +// --------------------------------------------------------------------------- + +func NBE_EmitExpr(nbe: *NBEmitter, node: *HirNode) { + if node == null as *HirNode { return; } + let kind: int = node.kind; + + // Literal + if kind == hLit { + if node.intValue == tkNull { + NBE_Append(nbe, "nil"); + } else if node.intValue == tkStringLiteral { + // Nim string: emit quoted with escapes + NBE_Append(nbe, "\""); + var s: String = node.strValue; + if s != null as String { + var slen: int = bux_strlen(s) as int; + var start: int = 0; + var end: int = slen; + // Strip outer quotes if present + if slen >= 2 { + var fc: char8 = s[0]; + var lc: char8 = s[slen - 1]; + if fc == 34 as char8 && lc == 34 as char8 { + start = 1; + end = slen - 1; + } + } + var i: int = start; + while i < end { + var c: char8 = s[i]; + if c == 10 as char8 { NBE_Append(nbe, "\\n"); } // \n + else if c == 13 as char8 { NBE_Append(nbe, "\\r"); } // \r + else if c == 9 as char8 { NBE_Append(nbe, "\\t"); } // \t + else if c == 34 as char8 { NBE_Append(nbe, "\\\""); } // \" + else if c == 92 as char8 { NBE_Append(nbe, "\\\\"); } // \\ + else { NBE_AppendChar(nbe, c); } + i = i + 1; + } + } + NBE_Append(nbe, "\""); + } else if node.intValue == tkBoolLiteral { + NBE_Append(nbe, node.strValue); + } else { + NBE_Append(nbe, node.strValue); + } + return; + } + + // Variable + if kind == hVar { + NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue)); + return; + } + + // Binary + if kind == hBinary { + // Special case: String == null → check for empty string + if node.intValue == tkEq || node.intValue == tkNe { + var isNullCmp: bool = false; + var other: *HirNode = null as *HirNode; + if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull { + isNullCmp = true; + other = node.child2; + } + if !isNullCmp && node.child2 != null as *HirNode && node.child2.kind == hLit && node.child2.intValue == tkNull { + isNullCmp = true; + other = node.child1; + } + if isNullCmp && other != null as *HirNode && other.typeKind == tyStr { + if node.intValue == tkEq { + NBE_EmitExpr(nbe, other); + NBE_Append(nbe, " == \"\""); + } else { + NBE_EmitExpr(nbe, other); + NBE_Append(nbe, " != \"\""); + } + return; + } + } + if node.child1 != null as *HirNode { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " "); + } + // Emit operator only if we have a left operand + if node.child1 != null as *HirNode { + NBE_Append(nbe, NimBackend_OpToNim(node.intValue)); + NBE_Append(nbe, " "); + } + NBE_EmitExpr(nbe, node.child2); + return; + } + + // Unary + if kind == hUnary { + // Skip address-of operator (&) — Nim uses automatic dereference + if node.intValue == tkAmp { + NBE_EmitExpr(nbe, node.child1); + return; + } + NBE_Append(nbe, NimBackend_OpToNim(node.intValue)); + NBE_Append(nbe, "("); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, ")"); + return; + } + + // Call + if kind == hCall { + var fname: String = node.strValue; + // Translate StringBuilder calls to Nim native operations + if String_Eq(fname, "StringBuilder_Append") { + // sb = sb & text (string concatenation) + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " & "); + NBE_EmitExpr(nbe, node.child2); + return; + } + if String_Eq(fname, "StringBuilder_NewCap") { + // Just empty string + NBE_Append(nbe, "\"\""); + return; + } + if String_Eq(fname, "StringBuilder_Build") { + // Just return the string field + NBE_EmitExpr(nbe, node.child1); + return; + } + NBE_Append(nbe, NimBackend_EscapeIdent(fname)); + NBE_Append(nbe, "("); + if node.child1 != null as *HirNode { + NBE_EmitExpr(nbe, node.child1); + } + if node.child2 != null as *HirNode { + NBE_Append(nbe, ", "); + NBE_EmitExpr(nbe, node.child2); + } + NBE_Append(nbe, ")"); + return; + } + + // Field access: obj.field → obj.field (Nim auto-dereferences) + if kind == hFieldPtr || kind == hArrowField { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, "."); + NBE_Append(nbe, node.strValue); + return; + } + + // Index: arr[idx] + if kind == hIndexPtr { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, "["); + NBE_EmitExpr(nbe, node.child2); + NBE_Append(nbe, "]"); + return; + } + + // Cast: expr as Type + if kind == hCast { + // Special case: null as Type → appropriate default + if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull { + var typeName: String = node.typeName; + if !String_Eq(typeName, "") { + var converted: String = NimBackend_ConvertTypeName(typeName); + if String_Eq(converted, "string") { + NBE_Append(nbe, "\"\""); + return; + } + if String_Eq(converted, "pointer") { + NBE_Append(nbe, "nil"); + return; + } + } + // For pointer types (ending in * or "ptr "), emit nil + var tlen: int = bux_strlen(typeName) as int; + if tlen > 1 { + var lastCh: char8 = typeName[tlen - 1]; + if lastCh == 42 as char8 { // '*' + NBE_Append(nbe, "nil"); + return; + } + } + } + NBE_Append(nbe, "cast["); + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, NimBackend_ConvertTypeName(node.typeName)); + } else { + NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind)); + } + NBE_Append(nbe, "]("); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, ")"); + return; + } + + // Is + if kind == hIs { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " is "); + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, node.typeName); + } + return; + } + + // SizeOf + if kind == hSizeOf { + NBE_Append(nbe, "sizeof("); + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, node.typeName); + } else { + NBE_Append(nbe, "int"); + } + NBE_Append(nbe, ")"); + return; + } + + // Alloca: variable declaration (used within Store for full decl) + if kind == hAlloca { + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.typeName)); + } else { + NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind)); + } + NBE_Append(nbe, " "); + NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue)); + return; + } + + // Assign: target = value + if kind == hAssign { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child2); + return; + } + + // Store: assignment or declaration with init + if kind == hStore { + if node.child1 != null as *HirNode && node.child1.kind == hAlloca { + // Declaration: var x: Type = value + NBE_Append(nbe, "var "); + NBE_Append(nbe, NimBackend_EscapeIdent(node.child1.strValue)); + if !String_Eq(node.child1.typeName, "") { + NBE_Append(nbe, ": "); + NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.child1.typeName)); + } + if node.child2 != null as *HirNode { + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child2); + } + return; + } + // Plain assignment + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child2); + return; + } + + // If + if kind == hIf { + NBE_Append(nbe, "if "); + NBE_EmitExpr(nbe, node.child1); + NBE_AppendLine(nbe, ":"); + if node.child2 != null as *HirNode { + nbe.indent = nbe.indent + 1; + if node.child2.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child2); + nbe.indent = nbe.indent - 1; + } else { + nbe.indent = nbe.indent + 1; + NBE_EmitLine(nbe, "discard"); + nbe.indent = nbe.indent - 1; + } + if node.child3 != null as *HirNode { + NBE_EmitIndent(nbe); // indent else at same level as if + NBE_AppendLine(nbe, "else:"); + nbe.indent = nbe.indent + 1; + if node.child3.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child3); + nbe.indent = nbe.indent - 1; + } + return; + } + + // While + if kind == hWhile { + NBE_Append(nbe, "while "); + NBE_EmitExpr(nbe, node.child1); + NBE_AppendLine(nbe, ":"); + if node.child2 != null as *HirNode { + nbe.indent = nbe.indent + 1; + if node.child2.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child2); + nbe.indent = nbe.indent - 1; + } + return; + } + + // Loop (infinite) + if kind == hLoop { + NBE_AppendLine(nbe, "while true:"); + if node.child1 != null as *HirNode { + nbe.indent = nbe.indent + 1; + if node.child1.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child1); + nbe.indent = nbe.indent - 1; + } + return; + } + + // Break / Continue + if kind == hBreak { + NBE_AppendLine(nbe, "break"); + return; + } + if kind == hContinue { + NBE_AppendLine(nbe, "continue"); + return; + } + + // Return + if kind == hReturn { + NBE_Append(nbe, "return "); + if node.child1 != null as *HirNode { + if node.child1.kind == hBinary { + NBE_Append(nbe, "("); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, ")"); + } else { + NBE_EmitExpr(nbe, node.child1); + } + } + return; + } + + // Struct init: TypeName { field: value, ... } + if kind == hStructInit { + NBE_Append(nbe, node.strValue); + NBE_Append(nbe, "("); + // Emit fields (chained via child3) + var field: *HirNode = node.child3; + var first: bool = true; + while field != null as *HirNode { + if !first { NBE_Append(nbe, ", "); } + NBE_Append(nbe, field.strValue); + NBE_Append(nbe, ": "); + NBE_EmitExpr(nbe, field.child1); + first = false; + field = field.child3; + } + NBE_Append(nbe, ")"); + return; + } + + // Load: dereference — in Nim just emit the pointer expression + if kind == hLoad { + NBE_EmitExpr(nbe, node.child1); + return; + } + + // Block — sequence of statements + if kind == hBlock { + var child: *HirNode = node.child1; + if child == null as *HirNode { + NBE_EmitIndent(nbe); + NBE_AppendLine(nbe, "discard"); + return; + } + // First pass: emit statements, merging continuation lines + while child != null as *HirNode { + NBE_EmitIndent(nbe); + // Check if this is a return/binary that has continuation siblings + if child.kind == hReturn && child.child1 != null as *HirNode { + // Emit return and open paren before expression + NBE_Append(nbe, "return ("); + NBE_EmitExpr(nbe, child.child1); + child = child.child3; + // Merge all continuation siblings + while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode { + NBE_Append(nbe, " "); + NBE_EmitExpr(nbe, child); + child = child.child3; + } + NBE_AppendLine(nbe, ")"); + } else { + // Wrap with discard for non-control-flow statements + if child.kind == hCall || child.kind == hAssign || child.kind == hStore { + NBE_Append(nbe, "discard ("); + NBE_EmitExpr(nbe, child); + NBE_Append(nbe, ")"); + } else { + NBE_EmitExpr(nbe, child); + } + child = child.child3; + // Merge continuation siblings for other statement types + while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode { + NBE_Append(nbe, " "); + NBE_EmitExpr(nbe, child); + child = child.child3; + } + NBE_AppendLine(nbe, ""); + } + } + return; + } + + // Match — simplified + if kind == hMatch { + NBE_EmitLine(nbe, "# match (simplified)"); + return; + } + + // Spawn + if kind == hSpawn { + NBE_Append(nbe, "spawn "); + NBE_Append(nbe, node.strValue); + NBE_Append(nbe, "("); + if node.child1 != null as *HirNode { + NBE_EmitExpr(nbe, node.child1); + } + NBE_Append(nbe, ")"); + return; + } + + // Await + if kind == hAwait { + NBE_Append(nbe, "await "); + NBE_EmitExpr(nbe, node.child1); + return; + } + + // Fallback + NBE_Append(nbe, "nil"); +} + +// --------------------------------------------------------------------------- +// Function declaration emission +// --------------------------------------------------------------------------- + +func NBE_EmitFuncDecl(nbe: *NBEmitter, f: *HirFunc) { + // Return type + var retType: String = f.retTypeName; + if String_Eq(retType, "") || String_Eq(retType, "void") { + // No return type annotation needed + } else if String_Eq(retType, "int") { + NBE_Append(nbe, "int"); + } else if String_Eq(retType, "bool") { + NBE_Append(nbe, "bool"); + } else if String_Eq(retType, "string") || String_Eq(retType, "String") { + NBE_Append(nbe, "string"); + } else { + NBE_Append(nbe, retType); + } +} + +// --------------------------------------------------------------------------- +// Convert type name from Bux to Nim +// --------------------------------------------------------------------------- + +func NimBackend_EscapeIdent(name: String) -> String { + if name == null as String || String_Eq(name, "") { return "x"; } + // Nim keywords that need escaping + if String_Eq(name, "block") { return "`block`"; } + if String_Eq(name, "type") { return "`type`"; } + if String_Eq(name, "object") { return "`object`"; } + if String_Eq(name, "proc") { return "`proc`"; } + if String_Eq(name, "var") { return "`var`"; } + if String_Eq(name, "let") { return "`let`"; } + if String_Eq(name, "const") { return "`const`"; } + if String_Eq(name, "from") { return "`from`"; } + if String_Eq(name, "import") { return "`import`"; } + if String_Eq(name, "export") { return "`export`"; } + if String_Eq(name, "include") { return "`include`"; } + if String_Eq(name, "iterator") { return "`iterator`"; } + if String_Eq(name, "method") { return "`method`"; } + if String_Eq(name, "func") { return "`func`"; } + if String_Eq(name, "string") { return "`string`"; } + if String_Eq(name, "int") { return "`int`"; } + if String_Eq(name, "bool") { return "`bool`"; } + if String_Eq(name, "float") { return "`float`"; } + if String_Eq(name, "char") { return "`char`"; } + if String_Eq(name, "ptr") { return "`ptr`"; } + if String_Eq(name, "ref") { return "`ref`"; } + if String_Eq(name, "nil") { return "`nil`"; } + if String_Eq(name, "true") { return "`true`"; } + if String_Eq(name, "false") { return "`false`"; } + if String_Eq(name, "and") { return "`and`"; } + if String_Eq(name, "or") { return "`or`"; } + if String_Eq(name, "not") { return "`not`"; } + if String_Eq(name, "xor") { return "`xor`"; } + if String_Eq(name, "shl") { return "`shl`"; } + if String_Eq(name, "shr") { return "`shr`"; } + if String_Eq(name, "div") { return "`div`"; } + if String_Eq(name, "mod") { return "`mod`"; } + if String_Eq(name, "is") { return "`is`"; } + if String_Eq(name, "of") { return "`of`"; } + if String_Eq(name, "in") { return "`in`"; } + if String_Eq(name, "out") { return "`out`"; } + if String_Eq(name, "static") { return "`static`"; } + if String_Eq(name, "enum") { return "`enum`"; } + if String_Eq(name, "tuple") { return "`tuple`"; } + if String_Eq(name, "concept") { return "`concept`"; } + if String_Eq(name, "distinct") { return "`distinct`"; } + if String_Eq(name, "Result") { return "`Result`"; } + return name; +} + +func NimBackend_ConvertTypeNameParam(tn: String) -> String { + // For function parameters: strip pointer indirection, Nim passes by ref + if tn == null as String || String_Eq(tn, "") { return "int"; } + if String_Eq(tn, "String") { return "string"; } + if String_Eq(tn, "bool") { return "bool"; } + if String_Eq(tn, "int") { return "int"; } + if String_Eq(tn, "int64") { return "int64"; } + if String_Eq(tn, "uint32") { return "uint32"; } + if String_Eq(tn, "uint") { return "uint"; } + if String_Eq(tn, "float64") { return "float64"; } + // Pointer types → ptr types in Nim + if !String_Eq(tn, "") { + var tlen: int = bux_strlen(tn) as int; + if tlen > 1 { + var lastCh: char8 = tn[tlen - 1]; + if lastCh == 42 as char8 { + var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint); + if String_Eq(base, "void") { return "pointer"; } + return String_Concat("ptr ", base); + } + } + } + return tn; +} + +func NimBackend_ConvertTypeName(tn: String) -> String { + if tn == null as String || String_Eq(tn, "") { return "int"; } + if String_Eq(tn, "String") { return "string"; } + if String_Eq(tn, "bool") { return "bool"; } + if String_Eq(tn, "int") { return "int"; } + if String_Eq(tn, "int64") { return "int64"; } + if String_Eq(tn, "uint32") { return "uint32"; } + if String_Eq(tn, "uint") { return "uint"; } + if String_Eq(tn, "float64") { return "float64"; } + // Convert pointer types: "Foo*" → "ptr Foo" + if !String_Eq(tn, "") { + var tlen: int = bux_strlen(tn) as int; + if tlen > 1 { + var lastCh: char8 = tn[tlen - 1]; + if lastCh == 42 as char8 { // '*' + // Extract base name (without *) + var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint); + if String_Eq(base, "void") { return "pointer"; } + return String_Concat("ptr ", base); + } + } + } + return tn; +} + +// --------------------------------------------------------------------------- +// Generate complete Nim module +// --------------------------------------------------------------------------- + +func NimBackend_Generate(mod: *HirModule) -> String { + let nbe: *NBEmitter = bux_alloc(sizeof(NBEmitter)) as *NBEmitter; + nbe.sb = StringBuilder_NewCap(8192); + nbe.indent = 0; + + // Header + NBE_AppendLine(nbe, "# Generated by Bux Nim Backend"); + NBE_AppendLine(nbe, ""); + NBE_AppendLine(nbe, "import std/strutils"); + NBE_AppendLine(nbe, "import std/tables"); + NBE_AppendLine(nbe, "import std/sequtils"); + NBE_AppendLine(nbe, "import std/os"); + NBE_AppendLine(nbe, ""); + + // Type definitions — skip builtins that Nim already has + NBE_AppendLine(nbe, "# Type aliases"); + NBE_AppendLine(nbe, "type"); + nbe.indent = nbe.indent + 1; + NBE_EmitLine(nbe, "String* = string"); + NBE_EmitLine(nbe, "char8* = char"); + // StringBuild is just a string in Nim + NBE_EmitLine(nbe, "StringBuilder* = string"); + // Close type block + nbe.indent = nbe.indent - 1; + NBE_AppendLine(nbe, ""); + + NBE_AppendLine(nbe, ""); + + // Struct definitions + if mod.structCount > 0 { + NBE_AppendLine(nbe, "# Struct types"); + NBE_AppendLine(nbe, "type"); + var si: int = 0; + while si < mod.structCount { + NBE_Append(nbe, " "); + NBE_Append(nbe, NimBackend_EscapeIdent(mod.structs[si].name)); + NBE_AppendLine(nbe, " = object"); + var fi: int = 0; + while fi < mod.structs[si].fieldCount { + var fname: String = mod.structs[si].fields[fi].name; + if !String_Eq(fname, "") { + NBE_Append(nbe, " "); + NBE_Append(nbe, NimBackend_EscapeIdent(fname)); + NBE_Append(nbe, ": "); + var ft: String = mod.structs[si].fields[fi].typeName; + NBE_Append(nbe, NimBackend_ConvertTypeName(ft)); + NBE_AppendLine(nbe, ""); + } + fi = fi + 1; + } + si = si + 1; + } + NBE_AppendLine(nbe, ""); + } + + // Const definitions + if mod.constCount > 0 { + NBE_AppendLine(nbe, "# Constants"); + var ci: int = 0; + while ci < mod.constCount { + NBE_Emit(nbe, "const "); + NBE_Append(nbe, mod.consts[ci].name); + NBE_Append(nbe, " = "); + NBE_Append(nbe, String_FromInt(mod.consts[ci].value as int64)); + NBE_AppendLine(nbe, ""); + ci = ci + 1; + } + NBE_AppendLine(nbe, ""); + } + + // Extern declarations — skip (Nim doesn't need them) + // Bux extern functions are C runtime functions that Nim can't directly call. + // Instead, we use Nim native equivalents (e.g., len() instead of bux_strlen). + + // Function definitions (reverse order: dependencies first) + var i: int = mod.funcCount; + while i > 0 { + i = i - 1; + let f: *HirFunc = &mod.funcs[i]; + if f.body == null as *HirNode { continue; } + + NBE_Emit(nbe, "proc "); + NBE_Append(nbe, NimBackend_EscapeIdent(f.name)); + NBE_Append(nbe, "("); + + // Parameters + var pi: int = 0; + var pnames: *HirParam = &f.param0; + while pi < f.paramCount { + if pi > 0 { NBE_Append(nbe, ", "); } + NBE_Append(nbe, NimBackend_EscapeIdent(pnames[pi].name)); + NBE_Append(nbe, ": "); + var pt: String = pnames[pi].typeName; + NBE_Append(nbe, NimBackend_ConvertTypeNameParam(pt)); + pi = pi + 1; + } + NBE_Append(nbe, "): "); + // Return type + var rtn: String = NimBackend_ConvertTypeNameParam(f.retTypeName); + if String_Eq(rtn, "") || String_Eq(rtn, "void") { + NBE_AppendLine(nbe, "void ="); + } else { + NBE_Append(nbe, rtn); + NBE_AppendLine(nbe, " ="); + } + + // Body + nbe.indent = nbe.indent + 1; + if f.body != null as *HirNode && f.body.kind != hBlock { + NBE_EmitIndent(nbe); + } + NBE_EmitExpr(nbe, f.body); + nbe.indent = nbe.indent - 1; + NBE_AppendLine(nbe, ""); + NBE_AppendLine(nbe, ""); + } + + // Main entry point + NBE_AppendLine(nbe, "# Entry point"); + NBE_AppendLine(nbe, "when isMainModule:"); + NBE_EmitLine(nbe, " var args: seq[string] = @[]"); + NBE_EmitLine(nbe, " var argCount = paramCount()"); + NBE_EmitLine(nbe, " discard Main()"); + NBE_EmitLine(nbe, ""); + + let result: String = NBE_Build(nbe); + return result; +} + +} // module NimBackend diff --git a/_selfhost/src/parser.bux b/_selfhost/src/parser.bux index a663f43..310131e 100644 --- a/_selfhost/src/parser.bux +++ b/_selfhost/src/parser.bux @@ -917,6 +917,174 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { } else if d.fieldCount == 7 { d.field7.name = fName.text; d.field7.refFieldType = fType; + } else if d.fieldCount == 8 { + d.field8.name = fName.text; + d.field8.refFieldType = fType; + } else if d.fieldCount == 9 { + d.field9.name = fName.text; + d.field9.refFieldType = fType; + } else if d.fieldCount == 10 { + d.field10.name = fName.text; + d.field10.refFieldType = fType; + } else if d.fieldCount == 11 { + d.field11.name = fName.text; + d.field11.refFieldType = fType; + } else if d.fieldCount == 12 { + d.field12.name = fName.text; + d.field12.refFieldType = fType; + } else if d.fieldCount == 13 { + d.field13.name = fName.text; + d.field13.refFieldType = fType; + } else if d.fieldCount == 14 { + d.field14.name = fName.text; + d.field14.refFieldType = fType; + } else if d.fieldCount == 15 { + d.field15.name = fName.text; + d.field15.refFieldType = fType; + } else if d.fieldCount == 16 { + d.field16.name = fName.text; + d.field16.refFieldType = fType; + } else if d.fieldCount == 17 { + d.field17.name = fName.text; + d.field17.refFieldType = fType; + } else if d.fieldCount == 18 { + d.field18.name = fName.text; + d.field18.refFieldType = fType; + } else if d.fieldCount == 19 { + d.field19.name = fName.text; + d.field19.refFieldType = fType; + } else if d.fieldCount == 20 { + d.field20.name = fName.text; + d.field20.refFieldType = fType; + } else if d.fieldCount == 21 { + d.field21.name = fName.text; + d.field21.refFieldType = fType; + } else if d.fieldCount == 22 { + d.field22.name = fName.text; + d.field22.refFieldType = fType; + } else if d.fieldCount == 23 { + d.field23.name = fName.text; + d.field23.refFieldType = fType; + } else if d.fieldCount == 24 { + d.field24.name = fName.text; + d.field24.refFieldType = fType; + } else if d.fieldCount == 25 { + d.field25.name = fName.text; + d.field25.refFieldType = fType; + } else if d.fieldCount == 26 { + d.field26.name = fName.text; + d.field26.refFieldType = fType; + } else if d.fieldCount == 27 { + d.field27.name = fName.text; + d.field27.refFieldType = fType; + } else if d.fieldCount == 28 { + d.field28.name = fName.text; + d.field28.refFieldType = fType; + } else if d.fieldCount == 29 { + d.field29.name = fName.text; + d.field29.refFieldType = fType; + } else if d.fieldCount == 30 { + d.field30.name = fName.text; + d.field30.refFieldType = fType; + } else if d.fieldCount == 31 { + d.field31.name = fName.text; + d.field31.refFieldType = fType; + } else if d.fieldCount == 32 { + d.field32.name = fName.text; + d.field32.refFieldType = fType; + } else if d.fieldCount == 33 { + d.field33.name = fName.text; + d.field33.refFieldType = fType; + } else if d.fieldCount == 34 { + d.field34.name = fName.text; + d.field34.refFieldType = fType; + } else if d.fieldCount == 35 { + d.field35.name = fName.text; + d.field35.refFieldType = fType; + } else if d.fieldCount == 36 { + d.field36.name = fName.text; + d.field36.refFieldType = fType; + } else if d.fieldCount == 37 { + d.field37.name = fName.text; + d.field37.refFieldType = fType; + } else if d.fieldCount == 38 { + d.field38.name = fName.text; + d.field38.refFieldType = fType; + } else if d.fieldCount == 39 { + d.field39.name = fName.text; + d.field39.refFieldType = fType; + } else if d.fieldCount == 40 { + d.field40.name = fName.text; + d.field40.refFieldType = fType; + } else if d.fieldCount == 41 { + d.field41.name = fName.text; + d.field41.refFieldType = fType; + } else if d.fieldCount == 42 { + d.field42.name = fName.text; + d.field42.refFieldType = fType; + } else if d.fieldCount == 43 { + d.field43.name = fName.text; + d.field43.refFieldType = fType; + } else if d.fieldCount == 44 { + d.field44.name = fName.text; + d.field44.refFieldType = fType; + } else if d.fieldCount == 45 { + d.field45.name = fName.text; + d.field45.refFieldType = fType; + } else if d.fieldCount == 46 { + d.field46.name = fName.text; + d.field46.refFieldType = fType; + } else if d.fieldCount == 47 { + d.field47.name = fName.text; + d.field47.refFieldType = fType; + } else if d.fieldCount == 48 { + d.field48.name = fName.text; + d.field48.refFieldType = fType; + } else if d.fieldCount == 49 { + d.field49.name = fName.text; + d.field49.refFieldType = fType; + } else if d.fieldCount == 50 { + d.field50.name = fName.text; + d.field50.refFieldType = fType; + } else if d.fieldCount == 51 { + d.field51.name = fName.text; + d.field51.refFieldType = fType; + } else if d.fieldCount == 52 { + d.field52.name = fName.text; + d.field52.refFieldType = fType; + } else if d.fieldCount == 53 { + d.field53.name = fName.text; + d.field53.refFieldType = fType; + } else if d.fieldCount == 54 { + d.field54.name = fName.text; + d.field54.refFieldType = fType; + } else if d.fieldCount == 55 { + d.field55.name = fName.text; + d.field55.refFieldType = fType; + } else if d.fieldCount == 56 { + d.field56.name = fName.text; + d.field56.refFieldType = fType; + } else if d.fieldCount == 57 { + d.field57.name = fName.text; + d.field57.refFieldType = fType; + } else if d.fieldCount == 58 { + d.field58.name = fName.text; + d.field58.refFieldType = fType; + } else if d.fieldCount == 59 { + d.field59.name = fName.text; + d.field59.refFieldType = fType; + } else if d.fieldCount == 60 { + d.field60.name = fName.text; + d.field60.refFieldType = fType; + } else if d.fieldCount == 61 { + d.field61.name = fName.text; + d.field61.refFieldType = fType; + } else if d.fieldCount == 62 { + d.field62.name = fName.text; + d.field62.refFieldType = fType; + } else if d.fieldCount == 63 { + d.field63.name = fName.text; + d.field63.refFieldType = fType; } d.fieldCount = d.fieldCount + 1; } diff --git a/_selfhost/src/scope.bux b/_selfhost/src/scope.bux index 062d359..2b18114 100644 --- a/_selfhost/src/scope.bux +++ b/_selfhost/src/scope.bux @@ -9,21 +9,21 @@ const skConst: int = 3; const skModule: int = 4; // Maximum symbols per scope -const maxSymbols: int = 256; +const maxSymbols: int = 1024; struct Symbol { - kind: int, - name: String, - typeKind: int, - typeName: String, - isMutable: bool, - isPublic: bool, + kind: int; + name: String; + typeKind: int; + typeName: String; + isMutable: bool; + isPublic: bool; } struct Scope { - symbols: *Symbol, - count: int, - parent: *Scope, + symbols: *Symbol; + count: int; + parent: *Scope; } // --------------------------------------------------------------------------- diff --git a/_selfhost/src/sema.bux b/_selfhost/src/sema.bux index 306c6eb..6a573ee 100644 --- a/_selfhost/src/sema.bux +++ b/_selfhost/src/sema.bux @@ -6,19 +6,19 @@ module Sema { // Sema context // --------------------------------------------------------------------------- struct Sema { - module: *Module, - scope: *Scope, - typeTable: *void, - methodTable: *void, - diagCount: int, - diags: *SemaDiag, - hasError: bool, + module: *Module; + scope: *Scope; + typeTable: *void; + methodTable: *void; + diagCount: int; + diags: *SemaDiag; + hasError: bool; } struct SemaDiag { - line: uint32, - column: uint32, - message: String, + line: uint32; + column: uint32; + message: String; } // --------------------------------------------------------------------------- diff --git a/_selfhost/src/source_location.bux b/_selfhost/src/source_location.bux index 3ab88e6..3d01e5e 100644 --- a/_selfhost/src/source_location.bux +++ b/_selfhost/src/source_location.bux @@ -2,9 +2,9 @@ module SourceLocation { struct SourceLocation { - line: uint32, - column: uint32, - offset: uint32, + line: uint32; + column: uint32; + offset: uint32; } func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation { diff --git a/_selfhost/src/token.bux b/_selfhost/src/token.bux index 6c652b0..f20fd7e 100644 --- a/_selfhost/src/token.bux +++ b/_selfhost/src/token.bux @@ -137,17 +137,18 @@ const tkUnknown: int = 101; const tkAsync: int = 102; const tkAwait: int = 103; const tkSpawn: int = 104; +const tkDiscard: int = 105; // --------------------------------------------------------------------------- // Token struct // --------------------------------------------------------------------------- struct Token { - kind: int, - text: String, - line: uint32, - column: uint32, - offset: uint32, + kind: int; + text: String; + line: uint32; + column: uint32; + offset: uint32; } // --------------------------------------------------------------------------- diff --git a/_selfhost/src/types.bux b/_selfhost/src/types.bux index 7e84503..f3bc6de 100644 --- a/_selfhost/src/types.bux +++ b/_selfhost/src/types.bux @@ -40,16 +40,16 @@ const tyFunc: int = 28; // --------------------------------------------------------------------------- struct Type { - kind: int, - name: String, + kind: int; + name: String; // inner types stored as array of pointers (simplified) - innerKind1: int, - innerName1: String, - innerKind2: int, - innerName2: String, - innerKind3: int, - innerName3: String, - innerCount: int, + innerKind1: int; + innerName1: String; + innerKind2: int; + innerName2: String; + innerKind3: int; + innerName3: String; + innerCount: int; } // --------------------------------------------------------------------------- diff --git a/buxc_debug b/buxc_debug new file mode 100755 index 0000000..81e7385 Binary files /dev/null and b/buxc_debug differ diff --git a/src/parser.nim b/src/parser.nim index a33dfbe..c5c71a6 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -600,7 +600,10 @@ proc parseUnary(p: var Parser): Expr = let op = p.advance().kind if op == tkAmp and p.check(tkMut): discard p.advance() # mut + let savedStructInit = p.structInitAllowed + p.structInitAllowed = false let operand = p.parseUnary() + p.structInitAllowed = savedStructInit return Expr(kind: ekUnary, loc: loc, exprUnaryOp: op, exprUnaryOperand: operand) of tkPlusPlus, tkMinusMinus: let op = p.advance().kind @@ -809,7 +812,9 @@ proc parseStmt(p: var Parser): Stmt = while p.check(tkNewLine): discard p.advance() if p.check(tkIf): discard p.advance() + p.structInitAllowed = false let elifCond = p.parseExpr() + p.structInitAllowed = true let elifBlk = p.parseBlock() elseIfs.add(ElseIf(loc: elseLoc, cond: elifCond, blk: elifBlk)) while p.check(tkNewLine): discard p.advance() diff --git a/src_bux/ast.bux b/src_bux/ast.bux index 15f8690..60b7a03 100644 --- a/src_bux/ast.bux +++ b/src_bux/ast.bux @@ -244,7 +244,7 @@ struct Decl { retType: *TypeExpr, // Body refBody: *Block, - // Struct fields (up to 8) + // Struct fields (up to 64) fieldCount: int, field0: StructField, field1: StructField, @@ -254,6 +254,62 @@ struct Decl { field5: StructField, field6: StructField, field7: StructField, + field8: StructField, + field9: StructField, + field10: StructField, + field11: StructField, + field12: StructField, + field13: StructField, + field14: StructField, + field15: StructField, + field16: StructField, + field17: StructField, + field18: StructField, + field19: StructField, + field20: StructField, + field21: StructField, + field22: StructField, + field23: StructField, + field24: StructField, + field25: StructField, + field26: StructField, + field27: StructField, + field28: StructField, + field29: StructField, + field30: StructField, + field31: StructField, + field32: StructField, + field33: StructField, + field34: StructField, + field35: StructField, + field36: StructField, + field37: StructField, + field38: StructField, + field39: StructField, + field40: StructField, + field41: StructField, + field42: StructField, + field43: StructField, + field44: StructField, + field45: StructField, + field46: StructField, + field47: StructField, + field48: StructField, + field49: StructField, + field50: StructField, + field51: StructField, + field52: StructField, + field53: StructField, + field54: StructField, + field55: StructField, + field56: StructField, + field57: StructField, + field58: StructField, + field59: StructField, + field60: StructField, + field61: StructField, + field62: StructField, + field63: StructField, // Enum variants (up to 8) variantCount: int, variant0: EnumVariant, diff --git a/src_bux/cli.bux b/src_bux/cli.bux index 9a91efa..283364f 100644 --- a/src_bux/cli.bux +++ b/src_bux/cli.bux @@ -1,5 +1,5 @@ // cli.bux — CLI driver for the Bux self-hosting compiler -// Wires together: Lexer → Parser → Sema → HirLower → CBackend +// Wires together: Lexer → Parser → Sema → HirLower → NimBackend module Cli { extern func PrintLine(s: String); @@ -10,7 +10,7 @@ extern func bux_file_exists(path: String) -> int; extern func bux_dir_exists(path: String) -> int; extern func bux_path_join(a: String, b: String) -> String; extern func bux_mkdir_if_needed(path: String) -> int; -extern func bux_run_cc(c_file: String, out_bin: String, runtime_c: String, io_c: String, math_lib: 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; func ReadFile(path: String) -> String { @@ -38,7 +38,9 @@ func DirExists(path: String) -> bool { func Cli_Compile(source: String, sourceName: String) -> String { // Phase 1: Lex + PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); + PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { PrintLine("Lex errors:"); var i: int = 0; @@ -91,13 +93,13 @@ func Cli_Compile(source: String, sourceName: String) -> String { PrintLine(""); // Phase 5: C code generation - let cCode: String = CBackend_Generate(hirMod); + let nimCode: String = NimBackend_Generate(hirMod); // Cleanup Lexer_Free(lex); Sema_Free(sema); - return cCode; + return nimCode; } // --------------------------------------------------------------------------- @@ -125,7 +127,7 @@ func Cli_Build(srcPath: String, outPath: String) -> int { // Write C output let ok: bool = WriteFile(outPath, cCode); if ok { - Print(" → C code written to "); + Print(" → Nim code written to "); PrintLine(outPath); return 0; } else { @@ -148,11 +150,37 @@ func Cli_Check(srcPath: String) -> int { } PrintLine(" File read OK"); - let cCode: String = Cli_Compile(source, srcPath); - if String_Eq(cCode, "") { + // Phase 1: Lex + PrintLine(" Lexing..."); + let lex: *Lexer = Lexer_Tokenize(source); + PrintLine(" Lex done"); + if Lexer_DiagCount(lex) > 0 { + PrintLine("Lex errors found"); return 1; } + // Phase 2: Parse + let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); + if mod == null as *Module { + PrintLine("Parse failed"); + return 1; + } + + // Flatten module wrappers + if mod.firstItem != null as *Decl && mod.firstItem.kind == dkModule && mod.firstItem.childDecl1 != null as *Decl { + mod.firstItem = mod.firstItem.childDecl1; + } + + // Phase 3: Sema + let sema: *Sema = Sema_Analyze(mod); + if Sema_HasError(sema) { + PrintLine("Sema errors found"); + return 1; + } + + // Phase 4: HIR lowering + let hirMod: *HirModule = HirLower_LowerModule(mod, sema); + PrintLine("Check passed"); return 0; } @@ -163,7 +191,9 @@ func Cli_Check(srcPath: String) -> int { func Cli_CompileSource(source: String, sourceName: String) -> *HirModule { // Phase 1: Lex + PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); + PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { Print("Lex errors in "); PrintLine(sourceName); @@ -286,34 +316,30 @@ func Cli_BuildProject(projectDir: String) -> int { PrintLine("Lowering to HIR..."); let hirMod: *HirModule = HirLower_LowerModule(merged, sema); - // C code generation - PrintLine("Generating C code..."); - let cCode: String = CBackend_Generate(hirMod); + // Nim code generation + PrintLine("Generating Nim code..."); + let nimCode: String = NimBackend_Generate(hirMod); // Create build directory let buildDir: String = bux_path_join(projectDir, "build"); discard bux_mkdir_if_needed(buildDir); - // Write C output - let cFile: String = bux_path_join(buildDir, "main.c"); - if !WriteFile(cFile, cCode) { - PrintLine("Error: cannot write main.c"); + // Write Nim output + let nimFile: String = bux_path_join(buildDir, "main.nim"); + if !WriteFile(nimFile, nimCode) { + PrintLine("Error: cannot write main.nim"); return 1; } - Print("C code written to "); - PrintLine(cFile); + Print("Nim code written to "); + PrintLine(nimFile); - // Find stdlib files - let runtimeC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/runtime.c"); - let ioC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/io.c"); - - // Invoke C compiler - PrintLine("Invoking C compiler..."); + // Invoke Nim compiler + PrintLine("Invoking Nim compiler..."); let outBin: String = bux_path_join(buildDir, "buxc3"); - let rc: int = bux_run_cc(cFile, outBin, runtimeC, ioC, "-lm"); + let rc: int = bux_run_nim(nimFile, outBin); if rc != 0 { - PrintLine("Error: C compilation failed"); + PrintLine("Error: Nim compilation failed"); return 1; } @@ -363,7 +389,7 @@ func Cli_Run(args: *String, argCount: int) -> int { if String_Eq(cmd, "build") { let src: String = "src/Main.bux"; - let out: String = "build/main.c"; + let out: String = "build/main.nim"; if argCount >= 3 { src = args[2]; } if argCount >= 4 { out = args[3]; } return Cli_Build(src, out); diff --git a/src_bux/hir.bux b/src_bux/hir.bux index 1df6e7f..ef21728 100644 --- a/src_bux/hir.bux +++ b/src_bux/hir.bux @@ -39,22 +39,22 @@ const hAwait: int = 30; // --------------------------------------------------------------------------- struct HirNode { - kind: int, - line: uint32, - column: uint32, - typeKind: int, - typeName: String, + kind: int; + line: uint32; + column: uint32; + typeKind: int; + typeName: String; // Common fields (used by multiple kinds) - strValue: String, // var name, callee name, field name, label - intValue: int, // token kind (for lit, unary op, binary op) - boolValue: bool, // range inclusive, isScope + strValue: String; // var name; callee name; field name; label + intValue: int; // token kind (for lit; unary op; binary op) + boolValue: bool; // range inclusive; isScope // Child nodes (up to 3) - child1: *HirNode, // left/operand/condition/base - child2: *HirNode, // right/value/then/body - child3: *HirNode, // else/third + child1: *HirNode; // left/operand/condition/base + child2: *HirNode; // right/value/then/body + child3: *HirNode; // else/third // Extra data pointer (for children arrays, field lists, etc.) - extraData: *void, - extraCount: int, + extraData: *void; + extraCount: int; } // --------------------------------------------------------------------------- @@ -62,24 +62,24 @@ struct HirNode { // --------------------------------------------------------------------------- struct HirParam { - name: String, - typeKind: int, - typeName: String, + name: String; + typeKind: int; + typeName: String; } struct HirFunc { - name: String, - paramCount: int, - param0: HirParam, - param1: HirParam, - param2: HirParam, - param3: HirParam, - param4: HirParam, - param5: HirParam, - retTypeKind: int, - retTypeName: String, - body: *HirNode, - isPublic: bool, + name: String; + paramCount: int; + param0: HirParam; + param1: HirParam; + param2: HirParam; + param3: HirParam; + param4: HirParam; + param5: HirParam; + retTypeKind: int; + retTypeName: String; + body: *HirNode; + isPublic: bool; } // --------------------------------------------------------------------------- @@ -87,12 +87,12 @@ struct HirFunc { // --------------------------------------------------------------------------- struct HirEnumVariant { - name: String, - fieldCount: int, - fieldType0: int, - fieldName0: String, - fieldType1: int, - fieldName1: String, + name: String; + fieldCount: int; + fieldType0: int; + fieldName0: String; + fieldType1: int; + fieldName1: String; } // --------------------------------------------------------------------------- @@ -100,24 +100,31 @@ struct HirEnumVariant { // --------------------------------------------------------------------------- struct HirStructField { - name: String, - typeName: String, + name: String; + typeName: String; } struct HirStruct { - name: String, - fieldCount: int, - fields: *HirStructField, + name: String; + fieldCount: int; + fields: *HirStructField; +} + +struct HirConst { + name: String; + value: int; } struct HirModule { - funcCount: int, - funcs: *HirFunc, - externCount: int, - externFuncs: *HirFunc, - structCount: int, - structs: *HirStruct, - enumCount: int, + funcCount: int; + funcs: *HirFunc; + externCount: int; + externFuncs: *HirFunc; + structCount: int; + structs: *HirStruct; + enumCount: int; + constCount: int; + consts: *HirConst; } // --------------------------------------------------------------------------- diff --git a/src_bux/lexer.bux b/src_bux/lexer.bux index 637e7b7..6bbfb2f 100644 --- a/src_bux/lexer.bux +++ b/src_bux/lexer.bux @@ -46,31 +46,31 @@ const maxTokens: int = 32768; const maxDiags: int = 128; struct LexerDiag { - line: uint32, - column: uint32, - message: String, + line: uint32; + column: uint32; + message: String; } struct Lexer { - source: String, - sourceLen: int, - pos: int, - line: uint32, - column: uint32, - startLine: uint32, - startColumn: uint32, - startPos: int, - tokenCount: int, - tokens: *LexToken, - diagCount: int, - diags: *LexerDiag, + source: String; + sourceLen: int; + pos: int; + line: uint32; + column: uint32; + startLine: uint32; + startColumn: uint32; + startPos: int; + tokenCount: int; + tokens: *LexToken; + diagCount: int; + diags: *LexerDiag; } struct LexToken { - kind: int, - text: String, - line: uint32, - column: uint32, + kind: int; + text: String; + line: uint32; + column: uint32; } // --------------------------------------------------------------------------- @@ -294,6 +294,7 @@ func lexKeywordKind(text: String) -> int { if String_Eq(text, "self") { return tkSelf; } if String_Eq(text, "super") { return tkSuper; } if String_Eq(text, "sizeof") { return tkSizeOf; } + if String_Eq(text, "discard") { return tkDiscard; } if String_Eq(text, "async") { return tkAsync; } if String_Eq(text, "await") { return tkAwait; } if String_Eq(text, "spawn") { return tkSpawn; } diff --git a/src_bux/manifest.bux b/src_bux/manifest.bux index ef130bd..e8cb3a0 100644 --- a/src_bux/manifest.bux +++ b/src_bux/manifest.bux @@ -6,10 +6,10 @@ module Manifest { // Manifest struct // --------------------------------------------------------------------------- struct Manifest { - name: String, - version: String, - pkgType: String, - output: String, + name: String; + version: String; + pkgType: String; + output: String; } // --------------------------------------------------------------------------- diff --git a/src_bux/nim_backend.bux b/src_bux/nim_backend.bux new file mode 100644 index 0000000..8484771 --- /dev/null +++ b/src_bux/nim_backend.bux @@ -0,0 +1,812 @@ +// nim_backend.bux — Nim transpiler backend +// Generates Nim code from the HIR. Much simpler than C backend. + +import Std::String; +import Std::Io::{Print, PrintLine}; + +module NimBackend { + +// --------------------------------------------------------------------------- +// Type → Nim type name +// --------------------------------------------------------------------------- + +func NimBackend_TypeToNim(kind: int) -> String { + if kind == tyVoid { return "void"; } + if kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32 { return "bool"; } + if kind == tyChar8 { return "char"; } + if kind == tyChar16 { return "uint16"; } + if kind == tyChar32 { return "uint32"; } + if kind == tyStr { return "string"; } + if kind == tyInt8 { return "int8"; } + if kind == tyInt16 { return "int16"; } + if kind == tyInt32 { return "int32"; } + if kind == tyInt64 { return "int64"; } + if kind == tyInt { return "int"; } + if kind == tyUInt8 { return "uint8"; } + if kind == tyUInt16 { return "uint16"; } + if kind == tyUInt32 { return "uint32"; } + if kind == tyUInt64 { return "uint64"; } + if kind == tyUInt { return "uint"; } + if kind == tyFloat32 { return "float32"; } + if kind == tyFloat64 { return "float64"; } + if kind == tyPointer { return "pointer"; } + return "int"; +} + +// --------------------------------------------------------------------------- +// Operator → Nim +// --------------------------------------------------------------------------- + +func NimBackend_OpToNim(op: int) -> String { + if op == tkPlus { return "+"; } + if op == tkMinus { return "-"; } + if op == tkStar { return "*"; } + if op == tkSlash { return "/"; } + if op == tkPercent { return "%"; } + if op == tkEq { return "=="; } + if op == tkNe { return "!="; } + if op == tkLt { return "<"; } + if op == tkLe { return "<="; } + if op == tkGt { return ">"; } + if op == tkGe { return ">="; } + if op == tkAmpAmp { return "and"; } + if op == tkPipePipe { return "or"; } + if op == tkBang { return "not"; } + if op == tkAmp { return "and"; } + if op == tkPipe { return "or"; } + if op == tkCaret { return "xor"; } + if op == tkShl { return "shl"; } + if op == tkShr { return "shr"; } + if op == tkAssign { return "="; } + return "?"; +} + +// --------------------------------------------------------------------------- +// Emitter +// --------------------------------------------------------------------------- + +struct NBEmitter { + sb: StringBuilder; + indent: int; +} + +func NBE_Init() -> NBEmitter { + var sb: StringBuilder = StringBuilder_NewCap(4096); + return NBEmitter { sb: sb, indent: 0 }; +} + +func NBE_EmitIndent(nbe: *NBEmitter) { + var i: int = 0; + while i < nbe.indent { + StringBuilder_Append(&nbe.sb, " "); + i = i + 1; + } +} + +func NBE_Emit(nbe: *NBEmitter, text: String) { + NBE_EmitIndent(nbe); + StringBuilder_Append(&nbe.sb, text); +} + +func NBE_EmitLine(nbe: *NBEmitter, text: String) { + NBE_Emit(nbe, text); + StringBuilder_Append(&nbe.sb, "\n"); +} + +func NBE_Build(nbe: *NBEmitter) -> String { + return StringBuilder_Build(&nbe.sb); +} + +func NBE_Append(nbe: *NBEmitter, text: String) { + StringBuilder_Append(&nbe.sb, text); +} + +func NBE_AppendLine(nbe: *NBEmitter, text: String) { + StringBuilder_Append(&nbe.sb, text); + StringBuilder_Append(&nbe.sb, "\n"); +} + +func NBE_AppendChar(nbe: *NBEmitter, c: char8) { + var tmp: *char8 = bux_alloc(2) as *char8; + tmp[0] = c; + tmp[1] = 0 as char8; + StringBuilder_Append(&nbe.sb, tmp); +} + +// --------------------------------------------------------------------------- +// Expression emission +// --------------------------------------------------------------------------- + +func NBE_EmitExpr(nbe: *NBEmitter, node: *HirNode) { + if node == null as *HirNode { return; } + let kind: int = node.kind; + + // Literal + if kind == hLit { + if node.intValue == tkNull { + NBE_Append(nbe, "nil"); + } else if node.intValue == tkStringLiteral { + // Nim string: emit quoted with escapes + NBE_Append(nbe, "\""); + var s: String = node.strValue; + if s != null as String { + var slen: int = bux_strlen(s) as int; + var start: int = 0; + var end: int = slen; + // Strip outer quotes if present + if slen >= 2 { + var fc: char8 = s[0]; + var lc: char8 = s[slen - 1]; + if fc == 34 as char8 && lc == 34 as char8 { + start = 1; + end = slen - 1; + } + } + var i: int = start; + while i < end { + var c: char8 = s[i]; + if c == 10 as char8 { NBE_Append(nbe, "\\n"); } // \n + else if c == 13 as char8 { NBE_Append(nbe, "\\r"); } // \r + else if c == 9 as char8 { NBE_Append(nbe, "\\t"); } // \t + else if c == 34 as char8 { NBE_Append(nbe, "\\\""); } // \" + else if c == 92 as char8 { NBE_Append(nbe, "\\\\"); } // \\ + else { NBE_AppendChar(nbe, c); } + i = i + 1; + } + } + NBE_Append(nbe, "\""); + } else if node.intValue == tkBoolLiteral { + NBE_Append(nbe, node.strValue); + } else { + NBE_Append(nbe, node.strValue); + } + return; + } + + // Variable + if kind == hVar { + NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue)); + return; + } + + // Binary + if kind == hBinary { + // Special case: String == null → check for empty string + if node.intValue == tkEq || node.intValue == tkNe { + var isNullCmp: bool = false; + var other: *HirNode = null as *HirNode; + if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull { + isNullCmp = true; + other = node.child2; + } + if !isNullCmp && node.child2 != null as *HirNode && node.child2.kind == hLit && node.child2.intValue == tkNull { + isNullCmp = true; + other = node.child1; + } + if isNullCmp && other != null as *HirNode && other.typeKind == tyStr { + if node.intValue == tkEq { + NBE_EmitExpr(nbe, other); + NBE_Append(nbe, " == \"\""); + } else { + NBE_EmitExpr(nbe, other); + NBE_Append(nbe, " != \"\""); + } + return; + } + } + if node.child1 != null as *HirNode { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " "); + } + // Emit operator only if we have a left operand + if node.child1 != null as *HirNode { + NBE_Append(nbe, NimBackend_OpToNim(node.intValue)); + NBE_Append(nbe, " "); + } + NBE_EmitExpr(nbe, node.child2); + return; + } + + // Unary + if kind == hUnary { + // Skip address-of operator (&) — Nim uses automatic dereference + if node.intValue == tkAmp { + NBE_EmitExpr(nbe, node.child1); + return; + } + NBE_Append(nbe, NimBackend_OpToNim(node.intValue)); + NBE_Append(nbe, "("); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, ")"); + return; + } + + // Call + if kind == hCall { + var fname: String = node.strValue; + // Translate StringBuilder calls to Nim native operations + if String_Eq(fname, "StringBuilder_Append") { + // sb = sb & text (string concatenation) + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " & "); + NBE_EmitExpr(nbe, node.child2); + return; + } + if String_Eq(fname, "StringBuilder_NewCap") { + // Just empty string + NBE_Append(nbe, "\"\""); + return; + } + if String_Eq(fname, "StringBuilder_Build") { + // Just return the string field + NBE_EmitExpr(nbe, node.child1); + return; + } + NBE_Append(nbe, NimBackend_EscapeIdent(fname)); + NBE_Append(nbe, "("); + if node.child1 != null as *HirNode { + NBE_EmitExpr(nbe, node.child1); + } + if node.child2 != null as *HirNode { + NBE_Append(nbe, ", "); + NBE_EmitExpr(nbe, node.child2); + } + NBE_Append(nbe, ")"); + return; + } + + // Field access: obj.field → obj.field (Nim auto-dereferences) + if kind == hFieldPtr || kind == hArrowField { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, "."); + NBE_Append(nbe, node.strValue); + return; + } + + // Index: arr[idx] + if kind == hIndexPtr { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, "["); + NBE_EmitExpr(nbe, node.child2); + NBE_Append(nbe, "]"); + return; + } + + // Cast: expr as Type + if kind == hCast { + // Special case: null as Type → appropriate default + if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull { + var typeName: String = node.typeName; + if !String_Eq(typeName, "") { + var converted: String = NimBackend_ConvertTypeName(typeName); + if String_Eq(converted, "string") { + NBE_Append(nbe, "\"\""); + return; + } + if String_Eq(converted, "pointer") { + NBE_Append(nbe, "nil"); + return; + } + } + // For pointer types (ending in * or "ptr "), emit nil + var tlen: int = bux_strlen(typeName) as int; + if tlen > 1 { + var lastCh: char8 = typeName[tlen - 1]; + if lastCh == 42 as char8 { // '*' + NBE_Append(nbe, "nil"); + return; + } + } + } + NBE_Append(nbe, "cast["); + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, NimBackend_ConvertTypeName(node.typeName)); + } else { + NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind)); + } + NBE_Append(nbe, "]("); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, ")"); + return; + } + + // Is + if kind == hIs { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " is "); + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, node.typeName); + } + return; + } + + // SizeOf + if kind == hSizeOf { + NBE_Append(nbe, "sizeof("); + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, node.typeName); + } else { + NBE_Append(nbe, "int"); + } + NBE_Append(nbe, ")"); + return; + } + + // Alloca: variable declaration (used within Store for full decl) + if kind == hAlloca { + if !String_Eq(node.typeName, "") { + NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.typeName)); + } else { + NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind)); + } + NBE_Append(nbe, " "); + NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue)); + return; + } + + // Assign: target = value + if kind == hAssign { + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child2); + return; + } + + // Store: assignment or declaration with init + if kind == hStore { + if node.child1 != null as *HirNode && node.child1.kind == hAlloca { + // Declaration: var x: Type = value + NBE_Append(nbe, "var "); + NBE_Append(nbe, NimBackend_EscapeIdent(node.child1.strValue)); + if !String_Eq(node.child1.typeName, "") { + NBE_Append(nbe, ": "); + NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.child1.typeName)); + } + if node.child2 != null as *HirNode { + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child2); + } + return; + } + // Plain assignment + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, " = "); + NBE_EmitExpr(nbe, node.child2); + return; + } + + // If + if kind == hIf { + NBE_Append(nbe, "if "); + NBE_EmitExpr(nbe, node.child1); + NBE_AppendLine(nbe, ":"); + if node.child2 != null as *HirNode { + nbe.indent = nbe.indent + 1; + if node.child2.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child2); + nbe.indent = nbe.indent - 1; + } else { + nbe.indent = nbe.indent + 1; + NBE_EmitLine(nbe, "discard"); + nbe.indent = nbe.indent - 1; + } + if node.child3 != null as *HirNode { + NBE_EmitIndent(nbe); // indent else at same level as if + NBE_AppendLine(nbe, "else:"); + nbe.indent = nbe.indent + 1; + if node.child3.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child3); + nbe.indent = nbe.indent - 1; + } + return; + } + + // While + if kind == hWhile { + NBE_Append(nbe, "while "); + NBE_EmitExpr(nbe, node.child1); + NBE_AppendLine(nbe, ":"); + if node.child2 != null as *HirNode { + nbe.indent = nbe.indent + 1; + if node.child2.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child2); + nbe.indent = nbe.indent - 1; + } + return; + } + + // Loop (infinite) + if kind == hLoop { + NBE_AppendLine(nbe, "while true:"); + if node.child1 != null as *HirNode { + nbe.indent = nbe.indent + 1; + if node.child1.kind != hBlock { NBE_EmitIndent(nbe); } + NBE_EmitExpr(nbe, node.child1); + nbe.indent = nbe.indent - 1; + } + return; + } + + // Break / Continue + if kind == hBreak { + NBE_AppendLine(nbe, "break"); + return; + } + if kind == hContinue { + NBE_AppendLine(nbe, "continue"); + return; + } + + // Return + if kind == hReturn { + NBE_Append(nbe, "return "); + if node.child1 != null as *HirNode { + if node.child1.kind == hBinary { + NBE_Append(nbe, "("); + NBE_EmitExpr(nbe, node.child1); + NBE_Append(nbe, ")"); + } else { + NBE_EmitExpr(nbe, node.child1); + } + } + return; + } + + // Struct init: TypeName { field: value, ... } + if kind == hStructInit { + NBE_Append(nbe, node.strValue); + NBE_Append(nbe, "("); + // Emit fields (chained via child3) + var field: *HirNode = node.child3; + var first: bool = true; + while field != null as *HirNode { + if !first { NBE_Append(nbe, ", "); } + NBE_Append(nbe, field.strValue); + NBE_Append(nbe, ": "); + NBE_EmitExpr(nbe, field.child1); + first = false; + field = field.child3; + } + NBE_Append(nbe, ")"); + return; + } + + // Load: dereference — in Nim just emit the pointer expression + if kind == hLoad { + NBE_EmitExpr(nbe, node.child1); + return; + } + + // Block — sequence of statements + if kind == hBlock { + var child: *HirNode = node.child1; + if child == null as *HirNode { + NBE_EmitIndent(nbe); + NBE_AppendLine(nbe, "discard"); + return; + } + // First pass: emit statements, merging continuation lines + while child != null as *HirNode { + NBE_EmitIndent(nbe); + // Check if this is a return/binary that has continuation siblings + if child.kind == hReturn && child.child1 != null as *HirNode { + // Emit return and open paren before expression + NBE_Append(nbe, "return ("); + NBE_EmitExpr(nbe, child.child1); + child = child.child3; + // Merge all continuation siblings + while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode { + NBE_Append(nbe, " "); + NBE_EmitExpr(nbe, child); + child = child.child3; + } + NBE_AppendLine(nbe, ")"); + } else { + // Wrap with discard for non-control-flow statements + if child.kind == hCall || child.kind == hAssign || child.kind == hStore { + NBE_Append(nbe, "discard ("); + NBE_EmitExpr(nbe, child); + NBE_Append(nbe, ")"); + } else { + NBE_EmitExpr(nbe, child); + } + child = child.child3; + // Merge continuation siblings for other statement types + while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode { + NBE_Append(nbe, " "); + NBE_EmitExpr(nbe, child); + child = child.child3; + } + NBE_AppendLine(nbe, ""); + } + } + return; + } + + // Match — simplified + if kind == hMatch { + NBE_EmitLine(nbe, "# match (simplified)"); + return; + } + + // Spawn + if kind == hSpawn { + NBE_Append(nbe, "spawn "); + NBE_Append(nbe, node.strValue); + NBE_Append(nbe, "("); + if node.child1 != null as *HirNode { + NBE_EmitExpr(nbe, node.child1); + } + NBE_Append(nbe, ")"); + return; + } + + // Await + if kind == hAwait { + NBE_Append(nbe, "await "); + NBE_EmitExpr(nbe, node.child1); + return; + } + + // Fallback + NBE_Append(nbe, "nil"); +} + +// --------------------------------------------------------------------------- +// Function declaration emission +// --------------------------------------------------------------------------- + +func NBE_EmitFuncDecl(nbe: *NBEmitter, f: *HirFunc) { + // Return type + var retType: String = f.retTypeName; + if String_Eq(retType, "") || String_Eq(retType, "void") { + // No return type annotation needed + } else if String_Eq(retType, "int") { + NBE_Append(nbe, "int"); + } else if String_Eq(retType, "bool") { + NBE_Append(nbe, "bool"); + } else if String_Eq(retType, "string") || String_Eq(retType, "String") { + NBE_Append(nbe, "string"); + } else { + NBE_Append(nbe, retType); + } +} + +// --------------------------------------------------------------------------- +// Convert type name from Bux to Nim +// --------------------------------------------------------------------------- + +func NimBackend_EscapeIdent(name: String) -> String { + if name == null as String || String_Eq(name, "") { return "x"; } + // Nim keywords that need escaping + if String_Eq(name, "block") { return "`block`"; } + if String_Eq(name, "type") { return "`type`"; } + if String_Eq(name, "object") { return "`object`"; } + if String_Eq(name, "proc") { return "`proc`"; } + if String_Eq(name, "var") { return "`var`"; } + if String_Eq(name, "let") { return "`let`"; } + if String_Eq(name, "const") { return "`const`"; } + if String_Eq(name, "from") { return "`from`"; } + if String_Eq(name, "import") { return "`import`"; } + if String_Eq(name, "export") { return "`export`"; } + if String_Eq(name, "include") { return "`include`"; } + if String_Eq(name, "iterator") { return "`iterator`"; } + if String_Eq(name, "method") { return "`method`"; } + if String_Eq(name, "func") { return "`func`"; } + if String_Eq(name, "string") { return "`string`"; } + if String_Eq(name, "int") { return "`int`"; } + if String_Eq(name, "bool") { return "`bool`"; } + if String_Eq(name, "float") { return "`float`"; } + if String_Eq(name, "char") { return "`char`"; } + if String_Eq(name, "ptr") { return "`ptr`"; } + if String_Eq(name, "ref") { return "`ref`"; } + if String_Eq(name, "nil") { return "`nil`"; } + if String_Eq(name, "true") { return "`true`"; } + if String_Eq(name, "false") { return "`false`"; } + if String_Eq(name, "and") { return "`and`"; } + if String_Eq(name, "or") { return "`or`"; } + if String_Eq(name, "not") { return "`not`"; } + if String_Eq(name, "xor") { return "`xor`"; } + if String_Eq(name, "shl") { return "`shl`"; } + if String_Eq(name, "shr") { return "`shr`"; } + if String_Eq(name, "div") { return "`div`"; } + if String_Eq(name, "mod") { return "`mod`"; } + if String_Eq(name, "is") { return "`is`"; } + if String_Eq(name, "of") { return "`of`"; } + if String_Eq(name, "in") { return "`in`"; } + if String_Eq(name, "out") { return "`out`"; } + if String_Eq(name, "static") { return "`static`"; } + if String_Eq(name, "enum") { return "`enum`"; } + if String_Eq(name, "tuple") { return "`tuple`"; } + if String_Eq(name, "concept") { return "`concept`"; } + if String_Eq(name, "distinct") { return "`distinct`"; } + if String_Eq(name, "Result") { return "`Result`"; } + return name; +} + +func NimBackend_ConvertTypeNameParam(tn: String) -> String { + // For function parameters: strip pointer indirection, Nim passes by ref + if tn == null as String || String_Eq(tn, "") { return "int"; } + if String_Eq(tn, "String") { return "string"; } + if String_Eq(tn, "bool") { return "bool"; } + if String_Eq(tn, "int") { return "int"; } + if String_Eq(tn, "int64") { return "int64"; } + if String_Eq(tn, "uint32") { return "uint32"; } + if String_Eq(tn, "uint") { return "uint"; } + if String_Eq(tn, "float64") { return "float64"; } + // Pointer types → ptr types in Nim + if !String_Eq(tn, "") { + var tlen: int = bux_strlen(tn) as int; + if tlen > 1 { + var lastCh: char8 = tn[tlen - 1]; + if lastCh == 42 as char8 { + var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint); + if String_Eq(base, "void") { return "pointer"; } + return String_Concat("ptr ", base); + } + } + } + return tn; +} + +func NimBackend_ConvertTypeName(tn: String) -> String { + if tn == null as String || String_Eq(tn, "") { return "int"; } + if String_Eq(tn, "String") { return "string"; } + if String_Eq(tn, "bool") { return "bool"; } + if String_Eq(tn, "int") { return "int"; } + if String_Eq(tn, "int64") { return "int64"; } + if String_Eq(tn, "uint32") { return "uint32"; } + if String_Eq(tn, "uint") { return "uint"; } + if String_Eq(tn, "float64") { return "float64"; } + // Convert pointer types: "Foo*" → "ptr Foo" + if !String_Eq(tn, "") { + var tlen: int = bux_strlen(tn) as int; + if tlen > 1 { + var lastCh: char8 = tn[tlen - 1]; + if lastCh == 42 as char8 { // '*' + // Extract base name (without *) + var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint); + if String_Eq(base, "void") { return "pointer"; } + return String_Concat("ptr ", base); + } + } + } + return tn; +} + +// --------------------------------------------------------------------------- +// Generate complete Nim module +// --------------------------------------------------------------------------- + +func NimBackend_Generate(mod: *HirModule) -> String { + let nbe: *NBEmitter = bux_alloc(sizeof(NBEmitter)) as *NBEmitter; + nbe.sb = StringBuilder_NewCap(8192); + nbe.indent = 0; + + // Header + NBE_AppendLine(nbe, "# Generated by Bux Nim Backend"); + NBE_AppendLine(nbe, ""); + NBE_AppendLine(nbe, "import std/strutils"); + NBE_AppendLine(nbe, "import std/tables"); + NBE_AppendLine(nbe, "import std/sequtils"); + NBE_AppendLine(nbe, "import std/os"); + NBE_AppendLine(nbe, ""); + + // Type definitions — skip builtins that Nim already has + NBE_AppendLine(nbe, "# Type aliases"); + NBE_AppendLine(nbe, "type"); + nbe.indent = nbe.indent + 1; + NBE_EmitLine(nbe, "String* = string"); + NBE_EmitLine(nbe, "char8* = char"); + // StringBuild is just a string in Nim + NBE_EmitLine(nbe, "StringBuilder* = string"); + // Close type block + nbe.indent = nbe.indent - 1; + NBE_AppendLine(nbe, ""); + + NBE_AppendLine(nbe, ""); + + // Struct definitions + if mod.structCount > 0 { + NBE_AppendLine(nbe, "# Struct types"); + NBE_AppendLine(nbe, "type"); + var si: int = 0; + while si < mod.structCount { + NBE_Append(nbe, " "); + NBE_Append(nbe, NimBackend_EscapeIdent(mod.structs[si].name)); + NBE_AppendLine(nbe, " = object"); + var fi: int = 0; + while fi < mod.structs[si].fieldCount { + var fname: String = mod.structs[si].fields[fi].name; + if !String_Eq(fname, "") { + NBE_Append(nbe, " "); + NBE_Append(nbe, NimBackend_EscapeIdent(fname)); + NBE_Append(nbe, ": "); + var ft: String = mod.structs[si].fields[fi].typeName; + NBE_Append(nbe, NimBackend_ConvertTypeName(ft)); + NBE_AppendLine(nbe, ""); + } + fi = fi + 1; + } + si = si + 1; + } + NBE_AppendLine(nbe, ""); + } + + // Const definitions + if mod.constCount > 0 { + NBE_AppendLine(nbe, "# Constants"); + var ci: int = 0; + while ci < mod.constCount { + NBE_Emit(nbe, "const "); + NBE_Append(nbe, mod.consts[ci].name); + NBE_Append(nbe, " = "); + NBE_Append(nbe, String_FromInt(mod.consts[ci].value as int64)); + NBE_AppendLine(nbe, ""); + ci = ci + 1; + } + NBE_AppendLine(nbe, ""); + } + + // Extern declarations — skip (Nim doesn't need them) + // Bux extern functions are C runtime functions that Nim can't directly call. + // Instead, we use Nim native equivalents (e.g., len() instead of bux_strlen). + + // Function definitions (reverse order: dependencies first) + var i: int = mod.funcCount; + while i > 0 { + i = i - 1; + let f: *HirFunc = &mod.funcs[i]; + if f.body == null as *HirNode { continue; } + + NBE_Emit(nbe, "proc "); + NBE_Append(nbe, NimBackend_EscapeIdent(f.name)); + NBE_Append(nbe, "("); + + // Parameters + var pi: int = 0; + var pnames: *HirParam = &f.param0; + while pi < f.paramCount { + if pi > 0 { NBE_Append(nbe, ", "); } + NBE_Append(nbe, NimBackend_EscapeIdent(pnames[pi].name)); + NBE_Append(nbe, ": "); + var pt: String = pnames[pi].typeName; + NBE_Append(nbe, NimBackend_ConvertTypeNameParam(pt)); + pi = pi + 1; + } + NBE_Append(nbe, "): "); + // Return type + var rtn: String = NimBackend_ConvertTypeNameParam(f.retTypeName); + if String_Eq(rtn, "") || String_Eq(rtn, "void") { + NBE_AppendLine(nbe, "void ="); + } else { + NBE_Append(nbe, rtn); + NBE_AppendLine(nbe, " ="); + } + + // Body + nbe.indent = nbe.indent + 1; + if f.body != null as *HirNode && f.body.kind != hBlock { + NBE_EmitIndent(nbe); + } + NBE_EmitExpr(nbe, f.body); + nbe.indent = nbe.indent - 1; + NBE_AppendLine(nbe, ""); + NBE_AppendLine(nbe, ""); + } + + // Main entry point + NBE_AppendLine(nbe, "# Entry point"); + NBE_AppendLine(nbe, "when isMainModule:"); + NBE_EmitLine(nbe, " var args: seq[string] = @[]"); + NBE_EmitLine(nbe, " var argCount = paramCount()"); + NBE_EmitLine(nbe, " discard Main()"); + NBE_EmitLine(nbe, ""); + + let result: String = NBE_Build(nbe); + return result; +} + +} // module NimBackend diff --git a/src_bux/parser.bux b/src_bux/parser.bux index a663f43..310131e 100644 --- a/src_bux/parser.bux +++ b/src_bux/parser.bux @@ -917,6 +917,174 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { } else if d.fieldCount == 7 { d.field7.name = fName.text; d.field7.refFieldType = fType; + } else if d.fieldCount == 8 { + d.field8.name = fName.text; + d.field8.refFieldType = fType; + } else if d.fieldCount == 9 { + d.field9.name = fName.text; + d.field9.refFieldType = fType; + } else if d.fieldCount == 10 { + d.field10.name = fName.text; + d.field10.refFieldType = fType; + } else if d.fieldCount == 11 { + d.field11.name = fName.text; + d.field11.refFieldType = fType; + } else if d.fieldCount == 12 { + d.field12.name = fName.text; + d.field12.refFieldType = fType; + } else if d.fieldCount == 13 { + d.field13.name = fName.text; + d.field13.refFieldType = fType; + } else if d.fieldCount == 14 { + d.field14.name = fName.text; + d.field14.refFieldType = fType; + } else if d.fieldCount == 15 { + d.field15.name = fName.text; + d.field15.refFieldType = fType; + } else if d.fieldCount == 16 { + d.field16.name = fName.text; + d.field16.refFieldType = fType; + } else if d.fieldCount == 17 { + d.field17.name = fName.text; + d.field17.refFieldType = fType; + } else if d.fieldCount == 18 { + d.field18.name = fName.text; + d.field18.refFieldType = fType; + } else if d.fieldCount == 19 { + d.field19.name = fName.text; + d.field19.refFieldType = fType; + } else if d.fieldCount == 20 { + d.field20.name = fName.text; + d.field20.refFieldType = fType; + } else if d.fieldCount == 21 { + d.field21.name = fName.text; + d.field21.refFieldType = fType; + } else if d.fieldCount == 22 { + d.field22.name = fName.text; + d.field22.refFieldType = fType; + } else if d.fieldCount == 23 { + d.field23.name = fName.text; + d.field23.refFieldType = fType; + } else if d.fieldCount == 24 { + d.field24.name = fName.text; + d.field24.refFieldType = fType; + } else if d.fieldCount == 25 { + d.field25.name = fName.text; + d.field25.refFieldType = fType; + } else if d.fieldCount == 26 { + d.field26.name = fName.text; + d.field26.refFieldType = fType; + } else if d.fieldCount == 27 { + d.field27.name = fName.text; + d.field27.refFieldType = fType; + } else if d.fieldCount == 28 { + d.field28.name = fName.text; + d.field28.refFieldType = fType; + } else if d.fieldCount == 29 { + d.field29.name = fName.text; + d.field29.refFieldType = fType; + } else if d.fieldCount == 30 { + d.field30.name = fName.text; + d.field30.refFieldType = fType; + } else if d.fieldCount == 31 { + d.field31.name = fName.text; + d.field31.refFieldType = fType; + } else if d.fieldCount == 32 { + d.field32.name = fName.text; + d.field32.refFieldType = fType; + } else if d.fieldCount == 33 { + d.field33.name = fName.text; + d.field33.refFieldType = fType; + } else if d.fieldCount == 34 { + d.field34.name = fName.text; + d.field34.refFieldType = fType; + } else if d.fieldCount == 35 { + d.field35.name = fName.text; + d.field35.refFieldType = fType; + } else if d.fieldCount == 36 { + d.field36.name = fName.text; + d.field36.refFieldType = fType; + } else if d.fieldCount == 37 { + d.field37.name = fName.text; + d.field37.refFieldType = fType; + } else if d.fieldCount == 38 { + d.field38.name = fName.text; + d.field38.refFieldType = fType; + } else if d.fieldCount == 39 { + d.field39.name = fName.text; + d.field39.refFieldType = fType; + } else if d.fieldCount == 40 { + d.field40.name = fName.text; + d.field40.refFieldType = fType; + } else if d.fieldCount == 41 { + d.field41.name = fName.text; + d.field41.refFieldType = fType; + } else if d.fieldCount == 42 { + d.field42.name = fName.text; + d.field42.refFieldType = fType; + } else if d.fieldCount == 43 { + d.field43.name = fName.text; + d.field43.refFieldType = fType; + } else if d.fieldCount == 44 { + d.field44.name = fName.text; + d.field44.refFieldType = fType; + } else if d.fieldCount == 45 { + d.field45.name = fName.text; + d.field45.refFieldType = fType; + } else if d.fieldCount == 46 { + d.field46.name = fName.text; + d.field46.refFieldType = fType; + } else if d.fieldCount == 47 { + d.field47.name = fName.text; + d.field47.refFieldType = fType; + } else if d.fieldCount == 48 { + d.field48.name = fName.text; + d.field48.refFieldType = fType; + } else if d.fieldCount == 49 { + d.field49.name = fName.text; + d.field49.refFieldType = fType; + } else if d.fieldCount == 50 { + d.field50.name = fName.text; + d.field50.refFieldType = fType; + } else if d.fieldCount == 51 { + d.field51.name = fName.text; + d.field51.refFieldType = fType; + } else if d.fieldCount == 52 { + d.field52.name = fName.text; + d.field52.refFieldType = fType; + } else if d.fieldCount == 53 { + d.field53.name = fName.text; + d.field53.refFieldType = fType; + } else if d.fieldCount == 54 { + d.field54.name = fName.text; + d.field54.refFieldType = fType; + } else if d.fieldCount == 55 { + d.field55.name = fName.text; + d.field55.refFieldType = fType; + } else if d.fieldCount == 56 { + d.field56.name = fName.text; + d.field56.refFieldType = fType; + } else if d.fieldCount == 57 { + d.field57.name = fName.text; + d.field57.refFieldType = fType; + } else if d.fieldCount == 58 { + d.field58.name = fName.text; + d.field58.refFieldType = fType; + } else if d.fieldCount == 59 { + d.field59.name = fName.text; + d.field59.refFieldType = fType; + } else if d.fieldCount == 60 { + d.field60.name = fName.text; + d.field60.refFieldType = fType; + } else if d.fieldCount == 61 { + d.field61.name = fName.text; + d.field61.refFieldType = fType; + } else if d.fieldCount == 62 { + d.field62.name = fName.text; + d.field62.refFieldType = fType; + } else if d.fieldCount == 63 { + d.field63.name = fName.text; + d.field63.refFieldType = fType; } d.fieldCount = d.fieldCount + 1; } diff --git a/src_bux/scope.bux b/src_bux/scope.bux index 062d359..2b18114 100644 --- a/src_bux/scope.bux +++ b/src_bux/scope.bux @@ -9,21 +9,21 @@ const skConst: int = 3; const skModule: int = 4; // Maximum symbols per scope -const maxSymbols: int = 256; +const maxSymbols: int = 1024; struct Symbol { - kind: int, - name: String, - typeKind: int, - typeName: String, - isMutable: bool, - isPublic: bool, + kind: int; + name: String; + typeKind: int; + typeName: String; + isMutable: bool; + isPublic: bool; } struct Scope { - symbols: *Symbol, - count: int, - parent: *Scope, + symbols: *Symbol; + count: int; + parent: *Scope; } // --------------------------------------------------------------------------- diff --git a/src_bux/sema.bux b/src_bux/sema.bux index 306c6eb..6a573ee 100644 --- a/src_bux/sema.bux +++ b/src_bux/sema.bux @@ -6,19 +6,19 @@ module Sema { // Sema context // --------------------------------------------------------------------------- struct Sema { - module: *Module, - scope: *Scope, - typeTable: *void, - methodTable: *void, - diagCount: int, - diags: *SemaDiag, - hasError: bool, + module: *Module; + scope: *Scope; + typeTable: *void; + methodTable: *void; + diagCount: int; + diags: *SemaDiag; + hasError: bool; } struct SemaDiag { - line: uint32, - column: uint32, - message: String, + line: uint32; + column: uint32; + message: String; } // --------------------------------------------------------------------------- diff --git a/src_bux/source_location.bux b/src_bux/source_location.bux index 3ab88e6..3d01e5e 100644 --- a/src_bux/source_location.bux +++ b/src_bux/source_location.bux @@ -2,9 +2,9 @@ module SourceLocation { struct SourceLocation { - line: uint32, - column: uint32, - offset: uint32, + line: uint32; + column: uint32; + offset: uint32; } func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation { diff --git a/src_bux/token.bux b/src_bux/token.bux index 6c652b0..f20fd7e 100644 --- a/src_bux/token.bux +++ b/src_bux/token.bux @@ -137,17 +137,18 @@ const tkUnknown: int = 101; const tkAsync: int = 102; const tkAwait: int = 103; const tkSpawn: int = 104; +const tkDiscard: int = 105; // --------------------------------------------------------------------------- // Token struct // --------------------------------------------------------------------------- struct Token { - kind: int, - text: String, - line: uint32, - column: uint32, - offset: uint32, + kind: int; + text: String; + line: uint32; + column: uint32; + offset: uint32; } // --------------------------------------------------------------------------- diff --git a/src_bux/types.bux b/src_bux/types.bux index 7e84503..f3bc6de 100644 --- a/src_bux/types.bux +++ b/src_bux/types.bux @@ -40,16 +40,16 @@ const tyFunc: int = 28; // --------------------------------------------------------------------------- struct Type { - kind: int, - name: String, + kind: int; + name: String; // inner types stored as array of pointers (simplified) - innerKind1: int, - innerName1: String, - innerKind2: int, - innerName2: String, - innerKind3: int, - innerName3: String, - innerCount: int, + innerKind1: int; + innerName1: String; + innerKind2: int; + innerName2: String; + innerKind3: int; + innerName3: String; + innerCount: int; } // --------------------------------------------------------------------------- diff --git a/stdlib/runtime.c b/stdlib/runtime.c index 6bf1093..258d743 100644 --- a/stdlib/runtime.c +++ b/stdlib/runtime.c @@ -43,6 +43,45 @@ void bux_free(void* ptr) { free(ptr); } +/* Escape a string for C output (newlines -> \n, quotes -> \", etc.) */ +/* Returns escaped content WITHOUT surrounding quotes */ +char* bux_escape_c_string(const char* s, int len) { + if (s == NULL || len <= 0) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + /* Worst case: every char needs escaping (e.g., all newlines -> 2 chars) */ + char* buf = (char*)bux_alloc(len * 2 + 1); + int j = 0; + for (int i = 0; i < len; i++) { + char c = s[i]; + switch (c) { + case '\n': buf[j++] = '\\'; buf[j++] = 'n'; break; + case '\r': buf[j++] = '\\'; buf[j++] = 'r'; break; + case '\t': buf[j++] = '\\'; buf[j++] = 't'; break; + case '\\': buf[j++] = '\\'; buf[j++] = '\\'; break; + case '"': buf[j++] = '\\'; buf[j++] = '"'; break; + default: buf[j++] = c; break; + } + } + buf[j] = '\0'; + return buf; +} + +int bux_run_nim(const char* nim_file, const char* out_bin) { + char cmd[4096]; + snprintf(cmd, sizeof(cmd), "nim c -o:%s -d:release --gc:orc %s 2>&1", out_bin, nim_file); + return system(cmd); +} + +int bux_strlen_c(const char* s) { + if (s == NULL) return 0; + const char* p = s; + while (*p) p++; + return (int)(p - s); +} + /* I/O */ void bux_print(const char* s) { if (s != NULL) { @@ -447,9 +486,33 @@ int bux_write_file(const char* path, const char* content) { FILE* f = fopen(path, "wb"); if (!f) return 0; size_t len = strlen(content); - size_t written = fwrite(content, 1, len, f); + size_t written = 0; + for (size_t i = 0; i < len; i++) { + if (content[i] == '\\' && i + 1 < len && content[i + 1] == 'n') { + if (fputc('\n', f) == EOF) break; + i++; + } else if (content[i] == '\\' && i + 1 < len && content[i + 1] == 't') { + if (fputc('\t', f) == EOF) break; + i++; + } else if (content[i] == '\\' && i + 1 < len && content[i + 1] == 'r') { + if (fputc('\r', f) == EOF) break; + i++; + } else if (content[i] == '\\' && i + 1 < len && content[i + 1] == '\\') { + if (fputc('\\', f) == EOF) break; + i++; + } else if (content[i] == '\\' && i + 1 < len && content[i + 1] == '"') { + if (fputc('"', f) == EOF) break; + i++; + } else if (content[i] == '\\' && i + 1 < len && content[i + 1] == '\'') { + if (fputc('\'', f) == EOF) break; + i++; + } else { + if (fputc(content[i], f) == EOF) break; + } + written++; + } fclose(f); - return written == len ? 1 : 0; + return written > 0 ? 1 : 0; } /* File I/O — check if file exists */ diff --git a/tests/test_ast_fields b/tests/test_ast_fields new file mode 100755 index 0000000..18cdeb4 Binary files /dev/null and b/tests/test_ast_fields differ diff --git a/tests/test_ast_fields.nim b/tests/test_ast_fields.nim new file mode 100644 index 0000000..2b3cf51 --- /dev/null +++ b/tests/test_ast_fields.nim @@ -0,0 +1,15 @@ +import "../src/lexer", "../src/parser", "../src/ast" + +let source = readFile("../_selfhost/src/ast.bux") +let lexRes = tokenize(source, "ast.bux") +let res = parse(lexRes.tokens, "ast.bux") +if res.module != nil: + echo "Module items: ", res.module.items.len + for decl in res.module.items: + if decl.kind == dkStruct: + echo "Struct: ", decl.declStructName, " fields: ", decl.declStructFields.len + elif decl.kind == dkModule: + echo "Inner module: ", decl.declModuleName, " items: ", decl.declModuleItems.len + for inner in decl.declModuleItems: + if inner.kind == dkStruct: + echo " Struct: ", inner.declStructName, " fields: ", inner.declStructFields.len diff --git a/tests/test_parse_ast b/tests/test_parse_ast new file mode 100755 index 0000000..b23f7a5 Binary files /dev/null and b/tests/test_parse_ast differ diff --git a/tests/test_parse_ast.nim b/tests/test_parse_ast.nim new file mode 100644 index 0000000..b7f2bfa --- /dev/null +++ b/tests/test_parse_ast.nim @@ -0,0 +1,16 @@ +import "../src/lexer", "../src/parser", "../src/ast", "../src/token" +import std/os + +let source = readFile("../_selfhost/src/ast.bux") +let lexRes = tokenize(source, "ast.bux") +echo "Tokens: ", lexRes.tokens.len +echo "Errors: ", lexRes.hasErrors +let res = parse(lexRes.tokens, "ast.bux") +if res.module == nil: + echo "Parse failed, diagnostics: ", res.diagnostics.len + for d in res.diagnostics: + echo " ", d.message +else: + echo "Parsed OK, items: ", res.module.items.len + for i, decl in res.module.items: + echo "Decl ", i, " kind=", decl.kind, " name=", decl.declFuncName