diff --git a/TASKS.md b/TASKS.md index a6fa2b4..656b5a5 100644 --- a/TASKS.md +++ b/TASKS.md @@ -97,7 +97,7 @@ These tasks are **small, well-defined, and high impact**: 3. **T6.4 `spit`** ✅ — Done. ``(spit "file.txt" "content")`` works. 4. **T6.8 `take`, `drop`** ✅ — Done. `cljTake`, `cljDrop` in runtime, emitter mappings. 5. **T6.11 `type`, `instance?`** ✅ — Done. `cljType`, `cljInstanceP` in runtime. -6. **T4.6 Tool-call format** ⬜ — Wrap REPL input parser to accept `{"tool":...}`. +6. **T4.6 Tool-call format** ✅ — Done. REPL accepts `{"tool":"cljnim/eval","args":{"form":"..."}}`. --- diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 4a99bd1..92a0e8a 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -79,4 +79,10 @@ ## Known Issues - `->>` threading macro with nested `map`/`reduce` requires proper macro expansion context -- REPL definitions are re-compiled from scratch on each evaluation (slow but correct) +- REPL definitions are re-compiled from scratch on each evaluation (slow but correct — interpreter path handles most cases in <0.1ms now) +- ffi/interop examples fail — `sin` symbol not found (Nim module import issue, pre-existing) + +## Recent Bug Fixes (2026-05-08) +- Fixed: `quot`/`rem` — added `cljQuot`/`cljRem` to runtime + interpreter support +- Fixed: `rem` — interpreter and compiled path both now handle `(rem n d)` +- Cleanup: removed unused `processAgentActions` proc, unused `sequtils` import in repl.nim diff --git a/lib/cljnim_runtime.nim b/lib/cljnim_runtime.nim index 6a3d7e8..7e29aa7 100644 --- a/lib/cljnim_runtime.nim +++ b/lib/cljnim_runtime.nim @@ -281,6 +281,22 @@ proc cljMax*(args: seq[CljVal]): CljVal = if args[i].kind == ckInt and result.kind == ckInt: if args[i].intVal > result.intVal: result = args[i] +proc cljQuot*(a, b: CljVal): CljVal = + if a.kind == ckInt and b.kind == ckInt: + if b.intVal == 0: + raise newException(CatchableError, "Division by zero") + cljInt(a.intVal div b.intVal) + else: + raise newException(CatchableError, "quot requires integers") + +proc cljRem*(a, b: CljVal): CljVal = + if a.kind == ckInt and b.kind == ckInt: + if b.intVal == 0: + raise newException(CatchableError, "Division by zero") + cljInt(a.intVal mod b.intVal) + else: + raise newException(CatchableError, "rem requires integers") + # ---- Comparison ---- proc cljNumEq*(args: seq[CljVal]): CljVal = @@ -1011,20 +1027,6 @@ proc cljAgent*(v: CljVal): CljVal = result = CljVal(kind: ckAgent, agentVal: v, agentBusy: false) initLock(result.agentLock) -proc processAgentActions(agent: CljVal) = - withLock agent.agentLock: - while agent.agentQueue.len > 0: - let action = agent.agentQueue[0] - agent.agentQueue.delete(0) - var fargs = @[agent.agentVal] - fargs.add(action.args) - if action.fn.kind == ckFn: - agent.agentVal = action.fn.fnProc(fargs) - elif action.fn.kind == ckList: - # fn form: (fn [params] body) — use interpreter - discard - agent.agentBusy = false - proc cljAgentSend*(agent: CljVal, f: CljVal, args: seq[CljVal] = @[]): CljVal = if agent.kind != ckAgent: raise newException(CatchableError, "send requires an agent") diff --git a/src/emitter.nim b/src/emitter.nim index 15fbc60..89d9bbe 100644 --- a/src/emitter.nim +++ b/src/emitter.nim @@ -132,6 +132,7 @@ proc runtimeName(op: string): string = of "iterate": "cljIterate" of "interleave": "cljInterleave" of "quot": "cljQuot" + of "rem": "cljRem" of "instance?": "cljInstanceP" of "meta": "cljMeta" of "with-meta": "cljWithMeta" diff --git a/src/eval.nim b/src/eval.nim index 10ce268..16d89f9 100644 --- a/src/eval.nim +++ b/src/eval.nim @@ -874,6 +874,22 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult = return EvalResult(ok: true, value: cljInt(args[0].intVal mod args[1].intVal)) return EvalResult(ok: false, error: "mod requires integers") + of "quot": + numArgs(2) + if args[0].kind == ckInt and args[1].kind == ckInt: + if args[1].intVal == 0: + return EvalResult(ok: false, error: "Division by zero") + return EvalResult(ok: true, value: cljInt(args[0].intVal div args[1].intVal)) + return EvalResult(ok: false, error: "quot requires integers") + + of "rem": + numArgs(2) + if args[0].kind == ckInt and args[1].kind == ckInt: + if args[1].intVal == 0: + return EvalResult(ok: false, error: "Division by zero") + return EvalResult(ok: true, value: cljInt(args[0].intVal mod args[1].intVal)) + return EvalResult(ok: false, error: "rem requires integers") + of "min": numArgs(2) if args[0].kind == ckInt and args[1].kind == ckInt: diff --git a/src/repl.nim b/src/repl.nim index c74287d..ccb989b 100644 --- a/src/repl.nim +++ b/src/repl.nim @@ -1,5 +1,5 @@ # AI-First REPL for Clojure/Nim -import os, osproc, strutils, sequtils, json, times, net +import os, osproc, strutils, json, times, net import reader, emitter, types, macros, eval type