Repository navigation
Expand file tree
/
Copy pathmain.lua
More file actions
335 lines (328 loc) · 8.08 KB
/
Copy pathmain.lua
File metadata and controls
335 lines (328 loc) · 8.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
-- h+
local lex = require"lexer"
local unpack = (unpack or table.unpack) ---@diagnostic disable-line
local insert, remove = table.insert, table.remove
local abs = math.abs
local write = io.write
local stack = {}
local vars = {} -- {double = lex"2*"}
local store = nil
local depth = 0
local buffer
local function copy(val)
if type(val) == "table" then
local cop = {}
for i, v in ipairs(val) do
cop[i] = v
end
return cop
end
return val
end
local function push(...)
for _, v in ipairs{...} do
insert(stack, v)
end
end
local function pop()
local v = remove(stack)
if v == nil then error'Not enough values on the stack' end
return v
end
local function truthy(v)
if type(v) == 'number' then
return v ~= 0
elseif type(v) == "string" then
return v ~= ""
elseif type(v) == "table" then
return #v > 0
end
return false
end
local function print_stack(value, seen)
if seen == nil then seen = {} end
for i, v in ipairs(value) do
if type(v) == 'table' then
if seen[v] then
write'###'
else
seen[v] = true
write'[ '
print_stack(v, seen)
write' ]'
end
else
write(tostring(v))
end
write(i < #value and ' ' or '')
end
end
local function execute(command)
if depth > 0 then
buffer[#buffer+1] = command
if command == '}' then
depth = depth - 1
if depth == 0 then
buffer[#buffer] = nil
push(buffer)
end
elseif command == '{' then
depth = depth + 1
end
elseif depth == -1 then
push(command)
depth = 0
else
if tonumber(command) then
push(tonumber(command))
elseif command:match"^\\." then -- escape
push(command:sub(2))
elseif command:lower():match"^%l+:$" then -- save var
local a = pop()
local name = command:sub(1, -2):lower()
vars[name] = a
elseif command:match"^%l[%l%u]*$" then -- load var
local name = command:lower()
local var = vars[name]
if var then
push(var)
else
error("Variable not found: "..name)
end
elseif command:match"^%u[%l%u]*$" then -- eval var
local name = command:lower()
local fnc = vars[name]
if fnc then
if type(fnc) == 'table' then
for _, cmd in ipairs(fnc) do execute(cmd) end
elseif type(fnc) == 'string' then
execute(fnc)
else
error("Non executable: "..fnc)
end
else
error("Variable not found: "..name)
end
elseif command:match'^-[.:]+$' then -- planet / bookkeeper
local buff = {}
for char in command:gmatch'.' do
local val = pop()
if char == ':' then insert(buff, 1, val) end
end
push(unpack(buff))
elseif command == '!' then -- eval
local fnc = pop()
if type(fnc) == 'table' then
for _, cmd in ipairs(fnc) do execute(cmd) end
elseif type(fnc) == "string" then
execute(fnc)
else
error("Non executable: "..fnc)
end
elseif command:match'^![.:]+$' then -- eval with planet / bookkeeper
local fnc = pop()
local buff = {}
for char in command:sub(2):gmatch'.' do
local val = pop()
if char == ':' then insert(buff, 1, val) end
end
if type(fnc) == 'table' then
for _, cmd in ipairs(fnc) do execute(cmd) end
elseif type(fnc) == 'string' then
execute(fnc)
else
error("Non executable: "..fnc)
end
push(unpack(buff))
elseif command == '!!' then -- foreach
local func, list = pop(), pop()
local buff = {}
for _, elem in ipairs(list) do
push(elem)
if type(func) == 'table' then
for _, cmd in ipairs(func) do execute(cmd) end
elseif type(func) == 'string' then
execute(func)
else
error("Non executable: "..func)
end
buff[#buff+1] = pop()
end
push(buff)
elseif command == '!/' then -- reduce
local func, list = pop(), pop()
push(remove(list, 1))
for _, elem in ipairs(list) do
push(elem)
if type(func) == 'table' then
for _, cmd in ipairs(func) do execute(cmd) end
elseif type(func) == 'string' then
execute(func)
else
error("Non executable: "..func)
end
end
elseif command == ';' then -- pop
pop()
elseif command == '.' then -- duplicate
local a = pop()
push(a, copy(a))
elseif command == ',' then -- over
local b, a = pop(), pop()
push(a, b, copy(a))
elseif command == '>>' then -- rotate right
local c, b, a = pop(), pop(), pop()
push(c, a, b)
elseif command == '<<' then -- rotate left
local c, b, a = pop(), pop(), pop()
push(b, c, a)
elseif command == '>>>' then -- rotate right with copy
local c, b, a = pop(), pop(), pop()
push(c, a, b, copy(c))
elseif command == '<<<' then -- rotate left with copy
local c, b, a = pop(), pop(), pop()
push(a, b, c, copy(a))
elseif command == '||' then -- absolute value
local a = pop()
if type(a) == 'table' then
push(#a)
else
push(abs(a))
end
elseif command == '<>' then -- range
local a = pop()
local buff = {}
for i = 1, a do buff[#buff+1] = i - 1 end
push(buff)
elseif command == '|' then -- or
local b, a = pop(), pop()
push((truthy(a) or truthy(b)) and 1 or 0)
elseif command == '&' then -- and
local b, a = pop(), pop()
push((truthy(a) and truthy(b)) and 1 or 0)
elseif command == '^' then -- xor
local b, a = pop(), pop()
push((truthy(a) ~= truthy(b)) and 1 or 0)
elseif command == '~' then -- not
local a = pop()
push(truthy(a) and 0 or 1)
elseif command == ':' then -- swap / flip
local b, a = pop(), pop()
push(b, a)
elseif command == '+' then -- add
local b, a = pop(), pop()
if "number" == type(a) and type(a) == type(b) then
push(a + b)
elseif type(a) == "table" then
local buff = {}
for _, v in ipairs(a) do buff[#buff+1] = v end
if type(b) == "table" then
for _, v in ipairs(b) do buff[#buff+1] = v end
push(buff)
else
buff[#buff+1] = b
push(buff)
end
end
elseif command == '-' then -- subtract
local b, a = pop(), pop()
push(a - b)
elseif command == '*' then -- multiply
local b, a = pop(), pop()
push(a * b)
elseif command == '%' then -- divide
local b, a = pop(), pop()
push(a / b)
elseif command == '<' then -- less than
local b, a = pop(), pop()
push(a < b and 1 or 0)
elseif command == '>' then -- greater than
local b, a = pop(), pop()
push(a > b and 1 or 0)
elseif command == '=' then -- equal
local b, a = pop(), pop()
push(a == b and 1 or 0)
elseif command == '?' then -- conditional
local c, b, a = pop(), pop(), pop()
push(truthy(a) and b or c)
elseif command == '@' then -- index
local b, a = pop(), pop()
if type(b) == 'table' then
local buff = {}
for _, v in ipairs(b) do buff[#buff+1] = a[v + 1] end
push(buff)
else
push(a[b + 1])
end
elseif command == '@=' then -- index assign
local c, b, a = pop(), pop(), pop()
a[b + 1] = c
push(a)
elseif command == '[]' then -- pack
local a = pop()
local buff = {}
for _ = 1, a do insert(buff, 1, pop()) end
push(buff)
elseif command == '..' then -- unpack
local a = pop()
for _, v in ipairs(a) do
push(v)
end
elseif command == '{' then -- begin function
depth = depth + 1
buffer = {}
elseif command == '}' then -- end function
error'Unmatched }'
elseif command == '\\' then -- escape
depth = -1
elseif command == '$=' then -- save
store = pop()
elseif command == '$' then -- load
push(store)
elseif command == '#' then -- system functions
local cmd = pop()
if cmd == "output" then
local val = pop()
if type(val) == 'table' then
print_stack(val)
write'\n'
else
print(val)
end
elseif cmd == "input" then
local input = io.read()
push(input or "")
end
else
print("Unknown command:", command)
end
end
end
if arg[1] == "lex" and arg[2] then
print(table.concat(lex(arg[2]), '\n'))
elseif arg[1] then
local file = assert(io.open(arg[1], 'r'))
for _, token in ipairs(lex(file:read'*a')) do
execute(token)
end
if #stack > 0 then
print"--- stack ---"
print_stack(stack)
end
if store then
print"--- store ---"
print_stack(store)
end
file:close()
else
while true do
write" "
local line = io.read()
if line == "" then break end
for _, token in ipairs(lex(line)) do
execute(token)
end
print_stack(stack)
print""
end
end