Phase 8: Tree-walking interpreter for in-memory REPL eval (~0.02ms vs 1133ms compiled). 66 eval tests.

This commit is contained in:
2026-05-08 20:16:42 +03:00
parent feb9cee287
commit 1f9d662ed9
7 changed files with 1540 additions and 10 deletions
+1079
View File
File diff suppressed because it is too large Load Diff
+76 -3
View File
@@ -1,6 +1,6 @@
# AI-First REPL for Clojure/Nim
import os, osproc, strutils, json, times, net
import reader, emitter, types, macros
import os, osproc, strutils, sequtils, json, times, net
import reader, emitter, types, macros, eval
type
ReplMode* = enum
@@ -13,6 +13,7 @@ type
tempDir*: string
sessionId*: string
libPath*: string
evalEnv*: Env
proc getLibPath(): string =
let appDir = getAppDir()
@@ -33,6 +34,7 @@ proc initReplState*(mode: ReplMode): ReplState =
createDir(result.tempDir)
result.sessionId = $getCurrentProcessId() & "_" & $epochTime().int
result.libPath = getLibPath()
result.evalEnv = newTopLevelEnv()
proc cleanup*(state: ReplState) =
try:
@@ -40,6 +42,32 @@ proc cleanup*(state: ReplState) =
except:
discard
proc cljReprLocal(v: CljVal): string =
if v.isNil: return "nil"
case v.kind
of ckNil: result = "nil"
of ckBool:
if v.boolVal: result = "true"
else: result = "false"
of ckInt: result = $v.intVal
of ckFloat: result = $v.floatVal
of ckString: result = v.strVal
of ckKeyword: result = ":" & v.kwName
of ckSymbol: result = v.symName
of ckList:
var parts: seq[string] = @[]
for item in v.items: parts.add(cljReprLocal(item))
result = "(" & parts.join(" ") & ")"
of ckVector:
var parts: seq[string] = @[]
for item in v.items: parts.add(cljReprLocal(item))
result = "[" & parts.join(" ") & "]"
of ckMap:
var parts: seq[string] = @[]
for i in 0..<v.mapKeys.len:
parts.add(cljReprLocal(v.mapKeys[i]) & " " & cljReprLocal(v.mapVals[i]))
result = "{" & parts.join(", ") & "}"
proc buildProgram(state: ReplState, form: CljVal, isDef: bool): string =
var allForms = state.defs
if isDef:
@@ -114,6 +142,51 @@ proc evalForm*(state: var ReplState, formStr: string): JsonNode =
parsed.items[0].kind == ckSymbol and
parsed.items[0].symName in ["def", "defn"]
# Try in-memory evaluation first (fast path)
let evalResult = evalAst(parsed, state.evalEnv)
if evalResult.ok:
let elapsed = (epochTime() - startTime) * 1000
if evalResult.isDef:
state.defs.add(parsed)
return %*{
"status": "ok",
"result": {
"type": "var",
"name": evalResult.defName,
"ns": state.ns,
"printed": "#\"" & state.ns & "/" & evalResult.defName & "\""
},
"meta": {
"ns": state.ns,
"ms": elapsed,
"form": formStr
}
}
var printed: string
if evalResult.value.kind == ckNil:
printed = "nil"
elif evalResult.value.kind == ckString:
printed = evalResult.value.strVal
else:
printed = cljReprLocal(evalResult.value)
return %*{
"status": "ok",
"result": {
"type": "interpreted",
"value": printed,
"printed": printed
},
"meta": {
"ns": state.ns,
"ms": elapsed,
"form": formStr
}
}
# Fall back to compilation (slow path)
let nimCode = buildProgram(state, parsed, isDef)
let (ok, output, error) = compileAndRun(state, nimCode)
@@ -154,7 +227,7 @@ proc evalForm*(state: var ReplState, formStr: string): JsonNode =
return %*{
"status": "ok",
"result": {
"type": "unknown",
"type": "compiled",
"value": output,
"printed": output
},