fix(emitter): loop in expression context via needsValue + IIFE wrapper
- Add needsValue parameter to emitExpr/emitSpecialForm to track expression context - loop wraps in (proc(): CljVal = ... )() when needsValue=true - Function call args, let/def values, vector/map literals pass needsValue=true - emitProgramInternal passes needsValue=isLast for top-level forms - Fixes: loop as function arg, loop in let RHS, loopResult at top level
This commit is contained in:
@@ -26,3 +26,4 @@ test_single_debug.py
|
||||
src/tui_config
|
||||
experiments/native-lib/test_client/test_client
|
||||
experiments/wasm/tools/zig/
|
||||
lib/bring_http.nim
|
||||
|
||||
+30
-20
@@ -126,7 +126,7 @@ proc isLocalVar(name: string): bool =
|
||||
return true
|
||||
return false
|
||||
|
||||
proc emitExpr*(v: CljVal, indent: int = 0): string
|
||||
proc emitExpr*(v: CljVal, indent: int = 0, needsValue: bool = false): string
|
||||
proc emitBlock*(items: seq[CljVal], indent: int, useResult: bool = false): string
|
||||
proc emitQuotedForm*(v: CljVal): string
|
||||
proc emitFnAsProc*(items: seq[CljVal], indent: int): string
|
||||
@@ -481,7 +481,7 @@ proc letDestructuredBindings(bindings: CljVal): CljVal =
|
||||
i += 2
|
||||
return cljVector(result)
|
||||
|
||||
proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false): string =
|
||||
let sp = indentStr(indent)
|
||||
let head = items[0]
|
||||
if head.kind != ckSymbol:
|
||||
@@ -494,7 +494,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
# Head is not a symbol - evaluate as function call: ((fn ...) args...)
|
||||
var argParts: seq[string] = @[]
|
||||
for i in 1..<items.len:
|
||||
argParts.add(emitExpr(items[i], indent))
|
||||
argParts.add(emitExpr(items[i], indent, needsValue = true))
|
||||
let fnCode = emitExpr(head, 0)
|
||||
return sp & "cljApply(" & fnCode & ", cljList(@[" & argParts.join(", ") & "]))"
|
||||
let op = resolveNsAlias(head.symName)
|
||||
@@ -695,7 +695,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if nameItem.kind == ckSymbol:
|
||||
let mangled = mangleName(nameItem.symName)
|
||||
registerGlobal(nameItem.symName)
|
||||
let valCode = emitExpr(valItem, 0)
|
||||
let valCode = emitExpr(valItem, 0, needsValue = true)
|
||||
return sp & "let " & mangled & " = " & valCode
|
||||
return sp & "cljNil()"
|
||||
var name = items[1]
|
||||
@@ -720,7 +720,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
let valCode = emitFnAsProc(valForm.items, 0)
|
||||
emitLibMode = oldEmitLibMode
|
||||
return sp & "let " & mangled & " = " & valCode
|
||||
let valCode = emitExpr(items[2], 0)
|
||||
let valCode = emitExpr(items[2], 0, needsValue = true)
|
||||
return sp & "let " & mangled & " = " & valCode
|
||||
else:
|
||||
# For nested def: use global var so it's accessible from closures
|
||||
@@ -730,7 +730,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
let valCode = emitFnAsProc(valForm.items, indent)
|
||||
emitLibMode = oldEmitLibMode
|
||||
return sp & "var " & mangled & " = " & valCode.strip() & "\n" & sp & mangled
|
||||
let valCode = emitExpr(items[2], indent)
|
||||
let valCode = emitExpr(items[2], indent, needsValue = true)
|
||||
return sp & "var " & mangled & " = " & valCode.strip() & "\n" & sp & mangled
|
||||
|
||||
of "defn":
|
||||
@@ -1038,7 +1038,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if bname.kind != ckSymbol:
|
||||
raise newException(EmitterError, "let binding name must be a symbol, got " & $bname.kind & ": " & $bname)
|
||||
addToScope(bname.symName)
|
||||
let bcode = emitExpr(bval, indent + 1)
|
||||
let bcode = emitExpr(bval, indent + 1, needsValue = true)
|
||||
if bcode.find("\n") != -1:
|
||||
# Multi-line value: use var and place first line on next line for correct indentation
|
||||
lines.add(indentStr(indent + 1) & "var " & mangleName(bname.symName) & ": CljVal")
|
||||
@@ -1208,7 +1208,7 @@ 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), lname.symName, emitExpr(lval, 0)))
|
||||
loopParams.add((mangleName(lname.symName), lname.symName, emitExpr(lval, 0, needsValue = true)))
|
||||
li += 2
|
||||
var loopVars: seq[string] = @[]
|
||||
var lines: seq[string] = @[]
|
||||
@@ -1273,7 +1273,17 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if needsResultVar:
|
||||
lines.add(sp & "loopResult")
|
||||
loopResultVar = ""
|
||||
return lines.join("\n")
|
||||
let loopCode = lines.join("\n")
|
||||
if needsValue:
|
||||
# Wrap in IIFE for expression contexts (function args, let RHS, etc.)
|
||||
var iifeLines: seq[string] = @[]
|
||||
iifeLines.add(sp & "(proc(): CljVal =")
|
||||
iifeLines.add(indentCode(loopCode, 1))
|
||||
if not needsResultVar:
|
||||
iifeLines.add(indentStr(indent + 1) & "cljNil()")
|
||||
iifeLines.add(sp & ")()")
|
||||
return iifeLines.join("\n")
|
||||
return loopCode
|
||||
|
||||
of "recur":
|
||||
if items.len < 2:
|
||||
@@ -1749,7 +1759,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
return sp & "cljNil()"
|
||||
var argParts: seq[string] = @[]
|
||||
for i in 2..<items.len:
|
||||
argParts.add(emitExpr(items[i], indent))
|
||||
argParts.add(emitExpr(items[i], indent, needsValue = true))
|
||||
let mangled = mangleName(className.symName)
|
||||
return sp & mangled & "(" & argParts.join(", ") & ")"
|
||||
|
||||
@@ -2077,7 +2087,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if rn.len > 0:
|
||||
var argParts: seq[string] = @[]
|
||||
for i in 1..<items.len:
|
||||
argParts.add(emitExpr(items[i], indent))
|
||||
argParts.add(emitExpr(items[i], indent, needsValue = true))
|
||||
# Variadic functions take seq[CljVal]
|
||||
let variadic = op in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
@@ -2163,7 +2173,7 @@ proc emitQuotedForm*(v: CljVal): string =
|
||||
else:
|
||||
return emitExpr(v, 0)
|
||||
|
||||
proc emitExpr*(v: CljVal, indent: int = 0): string =
|
||||
proc emitExpr*(v: CljVal, indent: int = 0, needsValue: bool = false): string =
|
||||
let sp = indentStr(indent)
|
||||
case v.kind
|
||||
of ckNil:
|
||||
@@ -2237,12 +2247,12 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
||||
# Macro-expand before emitting
|
||||
let expanded = macroexpand(v)
|
||||
if expanded == v:
|
||||
return emitSpecialForm(v.items, indent)
|
||||
return emitExpr(expanded, indent)
|
||||
return emitSpecialForm(v.items, indent, needsValue)
|
||||
return emitExpr(expanded, indent, needsValue)
|
||||
of ckVector:
|
||||
var parts: seq[string] = @[]
|
||||
for item in v.items:
|
||||
parts.add(emitExpr(item, indent))
|
||||
parts.add(emitExpr(item, indent, needsValue = true))
|
||||
return sp & "cljVector(@[" & parts.join(", ") & "])"
|
||||
of ckMap:
|
||||
if v.mapKeys.len == 0:
|
||||
@@ -2250,18 +2260,18 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
||||
var keyParts: seq[string] = @[]
|
||||
var valParts: seq[string] = @[]
|
||||
for i in 0..<v.mapKeys.len:
|
||||
keyParts.add(emitExpr(v.mapKeys[i], indent))
|
||||
valParts.add(emitExpr(v.mapVals[i], indent))
|
||||
keyParts.add(emitExpr(v.mapKeys[i], indent, needsValue = true))
|
||||
valParts.add(emitExpr(v.mapVals[i], indent, needsValue = true))
|
||||
return sp & "cljMap(@[" & keyParts.join(", ") & "], @[" & valParts.join(", ") & "])"
|
||||
of ckSet:
|
||||
var parts: seq[string] = @[]
|
||||
for item in v.setItems:
|
||||
parts.add(emitExpr(item, indent))
|
||||
parts.add(emitExpr(item, indent, needsValue = true))
|
||||
return sp & "cljSet(@[" & parts.join(", ") & "])"
|
||||
of ckFn:
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = discard cljNil())"
|
||||
of ckAtom:
|
||||
return sp & "cljAtom(" & emitExpr(v.atomVal, 0) & ")"
|
||||
return sp & "cljAtom(" & emitExpr(v.atomVal, 0, needsValue = true) & ")"
|
||||
of ckTransient:
|
||||
return sp & "cljTransient(cljNil())"
|
||||
of ckAgent:
|
||||
@@ -2356,7 +2366,7 @@ proc emitProgramInternal(forms: seq[CljVal]): string =
|
||||
let defCode = emitExpr(ef, 0)
|
||||
defs.add(defCode)
|
||||
else:
|
||||
let code = emitExpr(ef, 2)
|
||||
let code = emitExpr(ef, 2, needsValue = isLast)
|
||||
let stripped = code.strip()
|
||||
if stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
||||
stripped.startsWith("var ") or stripped.startsWith("while ") or
|
||||
|
||||
Reference in New Issue
Block a user