Fix: channels now support drain-after-close (close! marks closed, <! returns nil on exhausted closed channel, >! rejects on closed)

This commit is contained in:
2026-05-08 20:57:27 +03:00
parent 9996bc38de
commit 9aadb5dcb6
+8 -2
View File
@@ -10,6 +10,7 @@ type
Channel* = ref object Channel* = ref object
buf: Deque[CljVal] buf: Deque[CljVal]
capacity: int # 0 = unbuffered capacity: int # 0 = unbuffered
closed*: bool
var channelRegistry* = initTable[string, Channel]() var channelRegistry* = initTable[string, Channel]()
var channelCounter*: int64 = 0 var channelCounter*: int64 = 0
@@ -1074,7 +1075,7 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
channelCounter += 1 channelCounter += 1
let id = "chan_" & $channelCounter let id = "chan_" & $channelCounter
let cap = if args.len > 0 and args[0].kind == ckInt: args[0].intVal.int else: 0 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) let ch = Channel(buf: initDeque[CljVal](), capacity: cap, closed: false)
channelRegistry[id] = ch channelRegistry[id] = ch
return EvalResult(ok: true, value: cljString(id)) return EvalResult(ok: true, value: cljString(id))
@@ -1083,6 +1084,8 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal): if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal):
return EvalResult(ok: false, error: "put!/<! requires a channel") return EvalResult(ok: false, error: "put!/<! requires a channel")
let ch = channelRegistry[args[0].strVal] let ch = channelRegistry[args[0].strVal]
if ch.closed:
return EvalResult(ok: false, error: "Channel is closed")
if ch.capacity > 0 and ch.buf.len >= ch.capacity: if ch.capacity > 0 and ch.buf.len >= ch.capacity:
return EvalResult(ok: false, error: "Channel buffer full") return EvalResult(ok: false, error: "Channel buffer full")
ch.buf.addLast(args[1]) ch.buf.addLast(args[1])
@@ -1094,6 +1097,8 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
return EvalResult(ok: false, error: "take!/<! requires a channel") return EvalResult(ok: false, error: "take!/<! requires a channel")
let ch = channelRegistry[args[0].strVal] let ch = channelRegistry[args[0].strVal]
if ch.buf.len == 0: if ch.buf.len == 0:
if ch.closed:
return EvalResult(ok: true, value: cljNil())
return EvalResult(ok: true, value: cljNil()) return EvalResult(ok: true, value: cljNil())
return EvalResult(ok: true, value: ch.buf.popFirst()) return EvalResult(ok: true, value: ch.buf.popFirst())
@@ -1101,7 +1106,8 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
numArgs(1) numArgs(1)
if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal): if args[0].kind != ckString or not channelRegistry.hasKey(args[0].strVal):
return EvalResult(ok: false, error: "close! requires a channel") return EvalResult(ok: false, error: "close! requires a channel")
channelRegistry.del(args[0].strVal) let ch = channelRegistry[args[0].strVal]
ch.closed = true
return EvalResult(ok: true, value: cljNil()) return EvalResult(ok: true, value: cljNil())
of "go": of "go":