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