diff options
| author | def <dennis@felsin9.de> | 2015-03-01 06:08:18 +0100 |
|---|---|---|
| committer | def <dennis@felsin9.de> | 2015-03-01 06:11:49 +0100 |
| commit | f5cf5237eea749230250de07124b25f2947f251e (patch) | |
| tree | 0f00628308728015072e2cdecb99a00d1f616b8a | |
| parent | dc7f0b6afd9bd298103c42e7113a69b788d565c4 (diff) | |
| download | mal-f5cf5237eea749230250de07124b25f2947f251e.tar.gz mal-f5cf5237eea749230250de07124b25f2947f251e.zip | |
Nim: step8
| -rw-r--r-- | nim/core.nim | 16 | ||||
| -rw-r--r-- | nim/env.nim | 2 | ||||
| -rw-r--r-- | nim/step5_tco.nim | 9 | ||||
| -rw-r--r-- | nim/step6_file.nim | 9 | ||||
| -rw-r--r-- | nim/step7_quote.nim | 9 | ||||
| -rw-r--r-- | nim/step8_macros.nim | 169 | ||||
| -rw-r--r-- | nim/types.nim | 26 |
7 files changed, 221 insertions, 19 deletions
diff --git a/nim/core.nim b/nim/core.nim index afba5e5..0951f92 100644 --- a/nim/core.nim +++ b/nim/core.nim @@ -32,6 +32,18 @@ proc concat(xs: varargs[MalType]): MalType = for i in x.list: result.list.add i +proc nth(xs: varargs[MalType]): MalType = + if xs[1].number < xs[0].list.len: return xs[0].list[xs[1].number] + else: raise newException(ValueError, "nth: index out of range") + +proc first(xs: varargs[MalType]): MalType = + if xs[0].list.len > 0: xs[0].list[0] + else: nilObj + +proc rest(xs: varargs[MalType]): MalType = + if xs[0].list.len > 0: list xs[0].list[1 .. -1] + else: list() + template wrapNumberFun(op: expr): expr = fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) @@ -69,4 +81,8 @@ let ns* = { "slurp": fun slurp, "cons": fun cons, "concat": fun concat, + + "nth": fun nth, + "first": fun first, + "rest": fun rest, } diff --git a/nim/env.nim b/nim/env.nim index bb4ad41..3e326b9 100644 --- a/nim/env.nim +++ b/nim/env.nim @@ -15,7 +15,7 @@ proc set*(e: var Env, key: string, value: MalType): MalType {.discardable.} = e.data[key] = value value -proc find(e: Env, key: string): Env = +proc find*(e: Env, key: string): Env = if e.data.hasKey(key): return e if e.outer != nil: return e.outer.find(key) diff --git a/nim/step5_tco.nim b/nim/step5_tco.nim index 3c91706..a03dbd1 100644 --- a/nim/step5_tco.nim +++ b/nim/step5_tco.nim @@ -26,7 +26,7 @@ proc eval(ast: MalType, env: var Env): MalType = case f.kind of MalFun: ast = f.malfun.ast - env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. -1])) + env = initEnv(env, f.malfun.params, list(el.list[1 .. -1])) else: return f.fun(el.list[1 .. -1]) @@ -49,11 +49,14 @@ proc eval(ast: MalType, env: var Env): MalType = let a1 = ast.list[1] a2 = ast.list[2] + var let_env = Env(env) case a1.kind of List, Vector: for i in countup(0, a1.list.high, 2): - env.set(a1.list[i].str, a1.list[i+1].eval(env)) - else: discard + let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env)) + else: raise newException(ValueError, "Illegal kind in let*") + ast = a2 + env = let_env # Continue loop (TCO) of "do": diff --git a/nim/step6_file.nim b/nim/step6_file.nim index 53269aa..6f0e02a 100644 --- a/nim/step6_file.nim +++ b/nim/step6_file.nim @@ -26,7 +26,7 @@ proc eval(ast: MalType, env: var Env): MalType = case f.kind of MalFun: ast = f.malfun.ast - env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. -1])) + env = initEnv(env, f.malfun.params, list(el.list[1 .. -1])) else: return f.fun(el.list[1 .. -1]) @@ -49,11 +49,14 @@ proc eval(ast: MalType, env: var Env): MalType = let a1 = ast.list[1] a2 = ast.list[2] + var let_env = Env(env) case a1.kind of List, Vector: for i in countup(0, a1.list.high, 2): - env.set(a1.list[i].str, a1.list[i+1].eval(env)) - else: discard + let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env)) + else: raise newException(ValueError, "Illegal kind in let*") + ast = a2 + env = let_env # Continue loop (TCO) of "do": diff --git a/nim/step7_quote.nim b/nim/step7_quote.nim index ec8f6fb..58fc3b6 100644 --- a/nim/step7_quote.nim +++ b/nim/step7_quote.nim @@ -40,7 +40,7 @@ proc eval(ast: MalType, env: var Env): MalType = case f.kind of MalFun: ast = f.malfun.ast - env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. -1])) + env = initEnv(env, f.malfun.params, list(el.list[1 .. -1])) else: return f.fun(el.list[1 .. -1]) @@ -63,11 +63,14 @@ proc eval(ast: MalType, env: var Env): MalType = let a1 = ast.list[1] a2 = ast.list[2] + var let_env = Env(env) case a1.kind of List, Vector: for i in countup(0, a1.list.high, 2): - env.set(a1.list[i].str, a1.list[i+1].eval(env)) - else: discard + let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env)) + else: raise newException(ValueError, "Illegal kind in let*") + ast = a2 + env = let_env # Continue loop (TCO) of "quote": diff --git a/nim/step8_macros.nim b/nim/step8_macros.nim new file mode 100644 index 0000000..43c1cb4 --- /dev/null +++ b/nim/step8_macros.nim @@ -0,0 +1,169 @@ +import rdstdin, tables, sequtils, os, types, reader, printer, env, core + +proc read(str: string): MalType = str.read_str + +proc is_pair(x: MalType): bool = + x.kind in {List, Vector} and x.list.len > 0 + +proc quasiquote(ast: MalType): MalType = + if not ast.is_pair: + return list(symbol "quote", ast) + elif ast.list[0] == symbol "unquote": + return ast.list[1] + elif ast.list[0].is_pair and ast.list[0].list[0] == symbol "splice-unquote": + return list(symbol "concat", ast.list[0].list[1], + quasiquote(list ast.list[1 .. -1])) + else: + return list(symbol "cons", quasiquote(ast.list[0]), quasiquote(list(ast.list[1 .. -1]))) + +proc is_macro_call(ast: MalType, env: Env): bool = + ast.kind == List and ast.list[0].kind == Symbol and + env.find(ast.list[0].str) != nil and env.get(ast.list[0].str).macro_q + +proc macroexpand(ast: MalType, env: Env): MalType = + result = ast + while result.is_macro_call(env): + let mac = env.get(result.list[0].str) + result = mac.malfun.fn(result.list[1 .. -1]).macroexpand(env) + +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.str) + 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(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) + + ast = ast.macroexpand(env) + if ast.kind != List: return ast + if ast.list.len == 0: return ast + + let a0 = ast.list[0] + case a0.kind + of Symbol: + case a0.str + of "def!": + let + a1 = ast.list[1] + a2 = ast.list[2] + return env.set(a1.str, a2.eval(env)) + + of "let*": + let + a1 = ast.list[1] + a2 = ast.list[2] + var let_env = Env(env) + case a1.kind + of List, Vector: + for i in countup(0, a1.list.high, 2): + let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env)) + else: raise newException(ValueError, "Illegal kind in let*") + ast = a2 + env = let_env + # Continue loop (TCO) + + of "quote": + return ast.list[1] + + of "quasiquote": + ast = ast.list[1].quasiquote + # Continue loop (TCO) + + of "defmacro!": + var fun = ast.list[2].eval(env) + fun.malfun.is_macro = true + return env.set(ast.list[1].str, fun) + + of "macroexpand": + return ast.list[1].macroexpand(env) + + 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) +repl_env.set("eval", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env))) +var ps = commandLineParams() +repl_env.set("*ARGV*", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str))) + + +# core.nim: defined using nim +proc rep(str: string): string {.discardable.} = + str.read.eval(repl_env).print + +# core.mal: defined using mal itself +rep "(def! not (fn* (a) (if a false true)))" +rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" +rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" +rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" + +if paramCount() >= 1: + rep "(load-file \"" & paramStr(1) & "\")" + quit() + +while true: + try: + let line = readLineFromStdin("user> ") + echo line.rep + except Blank: discard + except: + echo getCurrentExceptionMsg() + echo getCurrentException().getStackTrace() diff --git a/nim/types.nim b/nim/types.nim index 278ae1b..9936d50 100644 --- a/nim/types.nim +++ b/nim/types.nim @@ -5,10 +5,11 @@ type List, Vector, HashMap, Fun, MalFun MalFunType* = ref object - fn*: proc(a: varargs[MalType]): MalType - ast*: MalType - params*: MalType - env*: Env + fn*: proc(a: varargs[MalType]): MalType + ast*: MalType + params*: MalType + env*: Env + is_macro*: bool MalType* = object case kind*: MalTypeKind @@ -17,7 +18,9 @@ type of String, Symbol: str*: string of List, Vector: list*: seq[MalType] of HashMap: hash_map*: TableRef[string, MalType] - of Fun: fun*: proc(xs: varargs[MalType]): MalType + of Fun: + fun*: proc(xs: varargs[MalType]): MalType + is_macro*: bool of MalFun: malfun*: MalFunType Env* = ref object @@ -53,12 +56,17 @@ proc hash_map*(xs: varargs[MalType]): MalType {.procvar.} = else: xs[i].str result.hash_map[s] = xs[i+1] -proc fun*(x: proc(xs: varargs[MalType]): MalType): MalType = MalType(kind: Fun, fun: x) +proc macro_q*(x: MalType): bool = + if x.kind == Fun: x.is_macro + else: x.malfun.is_macro + +proc fun*(x: proc(xs: varargs[MalType]): MalType, is_macro = false): MalType = + MalType(kind: Fun, fun: x, is_macro: is_macro) proc malfun*(fn: auto, ast, params: MalType, - env: Env): MalType = - MalType(kind: MalFun, - malfun: MalFunType(fn: fn, ast: ast, params: params, env: env)) + env: Env, is_macro = false): MalType = + MalType(kind: MalFun, malfun: MalFunType(fn: fn, ast: ast, params: params, + env: env, is_macro: is_macro)) proc boolObj(b: bool): MalType = if b: trueObj else: falseObj |
