// 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