feat: +12 tests (210/233, 90%) — fn wrapping, doseq, when-first, NaN?, delay, loop scope
Major changes: - fn handler wraps with cljFn for higher-order functions (rand, rand_int, rand_nth) - def handler emits raw proc for (def f (fn ...)) context - Local fn calls use .fnProc for cljFn values - Added when-first macro, lazy-seq special form, NaN? predicate - Added delay, rseq, listEmpty runtime functions - doseq macro uses next instead of rest, recur inside when body - loop handler pushScope/popScope for proper variable scoping - let handler: var/while/proc statement checks, block last-line discard - when handler: proper statement detection for body forms - defn/defn- allow empty body
This commit is contained in:
+155
-102
@@ -91,6 +91,7 @@ proc isLocalVar(name: string): bool =
|
||||
proc emitExpr*(v: CljVal, indent: int = 0): 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
|
||||
|
||||
proc indentStr(indent: int): string =
|
||||
" ".repeat(indent)
|
||||
@@ -184,6 +185,8 @@ proc runtimeName(op: string): string =
|
||||
of "sorted-set": "cljSortedSet"
|
||||
of "sorted?": "cljSortedQ"
|
||||
of "array-map": "cljArrayMap"
|
||||
of "rseq": "cljRseq"
|
||||
of "list": "cljListEmpty"
|
||||
# ---- Type predicates ----
|
||||
of "nil?": "cljIsNilP"
|
||||
of "some?": "cljIsSome"
|
||||
@@ -209,6 +212,7 @@ proc runtimeName(op: string): string =
|
||||
of "symbol": "cljSymbolFn"
|
||||
of "inf": "cljInf"
|
||||
of "nan": "cljNaN"
|
||||
of "NaN?": "cljNaNQ"
|
||||
of "name": "cljName"
|
||||
of "namespace": "cljNamespace"
|
||||
# of "key": "cljKey"
|
||||
@@ -264,6 +268,7 @@ proc runtimeName(op: string): string =
|
||||
of "isa?": "cljIsa"
|
||||
of "promise": "cljPromise"
|
||||
of "future": "cljFuture"
|
||||
of "delay": "cljDelay"
|
||||
of "create-ns": "cljCreateNs"
|
||||
of "aclone": "cljAclone"
|
||||
of "alength": "cljAlength"
|
||||
@@ -305,6 +310,40 @@ proc runtimeName(op: string): string =
|
||||
of "number": "cljToInt"
|
||||
else: ""
|
||||
|
||||
proc emitFnAsProc(items: seq[CljVal], indent: int): string =
|
||||
# Emit a fn form as a raw proc (without cljFn wrapper), used by def handler
|
||||
if items.len < 2:
|
||||
raise newException(EmitterError, "fn requires params")
|
||||
var paramsIdx = 1
|
||||
if items[1].kind == ckSymbol:
|
||||
paramsIdx = 2
|
||||
if paramsIdx >= items.len:
|
||||
raise newException(EmitterError, "fn requires params and body")
|
||||
var params = items[paramsIdx]
|
||||
if params.kind == ckList and params.items.len == 3 and
|
||||
params.items[0].kind == ckSymbol and params.items[0].symName == "with-meta":
|
||||
params = params.items[1]
|
||||
if params.kind != ckVector:
|
||||
raise newException(EmitterError, "fn params must be a vector")
|
||||
var paramNames: seq[string] = @[]
|
||||
pushScope()
|
||||
for p in params.items:
|
||||
if p.kind != ckSymbol:
|
||||
raise newException(EmitterError, "fn params must be symbols")
|
||||
addToScope(p.symName)
|
||||
paramNames.add(mangleName(p.symName) & ": CljVal")
|
||||
let body = items[(paramsIdx+1)..^1]
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 1)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
popScope()
|
||||
if bodyCode.find("\n") == -1:
|
||||
return "(proc (" & paramNames.join(", ") & "): CljVal = " & bodyCode.strip() & ")"
|
||||
else:
|
||||
return "(proc (" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & ")"
|
||||
|
||||
proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
let sp = indentStr(indent)
|
||||
let head = items[0]
|
||||
@@ -490,17 +529,34 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if name.kind != ckSymbol:
|
||||
raise newException(EmitterError, "def name must be a symbol")
|
||||
let mangled = mangleName(name.symName)
|
||||
let valForm = items[2]
|
||||
# Special handling: (def name (fn ...)) should emit raw proc, not cljFn wrapper
|
||||
let isFnDef = valForm.kind == ckList and valForm.items.len > 0 and
|
||||
valForm.items[0].kind == ckSymbol and valForm.items[0].symName == "fn"
|
||||
if indent == 0:
|
||||
if isFnDef:
|
||||
# Emit fn as raw proc for def context
|
||||
let oldEmitLibMode = emitLibMode
|
||||
emitLibMode = false
|
||||
let valCode = emitFnAsProc(valForm.items, 0)
|
||||
emitLibMode = oldEmitLibMode
|
||||
return sp & "let " & mangled & " = " & valCode
|
||||
let valCode = emitExpr(items[2], 0)
|
||||
return sp & "let " & mangled & " = " & valCode
|
||||
else:
|
||||
# For nested def: wrap in proc and use consistent indentation
|
||||
if isFnDef:
|
||||
let oldEmitLibMode = emitLibMode
|
||||
emitLibMode = false
|
||||
let valCode = emitFnAsProc(valForm.items, indent + 2)
|
||||
emitLibMode = oldEmitLibMode
|
||||
return sp & "((proc (): CljVal =\n" & indentStr(indent + 1) & "let " & mangled & " = " & valCode.strip() & "\n" & indentStr(indent + 1) & mangled & ")())"
|
||||
let valCode = emitExpr(items[2], indent + 2)
|
||||
return sp & "((proc (): CljVal =\n" & indentStr(indent + 1) & "let " & mangled & " = " & valCode.strip() & "\n" & indentStr(indent + 1) & mangled & ")())"
|
||||
|
||||
of "defn":
|
||||
if items.len < 4:
|
||||
raise newException(EmitterError, "defn requires name, params, and body")
|
||||
if items.len < 3:
|
||||
raise newException(EmitterError, "defn requires name and params")
|
||||
var name = items[1]
|
||||
if name.kind == ckList and name.items.len == 3 and
|
||||
name.items[0].kind == ckSymbol and name.items[0].symName == "with-meta":
|
||||
@@ -523,7 +579,9 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
let exportMarker = if emitLibMode: "*" else: ""
|
||||
if indent == 0:
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
if body.len == 0:
|
||||
bodyCode = indentStr(indent + 1) & "cljNil()"
|
||||
elif body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 1)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
@@ -539,8 +597,8 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
indentStr(indent + 1) & procName & ")"
|
||||
|
||||
of "defn-":
|
||||
if items.len < 4:
|
||||
raise newException(EmitterError, "defn- requires name, params, and body")
|
||||
if items.len < 3:
|
||||
raise newException(EmitterError, "defn- requires name and params")
|
||||
var name = items[1]
|
||||
# Strip metadata: (with-meta sym meta) -> sym
|
||||
if name.kind == ckList and name.items.len == 3 and
|
||||
@@ -560,7 +618,9 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
paramNames.add(mangleName(p.symName) & ": CljVal")
|
||||
let body = items[3..^1]
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
if body.len == 0:
|
||||
bodyCode = indentStr(indent + 1) & "cljNil()"
|
||||
elif body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 1)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
@@ -569,8 +629,8 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
return sp & "proc " & procName & "(" & paramNames.join(", ") & "): CljVal {.used.} =\n" & bodyCode
|
||||
|
||||
of "fn":
|
||||
if items.len < 3:
|
||||
raise newException(EmitterError, "fn requires params and body")
|
||||
if items.len < 2:
|
||||
raise newException(EmitterError, "fn requires params")
|
||||
var paramsIdx = 1
|
||||
# Handle named fn: (fn name [params] body)
|
||||
if items[1].kind == ckSymbol:
|
||||
@@ -584,13 +644,14 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
params = params.items[1]
|
||||
if params.kind != ckVector:
|
||||
raise newException(EmitterError, "fn params must be a vector")
|
||||
var paramNames: seq[string] = @[]
|
||||
let paramCount = params.items.len
|
||||
pushScope()
|
||||
var paramNames: seq[string] = @[]
|
||||
for p in params.items:
|
||||
if p.kind != ckSymbol:
|
||||
raise newException(EmitterError, "fn params must be symbols")
|
||||
addToScope(p.symName)
|
||||
paramNames.add(mangleName(p.symName) & ": CljVal")
|
||||
paramNames.add(mangleName(p.symName))
|
||||
let body = items[(paramsIdx+1)..^1]
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
@@ -598,10 +659,27 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
popScope()
|
||||
if bodyCode.find("\n") == -1:
|
||||
return sp & "(proc (" & paramNames.join(", ") & "): CljVal = " & bodyCode.strip() & ")"
|
||||
# Wrap with cljFn: convert params to args: seq[CljVal]
|
||||
var argsLines: seq[string] = @[]
|
||||
if paramCount > 0:
|
||||
for i in 0..<paramCount:
|
||||
argsLines.add(indentStr(indent + 1) & "let " & paramNames[i] & " = args[" & $i & "]")
|
||||
if argsLines.len > 0:
|
||||
# Multi-line: let bindings on separate lines, then body
|
||||
var allLines: seq[string] = @[]
|
||||
for a in argsLines:
|
||||
allLines.add(a)
|
||||
if bodyCode.find("\n") == -1:
|
||||
allLines.add(indentStr(indent + 1) & bodyCode.strip())
|
||||
else:
|
||||
allLines.add(bodyCode)
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal =\n" & allLines.join("\n") & ")"
|
||||
else:
|
||||
return sp & "(proc (" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & ")"
|
||||
# No params - simple case
|
||||
if bodyCode.find("\n") == -1:
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & bodyCode.strip() & ")"
|
||||
else:
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal =\n" & bodyCode & ")"
|
||||
|
||||
of "let":
|
||||
if items.len < 3:
|
||||
@@ -631,9 +709,22 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
else:
|
||||
let stripped = bcode.strip()
|
||||
if stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
||||
stripped.startsWith("block:") or stripped.startsWith("if ") or
|
||||
stripped.startsWith("try:"):
|
||||
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
||||
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
||||
stripped.startsWith("proc ") or stripped.startsWith("while "):
|
||||
lines.add(bcode)
|
||||
elif stripped.startsWith("block:"):
|
||||
# Multi-line block as non-last form: discard the last line
|
||||
var blockLines = bcode.split("\n")
|
||||
var lastIdx = blockLines.len - 1
|
||||
while lastIdx >= 0 and blockLines[lastIdx].strip() == "":
|
||||
lastIdx.dec
|
||||
if lastIdx >= 0:
|
||||
let lastLine = blockLines[lastIdx].strip()
|
||||
if not lastLine.startsWith("discard ") and not lastLine.startsWith("result = "):
|
||||
let prefixLen = blockLines[lastIdx].len - blockLines[lastIdx].strip().len
|
||||
blockLines[lastIdx] = blockLines[lastIdx][0..<prefixLen] & "discard " & lastLine
|
||||
lines.add(blockLines.join("\n"))
|
||||
else:
|
||||
lines.add(indentStr(indent + 1) & "discard " & stripped)
|
||||
popScope()
|
||||
@@ -657,74 +748,20 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
var lines: seq[string] = @[]
|
||||
lines.add(sp & "if cljIsTruthy(" & condCode.strip() & "):")
|
||||
let body = items[2..^1]
|
||||
for b in body:
|
||||
lines.add(emitExpr(b, indent + 1))
|
||||
lines.add(sp & "else:")
|
||||
lines.add(indentStr(indent + 1) & "cljNil()")
|
||||
return lines.join("\n")
|
||||
|
||||
of "when-let":
|
||||
if items.len < 4:
|
||||
raise newException(EmitterError, "when-let requires binding vector and body")
|
||||
let bindings = items[1]
|
||||
if bindings.kind != ckVector or bindings.items.len != 2:
|
||||
raise newException(EmitterError, "when-let binding must be a vector of [name val]")
|
||||
let bindName = bindings.items[0]
|
||||
let bindVal = bindings.items[1]
|
||||
if bindName.kind != ckSymbol:
|
||||
raise newException(EmitterError, "when-let binding name must be a symbol")
|
||||
let mangled = mangleName(bindName.symName)
|
||||
var lines: seq[string] = @[]
|
||||
lines.add(sp & "block:")
|
||||
lines.add(indentStr(indent + 1) & "let " & mangled & " = " & emitExpr(bindVal, 0))
|
||||
lines.add(indentStr(indent + 1) & "if not cljIsNil(" & mangled & "):")
|
||||
let body = items[2..^1]
|
||||
for b in body:
|
||||
lines.add(emitExpr(b, indent + 2))
|
||||
return lines.join("\n")
|
||||
|
||||
of "if-let":
|
||||
if items.len < 4:
|
||||
raise newException(EmitterError, "if-let requires binding vector, then, and optional else")
|
||||
let bindings = items[1]
|
||||
if bindings.kind != ckVector or bindings.items.len != 2:
|
||||
raise newException(EmitterError, "if-let binding must be a vector of [name val]")
|
||||
let bindName = bindings.items[0]
|
||||
let bindVal = bindings.items[1]
|
||||
if bindName.kind != ckSymbol:
|
||||
raise newException(EmitterError, "if-let binding name must be a symbol")
|
||||
let mangled = mangleName(bindName.symName)
|
||||
var lines: seq[string] = @[]
|
||||
lines.add(sp & "block:")
|
||||
lines.add(indentStr(indent + 1) & "let " & mangled & " = " & emitExpr(bindVal, 0))
|
||||
lines.add(indentStr(indent + 1) & "if not cljIsNil(" & mangled & "):")
|
||||
lines.add(emitExpr(items[2], indent + 2))
|
||||
if items.len == 4:
|
||||
lines.add(indentStr(indent + 1) & "else:")
|
||||
lines.add(emitExpr(items[3], indent + 2))
|
||||
return lines.join("\n")
|
||||
|
||||
of "cond":
|
||||
if items.len < 3 or items.len mod 2 != 1:
|
||||
raise newException(EmitterError, "cond requires pairs of test/expr")
|
||||
var lines: seq[string] = @[]
|
||||
var first = true
|
||||
var ci = 1
|
||||
while ci < items.len:
|
||||
let testExpr = items[ci]
|
||||
let thenExpr = items[ci+1]
|
||||
if (testExpr.kind == ckSymbol and testExpr.symName == ":else") or
|
||||
(testExpr.kind == ckKeyword and testExpr.kwName == "else"):
|
||||
lines.add(sp & "else:")
|
||||
lines.add(emitExpr(thenExpr, indent + 1))
|
||||
elif first:
|
||||
lines.add(sp & "if cljIsTruthy(" & emitExpr(testExpr, 0) & "):")
|
||||
lines.add(emitExpr(thenExpr, indent + 1))
|
||||
first = false
|
||||
for bi, b in body:
|
||||
let bcode = emitExpr(b, indent + 1)
|
||||
let stripped = bcode.strip()
|
||||
let isStatement = stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
||||
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
||||
stripped.startsWith("proc ") or stripped.startsWith("block:") or
|
||||
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
||||
stripped.startsWith("while ") or stripped.contains("continue")
|
||||
if isStatement:
|
||||
lines.add(bcode)
|
||||
else:
|
||||
lines.add(sp & "elif cljIsTruthy(" & emitExpr(testExpr, 0) & "):")
|
||||
lines.add(emitExpr(thenExpr, indent + 1))
|
||||
ci += 2
|
||||
lines.add(indentStr(indent + 1) & "discard " & stripped)
|
||||
lines.add(sp & "else:")
|
||||
lines.add(indentStr(indent + 1) & "discard cljNil()")
|
||||
return lines.join("\n")
|
||||
|
||||
of "loop":
|
||||
@@ -740,7 +777,6 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
while li < bindings.items.len:
|
||||
var lname = bindings.items[li]
|
||||
let lval = bindings.items[li+1]
|
||||
# Strip metadata: (with-meta sym meta) -> sym
|
||||
if lname.kind == ckList and lname.items.len == 3 and
|
||||
lname.items[0].kind == ckSymbol and lname.items[0].symName == "with-meta":
|
||||
lname = lname.items[1]
|
||||
@@ -750,7 +786,9 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
li += 2
|
||||
var loopVars: seq[string] = @[]
|
||||
var lines: seq[string] = @[]
|
||||
pushScope()
|
||||
for (lpName, lpVal) in loopParams:
|
||||
addToScope(lpName)
|
||||
lines.add(sp & "var " & lpName & ": CljVal = " & lpVal)
|
||||
loopVars.add(lpName)
|
||||
loopStack.add(loopVars)
|
||||
@@ -760,6 +798,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
lines.add(emitExpr(b, indent + 1))
|
||||
lines.add(indentStr(indent + 1) & "break")
|
||||
discard loopStack.pop()
|
||||
popScope()
|
||||
return lines.join("\n")
|
||||
|
||||
of "recur":
|
||||
@@ -781,6 +820,12 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
return sp & "discard cljNil()"
|
||||
return emitBlock(items[1..^1], indent)
|
||||
|
||||
of "lazy-seq":
|
||||
# lazy-seq is treated as do (eager evaluation for native compilation)
|
||||
if items.len < 2:
|
||||
return sp & "cljNil()"
|
||||
return emitExpr(items[1], indent)
|
||||
|
||||
of "try":
|
||||
if items.len < 2:
|
||||
raise newException(EmitterError, "try requires body")
|
||||
@@ -1402,21 +1447,22 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
argParts.add(emitExpr(items[i], indent))
|
||||
# Variadic functions take seq[CljVal]
|
||||
let variadic = op in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"atom", "concat", "min", "max", "merge", "interleave",
|
||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-set", "cons",
|
||||
"array-map", "inf", "nan",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns", "future",
|
||||
"aclone", "alength", "aget", "int-array", "identical?", "empty", "identity",
|
||||
"conj",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
"to-array", "vector", "rand", "rand-int",
|
||||
"rand-nth", "random-sample"]
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"atom", "concat", "min", "max", "merge", "interleave",
|
||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-set", "cons",
|
||||
"array-map", "inf", "nan", "list",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns", "future",
|
||||
"delay",
|
||||
"aclone", "alength", "aget", "int-array", "identical?", "empty", "identity",
|
||||
"conj",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
"to-array", "vector", "rand", "rand-int",
|
||||
"rand-nth", "random-sample"]
|
||||
var call: string
|
||||
if variadic:
|
||||
call = rn & "(@[" & argParts.join(", ") & "])"
|
||||
@@ -1432,7 +1478,11 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
var callOp = op
|
||||
if callOp.endsWith("."):
|
||||
callOp = callOp[0..^2]
|
||||
return sp & mangleName(callOp) & "(" & args.join(", ") & ")"
|
||||
let mangled = mangleName(callOp)
|
||||
if isLocalVar(callOp):
|
||||
# Local cljFn value: call via .fnProc
|
||||
return sp & mangled & ".fnProc(@[" & args.join(", ") & "])"
|
||||
return sp & mangled & "(" & args.join(", ") & ")"
|
||||
|
||||
proc emitQuotedForm*(v: CljVal): string =
|
||||
case v.kind
|
||||
@@ -1490,14 +1540,17 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"atom", "concat", "min", "max", "merge", "interleave",
|
||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-set", "cons",
|
||||
"array-map", "inf", "nan",
|
||||
"array-map", "inf", "nan", "list",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns", "future",
|
||||
"delay",
|
||||
"aclone", "alength", "aget", "int-array", "identical?", "empty", "identity",
|
||||
"conj",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun", "identity", "conj",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
"to-array", "vector", "rand", "rand-int",
|
||||
"rand-nth", "random-sample"]
|
||||
if variadic:
|
||||
|
||||
+26
-8
@@ -402,16 +402,20 @@ proc initMacros*() =
|
||||
innerBindings.add(name.items[j])
|
||||
innerBindings.add(cljList(@[cljSymbol("nth"), tmpGs, cljInt(j)]))
|
||||
let destructured = cljList(@[cljSymbol("let"), cljVector(innerBindings)] & body)
|
||||
let recurForm = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("next"), seqGs])])
|
||||
let loopBody = cljList(@[cljSymbol("when"), seqGs,
|
||||
cljList(@[cljSymbol("let"), cljVector(@[tmpGs, cljList(@[cljSymbol("first"), seqGs])]), destructured])])
|
||||
let recurBody = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("rest"), seqGs])])
|
||||
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[seqGs, cljList(@[cljSymbol("seq"), gs])]), loopBody, recurBody])
|
||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, coll]), loopForm])
|
||||
cljList(@[cljSymbol("let"), cljVector(@[tmpGs, cljList(@[cljSymbol("first"), seqGs])]), destructured]),
|
||||
recurForm])
|
||||
let seqColl = cljList(@[cljSymbol("seq"), coll])
|
||||
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[seqGs, seqColl]), loopBody])
|
||||
result = loopForm
|
||||
else:
|
||||
let loopBody = cljList(@[cljSymbol("when"), name] & body)
|
||||
let recurBody = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("first"), gs]), cljList(@[cljSymbol("rest"), gs])])
|
||||
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), gs]), gs, cljList(@[cljSymbol("rest"), gs])]), loopBody, recurBody])
|
||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, coll]), loopForm])
|
||||
# Simple binding: skip the let, pass coll directly to loop
|
||||
let seqColl = cljList(@[cljSymbol("seq"), coll])
|
||||
let recurForm = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("first"), gs]), cljList(@[cljSymbol("next"), gs])])
|
||||
let loopBody = cljList(@[cljSymbol("when"), name] & body & @[recurForm])
|
||||
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), seqColl]), gs, cljList(@[cljSymbol("next"), seqColl])]), loopBody])
|
||||
result = loopForm
|
||||
else:
|
||||
let name = bindings.items[0]
|
||||
let coll = bindings.items[1]
|
||||
@@ -498,6 +502,20 @@ proc initMacros*() =
|
||||
cljList(@[cljSymbol("do")] & args[1..^1])
|
||||
)
|
||||
|
||||
defineMacro("when-first", proc(args: seq[CljVal]): CljVal =
|
||||
if args.len < 2: return cljNil()
|
||||
let bindings = args[0]
|
||||
if bindings.kind != ckVector or bindings.items.len != 2:
|
||||
return cljNil()
|
||||
let bindName = bindings.items[0]
|
||||
let bindColl = bindings.items[1]
|
||||
let body = args[1..^1]
|
||||
# (when-first [x coll] body) => (when (seq coll) (let [x (first coll)] body))
|
||||
cljList(@[cljSymbol("when"),
|
||||
cljList(@[cljSymbol("seq"), bindColl]),
|
||||
cljList(@[cljSymbol("let"), cljVector(@[bindName, cljList(@[cljSymbol("first"), bindColl])])] & body)])
|
||||
)
|
||||
|
||||
defineMacro("thrown?", proc(args: seq[CljVal]): CljVal =
|
||||
if args.len == 0: return cljNil()
|
||||
let form = args[0]
|
||||
|
||||
Reference in New Issue
Block a user