diff options
| author | def <dennis@felsin9.de> | 2015-02-28 19:52:01 +0100 |
|---|---|---|
| committer | def <dennis@felsin9.de> | 2015-02-28 19:52:01 +0100 |
| commit | 819bd786cb21c393d4f94e682758addc253e1a20 (patch) | |
| tree | 271254e7f4f834654a79ef4254c5d4f97473c373 | |
| parent | 7af2994e50b178b31795532c8742da2ec24da9ad (diff) | |
| download | mal-819bd786cb21c393d4f94e682758addc253e1a20.tar.gz mal-819bd786cb21c393d4f94e682758addc253e1a20.zip | |
Nim: step4
| -rw-r--r-- | nim/core.nim | 51 | ||||
| -rw-r--r-- | nim/env.nim | 11 | ||||
| -rw-r--r-- | nim/printer.nim | 19 | ||||
| -rw-r--r-- | nim/reader.nim | 5 | ||||
| -rw-r--r-- | nim/step2_eval.nim | 2 | ||||
| -rw-r--r-- | nim/step3_env.nim | 7 | ||||
| -rw-r--r-- | nim/step4_if_fn_do.nim | 104 | ||||
| -rw-r--r-- | nim/types.nim | 70 |
8 files changed, 241 insertions, 28 deletions
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 = "#<function>" 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 .. <t.high].replace("\\\"", "\"") + elif t[0] == ':': keyword t[1 .. t.high] + elif t == "nil": nilObj + elif t == "true": trueObj + elif t == "false": falseObj else: symbol t proc read_form(r: var Reader): MalType = diff --git a/nim/step2_eval.nim b/nim/step2_eval.nim index 7418335..566003c 100644 --- a/nim/step2_eval.nim +++ b/nim/step2_eval.nim @@ -13,7 +13,7 @@ proc eval_ast(ast: MalType, env: Table[string, MalType]): MalType = of List: result = list ast.list.mapIt(MalType, it.eval(env)) of Vector: - result = vector ast.vector.mapIt(MalType, it.eval(env)) + result = vector ast.list.mapIt(MalType, it.eval(env)) of HashMap: result = hash_map() for k, v in ast.hash_map.pairs: diff --git a/nim/step3_env.nim b/nim/step3_env.nim index 9da4af4..e118872 100644 --- a/nim/step3_env.nim +++ b/nim/step3_env.nim @@ -11,7 +11,7 @@ proc eval_ast(ast: MalType, env: var Env): MalType = of List: result = list ast.list.mapIt(MalType, it.eval(env)) of Vector: - result = vector ast.vector.mapIt(MalType, it.eval(env)) + result = vector ast.list.mapIt(MalType, it.eval(env)) of HashMap: result = hash_map() for k, v in ast.hash_map.pairs: @@ -34,12 +34,9 @@ proc eval(ast: MalType, env: var Env): MalType = var letEnv: Env letEnv.deepCopy(env) case a1.kind - of List: + of List, Vector: 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: diff --git a/nim/step4_if_fn_do.nim b/nim/step4_if_fn_do.nim new file mode 100644 index 0000000..b235733 --- /dev/null +++ b/nim/step4_if_fn_do.nim @@ -0,0 +1,104 @@ +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 = + case ast.kind + of List: + let a0 = ast.list[0] + case a0.kind + of Symbol: + case a0.symbol + of "def!": + let + a1 = ast.list[1] + a2 = ast.list[2] + result = env.set(a1.symbol, a2.eval(env)) + + of "let*": + let + a1 = ast.list[1] + a2 = ast.list[2] + 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)) + else: discard + result = a2.eval(letEnv) + + of "do": + let el = (list ast.list[1 .. -1]).eval_ast(env) + result = el.list[el.list.high] + + 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: 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] |
