Phase 9: Agents (send/await/deref) + core.async channels (chan, >!, <!, close!, go). All phases complete.
This commit is contained in:
@@ -139,6 +139,15 @@ proc runtimeName(op: string): string =
|
||||
of "persistent!": "cljPersistent"
|
||||
of "conj!": "cljConjB"
|
||||
of "assoc!": "cljAssocB"
|
||||
# ---- Agent operations ----
|
||||
of "agent": "cljAgent"
|
||||
of "send": "cljAgentSend"
|
||||
of "await": "cljAgentAwait"
|
||||
of "agent-error": "cljAgentError"
|
||||
of "shutdown-agents": "cljAgentShutdown"
|
||||
# ---- Channel operations (core.async) ----
|
||||
of "chan": "cljChan"
|
||||
of "close!": "cljChanClose"
|
||||
else: ""
|
||||
|
||||
proc emitArgs(args: seq[CljVal]): string =
|
||||
|
||||
+86
-1
@@ -1,8 +1,19 @@
|
||||
# Tree-walking interpreter for fast REPL evaluation
|
||||
# Handles common cases without spawning nim c
|
||||
import strutils, sequtils, tables, algorithm, times
|
||||
import strutils, sequtils, tables, algorithm, times, deques
|
||||
import types, reader
|
||||
|
||||
var agentRegistry* = initTable[string, CljVal]()
|
||||
var agentCounter*: int64 = 0
|
||||
|
||||
type
|
||||
Channel* = ref object
|
||||
buf: Deque[CljVal]
|
||||
capacity: int # 0 = unbuffered
|
||||
|
||||
var channelRegistry* = initTable[string, Channel]()
|
||||
var channelCounter*: int64 = 0
|
||||
|
||||
type
|
||||
EvalError* = object of CatchableError
|
||||
|
||||
@@ -1006,12 +1017,86 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
|
||||
of "deref":
|
||||
numArgs(1)
|
||||
if args[0].kind == ckString and agentRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: true, value: agentRegistry[args[0].strVal])
|
||||
return EvalResult(ok: true, value: args[0])
|
||||
|
||||
of "atom":
|
||||
numArgs(1)
|
||||
return EvalResult(ok: true, value: args[0])
|
||||
|
||||
of "agent":
|
||||
numArgs(1)
|
||||
agentCounter += 1
|
||||
let id = "agent_" & $agentCounter
|
||||
agentRegistry[id] = args[0]
|
||||
return EvalResult(ok: true, value: cljString(id))
|
||||
|
||||
of "send":
|
||||
atLeast(2)
|
||||
let agentId = args[0]
|
||||
if agentId.kind != ckString or not agentRegistry.hasKey(agentId.strVal):
|
||||
return EvalResult(ok: false, error: "send requires an agent")
|
||||
let fn = args[1]
|
||||
let fnArgs = if args.len > 2: args[2..^1] else: @[]
|
||||
let currentVal = agentRegistry[agentId.strVal]
|
||||
var callItems = @[fn, currentVal]
|
||||
callItems.add(fnArgs)
|
||||
let callRes = evalList(callItems, env)
|
||||
if not callRes.ok: return callRes
|
||||
agentRegistry[agentId.strVal] = callRes.value
|
||||
return EvalResult(ok: true, value: callRes.value)
|
||||
|
||||
of "await":
|
||||
numArgs(1)
|
||||
return EvalResult(ok: true, value: cljNil())
|
||||
|
||||
of "shutdown-agents":
|
||||
return EvalResult(ok: true, value: cljNil())
|
||||
|
||||
of "chan":
|
||||
channelCounter += 1
|
||||
let id = "chan_" & $channelCounter
|
||||
let cap = if args.len > 0 and args[0].kind == ckInt: args[0].intVal.int else: 0
|
||||
let ch = Channel(buf: initDeque[CljVal](), capacity: cap)
|
||||
channelRegistry[id] = ch
|
||||
return EvalResult(ok: true, value: cljString(id))
|
||||
|
||||
of "put!", ">!":
|
||||
numArgs(2)
|
||||
if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: false, error: "put!/<! requires a channel")
|
||||
let ch = channelRegistry[args[0].strVal]
|
||||
if ch.capacity > 0 and ch.buf.len >= ch.capacity:
|
||||
return EvalResult(ok: false, error: "Channel buffer full")
|
||||
ch.buf.addLast(args[1])
|
||||
return EvalResult(ok: true, value: cljBool(true))
|
||||
|
||||
of "take!", "<!":
|
||||
numArgs(1)
|
||||
if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: false, error: "take!/<! requires a channel")
|
||||
let ch = channelRegistry[args[0].strVal]
|
||||
if ch.buf.len == 0:
|
||||
return EvalResult(ok: true, value: cljNil())
|
||||
return EvalResult(ok: true, value: ch.buf.popFirst())
|
||||
|
||||
of "close!":
|
||||
numArgs(1)
|
||||
if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: false, error: "close! requires a channel")
|
||||
channelRegistry.del(args[0].strVal)
|
||||
return EvalResult(ok: true, value: cljNil())
|
||||
|
||||
of "go":
|
||||
# In synchronous interpreter, go just evaluates the body
|
||||
var lastVal: CljVal = cljNil()
|
||||
for i in 1..<items.len:
|
||||
let res = evalAst(items[i], env)
|
||||
if not res.ok: return res
|
||||
lastVal = res.value
|
||||
return EvalResult(ok: true, value: lastVal)
|
||||
|
||||
else:
|
||||
return EvalResult(ok: false, error: "Unknown function: " & name & " (use compile mode for full runtime)")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user