feat: self-hosting progress - Nim backend, parser fixes, 64 struct fields, 13/14 modules check
This commit is contained in:
+57
-1
@@ -244,7 +244,7 @@ struct Decl {
|
|||||||
retType: *TypeExpr,
|
retType: *TypeExpr,
|
||||||
// Body
|
// Body
|
||||||
refBody: *Block,
|
refBody: *Block,
|
||||||
// Struct fields (up to 8)
|
// Struct fields (up to 64)
|
||||||
fieldCount: int,
|
fieldCount: int,
|
||||||
field0: StructField,
|
field0: StructField,
|
||||||
field1: StructField,
|
field1: StructField,
|
||||||
@@ -254,6 +254,62 @@ struct Decl {
|
|||||||
field5: StructField,
|
field5: StructField,
|
||||||
field6: StructField,
|
field6: StructField,
|
||||||
field7: 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)
|
// Enum variants (up to 8)
|
||||||
variantCount: int,
|
variantCount: int,
|
||||||
variant0: EnumVariant,
|
variant0: EnumVariant,
|
||||||
|
|||||||
+51
-25
@@ -1,5 +1,5 @@
|
|||||||
// cli.bux — CLI driver for the Bux self-hosting compiler
|
// 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 {
|
module Cli {
|
||||||
|
|
||||||
extern func PrintLine(s: String);
|
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_dir_exists(path: String) -> int;
|
||||||
extern func bux_path_join(a: String, b: String) -> String;
|
extern func bux_path_join(a: String, b: String) -> String;
|
||||||
extern func bux_mkdir_if_needed(path: String) -> int;
|
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;
|
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
|
||||||
|
|
||||||
func ReadFile(path: String) -> String {
|
func ReadFile(path: String) -> String {
|
||||||
@@ -38,7 +38,9 @@ func DirExists(path: String) -> bool {
|
|||||||
|
|
||||||
func Cli_Compile(source: String, sourceName: String) -> String {
|
func Cli_Compile(source: String, sourceName: String) -> String {
|
||||||
// Phase 1: Lex
|
// Phase 1: Lex
|
||||||
|
PrintLine(" Lexing...");
|
||||||
let lex: *Lexer = Lexer_Tokenize(source);
|
let lex: *Lexer = Lexer_Tokenize(source);
|
||||||
|
PrintLine(" Lex done");
|
||||||
if Lexer_DiagCount(lex) > 0 {
|
if Lexer_DiagCount(lex) > 0 {
|
||||||
PrintLine("Lex errors:");
|
PrintLine("Lex errors:");
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
@@ -91,13 +93,13 @@ func Cli_Compile(source: String, sourceName: String) -> String {
|
|||||||
PrintLine("");
|
PrintLine("");
|
||||||
|
|
||||||
// Phase 5: C code generation
|
// Phase 5: C code generation
|
||||||
let cCode: String = CBackend_Generate(hirMod);
|
let nimCode: String = NimBackend_Generate(hirMod);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Lexer_Free(lex);
|
Lexer_Free(lex);
|
||||||
Sema_Free(sema);
|
Sema_Free(sema);
|
||||||
|
|
||||||
return cCode;
|
return nimCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -125,7 +127,7 @@ func Cli_Build(srcPath: String, outPath: String) -> int {
|
|||||||
// Write C output
|
// Write C output
|
||||||
let ok: bool = WriteFile(outPath, cCode);
|
let ok: bool = WriteFile(outPath, cCode);
|
||||||
if ok {
|
if ok {
|
||||||
Print(" → C code written to ");
|
Print(" → Nim code written to ");
|
||||||
PrintLine(outPath);
|
PrintLine(outPath);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -148,11 +150,37 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
}
|
}
|
||||||
PrintLine(" File read OK");
|
PrintLine(" File read OK");
|
||||||
|
|
||||||
let cCode: String = Cli_Compile(source, srcPath);
|
// Phase 1: Lex
|
||||||
if String_Eq(cCode, "") {
|
PrintLine(" Lexing...");
|
||||||
|
let lex: *Lexer = Lexer_Tokenize(source);
|
||||||
|
PrintLine(" Lex done");
|
||||||
|
if Lexer_DiagCount(lex) > 0 {
|
||||||
|
PrintLine("Lex errors found");
|
||||||
return 1;
|
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");
|
PrintLine("Check passed");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -163,7 +191,9 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
|
|
||||||
func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
|
func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
|
||||||
// Phase 1: Lex
|
// Phase 1: Lex
|
||||||
|
PrintLine(" Lexing...");
|
||||||
let lex: *Lexer = Lexer_Tokenize(source);
|
let lex: *Lexer = Lexer_Tokenize(source);
|
||||||
|
PrintLine(" Lex done");
|
||||||
if Lexer_DiagCount(lex) > 0 {
|
if Lexer_DiagCount(lex) > 0 {
|
||||||
Print("Lex errors in ");
|
Print("Lex errors in ");
|
||||||
PrintLine(sourceName);
|
PrintLine(sourceName);
|
||||||
@@ -286,34 +316,30 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
PrintLine("Lowering to HIR...");
|
PrintLine("Lowering to HIR...");
|
||||||
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
||||||
|
|
||||||
// C code generation
|
// Nim code generation
|
||||||
PrintLine("Generating C code...");
|
PrintLine("Generating Nim code...");
|
||||||
let cCode: String = CBackend_Generate(hirMod);
|
let nimCode: String = NimBackend_Generate(hirMod);
|
||||||
|
|
||||||
// Create build directory
|
// Create build directory
|
||||||
let buildDir: String = bux_path_join(projectDir, "build");
|
let buildDir: String = bux_path_join(projectDir, "build");
|
||||||
discard bux_mkdir_if_needed(buildDir);
|
discard bux_mkdir_if_needed(buildDir);
|
||||||
|
|
||||||
// Write C output
|
// Write Nim output
|
||||||
let cFile: String = bux_path_join(buildDir, "main.c");
|
let nimFile: String = bux_path_join(buildDir, "main.nim");
|
||||||
if !WriteFile(cFile, cCode) {
|
if !WriteFile(nimFile, nimCode) {
|
||||||
PrintLine("Error: cannot write main.c");
|
PrintLine("Error: cannot write main.nim");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Print("C code written to ");
|
Print("Nim code written to ");
|
||||||
PrintLine(cFile);
|
PrintLine(nimFile);
|
||||||
|
|
||||||
// Find stdlib files
|
// Invoke Nim compiler
|
||||||
let runtimeC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/runtime.c");
|
PrintLine("Invoking Nim compiler...");
|
||||||
let ioC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/io.c");
|
|
||||||
|
|
||||||
// Invoke C compiler
|
|
||||||
PrintLine("Invoking C compiler...");
|
|
||||||
let outBin: String = bux_path_join(buildDir, "buxc3");
|
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 {
|
if rc != 0 {
|
||||||
PrintLine("Error: C compilation failed");
|
PrintLine("Error: Nim compilation failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +389,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
|
|
||||||
if String_Eq(cmd, "build") {
|
if String_Eq(cmd, "build") {
|
||||||
let src: String = "src/Main.bux";
|
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 >= 3 { src = args[2]; }
|
||||||
if argCount >= 4 { out = args[3]; }
|
if argCount >= 4 { out = args[3]; }
|
||||||
return Cli_Build(src, out);
|
return Cli_Build(src, out);
|
||||||
|
|||||||
+53
-46
@@ -39,22 +39,22 @@ const hAwait: int = 30;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirNode {
|
struct HirNode {
|
||||||
kind: int,
|
kind: int;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
typeKind: int,
|
typeKind: int;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
// Common fields (used by multiple kinds)
|
// Common fields (used by multiple kinds)
|
||||||
strValue: String, // var name, callee name, field name, label
|
strValue: String; // var name; callee name; field name; label
|
||||||
intValue: int, // token kind (for lit, unary op, binary op)
|
intValue: int; // token kind (for lit; unary op; binary op)
|
||||||
boolValue: bool, // range inclusive, isScope
|
boolValue: bool; // range inclusive; isScope
|
||||||
// Child nodes (up to 3)
|
// Child nodes (up to 3)
|
||||||
child1: *HirNode, // left/operand/condition/base
|
child1: *HirNode; // left/operand/condition/base
|
||||||
child2: *HirNode, // right/value/then/body
|
child2: *HirNode; // right/value/then/body
|
||||||
child3: *HirNode, // else/third
|
child3: *HirNode; // else/third
|
||||||
// Extra data pointer (for children arrays, field lists, etc.)
|
// Extra data pointer (for children arrays, field lists, etc.)
|
||||||
extraData: *void,
|
extraData: *void;
|
||||||
extraCount: int,
|
extraCount: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -62,24 +62,24 @@ struct HirNode {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirParam {
|
struct HirParam {
|
||||||
name: String,
|
name: String;
|
||||||
typeKind: int,
|
typeKind: int;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HirFunc {
|
struct HirFunc {
|
||||||
name: String,
|
name: String;
|
||||||
paramCount: int,
|
paramCount: int;
|
||||||
param0: HirParam,
|
param0: HirParam;
|
||||||
param1: HirParam,
|
param1: HirParam;
|
||||||
param2: HirParam,
|
param2: HirParam;
|
||||||
param3: HirParam,
|
param3: HirParam;
|
||||||
param4: HirParam,
|
param4: HirParam;
|
||||||
param5: HirParam,
|
param5: HirParam;
|
||||||
retTypeKind: int,
|
retTypeKind: int;
|
||||||
retTypeName: String,
|
retTypeName: String;
|
||||||
body: *HirNode,
|
body: *HirNode;
|
||||||
isPublic: bool,
|
isPublic: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -87,12 +87,12 @@ struct HirFunc {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirEnumVariant {
|
struct HirEnumVariant {
|
||||||
name: String,
|
name: String;
|
||||||
fieldCount: int,
|
fieldCount: int;
|
||||||
fieldType0: int,
|
fieldType0: int;
|
||||||
fieldName0: String,
|
fieldName0: String;
|
||||||
fieldType1: int,
|
fieldType1: int;
|
||||||
fieldName1: String,
|
fieldName1: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -100,24 +100,31 @@ struct HirEnumVariant {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirStructField {
|
struct HirStructField {
|
||||||
name: String,
|
name: String;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HirStruct {
|
struct HirStruct {
|
||||||
name: String,
|
name: String;
|
||||||
fieldCount: int,
|
fieldCount: int;
|
||||||
fields: *HirStructField,
|
fields: *HirStructField;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HirConst {
|
||||||
|
name: String;
|
||||||
|
value: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HirModule {
|
struct HirModule {
|
||||||
funcCount: int,
|
funcCount: int;
|
||||||
funcs: *HirFunc,
|
funcs: *HirFunc;
|
||||||
externCount: int,
|
externCount: int;
|
||||||
externFuncs: *HirFunc,
|
externFuncs: *HirFunc;
|
||||||
structCount: int,
|
structCount: int;
|
||||||
structs: *HirStruct,
|
structs: *HirStruct;
|
||||||
enumCount: int,
|
enumCount: int;
|
||||||
|
constCount: int;
|
||||||
|
consts: *HirConst;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+20
-19
@@ -46,31 +46,31 @@ const maxTokens: int = 32768;
|
|||||||
const maxDiags: int = 128;
|
const maxDiags: int = 128;
|
||||||
|
|
||||||
struct LexerDiag {
|
struct LexerDiag {
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
message: String,
|
message: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Lexer {
|
struct Lexer {
|
||||||
source: String,
|
source: String;
|
||||||
sourceLen: int,
|
sourceLen: int;
|
||||||
pos: int,
|
pos: int;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
startLine: uint32,
|
startLine: uint32;
|
||||||
startColumn: uint32,
|
startColumn: uint32;
|
||||||
startPos: int,
|
startPos: int;
|
||||||
tokenCount: int,
|
tokenCount: int;
|
||||||
tokens: *LexToken,
|
tokens: *LexToken;
|
||||||
diagCount: int,
|
diagCount: int;
|
||||||
diags: *LexerDiag,
|
diags: *LexerDiag;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LexToken {
|
struct LexToken {
|
||||||
kind: int,
|
kind: int;
|
||||||
text: String,
|
text: String;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -294,6 +294,7 @@ func lexKeywordKind(text: String) -> int {
|
|||||||
if String_Eq(text, "self") { return tkSelf; }
|
if String_Eq(text, "self") { return tkSelf; }
|
||||||
if String_Eq(text, "super") { return tkSuper; }
|
if String_Eq(text, "super") { return tkSuper; }
|
||||||
if String_Eq(text, "sizeof") { return tkSizeOf; }
|
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, "async") { return tkAsync; }
|
||||||
if String_Eq(text, "await") { return tkAwait; }
|
if String_Eq(text, "await") { return tkAwait; }
|
||||||
if String_Eq(text, "spawn") { return tkSpawn; }
|
if String_Eq(text, "spawn") { return tkSpawn; }
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ module Manifest {
|
|||||||
// Manifest struct
|
// Manifest struct
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
struct Manifest {
|
struct Manifest {
|
||||||
name: String,
|
name: String;
|
||||||
version: String,
|
version: String;
|
||||||
pkgType: String,
|
pkgType: String;
|
||||||
output: String,
|
output: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -917,6 +917,174 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl {
|
|||||||
} else if d.fieldCount == 7 {
|
} else if d.fieldCount == 7 {
|
||||||
d.field7.name = fName.text;
|
d.field7.name = fName.text;
|
||||||
d.field7.refFieldType = fType;
|
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;
|
d.fieldCount = d.fieldCount + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -9,21 +9,21 @@ const skConst: int = 3;
|
|||||||
const skModule: int = 4;
|
const skModule: int = 4;
|
||||||
|
|
||||||
// Maximum symbols per scope
|
// Maximum symbols per scope
|
||||||
const maxSymbols: int = 256;
|
const maxSymbols: int = 1024;
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
kind: int,
|
kind: int;
|
||||||
name: String,
|
name: String;
|
||||||
typeKind: int,
|
typeKind: int;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
isMutable: bool,
|
isMutable: bool;
|
||||||
isPublic: bool,
|
isPublic: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
symbols: *Symbol,
|
symbols: *Symbol;
|
||||||
count: int,
|
count: int;
|
||||||
parent: *Scope,
|
parent: *Scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+10
-10
@@ -6,19 +6,19 @@ module Sema {
|
|||||||
// Sema context
|
// Sema context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
struct Sema {
|
struct Sema {
|
||||||
module: *Module,
|
module: *Module;
|
||||||
scope: *Scope,
|
scope: *Scope;
|
||||||
typeTable: *void,
|
typeTable: *void;
|
||||||
methodTable: *void,
|
methodTable: *void;
|
||||||
diagCount: int,
|
diagCount: int;
|
||||||
diags: *SemaDiag,
|
diags: *SemaDiag;
|
||||||
hasError: bool,
|
hasError: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SemaDiag {
|
struct SemaDiag {
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
message: String,
|
message: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
module SourceLocation {
|
module SourceLocation {
|
||||||
|
|
||||||
struct SourceLocation {
|
struct SourceLocation {
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
offset: uint32,
|
offset: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation {
|
func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation {
|
||||||
|
|||||||
@@ -137,17 +137,18 @@ const tkUnknown: int = 101;
|
|||||||
const tkAsync: int = 102;
|
const tkAsync: int = 102;
|
||||||
const tkAwait: int = 103;
|
const tkAwait: int = 103;
|
||||||
const tkSpawn: int = 104;
|
const tkSpawn: int = 104;
|
||||||
|
const tkDiscard: int = 105;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Token struct
|
// Token struct
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
kind: int,
|
kind: int;
|
||||||
text: String,
|
text: String;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
offset: uint32,
|
offset: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -40,16 +40,16 @@ const tyFunc: int = 28;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct Type {
|
struct Type {
|
||||||
kind: int,
|
kind: int;
|
||||||
name: String,
|
name: String;
|
||||||
// inner types stored as array of pointers (simplified)
|
// inner types stored as array of pointers (simplified)
|
||||||
innerKind1: int,
|
innerKind1: int;
|
||||||
innerName1: String,
|
innerName1: String;
|
||||||
innerKind2: int,
|
innerKind2: int;
|
||||||
innerName2: String,
|
innerName2: String;
|
||||||
innerKind3: int,
|
innerKind3: int;
|
||||||
innerName3: String,
|
innerName3: String;
|
||||||
innerCount: int,
|
innerCount: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -600,7 +600,10 @@ proc parseUnary(p: var Parser): Expr =
|
|||||||
let op = p.advance().kind
|
let op = p.advance().kind
|
||||||
if op == tkAmp and p.check(tkMut):
|
if op == tkAmp and p.check(tkMut):
|
||||||
discard p.advance() # mut
|
discard p.advance() # mut
|
||||||
|
let savedStructInit = p.structInitAllowed
|
||||||
|
p.structInitAllowed = false
|
||||||
let operand = p.parseUnary()
|
let operand = p.parseUnary()
|
||||||
|
p.structInitAllowed = savedStructInit
|
||||||
return Expr(kind: ekUnary, loc: loc, exprUnaryOp: op, exprUnaryOperand: operand)
|
return Expr(kind: ekUnary, loc: loc, exprUnaryOp: op, exprUnaryOperand: operand)
|
||||||
of tkPlusPlus, tkMinusMinus:
|
of tkPlusPlus, tkMinusMinus:
|
||||||
let op = p.advance().kind
|
let op = p.advance().kind
|
||||||
@@ -809,7 +812,9 @@ proc parseStmt(p: var Parser): Stmt =
|
|||||||
while p.check(tkNewLine): discard p.advance()
|
while p.check(tkNewLine): discard p.advance()
|
||||||
if p.check(tkIf):
|
if p.check(tkIf):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
|
p.structInitAllowed = false
|
||||||
let elifCond = p.parseExpr()
|
let elifCond = p.parseExpr()
|
||||||
|
p.structInitAllowed = true
|
||||||
let elifBlk = p.parseBlock()
|
let elifBlk = p.parseBlock()
|
||||||
elseIfs.add(ElseIf(loc: elseLoc, cond: elifCond, blk: elifBlk))
|
elseIfs.add(ElseIf(loc: elseLoc, cond: elifCond, blk: elifBlk))
|
||||||
while p.check(tkNewLine): discard p.advance()
|
while p.check(tkNewLine): discard p.advance()
|
||||||
|
|||||||
+57
-1
@@ -244,7 +244,7 @@ struct Decl {
|
|||||||
retType: *TypeExpr,
|
retType: *TypeExpr,
|
||||||
// Body
|
// Body
|
||||||
refBody: *Block,
|
refBody: *Block,
|
||||||
// Struct fields (up to 8)
|
// Struct fields (up to 64)
|
||||||
fieldCount: int,
|
fieldCount: int,
|
||||||
field0: StructField,
|
field0: StructField,
|
||||||
field1: StructField,
|
field1: StructField,
|
||||||
@@ -254,6 +254,62 @@ struct Decl {
|
|||||||
field5: StructField,
|
field5: StructField,
|
||||||
field6: StructField,
|
field6: StructField,
|
||||||
field7: 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)
|
// Enum variants (up to 8)
|
||||||
variantCount: int,
|
variantCount: int,
|
||||||
variant0: EnumVariant,
|
variant0: EnumVariant,
|
||||||
|
|||||||
+51
-25
@@ -1,5 +1,5 @@
|
|||||||
// cli.bux — CLI driver for the Bux self-hosting compiler
|
// 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 {
|
module Cli {
|
||||||
|
|
||||||
extern func PrintLine(s: String);
|
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_dir_exists(path: String) -> int;
|
||||||
extern func bux_path_join(a: String, b: String) -> String;
|
extern func bux_path_join(a: String, b: String) -> String;
|
||||||
extern func bux_mkdir_if_needed(path: String) -> int;
|
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;
|
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
|
||||||
|
|
||||||
func ReadFile(path: String) -> String {
|
func ReadFile(path: String) -> String {
|
||||||
@@ -38,7 +38,9 @@ func DirExists(path: String) -> bool {
|
|||||||
|
|
||||||
func Cli_Compile(source: String, sourceName: String) -> String {
|
func Cli_Compile(source: String, sourceName: String) -> String {
|
||||||
// Phase 1: Lex
|
// Phase 1: Lex
|
||||||
|
PrintLine(" Lexing...");
|
||||||
let lex: *Lexer = Lexer_Tokenize(source);
|
let lex: *Lexer = Lexer_Tokenize(source);
|
||||||
|
PrintLine(" Lex done");
|
||||||
if Lexer_DiagCount(lex) > 0 {
|
if Lexer_DiagCount(lex) > 0 {
|
||||||
PrintLine("Lex errors:");
|
PrintLine("Lex errors:");
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
@@ -91,13 +93,13 @@ func Cli_Compile(source: String, sourceName: String) -> String {
|
|||||||
PrintLine("");
|
PrintLine("");
|
||||||
|
|
||||||
// Phase 5: C code generation
|
// Phase 5: C code generation
|
||||||
let cCode: String = CBackend_Generate(hirMod);
|
let nimCode: String = NimBackend_Generate(hirMod);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Lexer_Free(lex);
|
Lexer_Free(lex);
|
||||||
Sema_Free(sema);
|
Sema_Free(sema);
|
||||||
|
|
||||||
return cCode;
|
return nimCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -125,7 +127,7 @@ func Cli_Build(srcPath: String, outPath: String) -> int {
|
|||||||
// Write C output
|
// Write C output
|
||||||
let ok: bool = WriteFile(outPath, cCode);
|
let ok: bool = WriteFile(outPath, cCode);
|
||||||
if ok {
|
if ok {
|
||||||
Print(" → C code written to ");
|
Print(" → Nim code written to ");
|
||||||
PrintLine(outPath);
|
PrintLine(outPath);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -148,11 +150,37 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
}
|
}
|
||||||
PrintLine(" File read OK");
|
PrintLine(" File read OK");
|
||||||
|
|
||||||
let cCode: String = Cli_Compile(source, srcPath);
|
// Phase 1: Lex
|
||||||
if String_Eq(cCode, "") {
|
PrintLine(" Lexing...");
|
||||||
|
let lex: *Lexer = Lexer_Tokenize(source);
|
||||||
|
PrintLine(" Lex done");
|
||||||
|
if Lexer_DiagCount(lex) > 0 {
|
||||||
|
PrintLine("Lex errors found");
|
||||||
return 1;
|
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");
|
PrintLine("Check passed");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -163,7 +191,9 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
|
|
||||||
func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
|
func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
|
||||||
// Phase 1: Lex
|
// Phase 1: Lex
|
||||||
|
PrintLine(" Lexing...");
|
||||||
let lex: *Lexer = Lexer_Tokenize(source);
|
let lex: *Lexer = Lexer_Tokenize(source);
|
||||||
|
PrintLine(" Lex done");
|
||||||
if Lexer_DiagCount(lex) > 0 {
|
if Lexer_DiagCount(lex) > 0 {
|
||||||
Print("Lex errors in ");
|
Print("Lex errors in ");
|
||||||
PrintLine(sourceName);
|
PrintLine(sourceName);
|
||||||
@@ -286,34 +316,30 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
PrintLine("Lowering to HIR...");
|
PrintLine("Lowering to HIR...");
|
||||||
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
||||||
|
|
||||||
// C code generation
|
// Nim code generation
|
||||||
PrintLine("Generating C code...");
|
PrintLine("Generating Nim code...");
|
||||||
let cCode: String = CBackend_Generate(hirMod);
|
let nimCode: String = NimBackend_Generate(hirMod);
|
||||||
|
|
||||||
// Create build directory
|
// Create build directory
|
||||||
let buildDir: String = bux_path_join(projectDir, "build");
|
let buildDir: String = bux_path_join(projectDir, "build");
|
||||||
discard bux_mkdir_if_needed(buildDir);
|
discard bux_mkdir_if_needed(buildDir);
|
||||||
|
|
||||||
// Write C output
|
// Write Nim output
|
||||||
let cFile: String = bux_path_join(buildDir, "main.c");
|
let nimFile: String = bux_path_join(buildDir, "main.nim");
|
||||||
if !WriteFile(cFile, cCode) {
|
if !WriteFile(nimFile, nimCode) {
|
||||||
PrintLine("Error: cannot write main.c");
|
PrintLine("Error: cannot write main.nim");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Print("C code written to ");
|
Print("Nim code written to ");
|
||||||
PrintLine(cFile);
|
PrintLine(nimFile);
|
||||||
|
|
||||||
// Find stdlib files
|
// Invoke Nim compiler
|
||||||
let runtimeC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/runtime.c");
|
PrintLine("Invoking Nim compiler...");
|
||||||
let ioC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/io.c");
|
|
||||||
|
|
||||||
// Invoke C compiler
|
|
||||||
PrintLine("Invoking C compiler...");
|
|
||||||
let outBin: String = bux_path_join(buildDir, "buxc3");
|
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 {
|
if rc != 0 {
|
||||||
PrintLine("Error: C compilation failed");
|
PrintLine("Error: Nim compilation failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +389,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
|
|
||||||
if String_Eq(cmd, "build") {
|
if String_Eq(cmd, "build") {
|
||||||
let src: String = "src/Main.bux";
|
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 >= 3 { src = args[2]; }
|
||||||
if argCount >= 4 { out = args[3]; }
|
if argCount >= 4 { out = args[3]; }
|
||||||
return Cli_Build(src, out);
|
return Cli_Build(src, out);
|
||||||
|
|||||||
+53
-46
@@ -39,22 +39,22 @@ const hAwait: int = 30;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirNode {
|
struct HirNode {
|
||||||
kind: int,
|
kind: int;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
typeKind: int,
|
typeKind: int;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
// Common fields (used by multiple kinds)
|
// Common fields (used by multiple kinds)
|
||||||
strValue: String, // var name, callee name, field name, label
|
strValue: String; // var name; callee name; field name; label
|
||||||
intValue: int, // token kind (for lit, unary op, binary op)
|
intValue: int; // token kind (for lit; unary op; binary op)
|
||||||
boolValue: bool, // range inclusive, isScope
|
boolValue: bool; // range inclusive; isScope
|
||||||
// Child nodes (up to 3)
|
// Child nodes (up to 3)
|
||||||
child1: *HirNode, // left/operand/condition/base
|
child1: *HirNode; // left/operand/condition/base
|
||||||
child2: *HirNode, // right/value/then/body
|
child2: *HirNode; // right/value/then/body
|
||||||
child3: *HirNode, // else/third
|
child3: *HirNode; // else/third
|
||||||
// Extra data pointer (for children arrays, field lists, etc.)
|
// Extra data pointer (for children arrays, field lists, etc.)
|
||||||
extraData: *void,
|
extraData: *void;
|
||||||
extraCount: int,
|
extraCount: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -62,24 +62,24 @@ struct HirNode {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirParam {
|
struct HirParam {
|
||||||
name: String,
|
name: String;
|
||||||
typeKind: int,
|
typeKind: int;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HirFunc {
|
struct HirFunc {
|
||||||
name: String,
|
name: String;
|
||||||
paramCount: int,
|
paramCount: int;
|
||||||
param0: HirParam,
|
param0: HirParam;
|
||||||
param1: HirParam,
|
param1: HirParam;
|
||||||
param2: HirParam,
|
param2: HirParam;
|
||||||
param3: HirParam,
|
param3: HirParam;
|
||||||
param4: HirParam,
|
param4: HirParam;
|
||||||
param5: HirParam,
|
param5: HirParam;
|
||||||
retTypeKind: int,
|
retTypeKind: int;
|
||||||
retTypeName: String,
|
retTypeName: String;
|
||||||
body: *HirNode,
|
body: *HirNode;
|
||||||
isPublic: bool,
|
isPublic: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -87,12 +87,12 @@ struct HirFunc {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirEnumVariant {
|
struct HirEnumVariant {
|
||||||
name: String,
|
name: String;
|
||||||
fieldCount: int,
|
fieldCount: int;
|
||||||
fieldType0: int,
|
fieldType0: int;
|
||||||
fieldName0: String,
|
fieldName0: String;
|
||||||
fieldType1: int,
|
fieldType1: int;
|
||||||
fieldName1: String,
|
fieldName1: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -100,24 +100,31 @@ struct HirEnumVariant {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct HirStructField {
|
struct HirStructField {
|
||||||
name: String,
|
name: String;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HirStruct {
|
struct HirStruct {
|
||||||
name: String,
|
name: String;
|
||||||
fieldCount: int,
|
fieldCount: int;
|
||||||
fields: *HirStructField,
|
fields: *HirStructField;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HirConst {
|
||||||
|
name: String;
|
||||||
|
value: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HirModule {
|
struct HirModule {
|
||||||
funcCount: int,
|
funcCount: int;
|
||||||
funcs: *HirFunc,
|
funcs: *HirFunc;
|
||||||
externCount: int,
|
externCount: int;
|
||||||
externFuncs: *HirFunc,
|
externFuncs: *HirFunc;
|
||||||
structCount: int,
|
structCount: int;
|
||||||
structs: *HirStruct,
|
structs: *HirStruct;
|
||||||
enumCount: int,
|
enumCount: int;
|
||||||
|
constCount: int;
|
||||||
|
consts: *HirConst;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+20
-19
@@ -46,31 +46,31 @@ const maxTokens: int = 32768;
|
|||||||
const maxDiags: int = 128;
|
const maxDiags: int = 128;
|
||||||
|
|
||||||
struct LexerDiag {
|
struct LexerDiag {
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
message: String,
|
message: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Lexer {
|
struct Lexer {
|
||||||
source: String,
|
source: String;
|
||||||
sourceLen: int,
|
sourceLen: int;
|
||||||
pos: int,
|
pos: int;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
startLine: uint32,
|
startLine: uint32;
|
||||||
startColumn: uint32,
|
startColumn: uint32;
|
||||||
startPos: int,
|
startPos: int;
|
||||||
tokenCount: int,
|
tokenCount: int;
|
||||||
tokens: *LexToken,
|
tokens: *LexToken;
|
||||||
diagCount: int,
|
diagCount: int;
|
||||||
diags: *LexerDiag,
|
diags: *LexerDiag;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LexToken {
|
struct LexToken {
|
||||||
kind: int,
|
kind: int;
|
||||||
text: String,
|
text: String;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -294,6 +294,7 @@ func lexKeywordKind(text: String) -> int {
|
|||||||
if String_Eq(text, "self") { return tkSelf; }
|
if String_Eq(text, "self") { return tkSelf; }
|
||||||
if String_Eq(text, "super") { return tkSuper; }
|
if String_Eq(text, "super") { return tkSuper; }
|
||||||
if String_Eq(text, "sizeof") { return tkSizeOf; }
|
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, "async") { return tkAsync; }
|
||||||
if String_Eq(text, "await") { return tkAwait; }
|
if String_Eq(text, "await") { return tkAwait; }
|
||||||
if String_Eq(text, "spawn") { return tkSpawn; }
|
if String_Eq(text, "spawn") { return tkSpawn; }
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ module Manifest {
|
|||||||
// Manifest struct
|
// Manifest struct
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
struct Manifest {
|
struct Manifest {
|
||||||
name: String,
|
name: String;
|
||||||
version: String,
|
version: String;
|
||||||
pkgType: String,
|
pkgType: String;
|
||||||
output: String,
|
output: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -917,6 +917,174 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl {
|
|||||||
} else if d.fieldCount == 7 {
|
} else if d.fieldCount == 7 {
|
||||||
d.field7.name = fName.text;
|
d.field7.name = fName.text;
|
||||||
d.field7.refFieldType = fType;
|
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;
|
d.fieldCount = d.fieldCount + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -9,21 +9,21 @@ const skConst: int = 3;
|
|||||||
const skModule: int = 4;
|
const skModule: int = 4;
|
||||||
|
|
||||||
// Maximum symbols per scope
|
// Maximum symbols per scope
|
||||||
const maxSymbols: int = 256;
|
const maxSymbols: int = 1024;
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
kind: int,
|
kind: int;
|
||||||
name: String,
|
name: String;
|
||||||
typeKind: int,
|
typeKind: int;
|
||||||
typeName: String,
|
typeName: String;
|
||||||
isMutable: bool,
|
isMutable: bool;
|
||||||
isPublic: bool,
|
isPublic: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
symbols: *Symbol,
|
symbols: *Symbol;
|
||||||
count: int,
|
count: int;
|
||||||
parent: *Scope,
|
parent: *Scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+10
-10
@@ -6,19 +6,19 @@ module Sema {
|
|||||||
// Sema context
|
// Sema context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
struct Sema {
|
struct Sema {
|
||||||
module: *Module,
|
module: *Module;
|
||||||
scope: *Scope,
|
scope: *Scope;
|
||||||
typeTable: *void,
|
typeTable: *void;
|
||||||
methodTable: *void,
|
methodTable: *void;
|
||||||
diagCount: int,
|
diagCount: int;
|
||||||
diags: *SemaDiag,
|
diags: *SemaDiag;
|
||||||
hasError: bool,
|
hasError: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SemaDiag {
|
struct SemaDiag {
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
message: String,
|
message: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
module SourceLocation {
|
module SourceLocation {
|
||||||
|
|
||||||
struct SourceLocation {
|
struct SourceLocation {
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
offset: uint32,
|
offset: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation {
|
func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation {
|
||||||
|
|||||||
+6
-5
@@ -137,17 +137,18 @@ const tkUnknown: int = 101;
|
|||||||
const tkAsync: int = 102;
|
const tkAsync: int = 102;
|
||||||
const tkAwait: int = 103;
|
const tkAwait: int = 103;
|
||||||
const tkSpawn: int = 104;
|
const tkSpawn: int = 104;
|
||||||
|
const tkDiscard: int = 105;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Token struct
|
// Token struct
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
kind: int,
|
kind: int;
|
||||||
text: String,
|
text: String;
|
||||||
line: uint32,
|
line: uint32;
|
||||||
column: uint32,
|
column: uint32;
|
||||||
offset: uint32,
|
offset: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+9
-9
@@ -40,16 +40,16 @@ const tyFunc: int = 28;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct Type {
|
struct Type {
|
||||||
kind: int,
|
kind: int;
|
||||||
name: String,
|
name: String;
|
||||||
// inner types stored as array of pointers (simplified)
|
// inner types stored as array of pointers (simplified)
|
||||||
innerKind1: int,
|
innerKind1: int;
|
||||||
innerName1: String,
|
innerName1: String;
|
||||||
innerKind2: int,
|
innerKind2: int;
|
||||||
innerName2: String,
|
innerName2: String;
|
||||||
innerKind3: int,
|
innerKind3: int;
|
||||||
innerName3: String,
|
innerName3: String;
|
||||||
innerCount: int,
|
innerCount: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+65
-2
@@ -43,6 +43,45 @@ void bux_free(void* ptr) {
|
|||||||
free(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 */
|
/* I/O */
|
||||||
void bux_print(const char* s) {
|
void bux_print(const char* s) {
|
||||||
if (s != NULL) {
|
if (s != NULL) {
|
||||||
@@ -447,9 +486,33 @@ int bux_write_file(const char* path, const char* content) {
|
|||||||
FILE* f = fopen(path, "wb");
|
FILE* f = fopen(path, "wb");
|
||||||
if (!f) return 0;
|
if (!f) return 0;
|
||||||
size_t len = strlen(content);
|
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);
|
fclose(f);
|
||||||
return written == len ? 1 : 0;
|
return written > 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* File I/O — check if file exists */
|
/* File I/O — check if file exists */
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -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
|
||||||
Executable
BIN
Binary file not shown.
@@ -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
|
||||||
Reference in New Issue
Block a user