fix: clj_ prefix for identifiers + try/catch/finally fixes

- #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
This commit is contained in:
2026-05-12 00:48:50 +03:00
parent e36bf4e362
commit fb8928e830
2 changed files with 26 additions and 16 deletions
+19 -9
View File
@@ -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":