aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nim/env.nim4
-rw-r--r--nim/printer.nim1
-rw-r--r--nim/step5_tco.nim112
-rw-r--r--nim/types.nim19
4 files changed, 131 insertions, 5 deletions
diff --git a/nim/env.nim b/nim/env.nim
index b5c5a37..55e2a09 100644
--- a/nim/env.nim
+++ b/nim/env.nim
@@ -1,9 +1,5 @@
import tables, types
-type Env* = ref object
- data: Table[string, MalType]
- outer: Env
-
proc initEnv*(outer: Env = nil, binds, exprs: MalType = nilObj): Env =
result = Env(data: initTable[string, MalType](), outer: outer)
diff --git a/nim/printer.nim b/nim/printer.nim
index aa4507f..7bdf78c 100644
--- a/nim/printer.nim
+++ b/nim/printer.nim
@@ -6,6 +6,7 @@ proc pr_str*(m: MalType, pr = true): string =
of True: result = "true"
of False: result = "false"
of Fun: result = "#<function>"
+ of MalFun: result = "#<malfun>"
of Symbol: result = m.symbol
of String:
if m.str.len > 0 and m.str[0] == '\xff':
diff --git a/nim/step5_tco.nim b/nim/step5_tco.nim
new file mode 100644
index 0000000..f7140e7
--- /dev/null
+++ b/nim/step5_tco.nim
@@ -0,0 +1,112 @@
+import rdstdin, tables, sequtils, types, reader, printer, env, core
+
+proc read(str: string): MalType = str.read_str
+
+proc eval(ast: MalType, env: var Env): MalType
+
+proc eval_ast(ast: MalType, env: var Env): MalType =
+ case ast.kind
+ of Symbol:
+ result = env.get(ast.symbol)
+ of List:
+ result = list ast.list.mapIt(MalType, it.eval(env))
+ of Vector:
+ result = vector ast.list.mapIt(MalType, it.eval(env))
+ of HashMap:
+ result = hash_map()
+ for k, v in ast.hash_map.pairs:
+ result.hash_map[k] = v.eval(env)
+ else:
+ result = ast
+
+proc eval(ast: MalType, env: var Env): MalType =
+ template defaultApply =
+ let el = ast.eval_ast(env)
+ let f = el.list[0]
+ case f.kind
+ of MalFun:
+ ast = f.malfun.ast
+ env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. -1]))
+ else:
+ return f.fun(el.list[1 .. -1])
+
+ var ast = ast
+ while true:
+ if ast.kind != List:
+ return ast.eval_ast(env)
+
+ let a0 = ast.list[0]
+ case a0.kind
+ of Symbol:
+ case a0.symbol
+ of "def!":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ return env.set(a1.symbol, a2.eval(env))
+
+ of "let*":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ case a1.kind
+ of List, Vector:
+ for i in countup(0, a1.list.high, 2):
+ env.set(a1.list[i].symbol, a1.list[i+1].eval(env))
+ else: discard
+ # Continue loop (TCO)
+
+ of "do":
+ let last = ast.list.high
+ let el = (list ast.list[1 .. <last]).eval_ast(env)
+ ast = ast.list[last].eval(env)
+ # Continue loop (TCO)
+
+ of "if":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ cond = a1.eval(env)
+
+ if cond.kind in {Nil, False}:
+ if ast.list.len > 3: ast = ast.list[3]
+ else: ast = nilObj
+ else: ast = a2
+
+ of "fn*":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ var env2 = env
+ let fn = proc(a: varargs[MalType]): MalType =
+ var newEnv = initEnv(env2, a1, list(a))
+ a2.eval(newEnv)
+ return malfun(fn, a2, a1, env2)
+
+ else:
+ defaultApply()
+
+ else:
+ defaultApply()
+
+proc print(exp: MalType): string = exp.pr_str
+
+var repl_env = initEnv()
+
+for k, v in ns.items:
+ repl_env.set(k, v)
+
+# core.nim: defined using nim
+proc rep(str: string): string =
+ str.read.eval(repl_env).print
+
+# core.mal: defined using mal itself
+discard rep "(def! not (fn* (a) (if a false true)))"
+
+while true:
+ try:
+ let line = readLineFromStdin("user> ")
+ echo line.rep
+ except:
+ echo getCurrentExceptionMsg()
+ echo getCurrentException().getStackTrace()
diff --git a/nim/types.nim b/nim/types.nim
index 557a314..f677f06 100644
--- a/nim/types.nim
+++ b/nim/types.nim
@@ -2,7 +2,13 @@ import tables, strutils
type
MalTypeKind* = enum Nil, True, False, Number, Symbol, String,
- List, Vector, HashMap, Fun
+ List, Vector, HashMap, Fun, MalFun
+
+ MalFunType* = ref object
+ fn*: proc(a: varargs[MalType]): MalType
+ ast*: MalType
+ params*: MalType
+ env*: Env
MalType* = object
case kind*: MalTypeKind
@@ -13,6 +19,11 @@ type
of List, Vector: list*: seq[MalType]
of HashMap: hash_map*: TableRef[string, MalType]
of Fun: fun*: proc(xs: varargs[MalType]): MalType
+ of MalFun: malfun*: MalFunType
+
+ Env* = ref object
+ data*: Table[string, MalType]
+ outer*: Env
# Convenience procs
const nilObj* = MalType(kind: Nil)
@@ -42,6 +53,11 @@ proc hash_map*(xs: varargs[MalType]): MalType {.procvar.} =
proc fun*(x: proc(xs: varargs[MalType]): MalType): MalType = MalType(kind: Fun, fun: x)
+proc malfun*(fn: auto, ast, params: MalType,
+ env: Env): MalType =
+ MalType(kind: MalFun,
+ malfun: MalFunType(fn: fn, ast: ast, params: params, env: env))
+
proc boolObj(b: bool): MalType =
if b: trueObj else: falseObj
@@ -71,6 +87,7 @@ proc `==`*(x, y: MalType): bool =
of List, Vector: x.list == y.list
of HashMap: x.hash_map == y.hash_map
of Fun: x.fun == y.fun
+ of MalFun: x.malfun == y.malfun
proc equal*(xs: varargs[MalType]): MalType {.procvar.} =
boolObj xs[0] == xs[1]