diff options
| author | def <dennis@felsin9.de> | 2015-03-01 21:46:19 +0100 |
|---|---|---|
| committer | def <dennis@felsin9.de> | 2015-03-02 16:08:54 +0100 |
| commit | 4ce9e165bafb4f14b03624f515066c3c42719dd9 (patch) | |
| tree | e488eca49b0d547f9de8400b57d1cf576252db6f | |
| parent | 8de9f3085c2f251c0b38ca28e5a66e9f66f9d968 (diff) | |
| download | mal-4ce9e165bafb4f14b03624f515066c3c42719dd9.tar.gz mal-4ce9e165bafb4f14b03624f515066c3c42719dd9.zip | |
Nim: stepA
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | nim/Makefile | 35 | ||||
| -rw-r--r-- | nim/core.nim | 23 | ||||
| -rw-r--r-- | nim/env.nim | 2 | ||||
| -rw-r--r-- | nim/mal.nimble | 3 | ||||
| -rw-r--r-- | nim/nim.cfg | 1 | ||||
| -rw-r--r-- | nim/printer.nim | 2 | ||||
| -rw-r--r-- | nim/stepA_interop.nim | 190 | ||||
| -rw-r--r-- | nim/types.nim | 9 |
9 files changed, 218 insertions, 51 deletions
@@ -36,6 +36,7 @@ EXCLUDE_TESTS += test^php^step5 # test completes, even at 100,000 EXCLUDE_TESTS += test^ruby^step5 # test completes, even at 100,000 EXCLUDE_TESTS += test^rust^step5 # no catching stack overflows EXCLUDE_TESTS += test^ocaml^step5 # test completes, even at 1,000,000 +#EXCLUDE_TESTS += test^nim^step5 # test completes, even at 100,000 # interop tests now implemented yet EXCLUDE_TESTS += test^cs^stepA test^java^stepA test^mal^stepA \ @@ -110,7 +111,8 @@ ruby_RUNSTEP = ruby ../$(2) $(3) rust_RUNSTEP = ../$(2) $(3) scala_RUNSTEP = sbt 'run-main $($(1))$(if $(3), $(3),)' vb_RUNSTEP = mono ../$(2) --raw $(3) -nim_RUNSTEP = ../$(2) $(3) +#nim_RUNSTEP = ../$(2) $(3) +nim_RUNSTEP = nim -d:release --deadcodeelim:off c -r ../$(2) $(3) # Extra options to pass to runtest.py cs_TEST_OPTS = --redirect diff --git a/nim/Makefile b/nim/Makefile deleted file mode 100644 index 8f85496..0000000 --- a/nim/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -##################### - -SOURCES_BASE = types.nim reader.nim printer.nim -#SOURCES_LISP = env.nim core.nim stepA_interop.nim -SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) - -##################### - -SRCS = step0_repl.nim step1_read_print.nim step2_eval.nim -BINS = $(SRCS:%.nim=%) - -##################### - -all: $(BINS) mal - -mal: $(word $(words $(BINS)),$(BINS)) - #nimble build - cp $< $@ - -define dep_template -$(1): $(SOURCES_BASE) $(1).nim - nim c $$@ -endef -$(foreach b,$(BINS),$(eval $(call dep_template,$(b)))) - -clean: - rm -rf nimcache/ $(BINS) - rm -f mal - -.PHONY: stats stats-lisp - -stats: $(SOURCES) - @wc $^ -#stats-lisp: $(SOURCES_LISP) -# @wc $^ diff --git a/nim/core.nim b/nim/core.nim index ca25802..5fc9e63 100644 --- a/nim/core.nim +++ b/nim/core.nim @@ -1,14 +1,14 @@ -import strutils, rdstdin, tables, algorithm, types, printer, reader +import strutils, rdstdin, tables, algorithm, times, types, printer, reader type MalError* = object of Exception t*: MalType # String functions proc pr_str(xs: varargs[MalType]): MalType = - str(xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ").replace("\\", "\\\\")) + str(xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ")) proc do_str(xs: varargs[MalType]): MalType = - str(xs.map(proc(x: MalType): string = x.pr_str(false)).join.replace("\\", "\\\\")) + str(xs.map(proc(x: MalType): string = x.pr_str(false)).join) proc prn(xs: varargs[MalType]): MalType = echo xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ") @@ -43,23 +43,27 @@ proc nth(xs: varargs[MalType]): MalType = 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] + if xs[0].kind in {List, Vector} and 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] + if xs[0].kind in {List, Vector} and 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] + result = hash_map() + result.hash_map[] = xs[0].hash_map[] 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] + result = hash_map() + result.hash_map[] = xs[0].hash_map[] for i in 1 .. xs.high: if result.hash_map.hasKey(xs[i].str): result.hash_map.del(xs[i].str) @@ -131,6 +135,9 @@ proc swap_bang(xs: varargs[MalType]): MalType = xs[0].val[] = xs[1].getFun()(args) result = xs[0].val[] +proc time_ms(xs: varargs[MalType]): MalType = + number int(epochTime() * 1000) + template wrapNumberFun(op: expr): expr = fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) @@ -203,4 +210,6 @@ let ns* = { "deref": fun deref, "reset!": fun reset_bang, "swap!": fun swap_bang, + + "time-ms": fun time_ms, } diff --git a/nim/env.nim b/nim/env.nim index 3e326b9..7432d09 100644 --- a/nim/env.nim +++ b/nim/env.nim @@ -6,7 +6,7 @@ proc initEnv*(outer: Env = nil, binds, exprs: MalType = nilObj): Env = if binds.kind in {List, Vector}: for i, e in binds.list: if e.str == "&": - result.data[binds.list[i+1].str] = list(exprs.list[i .. exprs.list.high]) + result.data[binds.list[i+1].str] = list(exprs.list[i .. -1]) break else: result.data[e.str] = exprs.list[i] diff --git a/nim/mal.nimble b/nim/mal.nimble index 94e85df..8c7028d 100644 --- a/nim/mal.nimble +++ b/nim/mal.nimble @@ -5,8 +5,7 @@ author = "Dennis Felsing" description = "Mal code in Nim" license = "MIT" -bin = "step0_repl" -bin = "step1_read_print" +bin = "step0_repl, step1_read_print, step2_eval, step3_env, step4_if_fn_do, step5_tco, step6_file, step7_quote, step8_macros, step9_try, stepA_interop" [Deps] Requires = "nim >= 0.10.0, nre >= 0.6.0" diff --git a/nim/nim.cfg b/nim/nim.cfg index cd37578..55df3f9 100644 --- a/nim/nim.cfg +++ b/nim/nim.cfg @@ -1 +1,2 @@ d: pcreDynlib +deadCodeElim: off diff --git a/nim/printer.nim b/nim/printer.nim index b6bf4c7..95eb840 100644 --- a/nim/printer.nim +++ b/nim/printer.nim @@ -3,7 +3,7 @@ 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("\"", "\\\"") & "\"" + elif pr: result = "\"" & x.replace("\\", "\\\\").replace("\"", "\\\"") & "\"" else: result = x proc pr_str*(m: MalType, pr = true): string = diff --git a/nim/stepA_interop.nim b/nim/stepA_interop.nim new file mode 100644 index 0000000..48dd9dd --- /dev/null +++ b/nim/stepA_interop.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.len > 0 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] + # 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))))))))" +rep "(def! *host-language* \"nim\")" + +rep "(println (str \"Mal [\" *host-language* \"]\"))" +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 2a19eba..b5e1bec 100644 --- a/nim/types.nim +++ b/nim/types.nim @@ -32,7 +32,6 @@ type data*: Table[string, MalType] outer*: Env -# Convenience procs const nilObj* = MalType(kind: Nil) const trueObj* = MalType(kind: True) const falseObj* = MalType(kind: False) @@ -71,8 +70,9 @@ proc macro_q*(x: MalType): bool = else: x.malfun.is_macro proc getFun*(x: MalType): FunType = - if x.kind == Fun: x.fun - else: x.malfun.fn + if x.kind == Fun: result = x.fun + elif x.kind == MalFun: result = x.malfun.fn + else: echo x.kind proc fun*(x: proc(xs: varargs[MalType]): MalType, is_macro = false): MalType = MalType(kind: Fun, fun: x, is_macro: is_macro) @@ -139,7 +139,8 @@ proc `==`*(x, y: MalType): bool = of Symbol, String: x.str == y.str of List, Vector: x.list == y.list of HashMap: x.hash_map == y.hash_map - of Fun: x.fun == y.fun + of Fun: x.fun == y.fun and + x.is_macro == y.is_macro of MalFun: x.malfun == y.malfun of Atom: x.val == y.val |
