Phase 8: Tree-walking interpreter for in-memory REPL eval (~0.02ms vs 1133ms compiled). 66 eval tests.
This commit is contained in:
+1079
File diff suppressed because it is too large
Load Diff
+76
-3
@@ -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
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user