bash, c, go, lua, racket: fix macro result evaluation
[jackhill/mal.git] / lua / core.lua
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')
6 local socket = require('socket')
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), " "))
27 io.flush()
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), " "))
34 io.flush()
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
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
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,
205 ['time-ms'] = function() return math.floor(socket.gettime() * 1000) end,
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,
230 conj = conj,
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