From fb8928e830fb10353cf16db5031bb34c23e906c7 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Tue, 12 May 2026 00:48:50 +0300 Subject: [PATCH] fix: clj_ prefix for identifiers + try/catch/finally fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #9: Add clj_ prefix to all mangled identifiers to avoid Nim case-insensitive name conflicts (parseQueryString ≡ parse_query_string) - #11: Fix finally block - discard expression results - #11: Fix catch handler - use original name for scope tracking - Fix loop handler - use original name for addToScope - Update emitter tests for clj_ prefix --- src/emitter.nim | 28 +++++++++++++++++++--------- tests/test_emitter.nim | 14 +++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/emitter.nim b/src/emitter.nim index 67a625e..1ef002b 100644 --- a/src/emitter.nim +++ b/src/emitter.nim @@ -79,6 +79,8 @@ proc mangleName*(name: string): string = "interface", "lambda", "open", "quit", "result", "nil", "end"] if result in nimKeywords: result = result & "1" + # Prefix to avoid conflicts with Nim identifiers (case-insensitive + underscore ignoring) + result = "clj_" & result proc pushScope() = scopeStack.add(initHashSet[string]()) @@ -1165,7 +1167,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = raise newException(EmitterError, "loop bindings must be a vector") if bindings.items.len mod 2 != 0: raise newException(EmitterError, "loop bindings must have even number of elements") - var loopParams: seq[(string, string)] = @[] + var loopParams: seq[(string, string, string)] = @[] # (mangled, original, value) var li = 0 while li < bindings.items.len: var lname = bindings.items[li] @@ -1175,13 +1177,13 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = lname = lname.items[1] if lname.kind != ckSymbol: raise newException(EmitterError, "loop binding name must be a symbol") - loopParams.add((mangleName(lname.symName), emitExpr(lval, 0))) + loopParams.add((mangleName(lname.symName), lname.symName, emitExpr(lval, 0))) li += 2 var loopVars: seq[string] = @[] var lines: seq[string] = @[] pushScope() - for (lpName, lpVal) in loopParams: - addToScope(lpName) + for (lpName, lpOrig, lpVal) in loopParams: + addToScope(lpOrig) lines.add(sp & "var " & lpName & ": CljVal = " & lpVal) loopVars.add(lpName) loopStack.add(loopVars) @@ -1271,7 +1273,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = if items.len < 2: raise newException(EmitterError, "try requires body") var bodyForms: seq[CljVal] = @[] - var catchClauses: seq[(string, string, seq[CljVal])] = @[] + var catchClauses: seq[(string, string, string, seq[CljVal])] = @[] # (exType, mangledName, origName, body) var finallyBody: seq[CljVal] = @[] var ti = 1 while ti < items.len: @@ -1304,7 +1306,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = let exName = form.items[nameIdx] if exName.kind != ckSymbol: raise newException(EmitterError, "catch name must be a symbol") - catchClauses.add((exTypeStr, mangleName(exName.symName), form.items[bodyIdx..^1])) + catchClauses.add((exTypeStr, mangleName(exName.symName), exName.symName, form.items[bodyIdx..^1])) elif form.items[0].symName == "finally": finallyBody = form.items[1..^1] else: @@ -1330,7 +1332,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = lines.add(code) else: lines.add(indentStr(indent + 1) & "discard " & stripped) - for (exType, exVar, exBody) in catchClauses: + for (exType, exVar, exOrig, exBody) in catchClauses: lines.add(sp & "except " & exType & ":") lines.add(indentStr(indent + 1) & "let " & exVar & " = cljMap(@[cljKeyword(\"message\")], @[cljString(getCurrentExceptionMsg())])") lines.add(indentStr(indent + 1) & "discard cljAssoc(" & exVar & ", cljKeyword(\"type\"), cljString(\"" & exType & "\"))") @@ -1338,7 +1340,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = lines.add(indentStr(indent + 1) & "if exPtr" & exVar & " of ExInfo:") lines.add(indentStr(indent + 2) & "discard cljAssoc(" & exVar & ", cljKeyword(\"data\"), (ref ExInfo)(exPtr" & exVar & ").exData)") pushScope() - addToScope(exVar) + addToScope(exOrig) for i, eb in exBody: let isLast = (i == exBody.len - 1) let code = emitExpr(eb, indent + 1) @@ -1359,7 +1361,15 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string = if finallyBody.len > 0: lines.add(sp & "finally:") for fb in finallyBody: - lines.add(emitExpr(fb, indent + 1)) + let fbCode = emitExpr(fb, indent + 1) + let fbStripped = fbCode.strip() + if fbStripped.startsWith("echo ") or fbStripped.startsWith("discard ") or + fbStripped.startsWith("var ") or fbStripped.startsWith("let ") or + fbStripped.startsWith("proc ") or fbStripped.startsWith("if ") or + fbStripped.startsWith("try:") or fbStripped.startsWith("while "): + lines.add(fbCode) + else: + lines.add(indentStr(indent + 1) & "discard " & fbStripped) return lines.join("\n") of "throw": diff --git a/tests/test_emitter.nim b/tests/test_emitter.nim index d0e4280..e547289 100644 --- a/tests/test_emitter.nim +++ b/tests/test_emitter.nim @@ -43,13 +43,13 @@ suite "Emitter - Basic Values": registerGlobal("x") let v = cljSymbol("x") let code = emitExpr(v) - check code == "x" + check code == "clj_x" test "emit mangled symbol": registerGlobal("my-var") let v = cljSymbol("my-var") let code = emitExpr(v) - check code == "my_var" + check code == "clj_my_var" test "emit vector": let v = cljVector(@[cljInt(1), cljInt(2)]) @@ -70,12 +70,12 @@ suite "Emitter - Special Forms": test "emit def": let v = read("(def x 42)") let code = emitExpr(v) - check "let x" in code + check "let clj_x" in code test "emit defn": let v = read("(defn square [x] (* x x))") let code = emitExpr(v) - check "proc square" in code + check "proc clj_square" in code test "emit if": let v = read("(if true 1 2)") @@ -161,14 +161,14 @@ suite "Emitter - Program": test "emitProgram with defs and main": let forms = readAll("(def x 42) (println x)") let code = emitProgram(forms) - check "let x" in code + check "let clj_x" in code check "when isMainModule" in code test "emitProgram multiple defs": let forms = readAll("(def a 1) (def b 2)") let code = emitProgram(forms) - check "let a" in code - check "let b" in code + check "let clj_a" in code + check "let clj_b" in code suite "Emitter - Map Literals": test "emit empty map":