feat: swap! and reset! in interpreter
- atoms now work in REPL interpreter via string registry - swap! supports extra arguments: (swap! a + 5) - reset! sets atom value directly - Updated tests: 3 new atom tests (swap!, reset!, swap! with args) - 270+ tests pass across all suites
This commit is contained in:
+58
-32
@@ -5,6 +5,8 @@ import types, reader
|
||||
|
||||
var agentRegistry* = initTable[string, CljVal]()
|
||||
var agentCounter*: int64 = 0
|
||||
var atomRegistry* = initTable[string, CljVal]()
|
||||
var atomCounter*: int64 = 0
|
||||
|
||||
type
|
||||
Channel* = ref object
|
||||
@@ -22,8 +24,6 @@ type
|
||||
bindings*: Table[string, CljVal]
|
||||
parent*: Env
|
||||
|
||||
BuiltinFn = proc(args: seq[CljVal], env: Env): CljVal
|
||||
|
||||
EvalResult* = object
|
||||
ok*: bool
|
||||
value*: CljVal
|
||||
@@ -298,20 +298,20 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
if args[0].kind == ckInt: return EvalResult(ok: true, value: cljInt(-args[0].intVal))
|
||||
if args[0].kind == ckFloat: return EvalResult(ok: true, value: cljFloat(-args[0].floatVal))
|
||||
return EvalResult(ok: false, error: "- requires numbers")
|
||||
var result: float64
|
||||
if args[0].kind == ckInt: result = args[0].intVal.float64
|
||||
elif args[0].kind == ckFloat: result = args[0].floatVal
|
||||
var sum: float64
|
||||
if args[0].kind == ckInt: sum = args[0].intVal.float64
|
||||
elif args[0].kind == ckFloat: sum = args[0].floatVal
|
||||
else: return EvalResult(ok: false, error: "- requires numbers")
|
||||
for i in 1..<args.len:
|
||||
if args[i].kind == ckInt: result -= args[i].intVal.float64
|
||||
elif args[i].kind == ckFloat: result -= args[i].floatVal
|
||||
if args[i].kind == ckInt: sum -= args[i].intVal.float64
|
||||
elif args[i].kind == ckFloat: sum -= args[i].floatVal
|
||||
else: return EvalResult(ok: false, error: "- requires numbers")
|
||||
if result == result.int64.float64 and args[0].kind == ckInt:
|
||||
if sum == sum.int64.float64 and args[0].kind == ckInt:
|
||||
var allInt = true
|
||||
for i in 1..<args.len:
|
||||
if args[i].kind != ckInt: allInt = false
|
||||
if allInt: return EvalResult(ok: true, value: cljInt(result.int64))
|
||||
return EvalResult(ok: true, value: cljFloat(result))
|
||||
if allInt: return EvalResult(ok: true, value: cljInt(sum.int64))
|
||||
return EvalResult(ok: true, value: cljFloat(sum))
|
||||
|
||||
of "*":
|
||||
atLeast(1)
|
||||
@@ -594,11 +594,11 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
return EvalResult(ok: false, error: "cons requires a collection")
|
||||
|
||||
of "concat":
|
||||
var result: seq[CljVal] = @[]
|
||||
var res: seq[CljVal] = @[]
|
||||
for a in args:
|
||||
if a.kind in {ckList, ckVector}:
|
||||
result.add(a.items)
|
||||
return EvalResult(ok: true, value: cljList(result))
|
||||
res.add(a.items)
|
||||
return EvalResult(ok: true, value: cljList(res))
|
||||
|
||||
of "reverse":
|
||||
numArgs(1)
|
||||
@@ -629,13 +629,13 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
let fn = args[0]
|
||||
let coll = args[1]
|
||||
if coll.kind in {ckList, ckVector}:
|
||||
var result: seq[CljVal] = @[]
|
||||
var res: seq[CljVal] = @[]
|
||||
for item in coll.items:
|
||||
let callItems = @[fn, item]
|
||||
let callRes = evalList(callItems, env)
|
||||
if not callRes.ok: return callRes
|
||||
result.add(callRes.value)
|
||||
return EvalResult(ok: true, value: cljList(result))
|
||||
res.add(callRes.value)
|
||||
return EvalResult(ok: true, value: cljList(res))
|
||||
return EvalResult(ok: false, error: "map requires a function and collection")
|
||||
|
||||
of "filter":
|
||||
@@ -643,7 +643,7 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
let fn = args[0]
|
||||
let coll = args[1]
|
||||
if coll.kind in {ckList, ckVector}:
|
||||
var result: seq[CljVal] = @[]
|
||||
var res: seq[CljVal] = @[]
|
||||
for item in coll.items:
|
||||
let callItems = @[fn, item]
|
||||
let callRes = evalList(callItems, env)
|
||||
@@ -651,8 +651,8 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
let isTruthy = not (callRes.value.kind == ckNil or
|
||||
(callRes.value.kind == ckBool and not callRes.value.boolVal))
|
||||
if isTruthy:
|
||||
result.add(item)
|
||||
return EvalResult(ok: true, value: cljList(result))
|
||||
res.add(item)
|
||||
return EvalResult(ok: true, value: cljList(res))
|
||||
return EvalResult(ok: false, error: "filter requires a function and collection")
|
||||
|
||||
of "reduce":
|
||||
@@ -668,22 +668,22 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
if coll.kind in {ckList, ckVector} and coll.items.len > 0:
|
||||
acc = coll.items[0]
|
||||
# reduce rest
|
||||
var result = acc
|
||||
var res = acc
|
||||
for i in 1..<coll.items.len:
|
||||
let callItems = @[fn, result, coll.items[i]]
|
||||
let callItems = @[fn, res, coll.items[i]]
|
||||
let callRes = evalList(callItems, env)
|
||||
if not callRes.ok: return callRes
|
||||
result = callRes.value
|
||||
return EvalResult(ok: true, value: result)
|
||||
res = callRes.value
|
||||
return EvalResult(ok: true, value: res)
|
||||
return EvalResult(ok: true, value: cljNil())
|
||||
if coll.kind in {ckList, ckVector}:
|
||||
var result = acc
|
||||
var res = acc
|
||||
for item in coll.items:
|
||||
let callItems = @[fn, result, item]
|
||||
let callItems = @[fn, res, item]
|
||||
let callRes = evalList(callItems, env)
|
||||
if not callRes.ok: return callRes
|
||||
result = callRes.value
|
||||
return EvalResult(ok: true, value: result)
|
||||
res = callRes.value
|
||||
return EvalResult(ok: true, value: res)
|
||||
return EvalResult(ok: false, error: "reduce requires a function and collection")
|
||||
|
||||
of "apply":
|
||||
@@ -957,17 +957,17 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
of "distinct":
|
||||
numArgs(1)
|
||||
if args[0].kind in {ckList, ckVector}:
|
||||
var result: seq[CljVal] = @[]
|
||||
var res: seq[CljVal] = @[]
|
||||
for item in args[0].items:
|
||||
var isDup = false
|
||||
for existing in result:
|
||||
for existing in res:
|
||||
let eqRes = evalBuiltin("=", @[existing, item])
|
||||
if eqRes.ok and eqRes.value.kind == ckBool and eqRes.value.boolVal:
|
||||
isDup = true
|
||||
break
|
||||
if not isDup:
|
||||
result.add(item)
|
||||
return EvalResult(ok: true, value: cljList(result))
|
||||
res.add(item)
|
||||
return EvalResult(ok: true, value: cljList(res))
|
||||
return EvalResult(ok: false, error: "distinct requires a collection")
|
||||
|
||||
of "slurp":
|
||||
@@ -1036,11 +1036,37 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
||||
numArgs(1)
|
||||
if args[0].kind == ckString and agentRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: true, value: agentRegistry[args[0].strVal])
|
||||
if args[0].kind == ckString and atomRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: true, value: atomRegistry[args[0].strVal])
|
||||
return EvalResult(ok: true, value: args[0])
|
||||
|
||||
of "atom":
|
||||
numArgs(1)
|
||||
return EvalResult(ok: true, value: args[0])
|
||||
atomCounter += 1
|
||||
let id = "atom_" & $atomCounter
|
||||
atomRegistry[id] = args[0]
|
||||
return EvalResult(ok: true, value: cljString(id))
|
||||
|
||||
of "reset!":
|
||||
numArgs(2)
|
||||
if args[0].kind != ckString or not atomRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: false, error: "reset! requires an atom")
|
||||
atomRegistry[args[0].strVal] = args[1]
|
||||
return EvalResult(ok: true, value: args[1])
|
||||
|
||||
of "swap!":
|
||||
atLeast(2)
|
||||
if args[0].kind != ckString or not atomRegistry.hasKey(args[0].strVal):
|
||||
return EvalResult(ok: false, error: "swap! requires an atom")
|
||||
let fn = args[1]
|
||||
let currentVal = atomRegistry[args[0].strVal]
|
||||
var callItems = @[fn, currentVal]
|
||||
if args.len > 2:
|
||||
callItems.add(args[2..^1])
|
||||
let callRes = evalList(callItems, env)
|
||||
if not callRes.ok: return callRes
|
||||
atomRegistry[args[0].strVal] = callRes.value
|
||||
return EvalResult(ok: true, value: callRes.value)
|
||||
|
||||
of "agent":
|
||||
numArgs(1)
|
||||
|
||||
Reference in New Issue
Block a user