Commit | Line | Data |
---|---|---|
9d42904e JM |
1 | local utils = require('utils') |
2 | local types = require('types') | |
3 | local reader = require('reader') | |
4 | local printer = require('printer') | |
5 | local readline = require('readline') | |
af8d51d1 | 6 | local socket = require('socket') |
9d42904e JM |
7 | |
8 | local Nil, List, _pr_str = types.Nil, types.List, printer._pr_str | |
9 | ||
10 | local M = {} | |
11 | ||
12 | -- string functions | |
13 | ||
14 | function pr_str(...) | |
15 | return table.concat( | |
16 | utils.map(function(e) return _pr_str(e, true) end, arg), " ") | |
17 | end | |
18 | ||
19 | function str(...) | |
20 | return table.concat( | |
21 | utils.map(function(e) return _pr_str(e, false) end, arg), "") | |
22 | end | |
23 | ||
24 | function prn(...) | |
25 | print(table.concat( | |
26 | utils.map(function(e) return _pr_str(e, true) end, arg), " ")) | |
3e0b36dc | 27 | io.flush() |
9d42904e JM |
28 | return Nil |
29 | end | |
30 | ||
31 | function println(...) | |
32 | print(table.concat( | |
33 | utils.map(function(e) return _pr_str(e, false) end, arg), " ")) | |
3e0b36dc | 34 | io.flush() |
9d42904e JM |
35 | return Nil |
36 | end | |
37 | ||
38 | function slurp(file) | |
39 | local lines = {} | |
40 | for line in io.lines(file) do | |
41 | lines[#lines+1] = line | |
42 | end | |
43 | return table.concat(lines, "\n") .. "\n" | |
44 | end | |
45 | ||
46 | function do_readline(prompt) | |
47 | local line = readline.readline(prompt) | |
48 | if line == nil then | |
49 | return Nil | |
50 | else | |
51 | return line | |
52 | end | |
53 | end | |
54 | ||
55 | -- hash map functions | |
56 | ||
57 | function assoc(hm, ...) | |
58 | return types._assoc_BANG(types.copy(hm), unpack(arg)) | |
59 | end | |
60 | ||
61 | function dissoc(hm, ...) | |
62 | return types._dissoc_BANG(types.copy(hm), unpack(arg)) | |
63 | end | |
64 | ||
65 | function get(hm, key) | |
66 | local res = hm[key] | |
67 | if res == nil then return Nil end | |
68 | return res | |
69 | end | |
70 | ||
71 | function keys(hm) | |
72 | local res = {} | |
73 | for k,v in pairs(hm) do | |
74 | res[#res+1] = k | |
75 | end | |
76 | return List:new(res) | |
77 | end | |
78 | ||
79 | function vals(hm) | |
80 | local res = {} | |
81 | for k,v in pairs(hm) do | |
82 | res[#res+1] = v | |
83 | end | |
84 | return List:new(res) | |
85 | end | |
86 | ||
87 | -- sequential functions | |
88 | ||
89 | function cons(a,lst) | |
90 | local new_lst = lst:slice(1) | |
91 | table.insert(new_lst, 1, a) | |
92 | return List:new(new_lst) | |
93 | end | |
94 | ||
95 | function concat(...) | |
96 | local new_lst = {} | |
97 | for i = 1, #arg do | |
98 | for j = 1, #arg[i] do | |
99 | table.insert(new_lst, arg[i][j]) | |
100 | end | |
101 | end | |
102 | return List:new(new_lst) | |
103 | end | |
104 | ||
105 | function nth(seq, idx) | |
106 | if idx+1 <= #seq then | |
107 | return seq[idx+1] | |
108 | else | |
109 | types.throw("nth: index out of range") | |
110 | end | |
111 | end | |
112 | ||
113 | function first(a) | |
114 | if #a == 0 then | |
115 | return Nil | |
116 | else | |
117 | return a[1] | |
118 | end | |
119 | end | |
120 | ||
d46927d0 DM |
121 | function rest(a) |
122 | if a == Nil then | |
123 | return List:new() | |
124 | else | |
125 | return List:new(a:slice(2)) | |
126 | end | |
127 | end | |
128 | ||
9d42904e JM |
129 | function apply(f, ...) |
130 | if types._malfunc_Q(f) then | |
131 | f = f.fn | |
132 | end | |
133 | local args = concat(types.slice(arg, 1, #arg-1), | |
134 | arg[#arg]) | |
135 | return f(unpack(args)) | |
136 | end | |
137 | ||
138 | function map(f, lst) | |
139 | if types._malfunc_Q(f) then | |
140 | f = f.fn | |
141 | end | |
142 | return List:new(utils.map(f, lst)) | |
143 | end | |
144 | ||
145 | -- metadata functions | |
146 | ||
147 | function meta(obj) | |
148 | local m = getmetatable(obj) | |
149 | if m == nil or m.meta == nil then return Nil end | |
150 | return m.meta | |
151 | end | |
152 | ||
153 | function with_meta(obj, meta) | |
154 | local new_obj = types.copy(obj) | |
155 | getmetatable(new_obj).meta = meta | |
156 | return new_obj | |
157 | end | |
158 | ||
159 | -- atom functions | |
160 | ||
161 | function swap_BANG(atm,f,...) | |
162 | if types._malfunc_Q(f) then | |
163 | f = f.fn | |
164 | end | |
165 | local args = List:new(arg) | |
166 | table.insert(args, 1, atm.val) | |
167 | atm.val = f(unpack(args)) | |
168 | return atm.val | |
169 | end | |
170 | ||
1ad8d501 MK |
171 | local function conj(obj, ...) |
172 | local new_obj = types.copy(obj) | |
173 | if types._list_Q(new_obj) then | |
174 | for i, v in ipairs(arg) do | |
175 | table.insert(new_obj, 1, v) | |
176 | end | |
177 | else | |
178 | for i, v in ipairs(arg) do | |
179 | table.insert(new_obj, v) | |
180 | end | |
181 | end | |
182 | return new_obj | |
183 | end | |
184 | ||
5f7cbd77 DM |
185 | local function seq(obj, ...) |
186 | if obj == Nil or #obj == 0 then | |
187 | return Nil | |
188 | elseif types._list_Q(obj) then | |
189 | return obj | |
190 | elseif types._vector_Q(obj) then | |
191 | return List:new(obj) | |
192 | elseif types._string_Q(obj) then | |
193 | local chars = {} | |
194 | for i = 1, #obj do | |
195 | chars[#chars+1] = string.sub(obj,i,i) | |
196 | end | |
197 | return List:new(chars) | |
198 | end | |
199 | return Nil | |
200 | end | |
201 | ||
9d42904e JM |
202 | M.ns = { |
203 | ['='] = types._equal_Q, | |
204 | throw = types.throw, | |
205 | ||
206 | ['nil?'] = function(a) return a==Nil end, | |
207 | ['true?'] = function(a) return a==true end, | |
208 | ['false?'] = function(a) return a==false end, | |
209 | symbol = function(a) return types.Symbol:new(a) end, | |
210 | ['symbol?'] = function(a) return types._symbol_Q(a) end, | |
5f7cbd77 | 211 | ['string?'] = function(a) return types._string_Q(a) and "\177" ~= string.sub(a,1,1) end, |
9d42904e JM |
212 | keyword = function(a) return "\177"..a end, |
213 | ['keyword?'] = function(a) return types._keyword_Q(a) end, | |
214 | ||
215 | ['pr-str'] = pr_str, | |
216 | str = str, | |
217 | prn = prn, | |
218 | println = println, | |
219 | ['read-string'] = reader.read_str, | |
220 | readline = do_readline, | |
221 | slurp = slurp, | |
222 | ||
223 | ['<'] = function(a,b) return a<b end, | |
224 | ['<='] = function(a,b) return a<=b end, | |
225 | ['>'] = function(a,b) return a>b end, | |
226 | ['>='] = function(a,b) return a>=b end, | |
227 | ['+'] = function(a,b) return a+b end, | |
228 | ['-'] = function(a,b) return a-b end, | |
229 | ['*'] = function(a,b) return a*b end, | |
230 | ['/'] = function(a,b) return math.floor(a/b) end, | |
af8d51d1 | 231 | ['time-ms'] = function() return math.floor(socket.gettime() * 1000) end, |
9d42904e JM |
232 | |
233 | list = function(...) return List:new(arg) end, | |
234 | ['list?'] = function(a) return types._list_Q(a) end, | |
235 | vector = function(...) return types.Vector:new(arg) end, | |
236 | ['vector?'] = types._vector_Q, | |
237 | ['hash-map'] = types.hash_map, | |
238 | ['map?'] = types._hash_map_Q, | |
239 | assoc = assoc, | |
240 | dissoc = dissoc, | |
241 | get = get, | |
242 | ['contains?'] = function(a,b) return a[b] ~= nil end, | |
243 | keys = keys, | |
244 | vals = vals, | |
245 | ||
246 | ['sequential?'] = types._sequential_Q, | |
247 | cons = cons, | |
248 | concat = concat, | |
249 | nth = nth, | |
250 | first = first, | |
d46927d0 | 251 | rest = rest, |
9d42904e JM |
252 | ['empty?'] = function(a) return a==Nil or #a == 0 end, |
253 | count = function(a) return #a end, | |
254 | apply = apply, | |
255 | map = map, | |
1ad8d501 | 256 | conj = conj, |
5f7cbd77 | 257 | seq = seq, |
9d42904e JM |
258 | |
259 | meta = meta, | |
260 | ['with-meta'] = with_meta, | |
261 | atom = function(a) return types.Atom:new(a) end, | |
262 | ['atom?'] = types._atom_Q, | |
263 | deref = function(a) return a.val end, | |
264 | ['reset!'] = function(a,b) a.val = b; return b end, | |
265 | ['swap!'] = swap_BANG, | |
266 | } | |
267 | ||
268 | return M | |
269 |