Commit | Line | Data |
---|---|---|
41558f01 | 1 | import strutils, rdstdin, tables, times, sequtils, types, printer, reader |
8de9f308 | 2 | |
3 | type MalError* = object of Exception | |
4 | t*: MalType | |
819bd786 | 5 | |
6 | # String functions | |
7 | proc pr_str(xs: varargs[MalType]): MalType = | |
4ce9e165 | 8 | str(xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ")) |
819bd786 | 9 | |
10 | proc do_str(xs: varargs[MalType]): MalType = | |
4ce9e165 | 11 | str(xs.map(proc(x: MalType): string = x.pr_str(false)).join) |
819bd786 | 12 | |
13 | proc prn(xs: varargs[MalType]): MalType = | |
14 | echo xs.map(proc(x: MalType): string = x.pr_str(true)).join(" ") | |
15 | result = nilObj | |
16 | ||
17 | proc println(xs: varargs[MalType]): MalType = | |
c3256515 | 18 | echo xs.map(proc(x: MalType): string = x.pr_str(false)).join(" ") |
819bd786 | 19 | result = nilObj |
20 | ||
3603af96 | 21 | proc read_str(xs: varargs[MalType]): MalType = |
22 | read_str(xs[0].str) | |
23 | ||
8de9f308 | 24 | proc readline(xs: varargs[MalType]): MalType = |
25 | str readLineFromStdin(xs[0].str) | |
26 | ||
3603af96 | 27 | proc slurp(xs: varargs[MalType]): MalType = |
28 | str readFile(xs[0].str) | |
29 | ||
dc7f0b6a | 30 | proc cons(xs: varargs[MalType]): MalType = |
31 | result = list(xs[0]) | |
32 | for x in xs[1].list: result.list.add x | |
33 | ||
34 | proc concat(xs: varargs[MalType]): MalType = | |
35 | result = list() | |
36 | for x in xs: | |
37 | for i in x.list: | |
38 | result.list.add i | |
39 | ||
f5cf5237 | 40 | proc nth(xs: varargs[MalType]): MalType = |
41 | if xs[1].number < xs[0].list.len: return xs[0].list[xs[1].number] | |
42 | else: raise newException(ValueError, "nth: index out of range") | |
43 | ||
44 | proc first(xs: varargs[MalType]): MalType = | |
4ce9e165 | 45 | if xs[0].kind in {List, Vector} and xs[0].list.len > 0: |
46 | xs[0].list[0] | |
f5cf5237 | 47 | else: nilObj |
48 | ||
49 | proc rest(xs: varargs[MalType]): MalType = | |
4ce9e165 | 50 | if xs[0].kind in {List, Vector} and xs[0].list.len > 0: |
3f429bf4 | 51 | list xs[0].list[1 .. ^1] |
f5cf5237 | 52 | else: list() |
53 | ||
8de9f308 | 54 | proc throw(xs: varargs[MalType]): MalType = |
55 | raise (ref MalError)(t: list xs) | |
56 | ||
57 | proc assoc(xs: varargs[MalType]): MalType = | |
4ce9e165 | 58 | result = hash_map() |
2800f318 | 59 | result.hash_map = xs[0].hash_map |
8de9f308 | 60 | for i in countup(1, xs.high, 2): |
61 | result.hash_map[xs[i].str] = xs[i+1] | |
62 | ||
63 | proc dissoc(xs: varargs[MalType]): MalType = | |
4ce9e165 | 64 | result = hash_map() |
2800f318 | 65 | result.hash_map = xs[0].hash_map |
8de9f308 | 66 | for i in 1 .. xs.high: |
67 | if result.hash_map.hasKey(xs[i].str): result.hash_map.del(xs[i].str) | |
68 | ||
69 | proc get(xs: varargs[MalType]): MalType = | |
70 | if xs[0].kind == HashMap: | |
3c3e03c7 | 71 | if xs[1].str in xs[0].hash_map: |
72 | result = xs[0].hash_map[xs[1].str] | |
2800f318 | 73 | if not result.isNil: return |
74 | ||
75 | result = nilObj | |
8de9f308 | 76 | |
77 | proc contains_q(xs: varargs[MalType]): MalType = | |
78 | boolObj xs[0].hash_map.hasKey(xs[1].str) | |
79 | ||
80 | proc keys(xs: varargs[MalType]): MalType = | |
81 | result = list() | |
82 | for key in xs[0].hash_map.keys: | |
83 | result.list.add str(key) | |
84 | ||
85 | proc vals(xs: varargs[MalType]): MalType = | |
86 | result = list() | |
87 | for value in xs[0].hash_map.values: | |
88 | result.list.add value | |
89 | ||
08c894f7 JM |
90 | proc apply(xs: varargs[MalType]): MalType = |
91 | var s = newSeq[MalType]() | |
92 | if xs.len > 2: | |
93 | for j in 1 .. xs.high-1: | |
94 | s.add xs[j] | |
95 | s.add xs[xs.high].list | |
96 | xs[0].getFun()(s) | |
97 | ||
98 | proc map(xs: varargs[MalType]): MalType = | |
99 | result = list() | |
100 | for i in 0 .. xs[1].list.high: | |
101 | result.list.add xs[0].getFun()(xs[1].list[i]) | |
102 | ||
8de9f308 | 103 | proc conj(xs: varargs[MalType]): MalType = |
104 | if xs[0].kind == List: | |
105 | result = list() | |
106 | for i in countdown(xs.high, 1): | |
107 | result.list.add xs[i] | |
108 | result.list.add xs[0].list | |
109 | else: | |
110 | result = vector() | |
111 | result.list.add xs[0].list | |
112 | for i in 1..xs.high: | |
113 | result.list.add xs[i] | |
114 | result.meta = xs[0].meta | |
115 | ||
08c894f7 JM |
116 | proc seq(xs: varargs[MalType]): MalType = |
117 | if xs[0].kind == List: | |
118 | if len(xs[0].list) == 0: return nilObj | |
119 | result = xs[0] | |
120 | elif xs[0].kind == Vector: | |
121 | if len(xs[0].list) == 0: return nilObj | |
122 | result = list() | |
123 | result.list.add xs[0].list | |
124 | elif xs[0].kind == String: | |
125 | if len(xs[0].str) == 0: return nilObj | |
126 | result = list() | |
127 | for i in countup(0, len(xs[0].str) - 1): | |
4dab2092 | 128 | result.list.add(str xs[0].str.substr(i,i)) |
08c894f7 JM |
129 | elif xs[0] == nilObj: |
130 | result = nilObj | |
131 | else: | |
132 | raise newException(ValueError, "seq: called on non-sequence") | |
8de9f308 | 133 | |
134 | proc with_meta(xs: varargs[MalType]): MalType = | |
2800f318 | 135 | new result |
136 | result[] = xs[0][] | |
137 | result.meta = xs[1] | |
8de9f308 | 138 | |
139 | proc meta(xs: varargs[MalType]): MalType = | |
2800f318 | 140 | if not xs[0].meta.isNil: xs[0].meta |
8de9f308 | 141 | else: nilObj |
142 | ||
143 | proc deref(xs: varargs[MalType]): MalType = | |
2800f318 | 144 | xs[0].val |
8de9f308 | 145 | |
146 | proc reset_bang(xs: varargs[MalType]): MalType = | |
2800f318 | 147 | xs[0].val = xs[1] |
148 | result = xs[0].val | |
8de9f308 | 149 | |
150 | proc swap_bang(xs: varargs[MalType]): MalType = | |
2800f318 | 151 | var args = @[xs[0].val] |
8de9f308 | 152 | for i in 2 .. xs.high: |
153 | args.add xs[i] | |
2800f318 | 154 | xs[0].val = xs[1].getFun()(args) |
155 | result = xs[0].val | |
8de9f308 | 156 | |
4ce9e165 | 157 | proc time_ms(xs: varargs[MalType]): MalType = |
158 | number int(epochTime() * 1000) | |
159 | ||
4dab2092 | 160 | template wrapNumberFun(op): untyped = |
8de9f308 | 161 | fun proc(xs: varargs[MalType]): MalType = |
162 | number op(xs[0].number, xs[1].number) | |
819bd786 | 163 | |
4dab2092 | 164 | template wrapBoolFun(op): untyped = |
819bd786 | 165 | fun proc(xs: varargs[MalType]): MalType = |
166 | if op(xs[0].number, xs[1].number): trueObj else: falseObj | |
167 | ||
168 | let ns* = { | |
169 | "+": wrapNumberFun(`+`), | |
170 | "-": wrapNumberFun(`-`), | |
171 | "*": wrapNumberFun(`*`), | |
172 | "/": wrapNumberFun(`div`), | |
173 | ||
174 | "<": wrapBoolFun(`<`), | |
175 | "<=": wrapBoolFun(`<=`), | |
176 | ">": wrapBoolFun(`>`), | |
177 | ">=": wrapBoolFun(`>=`), | |
178 | ||
179 | "list": fun list, | |
180 | "list?": fun list_q, | |
181 | "vector": fun vector, | |
182 | "vector?": fun vector_q, | |
8de9f308 | 183 | "hash-map": fun hash_map, |
184 | "map?": fun hash_map_q, | |
819bd786 | 185 | "empty?": fun empty_q, |
8de9f308 | 186 | "assoc": fun assoc, |
187 | "dissoc": fun dissoc, | |
188 | "get": fun get, | |
189 | "contains?": fun contains_q, | |
190 | "keys": fun keys, | |
191 | "vals": fun vals, | |
192 | ||
819bd786 | 193 | "=": fun equal, |
194 | ||
195 | "pr-str": fun pr_str, | |
196 | "str": fun do_str, | |
197 | "prn": fun prn, | |
198 | "println": fun println, | |
3603af96 | 199 | |
200 | "read-string": fun read_str, | |
8de9f308 | 201 | "readline": fun readline, |
3603af96 | 202 | "slurp": fun slurp, |
8de9f308 | 203 | |
204 | "sequential?": fun seq_q, | |
dc7f0b6a | 205 | "cons": fun cons, |
206 | "concat": fun concat, | |
8de9f308 | 207 | "count": fun count, |
f5cf5237 | 208 | "nth": fun nth, |
209 | "first": fun first, | |
210 | "rest": fun rest, | |
8de9f308 | 211 | "apply": fun apply, |
212 | "map": fun map, | |
213 | ||
08c894f7 JM |
214 | "conj": fun conj, |
215 | "seq": fun seq, | |
216 | ||
8de9f308 | 217 | "throw": fun throw, |
218 | ||
219 | "nil?": fun nil_q, | |
220 | "true?": fun true_q, | |
221 | "false?": fun false_q, | |
08c894f7 | 222 | "string?": fun string_q, |
8de9f308 | 223 | "symbol": fun symbol, |
224 | "symbol?": fun symbol_q, | |
225 | "keyword": fun keyword, | |
226 | "keyword?": fun keyword_q, | |
c1709fad DM |
227 | "number?": fun number_q, |
228 | "fn?": fun fn_q, | |
229 | "macro?": fun macro_q, | |
8de9f308 | 230 | |
231 | "with-meta": fun with_meta, | |
232 | "meta": fun meta, | |
233 | "atom": fun atom, | |
234 | "atom?": fun atom_q, | |
235 | "deref": fun deref, | |
236 | "reset!": fun reset_bang, | |
237 | "swap!": fun swap_bang, | |
4ce9e165 | 238 | |
239 | "time-ms": fun time_ms, | |
819bd786 | 240 | } |