diff options
| author | def <dennis@felsin9.de> | 2015-03-01 17:39:15 +0100 |
|---|---|---|
| committer | def <dennis@felsin9.de> | 2015-03-01 21:37:07 +0100 |
| commit | 8de9f3085c2f251c0b38ca28e5a66e9f66f9d968 (patch) | |
| tree | e2eff996fb521bb656ed6122544d40773d56a1c5 | |
| parent | f5cf5237eea749230250de07124b25f2947f251e (diff) | |
| download | mal-8de9f3085c2f251c0b38ca28e5a66e9f66f9d968.tar.gz mal-8de9f3085c2f251c0b38ca28e5a66e9f66f9d968.zip | |
Nim: step9
| -rw-r--r-- | nim/core.nim | 130 | ||||
| -rw-r--r-- | nim/printer.nim | 15 | ||||
| -rw-r--r-- | nim/step9_try.nim | 190 | ||||
| -rw-r--r-- | nim/types.nim | 55 |
4 files changed, 373 insertions, 17 deletions
diff --git a/nim/core.nim b/nim/core.nim index 0951f92..ca25802 100644 --- a/nim/core.nim +++ b/nim/core.nim @@ -1,4 +1,7 @@ -import strutils, types, printer, reader +import strutils, rdstdin, tables, algorithm, types, printer, reader + +type MalError* = object of Exception + t*: MalType # String functions proc pr_str(xs: varargs[MalType]): MalType = @@ -19,6 +22,9 @@ proc println(xs: varargs[MalType]): MalType = proc read_str(xs: varargs[MalType]): MalType = read_str(xs[0].str) +proc readline(xs: varargs[MalType]): MalType = + str readLineFromStdin(xs[0].str) + proc slurp(xs: varargs[MalType]): MalType = str readFile(xs[0].str) @@ -44,8 +50,90 @@ proc rest(xs: varargs[MalType]): MalType = if xs[0].list.len > 0: list xs[0].list[1 .. -1] else: list() +proc throw(xs: varargs[MalType]): MalType = + raise (ref MalError)(t: list xs) + +proc assoc(xs: varargs[MalType]): MalType = + result.deepCopy xs[0] + for i in countup(1, xs.high, 2): + result.hash_map[xs[i].str] = xs[i+1] + +proc dissoc(xs: varargs[MalType]): MalType = + result.deepCopy xs[0] + for i in 1 .. xs.high: + if result.hash_map.hasKey(xs[i].str): result.hash_map.del(xs[i].str) + +proc get(xs: varargs[MalType]): MalType = + if xs[0].kind == HashMap: + xs[0].hash_map[xs[1].str] + else: + nilObj + +proc contains_q(xs: varargs[MalType]): MalType = + boolObj xs[0].hash_map.hasKey(xs[1].str) + +proc keys(xs: varargs[MalType]): MalType = + result = list() + for key in xs[0].hash_map.keys: + result.list.add str(key) + +proc vals(xs: varargs[MalType]): MalType = + result = list() + for value in xs[0].hash_map.values: + result.list.add value + +proc conj(xs: varargs[MalType]): MalType = + if xs[0].kind == List: + result = list() + for i in countdown(xs.high, 1): + result.list.add xs[i] + result.list.add xs[0].list + else: + result = vector() + result.list.add xs[0].list + for i in 1..xs.high: + result.list.add xs[i] + result.meta = xs[0].meta + +proc apply(xs: varargs[MalType]): MalType = + var s = newSeq[MalType]() + if xs.len > 2: + for j in 1 .. xs.high-1: + s.add xs[j] + s.add xs[xs.high].list + xs[0].getFun()(s) + +proc map(xs: varargs[MalType]): MalType = + result = list() + for i in 0 .. xs[1].list.high: + result.list.add xs[0].getFun()(xs[1].list[i]) + +proc with_meta(xs: varargs[MalType]): MalType = + result = xs[0] + new result.meta + result.meta[] = xs[1] + +proc meta(xs: varargs[MalType]): MalType = + if xs[0].meta != nil: xs[0].meta[] + else: nilObj + +proc deref(xs: varargs[MalType]): MalType = + xs[0].val[] + +proc reset_bang(xs: varargs[MalType]): MalType = + xs[0].val[] = xs[1] + result = xs[0].val[] + +proc swap_bang(xs: varargs[MalType]): MalType = + var args = @[xs[0].val[]] + for i in 2 .. xs.high: + args.add xs[i] + xs[0].val[] = xs[1].getFun()(args) + result = xs[0].val[] + template wrapNumberFun(op: expr): expr = - fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) + fun proc(xs: varargs[MalType]): MalType = + number op(xs[0].number, xs[1].number) template wrapBoolFun(op: expr): expr = fun proc(xs: varargs[MalType]): MalType = @@ -66,10 +154,16 @@ let ns* = { "list?": fun list_q, "vector": fun vector, "vector?": fun vector_q, - "hash_map": fun hash_map, - "hash_map?": fun hash_map_q, + "hash-map": fun hash_map, + "map?": fun hash_map_q, "empty?": fun empty_q, - "count": fun count, + "assoc": fun assoc, + "dissoc": fun dissoc, + "get": fun get, + "contains?": fun contains_q, + "keys": fun keys, + "vals": fun vals, + "=": fun equal, "pr-str": fun pr_str, @@ -78,11 +172,35 @@ let ns* = { "println": fun println, "read-string": fun read_str, + "readline": fun readline, "slurp": fun slurp, + + "sequential?": fun seq_q, "cons": fun cons, "concat": fun concat, - + "count": fun count, "nth": fun nth, "first": fun first, "rest": fun rest, + "conj": fun conj, + "apply": fun apply, + "map": fun map, + + "throw": fun throw, + + "nil?": fun nil_q, + "true?": fun true_q, + "false?": fun false_q, + "symbol": fun symbol, + "symbol?": fun symbol_q, + "keyword": fun keyword, + "keyword?": fun keyword_q, + + "with-meta": fun with_meta, + "meta": fun meta, + "atom": fun atom, + "atom?": fun atom_q, + "deref": fun deref, + "reset!": fun reset_bang, + "swap!": fun swap_bang, } diff --git a/nim/printer.nim b/nim/printer.nim index eb26671..b6bf4c7 100644 --- a/nim/printer.nim +++ b/nim/printer.nim @@ -1,5 +1,11 @@ import strutils, sequtils, tables, types +proc str_handle(x: string, pr = true): string = + if x.len > 0 and x[0] == '\xff': + result = ":" & x[1 .. x.high] + elif pr: result = "\"" & x.replace("\"", "\\\"") & "\"" + else: result = x + proc pr_str*(m: MalType, pr = true): string = case m.kind of Nil: result = "nil" @@ -7,12 +13,9 @@ proc pr_str*(m: MalType, pr = true): string = of False: result = "false" of Fun: result = "#<function>" of MalFun: result = "#<malfun>" + of Atom: result = "(atom " & m.val[].pr_str & ")" of Symbol: result = m.str - of String: - if m.str.len > 0 and m.str[0] == '\xff': - result = ":" & m.str[1 .. m.str.high] - elif pr: result = "\"" & m.str.replace("\"", "\\\"") & "\"" - else: result = m.str + of String: result = m.str.str_handle(pr) of Number: result = $m.number of List: result = "(" & m.list.mapIt(string, it.pr_str(pr)).join(" ") & ")" of Vector: result = "[" & m.list.mapIt(string, it.pr_str(pr)).join(" ") & "]" @@ -20,5 +23,5 @@ proc pr_str*(m: MalType, pr = true): string = result = "{" for key, val in m.hash_map.pairs: if result.len > 1: result.add " " - result.add key & " " & val.pr_str(pr) + result.add key.str_handle & " " & val.pr_str(pr) result.add "}" diff --git a/nim/step9_try.nim b/nim/step9_try.nim new file mode 100644 index 0000000..688817d --- /dev/null +++ b/nim/step9_try.nim @@ -0,0 +1,190 @@ +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: 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: Env): MalType = + var ast = ast + var env = env + + 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]) + + 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] + res = a2.eval(env) + return env.set(a1.str, res) + + 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 "try*": + let + a1 = ast.list[1] + a2 = ast.list[2] + if a2.list[0].str == "catch*": + try: + return a1.eval(env) + except MalError: + let exc = (ref MalError) getCurrentException() + var catchEnv = initEnv(env, list a2.list[1], exc.t) + return a2.list[2].eval(catchEnv) + except: + let exc = getCurrentExceptionMsg() + var catchEnv = initEnv(env, list a2.list[1], list str(exc)) + return a2.list[2].eval(catchEnv) + else: + return a1.eval(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, env) + + 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 9936d50..2a19eba 100644 --- a/nim/types.nim +++ b/nim/types.nim @@ -2,10 +2,12 @@ import tables, strutils type MalTypeKind* = enum Nil, True, False, Number, Symbol, String, - List, Vector, HashMap, Fun, MalFun + List, Vector, HashMap, Fun, MalFun, Atom + + FunType = proc(a: varargs[MalType]): MalType MalFunType* = ref object - fn*: proc(a: varargs[MalType]): MalType + fn*: FunType ast*: MalType params*: MalType env*: Env @@ -19,9 +21,12 @@ type of List, Vector: list*: seq[MalType] of HashMap: hash_map*: TableRef[string, MalType] of Fun: - fun*: proc(xs: varargs[MalType]): MalType + fun*: FunType is_macro*: bool of MalFun: malfun*: MalFunType + of Atom: val*: ref MalType + + meta*: ref MalType Env* = ref object data*: Table[string, MalType] @@ -40,6 +45,11 @@ proc str*(x: string): MalType {.procvar.} = MalType(kind: String, str: x) proc keyword*(x: string): MalType = MalType(kind: String, str: "\xff" & x) +proc atom*(x: MalType): MalType = + result = MalType(kind: Atom) + new result.val + result.val[] = x + proc list*(xs: varargs[MalType]): MalType {.procvar.} = result = MalType(kind: List, list: @[]) for x in xs: result.list.add x @@ -52,7 +62,7 @@ proc hash_map*(xs: varargs[MalType]): MalType {.procvar.} = result = MalType(kind: HashMap, hash_map: newTable[string, MalType]()) for i in countup(0, xs.high, 2): let s = case xs[i].kind - of String: "\"" & xs[i].str & "\"" + of String: xs[i].str else: xs[i].str result.hash_map[s] = xs[i+1] @@ -60,6 +70,10 @@ proc macro_q*(x: MalType): bool = if x.kind == Fun: x.is_macro else: x.malfun.is_macro +proc getFun*(x: MalType): FunType = + if x.kind == Fun: x.fun + else: x.malfun.fn + proc fun*(x: proc(xs: varargs[MalType]): MalType, is_macro = false): MalType = MalType(kind: Fun, fun: x, is_macro: is_macro) @@ -68,7 +82,7 @@ proc malfun*(fn: auto, ast, params: MalType, MalType(kind: MalFun, malfun: MalFunType(fn: fn, ast: ast, params: params, env: env, is_macro: is_macro)) -proc boolObj(b: bool): MalType = +proc boolObj*(b: bool): MalType = if b: trueObj else: falseObj proc list_q*(xs: varargs[MalType]): MalType {.procvar.} = @@ -77,12 +91,42 @@ proc list_q*(xs: varargs[MalType]): MalType {.procvar.} = proc vector_q*(xs: varargs[MalType]): MalType {.procvar.} = boolObj xs[0].kind == Vector +proc seq_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind in {List, Vector} + proc hash_map_q*(xs: varargs[MalType]): MalType {.procvar.} = boolObj xs[0].kind == HashMap proc empty_q*(xs: varargs[MalType]): MalType {.procvar.} = boolObj xs[0].list.len == 0 +proc nil_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == Nil + +proc true_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == True + +proc false_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == False + +proc symbol*(xs: varargs[MalType]): MalType {.procvar.} = + symbol(xs[0].str) + +proc symbol_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == Symbol + +proc keyword*(xs: varargs[MalType]): MalType {.procvar.} = + keyword(xs[0].str) + +proc keyword_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj(xs[0].kind == String and xs[0].str[0] == '\xff') + +proc atom*(xs: varargs[MalType]): MalType {.procvar.} = + atom(xs[0]) + +proc atom_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == Atom + proc count*(xs: varargs[MalType]): MalType {.procvar.} = number if xs[0].kind == Nil: 0 else: xs[0].list.len @@ -97,6 +141,7 @@ proc `==`*(x, y: MalType): bool = of HashMap: x.hash_map == y.hash_map of Fun: x.fun == y.fun of MalFun: x.malfun == y.malfun + of Atom: x.val == y.val proc equal*(xs: varargs[MalType]): MalType {.procvar.} = boolObj xs[0] == xs[1] |
