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 | ||
121 | function apply(f, ...) | |
122 | if types._malfunc_Q(f) then | |
123 | f = f.fn | |
124 | end | |
125 | local args = concat(types.slice(arg, 1, #arg-1), | |
126 | arg[#arg]) | |
127 | return f(unpack(args)) | |
128 | end | |
129 | ||
130 | function map(f, lst) | |
131 | if types._malfunc_Q(f) then | |
132 | f = f.fn | |
133 | end | |
134 | return List:new(utils.map(f, lst)) | |
135 | end | |
136 | ||
137 | -- metadata functions | |
138 | ||
139 | function meta(obj) | |
140 | local m = getmetatable(obj) | |
141 | if m == nil or m.meta == nil then return Nil end | |
142 | return m.meta | |
143 | end | |
144 | ||
145 | function with_meta(obj, meta) | |
146 | local new_obj = types.copy(obj) | |
147 | getmetatable(new_obj).meta = meta | |
148 | return new_obj | |
149 | end | |
150 | ||
151 | -- atom functions | |
152 | ||
153 | function swap_BANG(atm,f,...) | |
154 | if types._malfunc_Q(f) then | |
155 | f = f.fn | |
156 | end | |
157 | local args = List:new(arg) | |
158 | table.insert(args, 1, atm.val) | |
159 | atm.val = f(unpack(args)) | |
160 | return atm.val | |
161 | end | |
162 | ||
1ad8d501 MK |
163 | local function conj(obj, ...) |
164 | local new_obj = types.copy(obj) | |
165 | if types._list_Q(new_obj) then | |
166 | for i, v in ipairs(arg) do | |
167 | table.insert(new_obj, 1, v) | |
168 | end | |
169 | else | |
170 | for i, v in ipairs(arg) do | |
171 | table.insert(new_obj, v) | |
172 | end | |
173 | end | |
174 | return new_obj | |
175 | end | |
176 | ||
9d42904e JM |
177 | M.ns = { |
178 | ['='] = types._equal_Q, | |
179 | throw = types.throw, | |
180 | ||
181 | ['nil?'] = function(a) return a==Nil end, | |
182 | ['true?'] = function(a) return a==true end, | |
183 | ['false?'] = function(a) return a==false end, | |
184 | symbol = function(a) return types.Symbol:new(a) end, | |
185 | ['symbol?'] = function(a) return types._symbol_Q(a) end, | |
186 | keyword = function(a) return "\177"..a end, | |
187 | ['keyword?'] = function(a) return types._keyword_Q(a) end, | |
188 | ||
189 | ['pr-str'] = pr_str, | |
190 | str = str, | |
191 | prn = prn, | |
192 | println = println, | |
193 | ['read-string'] = reader.read_str, | |
194 | readline = do_readline, | |
195 | slurp = slurp, | |
196 | ||
197 | ['<'] = function(a,b) return a<b end, | |
198 | ['<='] = function(a,b) return a<=b end, | |
199 | ['>'] = function(a,b) return a>b end, | |
200 | ['>='] = function(a,b) return a>=b end, | |
201 | ['+'] = function(a,b) return a+b end, | |
202 | ['-'] = function(a,b) return a-b end, | |
203 | ['*'] = function(a,b) return a*b end, | |
204 | ['/'] = function(a,b) return math.floor(a/b) end, | |
af8d51d1 | 205 | ['time-ms'] = function() return math.floor(socket.gettime() * 1000) end, |
9d42904e JM |
206 | |
207 | list = function(...) return List:new(arg) end, | |
208 | ['list?'] = function(a) return types._list_Q(a) end, | |
209 | vector = function(...) return types.Vector:new(arg) end, | |
210 | ['vector?'] = types._vector_Q, | |
211 | ['hash-map'] = types.hash_map, | |
212 | ['map?'] = types._hash_map_Q, | |
213 | assoc = assoc, | |
214 | dissoc = dissoc, | |
215 | get = get, | |
216 | ['contains?'] = function(a,b) return a[b] ~= nil end, | |
217 | keys = keys, | |
218 | vals = vals, | |
219 | ||
220 | ['sequential?'] = types._sequential_Q, | |
221 | cons = cons, | |
222 | concat = concat, | |
223 | nth = nth, | |
224 | first = first, | |
225 | rest = function(a) return List:new(a:slice(2)) end, | |
226 | ['empty?'] = function(a) return a==Nil or #a == 0 end, | |
227 | count = function(a) return #a end, | |
228 | apply = apply, | |
229 | map = map, | |
1ad8d501 | 230 | conj = conj, |
9d42904e JM |
231 | |
232 | meta = meta, | |
233 | ['with-meta'] = with_meta, | |
234 | atom = function(a) return types.Atom:new(a) end, | |
235 | ['atom?'] = types._atom_Q, | |
236 | deref = function(a) return a.val end, | |
237 | ['reset!'] = function(a,b) a.val = b; return b end, | |
238 | ['swap!'] = swap_BANG, | |
239 | } | |
240 | ||
241 | return M | |
242 |