From 937f0cdc6614592b9057c023be67b8ff0db8758c Mon Sep 17 00:00:00 2001 From: def Date: Sat, 28 Feb 2015 05:15:38 +0100 Subject: Nim: step0 --- nim/mal.nimble | 11 +++++++++++ nim/step0_repl.nim | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 nim/mal.nimble create mode 100644 nim/step0_repl.nim diff --git a/nim/mal.nimble b/nim/mal.nimble new file mode 100644 index 0000000..f512616 --- /dev/null +++ b/nim/mal.nimble @@ -0,0 +1,11 @@ +[Package] +name = "mal" +version = "1.0" +author = "Dennis Felsing" +description = "Mal code in Nim" +license = "MIT" + +bin = "step0_repl" + +[Deps] +Requires: "nim >= 0.10.0" diff --git a/nim/step0_repl.nim b/nim/step0_repl.nim new file mode 100644 index 0000000..6ae7d89 --- /dev/null +++ b/nim/step0_repl.nim @@ -0,0 +1,11 @@ +import rdstdin + +proc read(str: string): string = str + +proc eval(ast: string): string = ast + +proc print(exp: string): string = exp + +while true: + let line = readLineFromStdin("user> ") + echo line.read.eval.print -- cgit v1.2.3 From b94acce656db4f0b129cd939765c0ca451f6a538 Mon Sep 17 00:00:00 2001 From: def Date: Sat, 28 Feb 2015 14:14:18 +0100 Subject: Nim: step1 --- Makefile | 4 +- nim/Makefile | 35 +++++++++++++++++ nim/mal.nimble | 3 +- nim/nim.cfg | 1 + nim/printer.nim | 15 ++++++++ nim/reader.nim | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ nim/step1_read_print.nim | 14 +++++++ nim/types.nim | 33 ++++++++++++++++ 8 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 nim/Makefile create mode 100644 nim/nim.cfg create mode 100644 nim/printer.nim create mode 100644 nim/reader.nim create mode 100644 nim/step1_read_print.nim create mode 100644 nim/types.nim diff --git a/Makefile b/Makefile index 47c7829..0a64d5e 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ PYTHON = python IMPLS = bash c clojure coffee cs forth go haskell java js lua make mal \ ocaml matlab miniMAL perl php ps python r racket ruby rust \ - scala vb + scala vb nim step0 = step0_repl step1 = step1_read_print @@ -76,6 +76,7 @@ ruby_STEP_TO_PROG = ruby/$($(1)).rb rust_STEP_TO_PROG = rust/target/$($(1)) scala_STEP_TO_PROG = scala/$($(1)).scala vb_STEP_TO_PROG = vb/$($(1)).exe +nim_STEP_TO_PROG = nim/$($(1)) # Needed some argument munging COMMA = , @@ -109,6 +110,7 @@ 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) # Extra options to pass to runtest.py cs_TEST_OPTS = --redirect diff --git a/nim/Makefile b/nim/Makefile new file mode 100644 index 0000000..5fba42c --- /dev/null +++ b/nim/Makefile @@ -0,0 +1,35 @@ +##################### + +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 +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/mal.nimble b/nim/mal.nimble index f512616..94e85df 100644 --- a/nim/mal.nimble +++ b/nim/mal.nimble @@ -6,6 +6,7 @@ description = "Mal code in Nim" license = "MIT" bin = "step0_repl" +bin = "step1_read_print" [Deps] -Requires: "nim >= 0.10.0" +Requires = "nim >= 0.10.0, nre >= 0.6.0" diff --git a/nim/nim.cfg b/nim/nim.cfg new file mode 100644 index 0000000..cd37578 --- /dev/null +++ b/nim/nim.cfg @@ -0,0 +1 @@ +d: pcreDynlib diff --git a/nim/printer.nim b/nim/printer.nim new file mode 100644 index 0000000..45c21f1 --- /dev/null +++ b/nim/printer.nim @@ -0,0 +1,15 @@ +import strutils, tables, types + +proc pr_str*(m: MalType): string = + case m.kind + of Nil: result = "nil" + of Symbol: result = m.symbol + of Number: result = $m.number + of List: result = "(" & m.list.map(pr_str).join(" ") & ")" + of Vector: result = "[" & m.vector.map(pr_str).join(" ") & "]" + of HashMap: + result = "{" + for key, val in m.hash_map.pairs: + if result.len > 1: result.add " " + result.add key & " " & val.pr_str + result.add "}" diff --git a/nim/reader.nim b/nim/reader.nim new file mode 100644 index 0000000..c8f9096 --- /dev/null +++ b/nim/reader.nim @@ -0,0 +1,97 @@ +import nre, optional_t, strutils, types + +var + tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""" + intRE = re"-?[0-9]+$" + +type Reader = object + tokens: seq[string] + position: int + +proc next(r: var Reader): string = + if r.position >= r.tokens.len: + result = nil + else: + result = r.tokens[r.position] + inc r.position + +proc peek(r: Reader): string = + if r.position >= r.tokens.len: nil + else: r.tokens[r.position] + +proc tokenize(str: string): seq[string] = + result = @[] + for match in str.findIter(tokenRE): + result.add match.captures[0] + +proc read_form(r: var Reader): MalType + +proc read_seq(r: var Reader, fr, to: string): seq[MalType] = + result = @[] + var t = r.next + if t != fr: raise newException(ValueError, "expected '" & fr & "'") + + t = r.peek + while t != to: + if t == nil: raise newException(ValueError, "expected '" & to & "', got EOF") + result.add r.read_form + t = r.peek + discard r.next + +proc read_list(r: var Reader): MalType = + result = list r.read_seq("(", ")") + +proc read_vector(r: var Reader): MalType = + result = vector r.read_seq("[", "]") + +proc read_hash_map(r: var Reader): MalType = + result = hash_map r.read_seq("{", "}") + +proc read_atom(r: var Reader): MalType = + let t = r.next + if t.match(intRE): number t.parseInt + else: symbol t + +proc read_form(r: var Reader): MalType = + if r.peek[0] == ';': + discard r.next + return nilObj + case r.peek + of "'": + discard r.next + result = list(symbol "quote", r.read_form) + of "`": + discard r.next + result = list(symbol "quasiquote", r.read_form) + of "~": + discard r.next + result = list(symbol "unquote", r.read_form) + of "~@": + discard r.next + result = list(symbol "splice-unquote", r.read_form) + of "^": + discard r.next + let meta = r.read_form + result = list(symbol "with-meta", r.read_form, meta) + of "@": + discard r.next + result = list(symbol "deref", r.read_form) + + # list + of "(": result = r.read_list + of ")": raise newException(ValueError, "unexpected ')'") + + # vector + of "[": result = r.read_vector + of "]": raise newException(ValueError, "unexpected ']'") + + # hash-map + of "{": result = r.read_hash_map + of "}": raise newException(ValueError, "unexpected '}'") + + # atom + else: result = r.read_atom + +proc read_str*(str: string): MalType = + var r = Reader(tokens: str.tokenize) + r.read_form diff --git a/nim/step1_read_print.nim b/nim/step1_read_print.nim new file mode 100644 index 0000000..4be5898 --- /dev/null +++ b/nim/step1_read_print.nim @@ -0,0 +1,14 @@ +import rdstdin, types, reader, printer + +proc read(str: string): MalType = str.read_str + +proc eval(ast: MalType): MalType = ast + +proc print(exp: MalType): string = exp.pr_str + +while true: + try: + let line = readLineFromStdin("user> ") + echo line.read.eval.print + except: + echo getCurrentExceptionMsg() diff --git a/nim/types.nim b/nim/types.nim new file mode 100644 index 0000000..866d6db --- /dev/null +++ b/nim/types.nim @@ -0,0 +1,33 @@ +import tables + +type + MalTypeKind* = enum Nil, Number, Symbol, List, Vector, HashMap + + MalType* = object + case kind*: MalTypeKind + of Nil: nil + of Number: number*: int + of Symbol: symbol*: string + of List: list*: seq[MalType] + of Vector: vector*: seq[MalType] + of HashMap: hash_map*: TableRef[string, MalType] + +# Convenience procs +const nilObj*: MalType = MalType(kind: Nil) + +proc number*(x: int): MalType = MalType(kind: Number, number: x) + +proc symbol*(x: string): MalType = MalType(kind: Symbol, symbol: x) + +proc list*(xs: varargs[MalType]): MalType = + result = MalType(kind: List, list: @[]) + for x in xs: result.list.add x + +proc vector*(xs: varargs[MalType]): MalType = + result = MalType(kind: Vector, vector: @[]) + for x in xs: result.vector.add x + +proc hash_map*(xs: varargs[MalType]): MalType = + result = MalType(kind: HashMap, hash_map: newTable[string, MalType]()) + for i in countup(0, xs.high, 2): + result.hash_map[xs[i].symbol] = xs[i+1] -- cgit v1.2.3 From c9d2371f7f0a97efe464565fdf8ebc8405eb8780 Mon Sep 17 00:00:00 2001 From: def Date: Sat, 28 Feb 2015 15:20:37 +0100 Subject: Nim: step2 --- nim/Makefile | 2 +- nim/printer.nim | 1 + nim/step2_eval.nim | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 5 ++++- 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 nim/step2_eval.nim diff --git a/nim/Makefile b/nim/Makefile index 5fba42c..8f85496 100644 --- a/nim/Makefile +++ b/nim/Makefile @@ -6,7 +6,7 @@ SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) ##################### -SRCS = step0_repl.nim step1_read_print.nim +SRCS = step0_repl.nim step1_read_print.nim step2_eval.nim BINS = $(SRCS:%.nim=%) ##################### diff --git a/nim/printer.nim b/nim/printer.nim index 45c21f1..e3d3aab 100644 --- a/nim/printer.nim +++ b/nim/printer.nim @@ -3,6 +3,7 @@ import strutils, tables, types proc pr_str*(m: MalType): string = case m.kind of Nil: result = "nil" + of Fun: result = "fun" of Symbol: result = m.symbol of Number: result = $m.number of List: result = "(" & m.list.map(pr_str).join(" ") & ")" diff --git a/nim/step2_eval.nim b/nim/step2_eval.nim new file mode 100644 index 0000000..7418335 --- /dev/null +++ b/nim/step2_eval.nim @@ -0,0 +1,52 @@ +import rdstdin, tables, sequtils, types, reader, printer + +proc read(str: string): MalType = str.read_str + +proc eval(ast: MalType, env: Table[string, MalType]): MalType + +proc eval_ast(ast: MalType, env: Table[string, MalType]): MalType = + case ast.kind + of Symbol: + if not env.hasKey(ast.symbol): + raise newException(ValueError, "'" & ast.symbol & "' not found") + result = env[ast.symbol] + of List: + result = list ast.list.mapIt(MalType, it.eval(env)) + of Vector: + result = vector ast.vector.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: Table[string, MalType]): MalType = + case ast.kind + of List: + let el = ast.eval_ast(env) + el.list[0].fun(el.list[1 .. -1]) + else: + ast.eval_ast(env) + +proc print(exp: MalType): string = exp.pr_str + +template wrapNumberFun(op: expr): expr = + fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) + +let repl_env = toTable({ + "+": wrapNumberFun `+`, + "-": wrapNumberFun `-`, + "*": wrapNumberFun `*`, + "/": wrapNumberFun `div`, +}) + +proc rep(str: string): string = + str.read.eval(repl_env).print + +while true: + try: + let line = readLineFromStdin("user> ") + echo line.rep + except: + echo getCurrentExceptionMsg() diff --git a/nim/types.nim b/nim/types.nim index 866d6db..dfab369 100644 --- a/nim/types.nim +++ b/nim/types.nim @@ -1,7 +1,7 @@ import tables type - MalTypeKind* = enum Nil, Number, Symbol, List, Vector, HashMap + MalTypeKind* = enum Nil, Number, Symbol, List, Vector, HashMap, Fun MalType* = object case kind*: MalTypeKind @@ -11,6 +11,7 @@ type of List: list*: seq[MalType] of Vector: vector*: seq[MalType] of HashMap: hash_map*: TableRef[string, MalType] + of Fun: fun*: proc(xs: varargs[MalType]): MalType # Convenience procs const nilObj*: MalType = MalType(kind: Nil) @@ -31,3 +32,5 @@ proc hash_map*(xs: varargs[MalType]): MalType = result = MalType(kind: HashMap, hash_map: newTable[string, MalType]()) for i in countup(0, xs.high, 2): result.hash_map[xs[i].symbol] = xs[i+1] + +proc fun*(x: proc(xs: varargs[MalType]): MalType): MalType = MalType(kind: Fun, fun: x) -- cgit v1.2.3 From 7af2994e50b178b31795532c8742da2ec24da9ad Mon Sep 17 00:00:00 2001 From: def Date: Sat, 28 Feb 2015 16:08:36 +0100 Subject: Nim: step3 --- nim/env.nim | 20 +++++++++++++++ nim/step3_env.nim | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 nim/env.nim create mode 100644 nim/step3_env.nim diff --git a/nim/env.nim b/nim/env.nim new file mode 100644 index 0000000..616c817 --- /dev/null +++ b/nim/env.nim @@ -0,0 +1,20 @@ +import tables, types + +type Env* = ref object + data: Table[string, MalType] + outer: Env + +proc initEnv*: Env = Env(data: initTable[string, MalType]()) + +proc set*(e: var Env, key: string, value: MalType): MalType {.discardable.} = + e.data[key] = value + value + +proc find(e: Env, key: string): Env = + if e.data.hasKey(key): return e + if e.outer != nil: return e.outer.find(key) + +proc get*(e: Env, key: string): MalType = + let env = e.find(key) + if env == nil: raise newException(ValueError, "'" & key & "' not found") + env.data[key] diff --git a/nim/step3_env.nim b/nim/step3_env.nim new file mode 100644 index 0000000..9da4af4 --- /dev/null +++ b/nim/step3_env.nim @@ -0,0 +1,73 @@ +import rdstdin, tables, sequtils, types, reader, printer, env + +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.vector.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 = + case ast.kind + of List: + let + a0 = ast.list[0] + a1 = ast.list[1] + a2 = ast.list[2] + + case a0.symbol + of "def!": + result = env.set(a1.symbol, a2.eval(env)) + of "let*": + var letEnv: Env + letEnv.deepCopy(env) + case a1.kind + of List: + for i in countup(0, a1.list.high, 2): + letEnv.set(a1.list[i].symbol, a1.list[i+1].eval(letEnv)) + of Vector: + for i in countup(0, a1.vector.high, 2): + letEnv.set(a1.vector[i].symbol, a1.vector[i+1].eval(letEnv)) + else: discard + result = a2.eval(letEnv) + else: + let el = ast.eval_ast(env) + result = el.list[0].fun(el.list[1 .. -1]) + else: + result = ast.eval_ast(env) + +proc print(exp: MalType): string = exp.pr_str + +template wrapNumberFun(op: expr): expr = + fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) + +var repl_env = initEnv() + +repl_env.set("+", wrapNumberFun(`+`)) +repl_env.set("-", wrapNumberFun(`-`)) +repl_env.set("*", wrapNumberFun(`*`)) +repl_env.set("/", wrapNumberFun(`div`)) +#repl_env.set("/", wrapNumberFun(proc(x,y: int): int = int(x.float / y.float))) + +proc rep(str: string): string = + str.read.eval(repl_env).print + +while true: + try: + let line = readLineFromStdin("user> ") + echo line.rep + except: + echo getCurrentExceptionMsg() + echo getCurrentException().getStackTrace() -- cgit v1.2.3 From 819bd786cb21c393d4f94e682758addc253e1a20 Mon Sep 17 00:00:00 2001 From: def Date: Sat, 28 Feb 2015 19:52:01 +0100 Subject: Nim: step4 --- nim/core.nim | 51 ++++++++++++++++++++++++ nim/env.nim | 11 +++++- nim/printer.nim | 19 ++++++--- nim/reader.nim | 5 +++ nim/step2_eval.nim | 2 +- nim/step3_env.nim | 7 +--- nim/step4_if_fn_do.nim | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 70 ++++++++++++++++++++++++++------- 8 files changed, 241 insertions(+), 28 deletions(-) create mode 100644 nim/core.nim create mode 100644 nim/step4_if_fn_do.nim diff --git a/nim/core.nim b/nim/core.nim new file mode 100644 index 0000000..5a6904c --- /dev/null +++ b/nim/core.nim @@ -0,0 +1,51 @@ +import strutils, types, printer + +# String functions +proc pr_str(xs: varargs[MalType]): MalType = + str(xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ").replace("\\", "\\\\")) + +proc do_str(xs: varargs[MalType]): MalType = + str(xs.map(proc(x: MalType): string = x.pr_str(false)).join.replace("\\", "\\\\")) + +proc prn(xs: varargs[MalType]): MalType = + echo xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ") + result = nilObj + +proc println(xs: varargs[MalType]): MalType = + let line = xs.map(proc(x: MalType): string = x.pr_str(false)).join(" ") + echo line.replace("\\n", "\n") + result = nilObj + +template wrapNumberFun(op: expr): expr = + 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 = + if op(xs[0].number, xs[1].number): trueObj else: falseObj + +let ns* = { + "+": wrapNumberFun(`+`), + "-": wrapNumberFun(`-`), + "*": wrapNumberFun(`*`), + "/": wrapNumberFun(`div`), + + "<": wrapBoolFun(`<`), + "<=": wrapBoolFun(`<=`), + ">": wrapBoolFun(`>`), + ">=": wrapBoolFun(`>=`), + + "list": fun list, + "list?": fun list_q, + "vector": fun vector, + "vector?": fun vector_q, + "hash_map": fun hash_map, + "hash_map?": fun hash_map_q, + "empty?": fun empty_q, + "count": fun count, + "=": fun equal, + + "pr-str": fun pr_str, + "str": fun do_str, + "prn": fun prn, + "println": fun println, +} diff --git a/nim/env.nim b/nim/env.nim index 616c817..b5c5a37 100644 --- a/nim/env.nim +++ b/nim/env.nim @@ -4,7 +4,16 @@ type Env* = ref object data: Table[string, MalType] outer: Env -proc initEnv*: Env = Env(data: initTable[string, MalType]()) +proc initEnv*(outer: Env = nil, binds, exprs: MalType = nilObj): Env = + result = Env(data: initTable[string, MalType](), outer: outer) + + if binds.kind in {List, Vector}: + for i, e in binds.list: + if e.symbol == "&": + result.data[binds.list[i+1].symbol] = list(exprs.list[i .. exprs.list.high]) + break + else: + result.data[e.symbol] = exprs.list[i] proc set*(e: var Env, key: string, value: MalType): MalType {.discardable.} = e.data[key] = value diff --git a/nim/printer.nim b/nim/printer.nim index e3d3aab..aa4507f 100644 --- a/nim/printer.nim +++ b/nim/printer.nim @@ -1,16 +1,23 @@ -import strutils, tables, types +import strutils, sequtils, tables, types -proc pr_str*(m: MalType): string = +proc pr_str*(m: MalType, pr = true): string = case m.kind of Nil: result = "nil" - of Fun: result = "fun" + of True: result = "true" + of False: result = "false" + of Fun: result = "#" of Symbol: result = m.symbol + 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 Number: result = $m.number - of List: result = "(" & m.list.map(pr_str).join(" ") & ")" - of Vector: result = "[" & m.vector.map(pr_str).join(" ") & "]" + of List: result = "(" & m.list.mapIt(string, it.pr_str(pr)).join(" ") & ")" + of Vector: result = "[" & m.list.mapIt(string, it.pr_str(pr)).join(" ") & "]" of HashMap: result = "{" for key, val in m.hash_map.pairs: if result.len > 1: result.add " " - result.add key & " " & val.pr_str + result.add key & " " & val.pr_str(pr) result.add "}" diff --git a/nim/reader.nim b/nim/reader.nim index c8f9096..fef213a 100644 --- a/nim/reader.nim +++ b/nim/reader.nim @@ -50,6 +50,11 @@ proc read_hash_map(r: var Reader): MalType = proc read_atom(r: var Reader): MalType = let t = r.next if t.match(intRE): number t.parseInt + elif t[0] == '"': str t[1 .. 3: result = ast.list[3].eval(env) + else: result = nilObj + else: result = a2.eval(env) + + of "fn*": + let + a1 = ast.list[1] + a2 = ast.list[2] + var env2 = env + result = fun(proc(a: varargs[MalType]): MalType = + var newEnv = initEnv(env2, a1, list(a)) + a2.eval(newEnv)) + + else: + let el = ast.eval_ast(env) + result = el.list[0].fun(el.list[1 .. -1]) + + else: + let el = ast.eval_ast(env) + result = el.list[0].fun(el.list[1 .. -1]) + + else: + result = ast.eval_ast(env) + +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 dfab369..557a314 100644 --- a/nim/types.nim +++ b/nim/types.nim @@ -1,36 +1,76 @@ -import tables +import tables, strutils type - MalTypeKind* = enum Nil, Number, Symbol, List, Vector, HashMap, Fun + MalTypeKind* = enum Nil, True, False, Number, Symbol, String, + List, Vector, HashMap, Fun MalType* = object case kind*: MalTypeKind - of Nil: nil - of Number: number*: int - of Symbol: symbol*: string - of List: list*: seq[MalType] - of Vector: vector*: seq[MalType] - of HashMap: hash_map*: TableRef[string, MalType] - of Fun: fun*: proc(xs: varargs[MalType]): MalType + of Nil, True, False: nil + of Number: number*: int + of Symbol: symbol*: string + of String: str*: string + of List, Vector: list*: seq[MalType] + of HashMap: hash_map*: TableRef[string, MalType] + of Fun: fun*: proc(xs: varargs[MalType]): MalType # Convenience procs -const nilObj*: MalType = MalType(kind: Nil) +const nilObj* = MalType(kind: Nil) +const trueObj* = MalType(kind: True) +const falseObj* = MalType(kind: False) proc number*(x: int): MalType = MalType(kind: Number, number: x) proc symbol*(x: string): MalType = MalType(kind: Symbol, symbol: x) -proc list*(xs: varargs[MalType]): MalType = +proc str*(x: string): MalType = MalType(kind: String, str: x) + +proc keyword*(x: string): MalType = MalType(kind: String, str: "\xff" & x) + +proc list*(xs: varargs[MalType]): MalType {.procvar.} = result = MalType(kind: List, list: @[]) for x in xs: result.list.add x -proc vector*(xs: varargs[MalType]): MalType = - result = MalType(kind: Vector, vector: @[]) - for x in xs: result.vector.add x +proc vector*(xs: varargs[MalType]): MalType {.procvar.} = + result = MalType(kind: Vector, list: @[]) + for x in xs: result.list.add x -proc hash_map*(xs: varargs[MalType]): MalType = +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): result.hash_map[xs[i].symbol] = xs[i+1] proc fun*(x: proc(xs: varargs[MalType]): MalType): MalType = MalType(kind: Fun, fun: x) + +proc boolObj(b: bool): MalType = + if b: trueObj else: falseObj + +proc list_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == List + +proc vector_q*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0].kind == 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 count*(xs: varargs[MalType]): MalType {.procvar.} = + number if xs[0].kind == Nil: 0 else: xs[0].list.len + +proc `==`*(x, y: MalType): bool = + if not (x.kind in {List, Vector} and y.kind in {List, Vector}): + if x.kind != y.kind: return false + result = case x.kind + of Nil, True, False: true + of Number: x.number == y.number + of Symbol: x.symbol == y.symbol + of 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 + +proc equal*(xs: varargs[MalType]): MalType {.procvar.} = + boolObj xs[0] == xs[1] -- cgit v1.2.3 From 5dfcbb6b4dccefd79c7ec5577b215fc50cef46d2 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 1 Mar 2015 03:55:05 +0100 Subject: Nim: step5 --- nim/env.nim | 4 -- nim/printer.nim | 1 + nim/step5_tco.nim | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 19 ++++++++- 4 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 nim/step5_tco.nim 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 = "#" + of MalFun: result = "#" 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 .. 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] -- cgit v1.2.3 From 3603af9666161a66c5a846c33a1b2663044c73af Mon Sep 17 00:00:00 2001 From: def Date: Sun, 1 Mar 2015 04:38:43 +0100 Subject: Nim: step6 --- nim/core.nim | 11 ++++- nim/env.nim | 6 +-- nim/printer.nim | 2 +- nim/reader.nim | 14 ++++-- nim/step2_eval.nim | 6 +-- nim/step3_env.nim | 8 ++-- nim/step4_if_fn_do.nim | 8 ++-- nim/step5_tco.nim | 8 ++-- nim/step6_file.nim | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 35 +++++++------- 10 files changed, 179 insertions(+), 41 deletions(-) create mode 100644 nim/step6_file.nim diff --git a/nim/core.nim b/nim/core.nim index 5a6904c..c32bc6f 100644 --- a/nim/core.nim +++ b/nim/core.nim @@ -1,4 +1,4 @@ -import strutils, types, printer +import strutils, types, printer, reader # String functions proc pr_str(xs: varargs[MalType]): MalType = @@ -16,6 +16,12 @@ proc println(xs: varargs[MalType]): MalType = echo line.replace("\\n", "\n") result = nilObj +proc read_str(xs: varargs[MalType]): MalType = + read_str(xs[0].str) + +proc slurp(xs: varargs[MalType]): MalType = + str readFile(xs[0].str) + template wrapNumberFun(op: expr): expr = fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) @@ -48,4 +54,7 @@ let ns* = { "str": fun do_str, "prn": fun prn, "println": fun println, + + "read-string": fun read_str, + "slurp": fun slurp, } diff --git a/nim/env.nim b/nim/env.nim index 55e2a09..bb4ad41 100644 --- a/nim/env.nim +++ b/nim/env.nim @@ -5,11 +5,11 @@ proc initEnv*(outer: Env = nil, binds, exprs: MalType = nilObj): Env = if binds.kind in {List, Vector}: for i, e in binds.list: - if e.symbol == "&": - result.data[binds.list[i+1].symbol] = list(exprs.list[i .. exprs.list.high]) + if e.str == "&": + result.data[binds.list[i+1].str] = list(exprs.list[i .. exprs.list.high]) break else: - result.data[e.symbol] = exprs.list[i] + result.data[e.str] = exprs.list[i] proc set*(e: var Env, key: string, value: MalType): MalType {.discardable.} = e.data[key] = value diff --git a/nim/printer.nim b/nim/printer.nim index 7bdf78c..eb26671 100644 --- a/nim/printer.nim +++ b/nim/printer.nim @@ -7,7 +7,7 @@ proc pr_str*(m: MalType, pr = true): string = of False: result = "false" of Fun: result = "#" of MalFun: result = "#" - of Symbol: result = m.symbol + of Symbol: result = m.str of String: if m.str.len > 0 and m.str[0] == '\xff': result = ":" & m.str[1 .. m.str.high] diff --git a/nim/reader.nim b/nim/reader.nim index fef213a..856a6a6 100644 --- a/nim/reader.nim +++ b/nim/reader.nim @@ -4,9 +4,12 @@ var tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""" intRE = re"-?[0-9]+$" -type Reader = object - tokens: seq[string] - position: int +type + Blank* = object of Exception + + Reader = object + tokens: seq[string] + position: int proc next(r: var Reader): string = if r.position >= r.tokens.len: @@ -22,7 +25,8 @@ proc peek(r: Reader): string = proc tokenize(str: string): seq[string] = result = @[] for match in str.findIter(tokenRE): - result.add match.captures[0] + if match.captures[0][0] != ';': + result.add match.captures[0] proc read_form(r: var Reader): MalType @@ -99,4 +103,6 @@ proc read_form(r: var Reader): MalType = proc read_str*(str: string): MalType = var r = Reader(tokens: str.tokenize) + if r.tokens.len == 0: + raise newException(Blank, "Blank line") r.read_form diff --git a/nim/step2_eval.nim b/nim/step2_eval.nim index 566003c..075b4ac 100644 --- a/nim/step2_eval.nim +++ b/nim/step2_eval.nim @@ -7,9 +7,9 @@ proc eval(ast: MalType, env: Table[string, MalType]): MalType proc eval_ast(ast: MalType, env: Table[string, MalType]): MalType = case ast.kind of Symbol: - if not env.hasKey(ast.symbol): - raise newException(ValueError, "'" & ast.symbol & "' not found") - result = env[ast.symbol] + if not env.hasKey(ast.str): + raise newException(ValueError, "'" & ast.str & "' not found") + result = env[ast.str] of List: result = list ast.list.mapIt(MalType, it.eval(env)) of Vector: diff --git a/nim/step3_env.nim b/nim/step3_env.nim index e118872..98438fe 100644 --- a/nim/step3_env.nim +++ b/nim/step3_env.nim @@ -7,7 +7,7 @@ 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) + result = env.get(ast.str) of List: result = list ast.list.mapIt(MalType, it.eval(env)) of Vector: @@ -27,16 +27,16 @@ proc eval(ast: MalType, env: var Env): MalType = a1 = ast.list[1] a2 = ast.list[2] - case a0.symbol + case a0.str of "def!": - result = env.set(a1.symbol, a2.eval(env)) + result = env.set(a1.str, a2.eval(env)) of "let*": var letEnv: Env letEnv.deepCopy(env) case a1.kind of List, Vector: for i in countup(0, a1.list.high, 2): - letEnv.set(a1.list[i].symbol, a1.list[i+1].eval(letEnv)) + letEnv.set(a1.list[i].str, a1.list[i+1].eval(letEnv)) else: discard result = a2.eval(letEnv) else: diff --git a/nim/step4_if_fn_do.nim b/nim/step4_if_fn_do.nim index b235733..a4a8dd1 100644 --- a/nim/step4_if_fn_do.nim +++ b/nim/step4_if_fn_do.nim @@ -7,7 +7,7 @@ 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) + result = env.get(ast.str) of List: result = list ast.list.mapIt(MalType, it.eval(env)) of Vector: @@ -25,12 +25,12 @@ proc eval(ast: MalType, env: var Env): MalType = let a0 = ast.list[0] case a0.kind of Symbol: - case a0.symbol + case a0.str of "def!": let a1 = ast.list[1] a2 = ast.list[2] - result = env.set(a1.symbol, a2.eval(env)) + result = env.set(a1.str, a2.eval(env)) of "let*": let @@ -42,7 +42,7 @@ proc eval(ast: MalType, env: var Env): MalType = case a1.kind of List, Vector: for i in countup(0, a1.list.high, 2): - letEnv.set(a1.list[i].symbol, a1.list[i+1].eval(letEnv)) + letEnv.set(a1.list[i].str, a1.list[i+1].eval(letEnv)) else: discard result = a2.eval(letEnv) diff --git a/nim/step5_tco.nim b/nim/step5_tco.nim index f7140e7..3c91706 100644 --- a/nim/step5_tco.nim +++ b/nim/step5_tco.nim @@ -7,7 +7,7 @@ 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) + result = env.get(ast.str) of List: result = list ast.list.mapIt(MalType, it.eval(env)) of Vector: @@ -38,12 +38,12 @@ proc eval(ast: MalType, env: var Env): MalType = let a0 = ast.list[0] case a0.kind of Symbol: - case a0.symbol + case a0.str of "def!": let a1 = ast.list[1] a2 = ast.list[2] - return env.set(a1.symbol, a2.eval(env)) + return env.set(a1.str, a2.eval(env)) of "let*": let @@ -52,7 +52,7 @@ proc eval(ast: MalType, env: var Env): MalType = 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)) + env.set(a1.list[i].str, a1.list[i+1].eval(env)) else: discard # Continue loop (TCO) diff --git a/nim/step6_file.nim b/nim/step6_file.nim new file mode 100644 index 0000000..53269aa --- /dev/null +++ b/nim/step6_file.nim @@ -0,0 +1,122 @@ +import rdstdin, tables, sequtils, os, 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.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(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.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] + 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 + # Continue loop (TCO) + + of "do": + let last = ast.list.high + let el = (list ast.list[1 .. 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) \")\")))))" + +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 f677f06..278ae1b 100644 --- a/nim/types.nim +++ b/nim/types.nim @@ -13,13 +13,12 @@ type MalType* = object case kind*: MalTypeKind of Nil, True, False: nil - of Number: number*: int - of Symbol: symbol*: string - of String: str*: string - 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 + of Number: number*: int + 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 MalFun: malfun*: MalFunType Env* = ref object data*: Table[string, MalType] @@ -32,9 +31,9 @@ const falseObj* = MalType(kind: False) proc number*(x: int): MalType = MalType(kind: Number, number: x) -proc symbol*(x: string): MalType = MalType(kind: Symbol, symbol: x) +proc symbol*(x: string): MalType = MalType(kind: Symbol, str: x) -proc str*(x: string): MalType = MalType(kind: String, str: x) +proc str*(x: string): MalType {.procvar.} = MalType(kind: String, str: x) proc keyword*(x: string): MalType = MalType(kind: String, str: "\xff" & x) @@ -49,7 +48,10 @@ proc vector*(xs: varargs[MalType]): MalType {.procvar.} = 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): - result.hash_map[xs[i].symbol] = xs[i+1] + let s = case xs[i].kind + of String: "\"" & xs[i].str & "\"" + 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) @@ -81,13 +83,12 @@ proc `==`*(x, y: MalType): bool = if x.kind != y.kind: return false result = case x.kind of Nil, True, False: true - of Number: x.number == y.number - of Symbol: x.symbol == y.symbol - of 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 MalFun: x.malfun == y.malfun + of Number: x.number == y.number + 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 MalFun: x.malfun == y.malfun proc equal*(xs: varargs[MalType]): MalType {.procvar.} = boolObj xs[0] == xs[1] -- cgit v1.2.3 From dc7f0b6afd9bd298103c42e7113a69b788d565c4 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 1 Mar 2015 04:57:38 +0100 Subject: Nim: step7 --- nim/core.nim | 12 +++++ nim/step7_quote.nim | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 nim/step7_quote.nim diff --git a/nim/core.nim b/nim/core.nim index c32bc6f..afba5e5 100644 --- a/nim/core.nim +++ b/nim/core.nim @@ -22,6 +22,16 @@ proc read_str(xs: varargs[MalType]): MalType = proc slurp(xs: varargs[MalType]): MalType = str readFile(xs[0].str) +proc cons(xs: varargs[MalType]): MalType = + result = list(xs[0]) + for x in xs[1].list: result.list.add x + +proc concat(xs: varargs[MalType]): MalType = + result = list() + for x in xs: + for i in x.list: + result.list.add i + template wrapNumberFun(op: expr): expr = fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number) @@ -57,4 +67,6 @@ let ns* = { "read-string": fun read_str, "slurp": fun slurp, + "cons": fun cons, + "concat": fun concat, } diff --git a/nim/step7_quote.nim b/nim/step7_quote.nim new file mode 100644 index 0000000..ec8f6fb --- /dev/null +++ b/nim/step7_quote.nim @@ -0,0 +1,143 @@ +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 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(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.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] + 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 + # Continue loop (TCO) + + of "quote": + return ast.list[1] + + of "quasiquote": + ast = ast.list[1].quasiquote + # Continue loop (TCO) + + of "do": + let last = ast.list.high + let el = (list ast.list[1 .. 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) \")\")))))" + +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() -- cgit v1.2.3 From f5cf5237eea749230250de07124b25f2947f251e Mon Sep 17 00:00:00 2001 From: def Date: Sun, 1 Mar 2015 06:08:18 +0100 Subject: Nim: step8 --- nim/core.nim | 16 +++++ nim/env.nim | 2 +- nim/step5_tco.nim | 9 ++- nim/step6_file.nim | 9 ++- nim/step7_quote.nim | 9 ++- nim/step8_macros.nim | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 26 +++++--- 7 files changed, 221 insertions(+), 19 deletions(-) create mode 100644 nim/step8_macros.nim 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 .. 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 -- cgit v1.2.3 From 8de9f3085c2f251c0b38ca28e5a66e9f66f9d968 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 1 Mar 2015 17:39:15 +0100 Subject: Nim: step9 --- nim/core.nim | 130 +++++++++++++++++++++++++++++++++++-- nim/printer.nim | 15 +++-- nim/step9_try.nim | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 55 ++++++++++++++-- 4 files changed, 373 insertions(+), 17 deletions(-) create mode 100644 nim/step9_try.nim 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 = "#" of MalFun: result = "#" + 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 .. 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] -- cgit v1.2.3 From 4ce9e165bafb4f14b03624f515066c3c42719dd9 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 1 Mar 2015 21:46:19 +0100 Subject: Nim: stepA --- Makefile | 4 +- nim/Makefile | 35 ---------- nim/core.nim | 23 ++++-- nim/env.nim | 2 +- nim/mal.nimble | 3 +- nim/nim.cfg | 1 + nim/printer.nim | 2 +- nim/stepA_interop.nim | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++ nim/types.nim | 9 +-- 9 files changed, 218 insertions(+), 51 deletions(-) delete mode 100644 nim/Makefile create mode 100644 nim/stepA_interop.nim diff --git a/Makefile b/Makefile index 0a64d5e..de9e2e5 100644 --- a/Makefile +++ b/Makefile @@ -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 .. 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 -- cgit v1.2.3 From a2cd0a3adae2ccf2566122bcd90230d905ab59dc Mon Sep 17 00:00:00 2001 From: def Date: Wed, 4 Mar 2015 04:17:43 +0100 Subject: Add instructions and makefile for Nim --- Makefile | 5 ++--- README.md | 16 +++++++++++++++- nim/Makefile | 33 +++++++++++++++++++++++++++++++++ nim/mal.nimble | 2 +- 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 nim/Makefile diff --git a/Makefile b/Makefile index de9e2e5..6bfd582 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +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 +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 \ @@ -111,8 +111,7 @@ 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 = nim -d:release --deadcodeelim:off c -r ../$(2) $(3) +nim_RUNSTEP = ../$(2) $(3) # Extra options to pass to runtest.py cs_TEST_OPTS = --redirect diff --git a/README.md b/README.md index 612743c..b35a4ae 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Mal is an Clojure inspired Lisp interpreter. -Mal is implemented in 26 different languages: +Mal is implemented in 27 different languages: * Bash shell * C @@ -21,6 +21,7 @@ Mal is implemented in 26 different languages: * mal itself * MATLAB * [miniMAL](https://github.com/kanaka/miniMAL) +* Nim * OCaml * Perl * PHP @@ -190,6 +191,19 @@ cd make make -f stepX_YYY.mk ``` +### Nim 0.10.3 + +Running the Nim implementation of mal requires Nim's current devel branch +(0.10.3) or later, and the nre library installed. + +``` +cd nim +make + # OR +nimble build +./stepX_YYY +``` + ### OCaml 4.01.0 ``` diff --git a/nim/Makefile b/nim/Makefile new file mode 100644 index 0000000..1da1ae6 --- /dev/null +++ b/nim/Makefile @@ -0,0 +1,33 @@ +##################### + +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 step3_env.nim \ + step4_if_fn_do.nim step5_tco.nim step6_file.nim step7_quote.nim \ + step8_macros.nim step9_try.nim stepA_interop.nim +BINS = $(SRCS:%.nim=%) + +##################### + +all: $(BINS) mal + +mal: $(word $(words $(BINS)),$(BINS)) + cp $< $@ + +$(BINS): + nim -d:release c $@ + +clean: + rm -rf nimcache/ $(BINS) + rm -f mal + +.PHONY: stats stats-lisp + +stats: $(SOURCES) + @wc $^ +stats-lisp: $(SOURCES_LISP) + @wc $^ diff --git a/nim/mal.nimble b/nim/mal.nimble index 8c7028d..db35b0b 100644 --- a/nim/mal.nimble +++ b/nim/mal.nimble @@ -8,4 +8,4 @@ license = "MIT" 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" +Requires = "nim >= 0.10.3, nre >= 0.6.0" -- cgit v1.2.3