mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
commit
2b0fa48326
5
Makefile
5
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
|
||||
@ -38,6 +38,7 @@ EXCLUDE_TESTS += test^racket^step5 # test completes
|
||||
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^go^stepA test^haskell^stepA \
|
||||
@ -79,6 +80,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 = ,
|
||||
@ -112,6 +114,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 = --mono
|
||||
|
16
README.md
16
README.md
@ -4,7 +4,7 @@
|
||||
|
||||
Mal is a 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
|
||||
@ -191,6 +192,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
|
||||
|
||||
```
|
||||
|
33
nim/Makefile
Normal file
33
nim/Makefile
Normal file
@ -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 $^
|
215
nim/core.nim
Normal file
215
nim/core.nim
Normal file
@ -0,0 +1,215 @@
|
||||
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(" "))
|
||||
|
||||
proc do_str(xs: varargs[MalType]): MalType =
|
||||
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(" ")
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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].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].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 = 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 = 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)
|
||||
|
||||
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[]
|
||||
|
||||
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)
|
||||
|
||||
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,
|
||||
"map?": fun hash_map_q,
|
||||
"empty?": fun empty_q,
|
||||
"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,
|
||||
"str": fun do_str,
|
||||
"prn": fun prn,
|
||||
"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,
|
||||
|
||||
"time-ms": fun time_ms,
|
||||
}
|
25
nim/env.nim
Normal file
25
nim/env.nim
Normal file
@ -0,0 +1,25 @@
|
||||
import tables, types
|
||||
|
||||
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.str == "&":
|
||||
result.data[binds.list[i+1].str] = list(exprs.list[i .. -1])
|
||||
break
|
||||
else:
|
||||
result.data[e.str] = exprs.list[i]
|
||||
|
||||
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]
|
11
nim/mal.nimble
Normal file
11
nim/mal.nimble
Normal file
@ -0,0 +1,11 @@
|
||||
[Package]
|
||||
name = "mal"
|
||||
version = "1.0"
|
||||
author = "Dennis Felsing"
|
||||
description = "Mal code in Nim"
|
||||
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.3, nre >= 0.6.0"
|
2
nim/nim.cfg
Normal file
2
nim/nim.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
d: pcreDynlib
|
||||
deadCodeElim: off
|
27
nim/printer.nim
Normal file
27
nim/printer.nim
Normal file
@ -0,0 +1,27 @@
|
||||
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("\\", "\\\\").replace("\"", "\\\"") & "\""
|
||||
else: result = x
|
||||
|
||||
proc pr_str*(m: MalType, pr = true): string =
|
||||
case m.kind
|
||||
of Nil: result = "nil"
|
||||
of True: result = "true"
|
||||
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: 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(" ") & "]"
|
||||
of HashMap:
|
||||
result = "{"
|
||||
for key, val in m.hash_map.pairs:
|
||||
if result.len > 1: result.add " "
|
||||
result.add key.str_handle & " " & val.pr_str(pr)
|
||||
result.add "}"
|
108
nim/reader.nim
Normal file
108
nim/reader.nim
Normal file
@ -0,0 +1,108 @@
|
||||
import nre, optional_t, strutils, types
|
||||
|
||||
var
|
||||
tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"""
|
||||
intRE = re"-?[0-9]+$"
|
||||
|
||||
type
|
||||
Blank* = object of Exception
|
||||
|
||||
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):
|
||||
if match.captures[0][0] != ';':
|
||||
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
|
||||
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 =
|
||||
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)
|
||||
if r.tokens.len == 0:
|
||||
raise newException(Blank, "Blank line")
|
||||
r.read_form
|
11
nim/step0_repl.nim
Normal file
11
nim/step0_repl.nim
Normal file
@ -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
|
14
nim/step1_read_print.nim
Normal file
14
nim/step1_read_print.nim
Normal file
@ -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()
|
52
nim/step2_eval.nim
Normal file
52
nim/step2_eval.nim
Normal file
@ -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.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:
|
||||
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: 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()
|
70
nim/step3_env.nim
Normal file
70
nim/step3_env.nim
Normal file
@ -0,0 +1,70 @@
|
||||
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.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 =
|
||||
case ast.kind
|
||||
of List:
|
||||
let
|
||||
a0 = ast.list[0]
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
|
||||
case a0.str
|
||||
of "def!":
|
||||
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].str, a1.list[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()
|
104
nim/step4_if_fn_do.nim
Normal file
104
nim/step4_if_fn_do.nim
Normal file
@ -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.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 =
|
||||
case ast.kind
|
||||
of List:
|
||||
let a0 = ast.list[0]
|
||||
case a0.kind
|
||||
of Symbol:
|
||||
case a0.str
|
||||
of "def!":
|
||||
let
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
result = env.set(a1.str, 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].str, 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()
|
115
nim/step5_tco.nim
Normal file
115
nim/step5_tco.nim
Normal file
@ -0,0 +1,115 @@
|
||||
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.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)
|
||||
|
||||
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 "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, 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()
|
125
nim/step6_file.nim
Normal file
125
nim/step6_file.nim
Normal file
@ -0,0 +1,125 @@
|
||||
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(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]
|
||||
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 "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, 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()
|
146
nim/step7_quote.nim
Normal file
146
nim/step7_quote.nim
Normal file
@ -0,0 +1,146 @@
|
||||
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(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]
|
||||
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 "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, 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()
|
169
nim/step8_macros.nim
Normal file
169
nim/step8_macros.nim
Normal file
@ -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 .. <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, 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()
|
190
nim/step9_try.nim
Normal file
190
nim/step9_try.nim
Normal file
@ -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()
|
190
nim/stepA_interop.nim
Normal file
190
nim/stepA_interop.nim
Normal file
@ -0,0 +1,190 @@
|
||||
import rdstdin, tables, sequtils, os, types, reader, printer, env, core
|
||||
|
||||
proc read(str: string): MalType = str.read_str
|
||||
|
||||
proc is_pair(x: MalType): bool =
|
||||
x.kind in {List, Vector} and x.list.len > 0
|
||||
|
||||
proc quasiquote(ast: MalType): MalType =
|
||||
if not ast.is_pair:
|
||||
return list(symbol "quote", ast)
|
||||
elif ast.list[0] == symbol "unquote":
|
||||
return ast.list[1]
|
||||
elif ast.list[0].is_pair and ast.list[0].list[0] == symbol "splice-unquote":
|
||||
return list(symbol "concat", ast.list[0].list[1],
|
||||
quasiquote(list ast.list[1 .. -1]))
|
||||
else:
|
||||
return list(symbol "cons", quasiquote(ast.list[0]), quasiquote(list(ast.list[1 .. -1])))
|
||||
|
||||
proc is_macro_call(ast: MalType, env: Env): bool =
|
||||
ast.kind == List and ast.list.len > 0 and ast.list[0].kind == Symbol and
|
||||
env.find(ast.list[0].str) != nil and env.get(ast.list[0].str).macro_q
|
||||
|
||||
proc macroexpand(ast: MalType, env: Env): MalType =
|
||||
result = ast
|
||||
while result.is_macro_call(env):
|
||||
let mac = env.get(result.list[0].str)
|
||||
result = mac.malfun.fn(result.list[1 .. -1]).macroexpand(env)
|
||||
|
||||
proc eval(ast: MalType, env: Env): MalType
|
||||
|
||||
proc eval_ast(ast: MalType, env: var Env): MalType =
|
||||
case ast.kind
|
||||
of Symbol:
|
||||
result = env.get(ast.str)
|
||||
of List:
|
||||
result = list ast.list.mapIt(MalType, it.eval(env))
|
||||
of Vector:
|
||||
result = vector ast.list.mapIt(MalType, it.eval(env))
|
||||
of HashMap:
|
||||
result = hash_map()
|
||||
for k, v in ast.hash_map.pairs:
|
||||
result.hash_map[k] = v.eval(env)
|
||||
else:
|
||||
result = ast
|
||||
|
||||
proc eval(ast: MalType, env: Env): MalType =
|
||||
var ast = ast
|
||||
var env = env
|
||||
|
||||
template defaultApply =
|
||||
let el = ast.eval_ast(env)
|
||||
let f = el.list[0]
|
||||
case f.kind
|
||||
of MalFun:
|
||||
ast = f.malfun.ast
|
||||
env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. -1]))
|
||||
else:
|
||||
return f.fun(el.list[1 .. -1])
|
||||
|
||||
while true:
|
||||
if ast.kind != List: return ast.eval_ast(env)
|
||||
|
||||
ast = ast.macroexpand(env)
|
||||
if ast.kind != List: return ast
|
||||
if ast.list.len == 0: return ast
|
||||
|
||||
let a0 = ast.list[0]
|
||||
case a0.kind
|
||||
of Symbol:
|
||||
case a0.str
|
||||
of "def!":
|
||||
let
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
res = a2.eval(env)
|
||||
return env.set(a1.str, res)
|
||||
|
||||
of "let*":
|
||||
let
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
var let_env = Env(env)
|
||||
case a1.kind
|
||||
of List, Vector:
|
||||
for i in countup(0, a1.list.high, 2):
|
||||
let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))
|
||||
else: raise newException(ValueError, "Illegal kind in let*")
|
||||
ast = a2
|
||||
env = let_env
|
||||
# Continue loop (TCO)
|
||||
|
||||
of "quote":
|
||||
return ast.list[1]
|
||||
|
||||
of "quasiquote":
|
||||
ast = ast.list[1].quasiquote
|
||||
# Continue loop (TCO)
|
||||
|
||||
of "defmacro!":
|
||||
var fun = ast.list[2].eval(env)
|
||||
fun.malfun.is_macro = true
|
||||
return env.set(ast.list[1].str, fun)
|
||||
|
||||
of "macroexpand":
|
||||
return ast.list[1].macroexpand(env)
|
||||
|
||||
of "try*":
|
||||
let
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
if a2.list[0].str == "catch*":
|
||||
try:
|
||||
return a1.eval(env)
|
||||
except MalError:
|
||||
let exc = (ref MalError) getCurrentException()
|
||||
var catchEnv = initEnv(env, list a2.list[1], exc.t)
|
||||
return a2.list[2].eval(catchEnv)
|
||||
except:
|
||||
let exc = getCurrentExceptionMsg()
|
||||
var catchEnv = initEnv(env, list a2.list[1], list str(exc))
|
||||
return a2.list[2].eval(catchEnv)
|
||||
else:
|
||||
return a1.eval(env)
|
||||
|
||||
of "do":
|
||||
let last = ast.list.high
|
||||
let el = (list ast.list[1 .. <last]).eval_ast(env)
|
||||
ast = ast.list[last]
|
||||
# Continue loop (TCO)
|
||||
|
||||
of "if":
|
||||
let
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
cond = a1.eval(env)
|
||||
|
||||
if cond.kind in {Nil, False}:
|
||||
if ast.list.len > 3: ast = ast.list[3]
|
||||
else: ast = nilObj
|
||||
else: ast = a2
|
||||
|
||||
of "fn*":
|
||||
let
|
||||
a1 = ast.list[1]
|
||||
a2 = ast.list[2]
|
||||
var env2 = env
|
||||
let fn = proc(a: varargs[MalType]): MalType =
|
||||
var newEnv = initEnv(env2, a1, list(a))
|
||||
a2.eval(newEnv)
|
||||
return malfun(fn, a2, a1, env)
|
||||
|
||||
else: defaultApply()
|
||||
|
||||
else: defaultApply()
|
||||
|
||||
proc print(exp: MalType): string = exp.pr_str
|
||||
|
||||
var repl_env = initEnv()
|
||||
|
||||
for k, v in ns.items:
|
||||
repl_env.set(k, v)
|
||||
repl_env.set("eval", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))
|
||||
var ps = commandLineParams()
|
||||
repl_env.set("*ARGV*", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))
|
||||
|
||||
|
||||
# core.nim: defined using nim
|
||||
proc rep(str: string): string {.discardable.} =
|
||||
str.read.eval(repl_env).print
|
||||
|
||||
# core.mal: defined using mal itself
|
||||
rep "(def! not (fn* (a) (if a false true)))"
|
||||
rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"
|
||||
rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"
|
||||
rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
|
||||
rep "(def! *host-language* \"nim\")"
|
||||
|
||||
rep "(println (str \"Mal [\" *host-language* \"]\"))"
|
||||
if paramCount() >= 1:
|
||||
rep "(load-file \"" & paramStr(1) & "\")"
|
||||
quit()
|
||||
|
||||
while true:
|
||||
try:
|
||||
let line = readLineFromStdin("user> ")
|
||||
echo line.rep
|
||||
except Blank: discard
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
echo getCurrentException().getStackTrace()
|
148
nim/types.nim
Normal file
148
nim/types.nim
Normal file
@ -0,0 +1,148 @@
|
||||
import tables, strutils
|
||||
|
||||
type
|
||||
MalTypeKind* = enum Nil, True, False, Number, Symbol, String,
|
||||
List, Vector, HashMap, Fun, MalFun, Atom
|
||||
|
||||
FunType = proc(a: varargs[MalType]): MalType
|
||||
|
||||
MalFunType* = ref object
|
||||
fn*: FunType
|
||||
ast*: MalType
|
||||
params*: MalType
|
||||
env*: Env
|
||||
is_macro*: bool
|
||||
|
||||
MalType* = object
|
||||
case kind*: MalTypeKind
|
||||
of Nil, True, False: nil
|
||||
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*: FunType
|
||||
is_macro*: bool
|
||||
of MalFun: malfun*: MalFunType
|
||||
of Atom: val*: ref MalType
|
||||
|
||||
meta*: ref MalType
|
||||
|
||||
Env* = ref object
|
||||
data*: Table[string, MalType]
|
||||
outer*: Env
|
||||
|
||||
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, str: x)
|
||||
|
||||
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
|
||||
|
||||
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 {.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
|
||||
else: xs[i].str
|
||||
result.hash_map[s] = xs[i+1]
|
||||
|
||||
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: 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)
|
||||
|
||||
proc malfun*(fn: auto, ast, params: MalType,
|
||||
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
|
||||
|
||||
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 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
|
||||
|
||||
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, 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 and
|
||||
x.is_macro == y.is_macro
|
||||
of MalFun: x.malfun == y.malfun
|
||||
of Atom: x.val == y.val
|
||||
|
||||
proc equal*(xs: varargs[MalType]): MalType {.procvar.} =
|
||||
boolObj xs[0] == xs[1]
|
Loading…
Reference in New Issue
Block a user