aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordef <dennis@felsin9.de>2015-03-01 17:39:15 +0100
committerdef <dennis@felsin9.de>2015-03-01 21:37:07 +0100
commit8de9f3085c2f251c0b38ca28e5a66e9f66f9d968 (patch)
treee2eff996fb521bb656ed6122544d40773d56a1c5
parentf5cf5237eea749230250de07124b25f2947f251e (diff)
downloadmal-8de9f3085c2f251c0b38ca28e5a66e9f66f9d968.tar.gz
mal-8de9f3085c2f251c0b38ca28e5a66e9f66f9d968.zip
Nim: step9
-rw-r--r--nim/core.nim130
-rw-r--r--nim/printer.nim15
-rw-r--r--nim/step9_try.nim190
-rw-r--r--nim/types.nim55
4 files changed, 373 insertions, 17 deletions
diff --git a/nim/core.nim b/nim/core.nim
index 0951f92..ca25802 100644
--- a/nim/core.nim
+++ b/nim/core.nim
@@ -1,4 +1,7 @@
-import strutils, types, printer, reader
+import strutils, rdstdin, tables, algorithm, types, printer, reader
+
+type MalError* = object of Exception
+ t*: MalType
# String functions
proc pr_str(xs: varargs[MalType]): MalType =
@@ -19,6 +22,9 @@ proc println(xs: varargs[MalType]): MalType =
proc read_str(xs: varargs[MalType]): MalType =
read_str(xs[0].str)
+proc readline(xs: varargs[MalType]): MalType =
+ str readLineFromStdin(xs[0].str)
+
proc slurp(xs: varargs[MalType]): MalType =
str readFile(xs[0].str)
@@ -44,8 +50,90 @@ proc rest(xs: varargs[MalType]): MalType =
if xs[0].list.len > 0: list xs[0].list[1 .. -1]
else: list()
+proc throw(xs: varargs[MalType]): MalType =
+ raise (ref MalError)(t: list xs)
+
+proc assoc(xs: varargs[MalType]): MalType =
+ result.deepCopy xs[0]
+ for i in countup(1, xs.high, 2):
+ result.hash_map[xs[i].str] = xs[i+1]
+
+proc dissoc(xs: varargs[MalType]): MalType =
+ result.deepCopy xs[0]
+ for i in 1 .. xs.high:
+ if result.hash_map.hasKey(xs[i].str): result.hash_map.del(xs[i].str)
+
+proc get(xs: varargs[MalType]): MalType =
+ if xs[0].kind == HashMap:
+ xs[0].hash_map[xs[1].str]
+ else:
+ nilObj
+
+proc contains_q(xs: varargs[MalType]): MalType =
+ boolObj xs[0].hash_map.hasKey(xs[1].str)
+
+proc keys(xs: varargs[MalType]): MalType =
+ result = list()
+ for key in xs[0].hash_map.keys:
+ result.list.add str(key)
+
+proc vals(xs: varargs[MalType]): MalType =
+ result = list()
+ for value in xs[0].hash_map.values:
+ result.list.add value
+
+proc conj(xs: varargs[MalType]): MalType =
+ if xs[0].kind == List:
+ result = list()
+ for i in countdown(xs.high, 1):
+ result.list.add xs[i]
+ result.list.add xs[0].list
+ else:
+ result = vector()
+ result.list.add xs[0].list
+ for i in 1..xs.high:
+ result.list.add xs[i]
+ result.meta = xs[0].meta
+
+proc apply(xs: varargs[MalType]): MalType =
+ var s = newSeq[MalType]()
+ if xs.len > 2:
+ for j in 1 .. xs.high-1:
+ s.add xs[j]
+ s.add xs[xs.high].list
+ xs[0].getFun()(s)
+
+proc map(xs: varargs[MalType]): MalType =
+ result = list()
+ for i in 0 .. xs[1].list.high:
+ result.list.add xs[0].getFun()(xs[1].list[i])
+
+proc with_meta(xs: varargs[MalType]): MalType =
+ result = xs[0]
+ new result.meta
+ result.meta[] = xs[1]
+
+proc meta(xs: varargs[MalType]): MalType =
+ if xs[0].meta != nil: xs[0].meta[]
+ else: nilObj
+
+proc deref(xs: varargs[MalType]): MalType =
+ xs[0].val[]
+
+proc reset_bang(xs: varargs[MalType]): MalType =
+ xs[0].val[] = xs[1]
+ result = xs[0].val[]
+
+proc swap_bang(xs: varargs[MalType]): MalType =
+ var args = @[xs[0].val[]]
+ for i in 2 .. xs.high:
+ args.add xs[i]
+ xs[0].val[] = xs[1].getFun()(args)
+ result = xs[0].val[]
+
template wrapNumberFun(op: expr): expr =
- fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number)
+ fun proc(xs: varargs[MalType]): MalType =
+ number op(xs[0].number, xs[1].number)
template wrapBoolFun(op: expr): expr =
fun proc(xs: varargs[MalType]): MalType =
@@ -66,10 +154,16 @@ let ns* = {
"list?": fun list_q,
"vector": fun vector,
"vector?": fun vector_q,
- "hash_map": fun hash_map,
- "hash_map?": fun hash_map_q,
+ "hash-map": fun hash_map,
+ "map?": fun hash_map_q,
"empty?": fun empty_q,
- "count": fun count,
+ "assoc": fun assoc,
+ "dissoc": fun dissoc,
+ "get": fun get,
+ "contains?": fun contains_q,
+ "keys": fun keys,
+ "vals": fun vals,
+
"=": fun equal,
"pr-str": fun pr_str,
@@ -78,11 +172,35 @@ let ns* = {
"println": fun println,
"read-string": fun read_str,
+ "readline": fun readline,
"slurp": fun slurp,
+
+ "sequential?": fun seq_q,
"cons": fun cons,
"concat": fun concat,
-
+ "count": fun count,
"nth": fun nth,
"first": fun first,
"rest": fun rest,
+ "conj": fun conj,
+ "apply": fun apply,
+ "map": fun map,
+
+ "throw": fun throw,
+
+ "nil?": fun nil_q,
+ "true?": fun true_q,
+ "false?": fun false_q,
+ "symbol": fun symbol,
+ "symbol?": fun symbol_q,
+ "keyword": fun keyword,
+ "keyword?": fun keyword_q,
+
+ "with-meta": fun with_meta,
+ "meta": fun meta,
+ "atom": fun atom,
+ "atom?": fun atom_q,
+ "deref": fun deref,
+ "reset!": fun reset_bang,
+ "swap!": fun swap_bang,
}
diff --git a/nim/printer.nim b/nim/printer.nim
index eb26671..b6bf4c7 100644
--- a/nim/printer.nim
+++ b/nim/printer.nim
@@ -1,5 +1,11 @@
import strutils, sequtils, tables, types
+proc str_handle(x: string, pr = true): string =
+ if x.len > 0 and x[0] == '\xff':
+ result = ":" & x[1 .. x.high]
+ elif pr: result = "\"" & x.replace("\"", "\\\"") & "\""
+ else: result = x
+
proc pr_str*(m: MalType, pr = true): string =
case m.kind
of Nil: result = "nil"
@@ -7,12 +13,9 @@ proc pr_str*(m: MalType, pr = true): string =
of False: result = "false"
of Fun: result = "#<function>"
of MalFun: result = "#<malfun>"
+ of Atom: result = "(atom " & m.val[].pr_str & ")"
of Symbol: result = m.str
- of String:
- if m.str.len > 0 and m.str[0] == '\xff':
- result = ":" & m.str[1 .. m.str.high]
- elif pr: result = "\"" & m.str.replace("\"", "\\\"") & "\""
- else: result = m.str
+ of String: result = m.str.str_handle(pr)
of Number: result = $m.number
of List: result = "(" & m.list.mapIt(string, it.pr_str(pr)).join(" ") & ")"
of Vector: result = "[" & m.list.mapIt(string, it.pr_str(pr)).join(" ") & "]"
@@ -20,5 +23,5 @@ proc pr_str*(m: MalType, pr = true): string =
result = "{"
for key, val in m.hash_map.pairs:
if result.len > 1: result.add " "
- result.add key & " " & val.pr_str(pr)
+ result.add key.str_handle & " " & val.pr_str(pr)
result.add "}"
diff --git a/nim/step9_try.nim b/nim/step9_try.nim
new file mode 100644
index 0000000..688817d
--- /dev/null
+++ b/nim/step9_try.nim
@@ -0,0 +1,190 @@
+import rdstdin, tables, sequtils, os, types, reader, printer, env, core
+
+proc read(str: string): MalType = str.read_str
+
+proc is_pair(x: MalType): bool =
+ x.kind in {List, Vector} and x.list.len > 0
+
+proc quasiquote(ast: MalType): MalType =
+ if not ast.is_pair:
+ return list(symbol "quote", ast)
+ elif ast.list[0] == symbol "unquote":
+ return ast.list[1]
+ elif ast.list[0].is_pair and ast.list[0].list[0] == symbol "splice-unquote":
+ return list(symbol "concat", ast.list[0].list[1],
+ quasiquote(list ast.list[1 .. -1]))
+ else:
+ return list(symbol "cons", quasiquote(ast.list[0]), quasiquote(list(ast.list[1 .. -1])))
+
+proc is_macro_call(ast: MalType, env: Env): bool =
+ ast.kind == List and ast.list[0].kind == Symbol and
+ env.find(ast.list[0].str) != nil and env.get(ast.list[0].str).macro_q
+
+proc macroexpand(ast: MalType, env: Env): MalType =
+ result = ast
+ while result.is_macro_call(env):
+ let mac = env.get(result.list[0].str)
+ result = mac.malfun.fn(result.list[1 .. -1]).macroexpand(env)
+
+proc eval(ast: MalType, env: Env): MalType
+
+proc eval_ast(ast: MalType, env: var Env): MalType =
+ case ast.kind
+ of Symbol:
+ result = env.get(ast.str)
+ of List:
+ result = list ast.list.mapIt(MalType, it.eval(env))
+ of Vector:
+ result = vector ast.list.mapIt(MalType, it.eval(env))
+ of HashMap:
+ result = hash_map()
+ for k, v in ast.hash_map.pairs:
+ result.hash_map[k] = v.eval(env)
+ else:
+ result = ast
+
+proc eval(ast: MalType, env: Env): MalType =
+ var ast = ast
+ var env = env
+
+ template defaultApply =
+ let el = ast.eval_ast(env)
+ let f = el.list[0]
+ case f.kind
+ of MalFun:
+ ast = f.malfun.ast
+ env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. -1]))
+ else:
+ return f.fun(el.list[1 .. -1])
+
+ while true:
+ if ast.kind != List: return ast.eval_ast(env)
+
+ ast = ast.macroexpand(env)
+ if ast.kind != List: return ast
+ if ast.list.len == 0: return ast
+
+ let a0 = ast.list[0]
+ case a0.kind
+ of Symbol:
+ case a0.str
+ of "def!":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ res = a2.eval(env)
+ return env.set(a1.str, res)
+
+ of "let*":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ var let_env = Env(env)
+ case a1.kind
+ of List, Vector:
+ for i in countup(0, a1.list.high, 2):
+ let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))
+ else: raise newException(ValueError, "Illegal kind in let*")
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
+
+ of "quote":
+ return ast.list[1]
+
+ of "quasiquote":
+ ast = ast.list[1].quasiquote
+ # Continue loop (TCO)
+
+ of "defmacro!":
+ var fun = ast.list[2].eval(env)
+ fun.malfun.is_macro = true
+ return env.set(ast.list[1].str, fun)
+
+ of "macroexpand":
+ return ast.list[1].macroexpand(env)
+
+ of "try*":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ if a2.list[0].str == "catch*":
+ try:
+ return a1.eval(env)
+ except MalError:
+ let exc = (ref MalError) getCurrentException()
+ var catchEnv = initEnv(env, list a2.list[1], exc.t)
+ return a2.list[2].eval(catchEnv)
+ except:
+ let exc = getCurrentExceptionMsg()
+ var catchEnv = initEnv(env, list a2.list[1], list str(exc))
+ return a2.list[2].eval(catchEnv)
+ else:
+ return a1.eval(env)
+
+ of "do":
+ let last = ast.list.high
+ let el = (list ast.list[1 .. <last]).eval_ast(env)
+ ast = ast.list[last].eval(env)
+ # Continue loop (TCO)
+
+ of "if":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ cond = a1.eval(env)
+
+ if cond.kind in {Nil, False}:
+ if ast.list.len > 3: ast = ast.list[3]
+ else: ast = nilObj
+ else: ast = a2
+
+ of "fn*":
+ let
+ a1 = ast.list[1]
+ a2 = ast.list[2]
+ var env2 = env
+ let fn = proc(a: varargs[MalType]): MalType =
+ var newEnv = initEnv(env2, a1, list(a))
+ a2.eval(newEnv)
+ return malfun(fn, a2, a1, env)
+
+ else:
+ defaultApply()
+
+ else:
+ defaultApply()
+
+proc print(exp: MalType): string = exp.pr_str
+
+var repl_env = initEnv()
+
+for k, v in ns.items:
+ repl_env.set(k, v)
+repl_env.set("eval", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))
+var ps = commandLineParams()
+repl_env.set("*ARGV*", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))
+
+
+# core.nim: defined using nim
+proc rep(str: string): string {.discardable.} =
+ str.read.eval(repl_env).print
+
+# core.mal: defined using mal itself
+rep "(def! not (fn* (a) (if a false true)))"
+rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"
+rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"
+rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
+
+if paramCount() >= 1:
+ rep "(load-file \"" & paramStr(1) & "\")"
+ quit()
+
+while true:
+ try:
+ let line = readLineFromStdin("user> ")
+ echo line.rep
+ except Blank: discard
+ except:
+ echo getCurrentExceptionMsg()
+ echo getCurrentException().getStackTrace()
diff --git a/nim/types.nim b/nim/types.nim
index 9936d50..2a19eba 100644
--- a/nim/types.nim
+++ b/nim/types.nim
@@ -2,10 +2,12 @@ import tables, strutils
type
MalTypeKind* = enum Nil, True, False, Number, Symbol, String,
- List, Vector, HashMap, Fun, MalFun
+ List, Vector, HashMap, Fun, MalFun, Atom
+
+ FunType = proc(a: varargs[MalType]): MalType
MalFunType* = ref object
- fn*: proc(a: varargs[MalType]): MalType
+ fn*: FunType
ast*: MalType
params*: MalType
env*: Env
@@ -19,9 +21,12 @@ type
of List, Vector: list*: seq[MalType]
of HashMap: hash_map*: TableRef[string, MalType]
of Fun:
- fun*: proc(xs: varargs[MalType]): MalType
+ fun*: FunType
is_macro*: bool
of MalFun: malfun*: MalFunType
+ of Atom: val*: ref MalType
+
+ meta*: ref MalType
Env* = ref object
data*: Table[string, MalType]
@@ -40,6 +45,11 @@ proc str*(x: string): MalType {.procvar.} = MalType(kind: String, str: x)
proc keyword*(x: string): MalType = MalType(kind: String, str: "\xff" & x)
+proc atom*(x: MalType): MalType =
+ result = MalType(kind: Atom)
+ new result.val
+ result.val[] = x
+
proc list*(xs: varargs[MalType]): MalType {.procvar.} =
result = MalType(kind: List, list: @[])
for x in xs: result.list.add x
@@ -52,7 +62,7 @@ proc hash_map*(xs: varargs[MalType]): MalType {.procvar.} =
result = MalType(kind: HashMap, hash_map: newTable[string, MalType]())
for i in countup(0, xs.high, 2):
let s = case xs[i].kind
- of String: "\"" & xs[i].str & "\""
+ of String: xs[i].str
else: xs[i].str
result.hash_map[s] = xs[i+1]
@@ -60,6 +70,10 @@ proc macro_q*(x: MalType): bool =
if x.kind == Fun: x.is_macro
else: x.malfun.is_macro
+proc getFun*(x: MalType): FunType =
+ if x.kind == Fun: x.fun
+ else: x.malfun.fn
+
proc fun*(x: proc(xs: varargs[MalType]): MalType, is_macro = false): MalType =
MalType(kind: Fun, fun: x, is_macro: is_macro)
@@ -68,7 +82,7 @@ proc malfun*(fn: auto, ast, params: MalType,
MalType(kind: MalFun, malfun: MalFunType(fn: fn, ast: ast, params: params,
env: env, is_macro: is_macro))
-proc boolObj(b: bool): MalType =
+proc boolObj*(b: bool): MalType =
if b: trueObj else: falseObj
proc list_q*(xs: varargs[MalType]): MalType {.procvar.} =
@@ -77,12 +91,42 @@ proc list_q*(xs: varargs[MalType]): MalType {.procvar.} =
proc vector_q*(xs: varargs[MalType]): MalType {.procvar.} =
boolObj xs[0].kind == Vector
+proc seq_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj xs[0].kind in {List, Vector}
+
proc hash_map_q*(xs: varargs[MalType]): MalType {.procvar.} =
boolObj xs[0].kind == HashMap
proc empty_q*(xs: varargs[MalType]): MalType {.procvar.} =
boolObj xs[0].list.len == 0
+proc nil_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj xs[0].kind == Nil
+
+proc true_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj xs[0].kind == True
+
+proc false_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj xs[0].kind == False
+
+proc symbol*(xs: varargs[MalType]): MalType {.procvar.} =
+ symbol(xs[0].str)
+
+proc symbol_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj xs[0].kind == Symbol
+
+proc keyword*(xs: varargs[MalType]): MalType {.procvar.} =
+ keyword(xs[0].str)
+
+proc keyword_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj(xs[0].kind == String and xs[0].str[0] == '\xff')
+
+proc atom*(xs: varargs[MalType]): MalType {.procvar.} =
+ atom(xs[0])
+
+proc atom_q*(xs: varargs[MalType]): MalType {.procvar.} =
+ boolObj xs[0].kind == Atom
+
proc count*(xs: varargs[MalType]): MalType {.procvar.} =
number if xs[0].kind == Nil: 0 else: xs[0].list.len
@@ -97,6 +141,7 @@ proc `==`*(x, y: MalType): bool =
of HashMap: x.hash_map == y.hash_map
of Fun: x.fun == y.fun
of MalFun: x.malfun == y.malfun
+ of Atom: x.val == y.val
proc equal*(xs: varargs[MalType]): MalType {.procvar.} =
boolObj xs[0] == xs[1]