chore: emitter/runtime improvements from previous session
This commit is contained in:
+195
-3
@@ -11,6 +11,9 @@ type
|
|||||||
fn: CljVal
|
fn: CljVal
|
||||||
args: seq[CljVal]
|
args: seq[CljVal]
|
||||||
|
|
||||||
|
ExInfo* = object of CatchableError
|
||||||
|
exData*: CljVal
|
||||||
|
|
||||||
CljVal* = ref CljValObj
|
CljVal* = ref CljValObj
|
||||||
CljValObj = object
|
CljValObj = object
|
||||||
meta*: CljVal
|
meta*: CljVal
|
||||||
@@ -110,6 +113,24 @@ proc cljSet*(items: seq[CljVal]): CljVal =
|
|||||||
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
||||||
CljVal(kind: ckSet, setData: s)
|
CljVal(kind: ckSet, setData: s)
|
||||||
|
|
||||||
|
proc cljSet*(coll: CljVal): CljVal =
|
||||||
|
var s = newPersistentMap[CljVal, bool]()
|
||||||
|
if coll.isNil: return CljVal(kind: ckSet, setData: s)
|
||||||
|
case coll.kind
|
||||||
|
of ckList:
|
||||||
|
for item in coll.listItems:
|
||||||
|
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
||||||
|
of ckVector:
|
||||||
|
for item in toSeq(coll.vecData):
|
||||||
|
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
||||||
|
of ckSet:
|
||||||
|
return coll
|
||||||
|
of ckMap:
|
||||||
|
for key in pmapKeys(coll.mapData):
|
||||||
|
s = pmapAssoc(s, key, true, hash(key), cljEq)
|
||||||
|
else: discard
|
||||||
|
CljVal(kind: ckSet, setData: s)
|
||||||
|
|
||||||
proc cljHashMap*(args: seq[CljVal]): CljVal =
|
proc cljHashMap*(args: seq[CljVal]): CljVal =
|
||||||
if args.len mod 2 != 0:
|
if args.len mod 2 != 0:
|
||||||
raise newException(CatchableError, "hash-map requires even number of arguments")
|
raise newException(CatchableError, "hash-map requires even number of arguments")
|
||||||
@@ -185,6 +206,47 @@ proc cljIsColl*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckLi
|
|||||||
proc cljIsSequential*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckList, ckVector})
|
proc cljIsSequential*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckList, ckVector})
|
||||||
proc cljIsFn*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind == ckFn)
|
proc cljIsFn*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind == ckFn)
|
||||||
proc cljIsVar*(v: CljVal): CljVal = cljBool(false)
|
proc cljIsVar*(v: CljVal): CljVal = cljBool(false)
|
||||||
|
|
||||||
|
proc cljParseUuid*(s: CljVal): CljVal =
|
||||||
|
if s.kind != ckString:
|
||||||
|
raise newException(CatchableError, "Invalid UUID string")
|
||||||
|
let str = s.strVal
|
||||||
|
if str.len == 0:
|
||||||
|
return cljNil()
|
||||||
|
# Simple UUID validation: 8-4-4-4-12 hex digits
|
||||||
|
var parts: seq[string] = @[]
|
||||||
|
var current = ""
|
||||||
|
for c in str:
|
||||||
|
if c == '-':
|
||||||
|
parts.add(current)
|
||||||
|
current = ""
|
||||||
|
else:
|
||||||
|
current.add(c)
|
||||||
|
parts.add(current)
|
||||||
|
if parts.len == 5 and
|
||||||
|
parts[0].len == 8 and parts[1].len == 4 and parts[2].len == 4 and
|
||||||
|
parts[3].len == 4 and parts[4].len == 12:
|
||||||
|
for p in parts:
|
||||||
|
for c in p:
|
||||||
|
if c notin {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||||
|
return cljNil()
|
||||||
|
return cljString(str.toLowerAscii())
|
||||||
|
return cljNil()
|
||||||
|
|
||||||
|
proc cljIsUuid*(v: CljVal): CljVal =
|
||||||
|
if v.kind != ckString:
|
||||||
|
return cljBool(false)
|
||||||
|
let str = v.strVal
|
||||||
|
var parts = str.split('-')
|
||||||
|
if parts.len == 5 and
|
||||||
|
parts[0].len == 8 and parts[1].len == 4 and parts[2].len == 4 and
|
||||||
|
parts[3].len == 4 and parts[4].len == 12:
|
||||||
|
for p in parts:
|
||||||
|
for c in p:
|
||||||
|
if c notin {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||||
|
return cljBool(false)
|
||||||
|
return cljBool(true)
|
||||||
|
return cljBool(false)
|
||||||
proc cljIsIfn*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckFn, ckKeyword, ckSymbol, ckMap, ckSet, ckVector})
|
proc cljIsIfn*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckFn, ckKeyword, ckSymbol, ckMap, ckSet, ckVector})
|
||||||
proc cljIsBool*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind == ckBool)
|
proc cljIsBool*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind == ckBool)
|
||||||
proc cljIsTrueP*(v: CljVal): CljVal = cljBool(cljIsTrue(v))
|
proc cljIsTrueP*(v: CljVal): CljVal = cljBool(cljIsTrue(v))
|
||||||
@@ -772,10 +834,20 @@ proc cljSortedMap*(args: seq[CljVal]): CljVal =
|
|||||||
# Fallback to regular hash-map
|
# Fallback to regular hash-map
|
||||||
cljHashMap(args)
|
cljHashMap(args)
|
||||||
|
|
||||||
|
proc cljSortedMapBy*(args: seq[CljVal]): CljVal =
|
||||||
|
# Fallback to regular hash-map (ignores comparator)
|
||||||
|
if args.len < 1: return cljMap(@[], @[])
|
||||||
|
cljHashMap(args[1..^1])
|
||||||
|
|
||||||
proc cljSortedSet*(args: seq[CljVal]): CljVal =
|
proc cljSortedSet*(args: seq[CljVal]): CljVal =
|
||||||
# Fallback to regular hash-set
|
# Fallback to regular hash-set
|
||||||
cljHashSet(args)
|
cljHashSet(args)
|
||||||
|
|
||||||
|
proc cljSortedSetBy*(args: seq[CljVal]): CljVal =
|
||||||
|
# Fallback to regular hash-set (ignores comparator)
|
||||||
|
if args.len < 1: return cljSet(@[])
|
||||||
|
cljHashSet(args[1..^1])
|
||||||
|
|
||||||
proc cljArrayMap*(args: seq[CljVal]): CljVal =
|
proc cljArrayMap*(args: seq[CljVal]): CljVal =
|
||||||
# Fallback to regular hash-map
|
# Fallback to regular hash-map
|
||||||
cljHashMap(args)
|
cljHashMap(args)
|
||||||
@@ -845,6 +917,14 @@ proc cljToArray*(args: seq[CljVal]): CljVal =
|
|||||||
for c in v.strVal: items.add(cljString($c))
|
for c in v.strVal: items.add(cljString($c))
|
||||||
return cljVector(items)
|
return cljVector(items)
|
||||||
else: return cljVector(@[v])
|
else: return cljVector(@[v])
|
||||||
|
|
||||||
|
proc cljIntoArray*(args: seq[CljVal]): CljVal =
|
||||||
|
if args.len == 0: return cljVector(@[])
|
||||||
|
if args.len == 1:
|
||||||
|
return cljToArray(@[args[0]])
|
||||||
|
# Ignore type argument, convert last collection argument to vector
|
||||||
|
return cljToArray(@[args[^1]])
|
||||||
|
|
||||||
proc cljVolatileBang*(args: seq[CljVal]): CljVal =
|
proc cljVolatileBang*(args: seq[CljVal]): CljVal =
|
||||||
if args.len == 0: return cljNil()
|
if args.len == 0: return cljNil()
|
||||||
# volatile! creates a mutable holder - stub as atom
|
# volatile! creates a mutable holder - stub as atom
|
||||||
@@ -948,6 +1028,19 @@ proc cljDescendants*(args: seq[CljVal]): CljVal =
|
|||||||
if args.len < 1: return cljNil()
|
if args.len < 1: return cljNil()
|
||||||
cljSet(@[])
|
cljSet(@[])
|
||||||
|
|
||||||
|
proc cljUseFixtures*(args: seq[CljVal]): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljAddWatch*(args: seq[CljVal]): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljRemoveWatch*(args: seq[CljVal]): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljAlterVarRoot*(args: seq[CljVal]): CljVal =
|
||||||
|
if args.len >= 2: return args[1]
|
||||||
|
cljNil()
|
||||||
|
|
||||||
proc cljParents*(args: seq[CljVal]): CljVal =
|
proc cljParents*(args: seq[CljVal]): CljVal =
|
||||||
if args.len < 1: return cljNil()
|
if args.len < 1: return cljNil()
|
||||||
cljSet(@[])
|
cljSet(@[])
|
||||||
@@ -1254,6 +1347,42 @@ proc cljGetDefault*(m: CljVal, key: CljVal, default: CljVal): CljVal =
|
|||||||
else: default
|
else: default
|
||||||
else: default
|
else: default
|
||||||
|
|
||||||
|
proc cljExInfo*(msg: CljVal, data: CljVal): CljVal =
|
||||||
|
var ex = newException(ExInfo, cljStr(msg))
|
||||||
|
ex.exData = data
|
||||||
|
raise ex
|
||||||
|
|
||||||
|
proc cljExData*(ex: CljVal): CljVal =
|
||||||
|
if ex.isNil: return cljNil()
|
||||||
|
case ex.kind
|
||||||
|
of ckMap:
|
||||||
|
let data = cljGet(ex, cljKeyword("data"))
|
||||||
|
if data.kind == ckNil:
|
||||||
|
return ex
|
||||||
|
return data
|
||||||
|
else: return cljNil()
|
||||||
|
|
||||||
|
proc cljCall*(f: CljVal, args: seq[CljVal]): CljVal =
|
||||||
|
if f.isNil: return cljNil()
|
||||||
|
case f.kind
|
||||||
|
of ckFn:
|
||||||
|
if args.len > 0: return f.fnProc(args)
|
||||||
|
else: return f.fnProc(@[])
|
||||||
|
of ckMap:
|
||||||
|
if args.len > 0: return cljGet(f, args[0])
|
||||||
|
else: return cljNil()
|
||||||
|
of ckSet:
|
||||||
|
if args.len > 0: return cljBool(pmapContains(f.setData, args[0], hash(args[0]), cljEq))
|
||||||
|
else: return cljBool(false)
|
||||||
|
of ckVector:
|
||||||
|
if args.len > 0: return cljNth(f, args[0])
|
||||||
|
else: return cljNil()
|
||||||
|
of ckKeyword:
|
||||||
|
if args.len > 0: return cljGet(args[0], f)
|
||||||
|
else: return cljNil()
|
||||||
|
else:
|
||||||
|
raise newException(CatchableError, "Cannot call value of type " & $f.kind)
|
||||||
|
|
||||||
proc cljAssoc*(m: CljVal, key: CljVal, val: CljVal): CljVal =
|
proc cljAssoc*(m: CljVal, key: CljVal, val: CljVal): CljVal =
|
||||||
if m.isNil or m.kind == ckNil:
|
if m.isNil or m.kind == ckNil:
|
||||||
return CljVal(kind: ckMap, mapData: pmapAssoc(newPersistentMap[CljVal, CljVal](), key, val, hash(key), cljEq))
|
return CljVal(kind: ckMap, mapData: pmapAssoc(newPersistentMap[CljVal, CljVal](), key, val, hash(key), cljEq))
|
||||||
@@ -1340,6 +1469,29 @@ proc cljMap*(f: CljVal, coll: CljVal): CljVal =
|
|||||||
if f.kind == ckFn: cljMap(f.fnProc, coll)
|
if f.kind == ckFn: cljMap(f.fnProc, coll)
|
||||||
else: raise newException(CatchableError, "map requires a function")
|
else: raise newException(CatchableError, "map requires a function")
|
||||||
|
|
||||||
|
proc cljMapN*(f: CljVal, colls: seq[CljVal]): CljVal =
|
||||||
|
if f.kind != ckFn: raise newException(CatchableError, "map requires a function")
|
||||||
|
let fn = f.fnProc
|
||||||
|
var seqs: seq[seq[CljVal]] = newSeq[seq[CljVal]]()
|
||||||
|
for coll in colls:
|
||||||
|
if coll.isNil:
|
||||||
|
seqs.add(@[])
|
||||||
|
else:
|
||||||
|
case coll.kind
|
||||||
|
of ckList: seqs.add(coll.listItems)
|
||||||
|
of ckVector: seqs.add(toSeq(coll.vecData))
|
||||||
|
else: seqs.add(@[])
|
||||||
|
if seqs.len == 0: return cljList(@[])
|
||||||
|
var res = newSeq[CljVal]()
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
var args = newSeq[CljVal]()
|
||||||
|
for s in seqs:
|
||||||
|
if i >= s.len: return cljList(res)
|
||||||
|
args.add(s[i])
|
||||||
|
res.add(fn(args))
|
||||||
|
i += 1
|
||||||
|
|
||||||
proc cljFilter*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
proc cljFilter*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
||||||
if coll.isNil: return cljList(@[])
|
if coll.isNil: return cljList(@[])
|
||||||
case coll.kind
|
case coll.kind
|
||||||
@@ -1362,6 +1514,15 @@ proc cljReduce*(f: CljVal, init: CljVal, coll: CljVal): CljVal =
|
|||||||
if f.kind == ckFn: cljReduce(f.fnProc, init, coll)
|
if f.kind == ckFn: cljReduce(f.fnProc, init, coll)
|
||||||
else: raise newException(CatchableError, "reduce requires a function")
|
else: raise newException(CatchableError, "reduce requires a function")
|
||||||
|
|
||||||
|
proc cljTransduce*(xform: CljVal, f: CljVal, init: CljVal, coll: CljVal): CljVal =
|
||||||
|
# Stub: apply xform to f and reduce
|
||||||
|
if xform.kind == ckFn and f.kind == ckFn:
|
||||||
|
let rf = xform.fnProc(@[f])
|
||||||
|
if rf.kind == ckFn:
|
||||||
|
return cljReduce(rf, init, coll)
|
||||||
|
# Fallback: just return init
|
||||||
|
return init
|
||||||
|
|
||||||
proc cljMapv*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
proc cljMapv*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
||||||
if coll.isNil: return cljVector(@[])
|
if coll.isNil: return cljVector(@[])
|
||||||
case coll.kind
|
case coll.kind
|
||||||
@@ -1590,9 +1751,32 @@ proc cljAtom*(args: seq[CljVal]): CljVal =
|
|||||||
i += 2
|
i += 2
|
||||||
a
|
a
|
||||||
|
|
||||||
|
proc cljPromise*(): CljVal =
|
||||||
|
cljAtom(cljNil())
|
||||||
|
|
||||||
proc cljPromise*(args: seq[CljVal]): CljVal =
|
proc cljPromise*(args: seq[CljVal]): CljVal =
|
||||||
cljAtom(cljNil())
|
cljAtom(cljNil())
|
||||||
|
|
||||||
|
proc cljForce*(v: CljVal): CljVal =
|
||||||
|
if v.kind == ckAtom: v.atomVal
|
||||||
|
else: v
|
||||||
|
|
||||||
|
proc cljSleep*(ms: CljVal): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljTap*(args: seq[CljVal]): CljVal =
|
||||||
|
if args.len >= 1: return args[0]
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljFutureCancel*(v: CljVal): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljAddTap*(args: seq[CljVal]): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
|
proc cljRemoveTap*(args: seq[CljVal]): CljVal =
|
||||||
|
cljNil()
|
||||||
|
|
||||||
proc cljFuture*(args: seq[CljVal]): CljVal =
|
proc cljFuture*(args: seq[CljVal]): CljVal =
|
||||||
if args.len == 0: return cljAtom(cljNil())
|
if args.len == 0: return cljAtom(cljNil())
|
||||||
let f = args[0]
|
let f = args[0]
|
||||||
@@ -1621,7 +1805,7 @@ proc cljReset*(a: CljVal, v: CljVal): CljVal =
|
|||||||
else:
|
else:
|
||||||
raise newException(CatchableError, "reset! requires an atom")
|
raise newException(CatchableError, "reset! requires an atom")
|
||||||
|
|
||||||
proc cljSwap*(a: CljVal, f: proc(args: seq[CljVal]): CljVal, args: seq[CljVal]): CljVal =
|
proc cljSwapImpl(a: CljVal, f: proc(args: seq[CljVal]): CljVal, args: seq[CljVal]): CljVal =
|
||||||
if a.kind == ckAtom:
|
if a.kind == ckAtom:
|
||||||
var fargs = @[a.atomVal]
|
var fargs = @[a.atomVal]
|
||||||
fargs.add(args)
|
fargs.add(args)
|
||||||
@@ -1630,10 +1814,18 @@ proc cljSwap*(a: CljVal, f: proc(args: seq[CljVal]): CljVal, args: seq[CljVal]):
|
|||||||
else:
|
else:
|
||||||
raise newException(CatchableError, "swap! requires an atom")
|
raise newException(CatchableError, "swap! requires an atom")
|
||||||
|
|
||||||
proc cljSwap*(a: CljVal, f: CljVal, args: seq[CljVal] = @[]): CljVal =
|
proc cljSwap*(args: seq[CljVal]): CljVal =
|
||||||
if f.kind == ckFn: cljSwap(a, f.fnProc, args)
|
if args.len < 2: raise newException(CatchableError, "swap! requires at least 2 arguments")
|
||||||
|
let a = args[0]
|
||||||
|
let f = args[1]
|
||||||
|
let rest = if args.len > 2: args[2..^1] else: @[]
|
||||||
|
if f.kind == ckFn: cljSwapImpl(a, f.fnProc, rest)
|
||||||
else: raise newException(CatchableError, "swap! requires a function")
|
else: raise newException(CatchableError, "swap! requires a function")
|
||||||
|
|
||||||
|
proc cljVswap*(args: seq[CljVal]): CljVal =
|
||||||
|
# Treat volatile like atom for now
|
||||||
|
cljSwap(args)
|
||||||
|
|
||||||
# ---- Agents ----
|
# ---- Agents ----
|
||||||
|
|
||||||
proc cljAgent*(v: CljVal): CljVal =
|
proc cljAgent*(v: CljVal): CljVal =
|
||||||
|
|||||||
+306
-59
@@ -1,5 +1,5 @@
|
|||||||
# Clojure → Nim Emitter
|
# Clojure → Nim Emitter
|
||||||
import strutils, sets
|
import strutils, sets, algorithm
|
||||||
import types
|
import types
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ proc mangleName*(name: string): string =
|
|||||||
"raise", "import", "export", "from", "include", "using", "bind",
|
"raise", "import", "export", "from", "include", "using", "bind",
|
||||||
"mixin", "asm", "defer", "block", "static", "yield", "assert",
|
"mixin", "asm", "defer", "block", "static", "yield", "assert",
|
||||||
"do", "enum", "tuple", "shared", "guard", "concept", "distinct",
|
"do", "enum", "tuple", "shared", "guard", "concept", "distinct",
|
||||||
"interface", "lambda", "open", "quit", "result", "nil"]
|
"interface", "lambda", "open", "quit", "result", "nil", "end"]
|
||||||
if result in nimKeywords:
|
if result in nimKeywords:
|
||||||
result = result & "1"
|
result = result & "1"
|
||||||
|
|
||||||
@@ -182,8 +182,26 @@ proc runtimeName(op: string): string =
|
|||||||
of "zipmap": "cljZipmap"
|
of "zipmap": "cljZipmap"
|
||||||
of "object-array": "cljObjectArray"
|
of "object-array": "cljObjectArray"
|
||||||
of "sorted-map": "cljSortedMap"
|
of "sorted-map": "cljSortedMap"
|
||||||
|
of "sorted-map-by": "cljSortedMapBy"
|
||||||
of "sorted-set": "cljSortedSet"
|
of "sorted-set": "cljSortedSet"
|
||||||
|
of "sorted-set-by": "cljSortedSetBy"
|
||||||
of "sorted?": "cljSortedQ"
|
of "sorted?": "cljSortedQ"
|
||||||
|
of "use-fixtures": "cljUseFixtures"
|
||||||
|
of "transduce": "cljTransduce"
|
||||||
|
of "vswap!": "cljVswap"
|
||||||
|
of "split": "cljStrSplit"
|
||||||
|
of "str/split": "cljStrSplit"
|
||||||
|
of "add-watch": "cljAddWatch"
|
||||||
|
of "remove-watch": "cljRemoveWatch"
|
||||||
|
of "ex-info": "cljExInfo"
|
||||||
|
of "ex-data": "cljExData"
|
||||||
|
of "alter-var-root": "cljAlterVarRoot"
|
||||||
|
of "force": "cljForce"
|
||||||
|
of "sleep": "cljSleep"
|
||||||
|
of "tap>": "cljTap"
|
||||||
|
of "future-cancel": "cljFutureCancel"
|
||||||
|
of "add-tap": "cljAddTap"
|
||||||
|
of "remove-tap": "cljRemoveTap"
|
||||||
of "array-map": "cljArrayMap"
|
of "array-map": "cljArrayMap"
|
||||||
of "rseq": "cljRseq"
|
of "rseq": "cljRseq"
|
||||||
of "list": "cljListEmpty"
|
of "list": "cljListEmpty"
|
||||||
@@ -262,6 +280,8 @@ proc runtimeName(op: string): string =
|
|||||||
of "deliver": "cljDeliver"
|
of "deliver": "cljDeliver"
|
||||||
of "var?": "cljIsVar"
|
of "var?": "cljIsVar"
|
||||||
of "ifn?": "cljIsIfn"
|
of "ifn?": "cljIsIfn"
|
||||||
|
of "parse-uuid": "cljParseUuid"
|
||||||
|
of "uuid?": "cljIsUuid"
|
||||||
of "ancestors": "cljAncestors"
|
of "ancestors": "cljAncestors"
|
||||||
of "descendants": "cljDescendants"
|
of "descendants": "cljDescendants"
|
||||||
of "parents": "cljParents"
|
of "parents": "cljParents"
|
||||||
@@ -283,6 +303,7 @@ proc runtimeName(op: string): string =
|
|||||||
of "fnil": "cljFnil"
|
of "fnil": "cljFnil"
|
||||||
of "intern": "cljIntern"
|
of "intern": "cljIntern"
|
||||||
of "to-array": "cljToArray"
|
of "to-array": "cljToArray"
|
||||||
|
of "into-array": "cljIntoArray"
|
||||||
of "repeatedly": "cljRepeatedly"
|
of "repeatedly": "cljRepeatedly"
|
||||||
of "make-hierarchy": "cljMakeHierarchy"
|
of "make-hierarchy": "cljMakeHierarchy"
|
||||||
of "derive": "cljDerive"
|
of "derive": "cljDerive"
|
||||||
@@ -339,14 +360,64 @@ proc emitFnAsProc(items: seq[CljVal], indent: int): string =
|
|||||||
else:
|
else:
|
||||||
bodyCode = emitBlock(body, indent + 1)
|
bodyCode = emitBlock(body, indent + 1)
|
||||||
popScope()
|
popScope()
|
||||||
|
let sp = indentStr(indent)
|
||||||
if bodyCode.find("\n") == -1:
|
if bodyCode.find("\n") == -1:
|
||||||
return "(proc (" & paramNames.join(", ") & "): CljVal = " & bodyCode.strip() & ")"
|
return sp & "(proc (" & paramNames.join(", ") & "): CljVal = " & bodyCode.strip() & ")"
|
||||||
else:
|
else:
|
||||||
return "(proc (" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & ")"
|
return sp & "(proc (" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & ")"
|
||||||
|
|
||||||
|
proc letDestructuredBindings(bindings: CljVal): CljVal =
|
||||||
|
# Expand let bindings with vector/map destructuring into simple symbol bindings
|
||||||
|
if bindings.kind != ckVector:
|
||||||
|
return bindings
|
||||||
|
var result: seq[CljVal] = @[]
|
||||||
|
var i = 0
|
||||||
|
while i < bindings.items.len:
|
||||||
|
let bname = bindings.items[i]
|
||||||
|
let bval = bindings.items[i+1]
|
||||||
|
if bname.kind == ckSymbol:
|
||||||
|
result.add(bname)
|
||||||
|
result.add(bval)
|
||||||
|
elif bname.kind == ckVector:
|
||||||
|
let tmp = cljSymbol("ds_" & $i)
|
||||||
|
result.add(tmp)
|
||||||
|
result.add(bval)
|
||||||
|
for j in 0..<bname.items.len:
|
||||||
|
if bname.items[j].kind == ckSymbol:
|
||||||
|
result.add(bname.items[j])
|
||||||
|
result.add(cljList(@[cljSymbol("nth"), tmp, cljInt(j)]))
|
||||||
|
elif bname.kind == ckMap:
|
||||||
|
let tmp = cljSymbol("ds_" & $i)
|
||||||
|
result.add(tmp)
|
||||||
|
result.add(bval)
|
||||||
|
var hasAs = false
|
||||||
|
var asName: CljVal = nil
|
||||||
|
var j = 0
|
||||||
|
while j < bname.mapKeys.len:
|
||||||
|
let key = bname.mapKeys[j]
|
||||||
|
let val = bname.mapVals[j]
|
||||||
|
if key.kind == ckKeyword and key.kwName == "keys" and val.kind == ckVector:
|
||||||
|
for k in 0..<val.items.len:
|
||||||
|
if val.items[k].kind == ckSymbol:
|
||||||
|
result.add(val.items[k])
|
||||||
|
result.add(cljList(@[cljSymbol("get"), tmp, cljKeyword(val.items[k].symName)]))
|
||||||
|
elif key.kind == ckKeyword and key.kwName == "as":
|
||||||
|
hasAs = true
|
||||||
|
asName = val
|
||||||
|
j += 1
|
||||||
|
if hasAs and asName != nil and asName.kind == ckSymbol:
|
||||||
|
result.add(asName)
|
||||||
|
result.add(tmp)
|
||||||
|
i += 2
|
||||||
|
return cljVector(result)
|
||||||
|
|
||||||
proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||||
let sp = indentStr(indent)
|
let sp = indentStr(indent)
|
||||||
let head = items[0]
|
let head = items[0]
|
||||||
|
if head.kind == ckSymbol:
|
||||||
|
stderr.writeLine("DEBUG emitSpecialForm head=", head.symName)
|
||||||
|
if head.kind == ckSymbol and head.symName == "fn*":
|
||||||
|
stderr.writeLine("DEBUG emitSpecialForm fn* reached, op=", resolveNsAlias(head.symName))
|
||||||
if head.kind != ckSymbol:
|
if head.kind != ckSymbol:
|
||||||
# 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] = @[]
|
||||||
@@ -529,11 +600,14 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
if name.kind != ckSymbol:
|
if name.kind != ckSymbol:
|
||||||
raise newException(EmitterError, "def name must be a symbol")
|
raise newException(EmitterError, "def name must be a symbol")
|
||||||
let mangled = mangleName(name.symName)
|
let mangled = mangleName(name.symName)
|
||||||
|
addToScope(name.symName)
|
||||||
let valForm = items[2]
|
let valForm = items[2]
|
||||||
# Special handling: (def name (fn ...)) should emit raw proc, not cljFn wrapper
|
# Special handling: (def name (fn ...)) should emit raw proc, not cljFn wrapper
|
||||||
let isFnDef = valForm.kind == ckList and valForm.items.len > 0 and
|
let isFnDef = valForm.kind == ckList and valForm.items.len > 0 and
|
||||||
valForm.items[0].kind == ckSymbol and valForm.items[0].symName == "fn"
|
valForm.items[0].kind == ckSymbol and valForm.items[0].symName == "fn"
|
||||||
|
stderr.writeLine("DEBUG def mangled=", mangled, " indent=", indent, " items.len=", items.len, " isFnDef=", isFnDef)
|
||||||
if indent == 0:
|
if indent == 0:
|
||||||
|
stderr.writeLine("DEBUG def indent=0 branch")
|
||||||
if isFnDef:
|
if isFnDef:
|
||||||
# Emit fn as raw proc for def context
|
# Emit fn as raw proc for def context
|
||||||
let oldEmitLibMode = emitLibMode
|
let oldEmitLibMode = emitLibMode
|
||||||
@@ -544,15 +618,17 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
let valCode = emitExpr(items[2], 0)
|
let valCode = emitExpr(items[2], 0)
|
||||||
return sp & "let " & mangled & " = " & valCode
|
return sp & "let " & mangled & " = " & valCode
|
||||||
else:
|
else:
|
||||||
# For nested def: wrap in proc and use consistent indentation
|
stderr.writeLine("DEBUG def indent>0 branch")
|
||||||
|
# For nested def: use global var so it's accessible from closures
|
||||||
if isFnDef:
|
if isFnDef:
|
||||||
let oldEmitLibMode = emitLibMode
|
let oldEmitLibMode = emitLibMode
|
||||||
emitLibMode = false
|
emitLibMode = false
|
||||||
let valCode = emitFnAsProc(valForm.items, indent + 2)
|
let valCode = emitFnAsProc(valForm.items, indent)
|
||||||
emitLibMode = oldEmitLibMode
|
emitLibMode = oldEmitLibMode
|
||||||
return sp & "((proc (): CljVal =\n" & indentStr(indent + 1) & "let " & mangled & " = " & valCode.strip() & "\n" & indentStr(indent + 1) & mangled & ")())"
|
return sp & "var " & mangled & " {.global.} = " & valCode.strip() & "\n" & sp & mangled
|
||||||
let valCode = emitExpr(items[2], indent + 2)
|
let valCode = emitExpr(items[2], indent)
|
||||||
return sp & "((proc (): CljVal =\n" & indentStr(indent + 1) & "let " & mangled & " = " & valCode.strip() & "\n" & indentStr(indent + 1) & mangled & ")())"
|
stderr.writeLine("DEBUG def about to return var global for ", mangled)
|
||||||
|
return sp & "var " & mangled & " {.global.} = " & valCode.strip() & "\n" & sp & mangled
|
||||||
|
|
||||||
of "defn":
|
of "defn":
|
||||||
if items.len < 3:
|
if items.len < 3:
|
||||||
@@ -574,7 +650,6 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
addToScope(p.symName)
|
addToScope(p.symName)
|
||||||
paramNames.add(mangleName(p.symName) & ": CljVal")
|
paramNames.add(mangleName(p.symName) & ": CljVal")
|
||||||
let body = items[3..^1]
|
let body = items[3..^1]
|
||||||
popScope()
|
|
||||||
let procName = mangleName(name.symName)
|
let procName = mangleName(name.symName)
|
||||||
let exportMarker = if emitLibMode: "*" else: ""
|
let exportMarker = if emitLibMode: "*" else: ""
|
||||||
if indent == 0:
|
if indent == 0:
|
||||||
@@ -585,6 +660,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
bodyCode = emitExpr(body[0], indent + 1)
|
bodyCode = emitExpr(body[0], indent + 1)
|
||||||
else:
|
else:
|
||||||
bodyCode = emitBlock(body, indent + 1)
|
bodyCode = emitBlock(body, indent + 1)
|
||||||
|
popScope()
|
||||||
return sp & "proc " & procName & exportMarker & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode
|
return sp & "proc " & procName & exportMarker & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode
|
||||||
else:
|
else:
|
||||||
var bodyCode = ""
|
var bodyCode = ""
|
||||||
@@ -592,9 +668,10 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
bodyCode = emitExpr(body[0], indent + 2)
|
bodyCode = emitExpr(body[0], indent + 2)
|
||||||
else:
|
else:
|
||||||
bodyCode = emitBlock(body, indent + 2)
|
bodyCode = emitBlock(body, indent + 2)
|
||||||
|
popScope()
|
||||||
return sp & "(block:\n" &
|
return sp & "(block:\n" &
|
||||||
indentStr(indent + 1) & "proc " & procName & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & "\n" &
|
indentStr(indent + 1) & "proc " & procName & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & "\n" &
|
||||||
indentStr(indent + 1) & procName & ")"
|
indentStr(indent + 1) & "cljNil())"
|
||||||
|
|
||||||
of "defn-":
|
of "defn-":
|
||||||
if items.len < 3:
|
if items.len < 3:
|
||||||
@@ -629,12 +706,15 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
return sp & "proc " & procName & "(" & paramNames.join(", ") & "): CljVal {.used.} =\n" & bodyCode
|
return sp & "proc " & procName & "(" & paramNames.join(", ") & "): CljVal {.used.} =\n" & bodyCode
|
||||||
|
|
||||||
of "fn":
|
of "fn":
|
||||||
|
stderr.writeLine("DEBUG emitSpecialForm fn indent=", indent)
|
||||||
if items.len < 2:
|
if items.len < 2:
|
||||||
raise newException(EmitterError, "fn requires params")
|
raise newException(EmitterError, "fn requires params")
|
||||||
var paramsIdx = 1
|
var paramsIdx = 1
|
||||||
|
var fnName = ""
|
||||||
# Handle named fn: (fn name [params] body)
|
# Handle named fn: (fn name [params] body)
|
||||||
if items[1].kind == ckSymbol:
|
if items[1].kind == ckSymbol:
|
||||||
paramsIdx = 2
|
paramsIdx = 2
|
||||||
|
fnName = items[1].symName
|
||||||
if paramsIdx >= items.len:
|
if paramsIdx >= items.len:
|
||||||
raise newException(EmitterError, "fn requires params and body")
|
raise newException(EmitterError, "fn requires params and body")
|
||||||
var params = items[paramsIdx]
|
var params = items[paramsIdx]
|
||||||
@@ -647,6 +727,8 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
let paramCount = params.items.len
|
let paramCount = params.items.len
|
||||||
pushScope()
|
pushScope()
|
||||||
var paramNames: seq[string] = @[]
|
var paramNames: seq[string] = @[]
|
||||||
|
if fnName.len > 0:
|
||||||
|
addToScope(fnName)
|
||||||
for p in params.items:
|
for p in params.items:
|
||||||
if p.kind != ckSymbol:
|
if p.kind != ckSymbol:
|
||||||
raise newException(EmitterError, "fn params must be symbols")
|
raise newException(EmitterError, "fn params must be symbols")
|
||||||
@@ -664,27 +746,40 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
if paramCount > 0:
|
if paramCount > 0:
|
||||||
for i in 0..<paramCount:
|
for i in 0..<paramCount:
|
||||||
argsLines.add(indentStr(indent + 1) & "let " & paramNames[i] & " = args[" & $i & "]")
|
argsLines.add(indentStr(indent + 1) & "let " & paramNames[i] & " = args[" & $i & "]")
|
||||||
|
var fnBodyLines: seq[string] = @[]
|
||||||
if argsLines.len > 0:
|
if argsLines.len > 0:
|
||||||
# Multi-line: let bindings on separate lines, then body
|
|
||||||
var allLines: seq[string] = @[]
|
|
||||||
for a in argsLines:
|
for a in argsLines:
|
||||||
allLines.add(a)
|
fnBodyLines.add(a)
|
||||||
if bodyCode.find("\n") == -1:
|
if bodyCode.find("\n") == -1:
|
||||||
allLines.add(indentStr(indent + 1) & bodyCode.strip())
|
fnBodyLines.add(indentStr(indent + 1) & bodyCode.strip())
|
||||||
else:
|
else:
|
||||||
allLines.add(bodyCode)
|
fnBodyLines.add(bodyCode)
|
||||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal =\n" & allLines.join("\n") & ")"
|
if fnName.len > 0 or argsLines.len > 0 or bodyCode.find("\n") != -1:
|
||||||
|
# Wrap in IIFE to avoid indentation issues when used as an argument
|
||||||
|
var wrapperLines: seq[string] = @[]
|
||||||
|
stderr.writeLine("DEBUG fn indent=", indent, " spLen=", sp.len, " indent+1Len=", indentStr(indent+1).len)
|
||||||
|
wrapperLines.add(sp & "((proc (): CljVal =")
|
||||||
|
var tempName: string
|
||||||
|
if fnName.len > 0:
|
||||||
|
tempName = mangleName(fnName)
|
||||||
|
else:
|
||||||
|
tempName = "fnResult"
|
||||||
|
wrapperLines.add(indentStr(indent + 1) & "var " & tempName & ": CljVal")
|
||||||
|
wrapperLines.add(indentStr(indent + 1) & tempName & " = cljFn(proc(args: seq[CljVal]): CljVal =")
|
||||||
|
let indentedFnBody = indentCode(fnBodyLines.join("\n"), 1)
|
||||||
|
for line in indentedFnBody.split("\n"):
|
||||||
|
wrapperLines.add(line)
|
||||||
|
wrapperLines.add(indentStr(indent + 1) & ")")
|
||||||
|
wrapperLines.add(indentStr(indent + 1) & tempName)
|
||||||
|
wrapperLines.add(sp & ")())")
|
||||||
|
return wrapperLines.join("\n")
|
||||||
else:
|
else:
|
||||||
# No params - simple case
|
|
||||||
if bodyCode.find("\n") == -1:
|
|
||||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & bodyCode.strip() & ")"
|
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & bodyCode.strip() & ")"
|
||||||
else:
|
|
||||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal =\n" & bodyCode & ")"
|
|
||||||
|
|
||||||
of "let":
|
of "let":
|
||||||
if items.len < 3:
|
if items.len < 3:
|
||||||
raise newException(EmitterError, "let requires bindings and body")
|
raise newException(EmitterError, "let requires bindings and body")
|
||||||
let bindings = items[1]
|
var bindings = letDestructuredBindings(items[1])
|
||||||
if bindings.kind != ckVector:
|
if bindings.kind != ckVector:
|
||||||
raise newException(EmitterError, "let bindings must be a vector")
|
raise newException(EmitterError, "let bindings must be a vector")
|
||||||
if bindings.items.len mod 2 != 0:
|
if bindings.items.len mod 2 != 0:
|
||||||
@@ -697,9 +792,20 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
let bname = bindings.items[bi]
|
let bname = bindings.items[bi]
|
||||||
let bval = bindings.items[bi+1]
|
let bval = bindings.items[bi+1]
|
||||||
if bname.kind != ckSymbol:
|
if bname.kind != ckSymbol:
|
||||||
raise newException(EmitterError, "let binding name must be a symbol")
|
raise newException(EmitterError, "let binding name must be a symbol, got " & $bname.kind & ": " & $bname)
|
||||||
addToScope(bname.symName)
|
addToScope(bname.symName)
|
||||||
lines.add(indentStr(indent + 1) & "let " & mangleName(bname.symName) & " = " & emitExpr(bval, indent + 1))
|
let bcode = emitExpr(bval, indent + 1)
|
||||||
|
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")
|
||||||
|
var bcodeLines = bcode.split("\n")
|
||||||
|
for j, line in bcodeLines:
|
||||||
|
if j == 0:
|
||||||
|
lines.add(indentStr(indent + 1) & mangleName(bname.symName) & " = " & line)
|
||||||
|
else:
|
||||||
|
lines.add(line)
|
||||||
|
else:
|
||||||
|
lines.add(indentStr(indent + 1) & "let " & mangleName(bname.symName) & " = " & bcode)
|
||||||
bi += 2
|
bi += 2
|
||||||
let body = items[2..^1]
|
let body = items[2..^1]
|
||||||
for j, b in body:
|
for j, b in body:
|
||||||
@@ -708,7 +814,11 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
lines.add(bcode)
|
lines.add(bcode)
|
||||||
else:
|
else:
|
||||||
let stripped = bcode.strip()
|
let stripped = bcode.strip()
|
||||||
if stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
if stripped.contains("\n"):
|
||||||
|
# Multi-line expression as non-last form: wrap in proc
|
||||||
|
let indentedCode = indentCode(bcode, 1)
|
||||||
|
lines.add(indentStr(indent + 1) & "discard ((proc (): CljVal =\n" & indentedCode & "\n" & indentStr(indent + 1) & ")())")
|
||||||
|
elif stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
||||||
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
||||||
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
||||||
stripped.startsWith("proc ") or stripped.startsWith("while "):
|
stripped.startsWith("proc ") or stripped.startsWith("while "):
|
||||||
@@ -751,13 +861,26 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
for bi, b in body:
|
for bi, b in body:
|
||||||
let bcode = emitExpr(b, indent + 1)
|
let bcode = emitExpr(b, indent + 1)
|
||||||
let stripped = bcode.strip()
|
let stripped = bcode.strip()
|
||||||
|
let isLast = (bi == body.len - 1)
|
||||||
let isStatement = stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
let isStatement = stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
||||||
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
||||||
stripped.startsWith("proc ") or stripped.startsWith("block:") or
|
stripped.startsWith("proc ") or
|
||||||
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
||||||
stripped.startsWith("while ") or stripped.contains("continue")
|
stripped.startsWith("while ") or stripped.contains("continue")
|
||||||
if isStatement:
|
if isLast or isStatement:
|
||||||
lines.add(bcode)
|
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:
|
else:
|
||||||
lines.add(indentStr(indent + 1) & "discard " & stripped)
|
lines.add(indentStr(indent + 1) & "discard " & stripped)
|
||||||
lines.add(sp & "else:")
|
lines.add(sp & "else:")
|
||||||
@@ -794,8 +917,31 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
loopStack.add(loopVars)
|
loopStack.add(loopVars)
|
||||||
lines.add(sp & "while true:")
|
lines.add(sp & "while true:")
|
||||||
let body = items[2..^1]
|
let body = items[2..^1]
|
||||||
for b in body:
|
for bi, b in body:
|
||||||
lines.add(emitExpr(b, indent + 1))
|
let bcode = emitExpr(b, indent + 1)
|
||||||
|
let stripped = bcode.strip()
|
||||||
|
let isLast = (bi == body.len - 1)
|
||||||
|
let isStatement = stripped.startsWith("echo ") or stripped.startsWith("discard ") or
|
||||||
|
stripped.startsWith("var ") or stripped.startsWith("let ") or
|
||||||
|
stripped.startsWith("proc ") or
|
||||||
|
stripped.startsWith("if ") or stripped.startsWith("try:") or
|
||||||
|
stripped.startsWith("while ") or stripped.contains("continue")
|
||||||
|
if isLast or isStatement:
|
||||||
|
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)
|
||||||
lines.add(indentStr(indent + 1) & "break")
|
lines.add(indentStr(indent + 1) & "break")
|
||||||
discard loopStack.pop()
|
discard loopStack.pop()
|
||||||
popScope()
|
popScope()
|
||||||
@@ -838,15 +984,25 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
if form.kind == ckList and form.items.len > 0 and form.items[0].kind == ckSymbol:
|
if form.kind == ckList and form.items.len > 0 and form.items[0].kind == ckSymbol:
|
||||||
if form.items[0].symName == "catch":
|
if form.items[0].symName == "catch":
|
||||||
if form.items.len < 3:
|
if form.items.len < 3:
|
||||||
raise newException(EmitterError, "catch requires exception type, name, and body")
|
raise newException(EmitterError, "catch requires at least name and body")
|
||||||
let exType = form.items[1]
|
|
||||||
var exTypeStr = "CatchableError"
|
var exTypeStr = "CatchableError"
|
||||||
if exType.kind == ckSymbol:
|
var nameIdx = 2
|
||||||
exTypeStr = exType.symName
|
var bodyIdx = 3
|
||||||
let exName = form.items[2]
|
if form.items[1].kind == ckSymbol and form.items.len >= 4:
|
||||||
|
exTypeStr = form.items[1].symName
|
||||||
|
elif form.items[1].kind == ckKeyword:
|
||||||
|
if form.items[1].kwName == "default":
|
||||||
|
exTypeStr = "CatchableError"
|
||||||
|
else:
|
||||||
|
exTypeStr = form.items[1].kwName
|
||||||
|
else:
|
||||||
|
# (catch name body...)
|
||||||
|
nameIdx = 1
|
||||||
|
bodyIdx = 2
|
||||||
|
let exName = form.items[nameIdx]
|
||||||
if exName.kind != ckSymbol:
|
if exName.kind != ckSymbol:
|
||||||
raise newException(EmitterError, "catch name must be a symbol")
|
raise newException(EmitterError, "catch name must be a symbol")
|
||||||
catchClauses.add((exTypeStr, mangleName(exName.symName), form.items[3..^1]))
|
catchClauses.add((exTypeStr, mangleName(exName.symName), form.items[bodyIdx..^1]))
|
||||||
elif form.items[0].symName == "finally":
|
elif form.items[0].symName == "finally":
|
||||||
finallyBody = form.items[1..^1]
|
finallyBody = form.items[1..^1]
|
||||||
else:
|
else:
|
||||||
@@ -876,6 +1032,9 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
lines.add(sp & "except " & exType & ":")
|
lines.add(sp & "except " & exType & ":")
|
||||||
lines.add(indentStr(indent + 1) & "let " & exVar & " = cljMap(@[cljKeyword(\"message\")], @[cljString(getCurrentExceptionMsg())])")
|
lines.add(indentStr(indent + 1) & "let " & exVar & " = cljMap(@[cljKeyword(\"message\")], @[cljString(getCurrentExceptionMsg())])")
|
||||||
lines.add(indentStr(indent + 1) & "discard cljAssoc(" & exVar & ", cljKeyword(\"type\"), cljString(\"" & exType & "\"))")
|
lines.add(indentStr(indent + 1) & "discard cljAssoc(" & exVar & ", cljKeyword(\"type\"), cljString(\"" & exType & "\"))")
|
||||||
|
lines.add(indentStr(indent + 1) & "let exPtr" & exVar & " = getCurrentException()")
|
||||||
|
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)")
|
||||||
for i, eb in exBody:
|
for i, eb in exBody:
|
||||||
let isLast = (i == exBody.len - 1)
|
let isLast = (i == exBody.len - 1)
|
||||||
let code = emitExpr(eb, indent + 1)
|
let code = emitExpr(eb, indent + 1)
|
||||||
@@ -945,17 +1104,50 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
return emitExpr(quoted, indent)
|
return emitExpr(quoted, indent)
|
||||||
|
|
||||||
of "map":
|
of "map":
|
||||||
if items.len != 3:
|
if items.len < 3:
|
||||||
raise newException(EmitterError, "map requires exactly 2 arguments (f, coll)")
|
raise newException(EmitterError, "map requires at least 2 arguments (f, coll)")
|
||||||
let fnArg = items[1]
|
let fnArg = items[1]
|
||||||
let collArg = emitExpr(items[2], 0)
|
let isVariadic = items.len > 3
|
||||||
|
var collArgs: seq[string] = @[]
|
||||||
|
for idx in 2..<items.len:
|
||||||
|
collArgs.add(emitExpr(items[idx], 0))
|
||||||
|
if isVariadic:
|
||||||
|
# Multiple collections: use cljMapN
|
||||||
|
let collList = "@[" & collArgs.join(", ") & "]"
|
||||||
if fnArg.kind == ckSymbol:
|
if fnArg.kind == ckSymbol:
|
||||||
let rn = runtimeName(fnArg.symName)
|
let rn = runtimeName(fnArg.symName)
|
||||||
if rn.len > 0:
|
if rn.len > 0:
|
||||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args)), " & collArg & ")"
|
return sp & "cljMapN(cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args)), " & collList & ")"
|
||||||
|
return sp & "cljMapN(cljFn(proc(args: seq[CljVal]): CljVal = " & mangleName(fnArg.symName) & "(args)), " & collList & ")"
|
||||||
|
elif fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName in ["fn", "fn*"]:
|
||||||
|
let fnParams = fnArg.items[1]
|
||||||
|
let fnBody = fnArg.items[2..^1]
|
||||||
|
var pNames: seq[string] = @[]
|
||||||
|
for p in fnParams.items:
|
||||||
|
pNames.add(mangleName(p.symName))
|
||||||
|
var bodyExpr = ""
|
||||||
|
if fnBody.len == 1:
|
||||||
|
bodyExpr = emitExpr(fnBody[0], 0).strip()
|
||||||
|
else:
|
||||||
|
var parts: seq[string] = @[]
|
||||||
|
for b in fnBody:
|
||||||
|
parts.add(emitExpr(b, 0).strip())
|
||||||
|
bodyExpr = parts.join("; ")
|
||||||
|
var letBindings = ""
|
||||||
|
for j in 0..<pNames.len:
|
||||||
|
letBindings.add("let " & pNames[j] & " = args[" & $j & "]; ")
|
||||||
|
let fnProc = "proc(args: seq[CljVal]): CljVal = (" & letBindings & bodyExpr & ")"
|
||||||
|
return sp & "cljMapN(cljFn(" & fnProc & "), " & collList & ")"
|
||||||
|
else:
|
||||||
|
return sp & "cljMapN(" & emitExpr(fnArg, 0) & ", " & collList & ")"
|
||||||
|
else:
|
||||||
|
let collArg = collArgs[0]
|
||||||
|
if fnArg.kind == ckSymbol:
|
||||||
|
let rn = runtimeName(fnArg.symName)
|
||||||
|
if rn.len > 0:
|
||||||
|
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args[0])), " & collArg & ")"
|
||||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & mangleName(fnArg.symName) & "(args[0])), " & collArg & ")"
|
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & mangleName(fnArg.symName) & "(args[0])), " & collArg & ")"
|
||||||
elif fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName == "fn":
|
elif fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName in ["fn", "fn*"]:
|
||||||
# Inline fn: (map (fn [x] ...) coll)
|
|
||||||
let fnParams = fnArg.items[1]
|
let fnParams = fnArg.items[1]
|
||||||
let fnBody = fnArg.items[2..^1]
|
let fnBody = fnArg.items[2..^1]
|
||||||
var pNames: seq[string] = @[]
|
var pNames: seq[string] = @[]
|
||||||
@@ -979,7 +1171,9 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
raise newException(EmitterError, "filter requires exactly 2 arguments")
|
raise newException(EmitterError, "filter requires exactly 2 arguments")
|
||||||
let fnArg = items[1]
|
let fnArg = items[1]
|
||||||
let collArg = emitExpr(items[2], 0)
|
let collArg = emitExpr(items[2], 0)
|
||||||
if fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName == "fn":
|
let isFn = fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName in ["fn", "fn*"]
|
||||||
|
stderr.writeLine("DEBUG filter isFn=", isFn, " symName=", (if fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol: fnArg.items[0].symName else: "N/A"))
|
||||||
|
if isFn:
|
||||||
let fnParams = fnArg.items[1]
|
let fnParams = fnArg.items[1]
|
||||||
let fnBody = fnArg.items[2..^1]
|
let fnBody = fnArg.items[2..^1]
|
||||||
var pNames: seq[string] = @[]
|
var pNames: seq[string] = @[]
|
||||||
@@ -997,7 +1191,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
let fnProc = "proc(args: seq[CljVal]): CljVal = (let " & pNames[0] & " = args[0]; " & bodyExpr & ")"
|
let fnProc = "proc(args: seq[CljVal]): CljVal = (let " & pNames[0] & " = args[0]; " & bodyExpr & ")"
|
||||||
return sp & "cljFilter(cljFn(" & fnProc & "), " & collArg & ")"
|
return sp & "cljFilter(cljFn(" & fnProc & "), " & collArg & ")"
|
||||||
else:
|
else:
|
||||||
return sp & "cljFilter(" & emitExpr(fnArg, 0) & ", " & collArg & ")"
|
return sp & "cljFilter(" & emitExpr(fnArg, indent) & ", " & collArg & ")"
|
||||||
|
|
||||||
of "reduce":
|
of "reduce":
|
||||||
if items.len < 3 or items.len > 4:
|
if items.len < 3 or items.len > 4:
|
||||||
@@ -1057,6 +1251,26 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
of "partial":
|
of "partial":
|
||||||
var parts: seq[string] = @[]
|
var parts: seq[string] = @[]
|
||||||
for i in 1..<items.len:
|
for i in 1..<items.len:
|
||||||
|
if items[i].kind == ckList and items[i].items.len > 0 and items[i].items[0].kind == ckSymbol and items[i].items[0].symName in ["fn", "fn*"]:
|
||||||
|
let fnParams = items[i].items[1]
|
||||||
|
let fnBody = items[i].items[2..^1]
|
||||||
|
var pNames: seq[string] = @[]
|
||||||
|
for p in fnParams.items:
|
||||||
|
pNames.add(mangleName(p.symName))
|
||||||
|
var bodyExpr = ""
|
||||||
|
if fnBody.len == 1:
|
||||||
|
bodyExpr = emitExpr(fnBody[0], 0).strip()
|
||||||
|
else:
|
||||||
|
var bodyParts: seq[string] = @[]
|
||||||
|
for b in fnBody:
|
||||||
|
bodyParts.add(emitExpr(b, 0).strip())
|
||||||
|
bodyExpr = bodyParts.join("; ")
|
||||||
|
var letBindings = ""
|
||||||
|
for j in 0..<pNames.len:
|
||||||
|
letBindings.add("let " & pNames[j] & " = args[" & $j & "]; ")
|
||||||
|
let fnProc = "proc(args: seq[CljVal]): CljVal = (" & letBindings & bodyExpr & ")"
|
||||||
|
parts.add("cljFn(" & fnProc & ")")
|
||||||
|
else:
|
||||||
parts.add(emitExpr(items[i], 0))
|
parts.add(emitExpr(items[i], 0))
|
||||||
return sp & "cljPartial(" & parts[0] & ", @[" & parts[1..^1].join(", ") & "])"
|
return sp & "cljPartial(" & parts[0] & ", @[" & parts[1..^1].join(", ") & "])"
|
||||||
|
|
||||||
@@ -1215,8 +1429,9 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
return emitExpr(fnForm, indent)
|
return emitExpr(fnForm, indent)
|
||||||
|
|
||||||
of "bound-fn*":
|
of "bound-fn*":
|
||||||
let fnForm = cljList(@[cljSymbol("fn")] & items[1..^1])
|
if items.len < 2:
|
||||||
return emitExpr(fnForm, indent)
|
raise newException(EmitterError, "bound-fn* requires a function argument")
|
||||||
|
return emitExpr(items[1], indent)
|
||||||
|
|
||||||
of "promise":
|
of "promise":
|
||||||
if items.len < 1: return sp & "cljPromise()"
|
if items.len < 1: return sp & "cljPromise()"
|
||||||
@@ -1322,10 +1537,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
parts.add(emitExpr(item, 0))
|
parts.add(emitExpr(item, 0))
|
||||||
return sp & "cljSet(@[" & parts.join(", ") & "])"
|
return sp & "cljSet(@[" & parts.join(", ") & "])"
|
||||||
of ckList:
|
of ckList:
|
||||||
var parts: seq[string] = @[]
|
return sp & "cljSet(" & emitExpr(arg, 0) & ")"
|
||||||
for item in arg.items:
|
|
||||||
parts.add(emitExpr(item, 0))
|
|
||||||
return sp & "cljSet(@[" & parts.join(", ") & "])"
|
|
||||||
else:
|
else:
|
||||||
return sp & "cljSet(@[" & emitExpr(arg, 0) & "])"
|
return sp & "cljSet(@[" & emitExpr(arg, 0) & "])"
|
||||||
|
|
||||||
@@ -1444,12 +1656,14 @@ 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:
|
||||||
|
if items[i].kind == ckList and items[i].items.len > 0 and items[i].items[0].kind == ckSymbol and items[i].items[0].symName in ["fn", "fn*"]:
|
||||||
|
stderr.writeLine("DEBUG runtimeCall fn arg indent=", indent)
|
||||||
argParts.add(emitExpr(items[i], indent))
|
argParts.add(emitExpr(items[i], indent))
|
||||||
# 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",
|
||||||
"atom", "concat", "min", "max", "merge", "interleave",
|
"atom", "concat", "min", "max", "merge", "interleave",
|
||||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-set", "cons",
|
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by", "cons", "use-fixtures", "vswap!", "swap!", "tap>", "add-tap", "remove-tap", "add-watch", "remove-watch", "alter-var-root",
|
||||||
"array-map", "inf", "nan", "list",
|
"array-map", "inf", "nan", "list",
|
||||||
"float", "int", "double", "long", "short", "byte",
|
"float", "int", "double", "long", "short", "byte",
|
||||||
"boolean", "num", "number",
|
"boolean", "num", "number",
|
||||||
@@ -1461,7 +1675,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||||
"println-str", "prn-str", "binding", "aset",
|
"println-str", "prn-str", "binding", "aset",
|
||||||
"volatile!", "deliver", "doall", "dorun",
|
"volatile!", "deliver", "doall", "dorun",
|
||||||
"to-array", "vector", "rand", "rand-int",
|
"to-array", "into-array", "vector", "rand", "rand-int",
|
||||||
"rand-nth", "random-sample"]
|
"rand-nth", "random-sample"]
|
||||||
var call: string
|
var call: string
|
||||||
if variadic:
|
if variadic:
|
||||||
@@ -1480,8 +1694,8 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
|||||||
callOp = callOp[0..^2]
|
callOp = callOp[0..^2]
|
||||||
let mangled = mangleName(callOp)
|
let mangled = mangleName(callOp)
|
||||||
if isLocalVar(callOp):
|
if isLocalVar(callOp):
|
||||||
# Local cljFn value: call via .fnProc
|
# Local value (may be fn, map, set, vector, keyword) — use runtime dispatch
|
||||||
return sp & mangled & ".fnProc(@[" & args.join(", ") & "])"
|
return sp & "cljCall(" & mangled & ", @[" & args.join(", ") & "])"
|
||||||
return sp & mangled & "(" & args.join(", ") & ")"
|
return sp & mangled & "(" & args.join(", ") & ")"
|
||||||
|
|
||||||
proc emitQuotedForm*(v: CljVal): string =
|
proc emitQuotedForm*(v: CljVal): string =
|
||||||
@@ -1539,7 +1753,7 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
|||||||
let variadic = symName in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
let variadic = symName in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||||
"println", "prn", "print", "str", "pr-str",
|
"println", "prn", "print", "str", "pr-str",
|
||||||
"atom", "concat", "min", "max", "merge", "interleave",
|
"atom", "concat", "min", "max", "merge", "interleave",
|
||||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-set", "cons",
|
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by", "cons", "use-fixtures", "vswap!", "swap!", "tap>", "add-tap", "remove-tap", "add-watch", "remove-watch", "alter-var-root",
|
||||||
"array-map", "inf", "nan", "list",
|
"array-map", "inf", "nan", "list",
|
||||||
"float", "int", "double", "long", "short", "byte",
|
"float", "int", "double", "long", "short", "byte",
|
||||||
"boolean", "num", "number",
|
"boolean", "num", "number",
|
||||||
@@ -1551,7 +1765,7 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
|||||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||||
"println-str", "prn-str", "binding", "aset",
|
"println-str", "prn-str", "binding", "aset",
|
||||||
"volatile!", "deliver", "doall", "dorun",
|
"volatile!", "deliver", "doall", "dorun",
|
||||||
"to-array", "vector", "rand", "rand-int",
|
"to-array", "into-array", "vector", "rand", "rand-int",
|
||||||
"rand-nth", "random-sample"]
|
"rand-nth", "random-sample"]
|
||||||
if variadic:
|
if variadic:
|
||||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
||||||
@@ -1602,6 +1816,8 @@ proc emitBlock(items: seq[CljVal], indent: int, useResult: bool = false): string
|
|||||||
var lines: seq[string] = @[]
|
var lines: seq[string] = @[]
|
||||||
for i, item in items:
|
for i, item in items:
|
||||||
var code = emitExpr(item, indent)
|
var code = emitExpr(item, indent)
|
||||||
|
if code.contains("testvar_a"):
|
||||||
|
stderr.writeLine("DEBUG emitBlock indent=", indent, " code=", code.repr)
|
||||||
let stripped = code.strip()
|
let stripped = code.strip()
|
||||||
if i < items.len - 1:
|
if i < items.len - 1:
|
||||||
let firstNl = code.find("\n")
|
let firstNl = code.find("\n")
|
||||||
@@ -1620,6 +1836,8 @@ proc emitBlock(items: seq[CljVal], indent: int, useResult: bool = false): string
|
|||||||
# Wrap multi-line expression in proc so discard can apply
|
# Wrap multi-line expression in proc so discard can apply
|
||||||
let indentedCode = indentCode(code, 1)
|
let indentedCode = indentCode(code, 1)
|
||||||
code = indentStr(indent) & "discard ((proc (): CljVal =\n" & indentedCode & "\n" & indentStr(indent) & ")())"
|
code = indentStr(indent) & "discard ((proc (): CljVal =\n" & indentedCode & "\n" & indentStr(indent) & ")())"
|
||||||
|
if code.contains("testvar_a"):
|
||||||
|
stderr.writeLine("DEBUG emitBlock after wrap has var=", code.contains("var testvar_a"), " has let=", code.contains("let testvar_a"))
|
||||||
elif useResult:
|
elif useResult:
|
||||||
if stripped.startsWith("discard "):
|
if stripped.startsWith("discard "):
|
||||||
code = indentStr(indent) & "result = " & stripped[8..^1]
|
code = indentStr(indent) & "result = " & stripped[8..^1]
|
||||||
@@ -1635,6 +1853,11 @@ proc emitBlock(items: seq[CljVal], indent: int, useResult: bool = false): string
|
|||||||
return lines.join("\n")
|
return lines.join("\n")
|
||||||
|
|
||||||
proc emitProgramInternal(forms: seq[CljVal]): string =
|
proc emitProgramInternal(forms: seq[CljVal]): string =
|
||||||
|
stderr.writeLine("DEBUG emitProgramInternal forms.len=", forms.len)
|
||||||
|
if forms.len > 0 and forms[0].kind == ckList and forms[0].items.len > 0 and forms[0].items[0].kind == ckSymbol:
|
||||||
|
stderr.writeLine("DEBUG emitProgramInternal forms[0]=", forms[0].items[0].symName, " forms[0].len=", forms[0].items.len)
|
||||||
|
scopeStack = @[]
|
||||||
|
pushScope()
|
||||||
requiredImports = initHashSet[string]()
|
requiredImports = initHashSet[string]()
|
||||||
var headerLines: seq[string] = @[
|
var headerLines: seq[string] = @[
|
||||||
"# Generated by Clojure/Nim",
|
"# Generated by Clojure/Nim",
|
||||||
@@ -1657,6 +1880,7 @@ proc emitProgramInternal(forms: seq[CljVal]): string =
|
|||||||
# Recurse into (do ...) forms produced by macro expansion
|
# Recurse into (do ...) forms produced by macro expansion
|
||||||
if headSym and headName == "do":
|
if headSym and headName == "do":
|
||||||
let subForms = ef.items[1..^1]
|
let subForms = ef.items[1..^1]
|
||||||
|
stderr.writeLine("DEBUG processOneForm do subForms.len=", subForms.len)
|
||||||
for j in countdown(subForms.len - 1, 0):
|
for j in countdown(subForms.len - 1, 0):
|
||||||
let subLast = isLast and (j == subForms.len - 1)
|
let subLast = isLast and (j == subForms.len - 1)
|
||||||
worklist.add((subForms[j], subLast))
|
worklist.add((subForms[j], subLast))
|
||||||
@@ -1664,12 +1888,19 @@ proc emitProgramInternal(forms: seq[CljVal]): string =
|
|||||||
let isDef = headSym and headName in ["def", "defn", "defn-", "defprotocol", "defrecord", "deftype", "defmulti", "defmethod", "var"]
|
let isDef = headSym and headName in ["def", "defn", "defn-", "defprotocol", "defrecord", "deftype", "defmulti", "defmethod", "var"]
|
||||||
let isMacro = headSym and headName in ["defmacro"]
|
let isMacro = headSym and headName in ["defmacro"]
|
||||||
let isNs = headSym and headName == "ns"
|
let isNs = headSym and headName == "ns"
|
||||||
|
if headName == "def" and ($form).contains("testvar"):
|
||||||
|
stderr.writeLine("DEBUG processOneForm testvar isDef=", isDef, " headName=", headName)
|
||||||
if isNs:
|
if isNs:
|
||||||
discard
|
discard
|
||||||
elif isDef or isMacro:
|
elif isDef or isMacro:
|
||||||
defs.add(emitExpr(ef, 0))
|
let defCode = emitExpr(ef, 0)
|
||||||
|
if defCode.contains("testvar_a"):
|
||||||
|
stderr.writeLine("DEBUG defs adding testvar_a code=", defCode.repr)
|
||||||
|
defs.add(defCode)
|
||||||
else:
|
else:
|
||||||
let code = emitExpr(ef, 2)
|
let code = emitExpr(ef, 2)
|
||||||
|
if code.contains("testvar_a"):
|
||||||
|
stderr.writeLine("DEBUG emitProgramInternal adding testvar_a to mainForms")
|
||||||
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 "):
|
stripped.startsWith("var "):
|
||||||
@@ -1686,14 +1917,29 @@ proc emitProgramInternal(forms: seq[CljVal]): string =
|
|||||||
for j in countdown(subForms.len - 1, 0):
|
for j in countdown(subForms.len - 1, 0):
|
||||||
let subLast = isLast and (j == subForms.len - 1)
|
let subLast = isLast and (j == subForms.len - 1)
|
||||||
worklist.add((subForms[j], subLast))
|
worklist.add((subForms[j], subLast))
|
||||||
|
elif form.kind == ckList and form.items.len == 2 and
|
||||||
|
form.items[0].kind == ckSymbol and form.items[0].symName == "splice-unwrap":
|
||||||
|
stderr.writeLine("DEBUG unwrapOneForm splice-unwrap")
|
||||||
|
let inner = form.items[1]
|
||||||
|
if inner.kind == ckVector:
|
||||||
|
stderr.writeLine("DEBUG unwrapOneForm splice-unwrap vector len=", inner.items.len)
|
||||||
|
for j in countdown(inner.items.len - 1, 0):
|
||||||
|
let subLast = isLast and (j == inner.items.len - 1)
|
||||||
|
worklist.add((inner.items[j], subLast))
|
||||||
|
else:
|
||||||
|
processOneForm(form, isLast)
|
||||||
else:
|
else:
|
||||||
processOneForm(form, isLast)
|
processOneForm(form, isLast)
|
||||||
|
|
||||||
for i, form in forms:
|
for i in countdown(forms.len - 1, 0):
|
||||||
worklist.add((form, i == forms.len - 1))
|
if forms[i].kind == ckList and forms[i].items.len > 0 and forms[i].items[0].kind == ckSymbol and forms[i].items[0].symName == "def" and ($forms[i]).contains("testvar"):
|
||||||
|
stderr.writeLine("DEBUG forms adding testvar to worklist")
|
||||||
|
worklist.add((forms[i], i == forms.len - 1))
|
||||||
|
|
||||||
while worklist.len > 0:
|
while worklist.len > 0:
|
||||||
let (item, itemIsLast) = worklist.pop()
|
let (item, itemIsLast) = worklist.pop()
|
||||||
|
if item.kind == ckList and item.items.len > 0 and item.items[0].kind == ckSymbol:
|
||||||
|
stderr.writeLine("DEBUG worklist popping ", item.items[0].symName)
|
||||||
unwrapOneForm(item, itemIsLast)
|
unwrapOneForm(item, itemIsLast)
|
||||||
|
|
||||||
var lines = headerLines
|
var lines = headerLines
|
||||||
@@ -1701,7 +1947,8 @@ proc emitProgramInternal(forms: seq[CljVal]): string =
|
|||||||
for imp in requiredImports:
|
for imp in requiredImports:
|
||||||
lines.add("import " & imp)
|
lines.add("import " & imp)
|
||||||
lines.add("")
|
lines.add("")
|
||||||
lines.add(defs)
|
for d in defs:
|
||||||
|
lines.add(d)
|
||||||
|
|
||||||
if mainForms.len > 0:
|
if mainForms.len > 0:
|
||||||
lines.add("")
|
lines.add("")
|
||||||
|
|||||||
Reference in New Issue
Block a user