-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlisp_compiler.lua
670 lines (524 loc) · 19.1 KB
/
lisp_compiler.lua
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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
local log = require('log')
local ffi = require('ffi')
local dynasm = require('dynasm')
local dasm = require('dasm')
local utils = require('lisp_utils')
ffi.cdef[[
unsigned int sleep(unsigned int seconds);
enum iterator_type {
/* ITER_EQ must be the first member for request_create */
ITER_EQ = 0, /* key == x ASC order */
ITER_REQ = 1, /* key == x DESC order */
ITER_ALL = 2, /* all tuples */
ITER_LT = 3, /* key < x */
ITER_LE = 4, /* key <= x */
ITER_GE = 5, /* key >= x */
ITER_GT = 6, /* key > x */
ITER_BITS_ALL_SET = 7, /* all bits from x are set in key */
ITER_BITS_ANY_SET = 8, /* at least one x's bit is set */
ITER_BITS_ALL_NOT_SET = 9, /* all bits are not set */
ITER_OVERLAPS = 10, /* key overlaps x */
ITER_NEIGHBOR = 11, /* tuples in distance ascending order from specified point */
iterator_type_MAX
};
uint32_t box_space_id_by_name(const char *name, uint32_t len);
uint32_t box_index_id_by_name(uint32_t space_id, const char *name, uint32_t len);
/* Search loop */
box_iterator_t *box_index_iterator(uint32_t space_id, uint32_t index_id, int type, const char *key, const char *key_end);
int box_iterator_next(box_iterator_t *iterator, box_tuple_t **result);
void box_iterator_free(box_iterator_t *iterator);
typedef struct box_error_t {} box_error_t;
box_error_t * box_error_last(void);
const char *box_tuple_field(const box_tuple_t *tuple, uint32_t field_id);
/* Utility */
int iterator_direction(enum iterator_type type);
ssize_t box_index_len(uint32_t space_id, uint32_t index_id);
void* calloc(size_t count, size_t size);
void free(void*);
]]
local lisp_x64 = dynasm.loadfile('lisp_x64.dasl')()
local function discard_result(state, form, context, old_index)
if old_index ~= context.reg_index then
if old_index + 1 == context.reg_index then
lisp_x64.gen.discard_result(state, context)
else
utils.errorx('stack corrupt at end form position %q',
form.position)
end
end
end
local function prolog(state, ast, context)
lisp_x64.gen.prolog(state)
end
local function epilog(state, ast, context)
lisp_x64.gen.epilog(state)
end
local compile_form = nil
local symbols = {}
symbols['atom'] = function(state, form, context)
-- it's nil
if #form == 1 then
lisp_x64.gen.push(state, 0, context)
end
-- it's number
local num = tonumber64(form[2].atom)
if num ~= nil then
lisp_x64.gen.push(state, num, context)
return
end
-- it's binding
if context.cells[form[2].atom] ~= nil then
lisp_x64.gen.push_result_from_mem(state, context,
context.cells[form[2].atom])
return
end
-- it's symbol string
local result = ffi.new('char[?]', #form[2].atom + 1, form[2].atom)
table.insert(context.parking, result)
lisp_x64.gen.push(state, result, context)
end
function symbols.nop(state, form, context)
lisp_x64.gen.nop(state, context)
end
function symbols.comment(state, form, context)
lisp_x64.gen.nop(state, context)
end
symbols['not'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for not at position', form.position)
end
compile_form(state, form[2], context)
lisp_x64.gen['logical_not'](state, context)
end
symbols['and'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for and at position %q', form.position)
end
local label = context.label
context.label = context.label + 1
dasm.growpc(state, context.label)
for i=2, #form do
local old_index = context.reg_index
compile_form(state, form[i], context)
lisp_x64.gen.test_jz(state, context, label)
if i ~= #form then
discard_result(state, form, context, old_index)
end
end
lisp_x64.gen.label(state, context, label)
end
symbols['or'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for or at position %q', form.position)
end
local label = context.label
context.label = context.label + 1
dasm.growpc(state, context.label)
for i=2, #form do
local old_index = context.reg_index
compile_form(state, form[i], context)
lisp_x64.gen.test_jnz(state, context, label)
if i ~= #form then
discard_result(state, form, context, old_index)
end
end
lisp_x64.gen.label(state, context, label)
end
symbols['if'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for if at position %q', form.position)
end
if #form > 4 then
utils.errorx('Too much arguments for if at position %q', form.position)
end
local label = context.label
context.label = context.label + 1
dasm.growpc(state, context.label)
local label_end = context.label
context.label = context.label + 1
dasm.growpc(state, context.label)
-- if
local old_index = context.reg_index
compile_form(state, form[2], context)
if old_index + 1 ~= context.reg_index then
utils.errorx('Condition have to return result at position %q', form[2].position)
end
lisp_x64.gen.pop_test_jz(state, context, label)
-- then
local old_index = context.reg_index
compile_form(state, form[3], context)
-- in case of non return return 0
if old_index == context.reg_index then
lisp_x64.gen.push(state, 0, context)
end
if #form == 4 then
lisp_x64.gen.jmp(state, context, label_end) -- jump to end
-- `else`
-- decrease compiler context stack level
-- because the only one will be executed
-- `then` or `else` form
context.reg_index = old_index
lisp_x64.gen.label(state, context, label) -- else label
local old_index = context.reg_index
compile_form(state, form[4], context)
if old_index == context.reg_index then
lisp_x64.gen.push(state, 0, context)
end
else
lisp_x64.gen.jmp(state, context, label_end) -- jump to end
context.reg_index = old_index
lisp_x64.gen.label(state, context, label) -- else label
lisp_x64.gen.push(state, 0, context)
end
-- end
lisp_x64.gen.label(state, context, label_end)
end
-- generate numeric comparators
for _, op in ipairs({'=', '<', '<=', '>', '>=', "/="}) do
symbols[op] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for %s at position %q', op, form.position)
end
if #form > 3 then
utils.errorx('Too much arguments for %s at position %q', op, form.position)
end
local old_index = context.reg_index
compile_form(state, form[2], context)
if context.reg_index ~= old_index + 1 then
utils.errorx('No result 1st argument of %s at position %q', op, form.position)
end
old_index = context.reg_index
compile_form(state, form[3], context)
if context.reg_index ~= old_index + 1 then
utils.errorx('No result 2nd argument of %s at position %q', op, form.position)
end
lisp_x64.gen[op](state, context)
end
end
-- do
symbols['do'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for do at position %q', form.position)
end
if #form > 2 then
utils.errorx('Too much arguments for do at position %q', form.position)
end
local label = context.label
context.label = context.label + 1
dasm.growpc(state, context.label)
local label_end = context.label
context.label = context.label + 1
dasm.growpc(state, context.label)
table.insert(context.docontinue, label)
table.insert(context.dobreak, label_end)
table.insert(context.dostack, context.reg_index)
-- begin
lisp_x64.gen.label(state, context, label)
local old_index = context.reg_index
compile_form(state, form[2], context)
discard_result(state, form, context, old_index) -- discard body loop
-- continue
lisp_x64.gen.jmp(state, context, label)
-- end
lisp_x64.gen.label(state, context, label_end)
table.remove(context.dostack)
table.remove(context.dobreak)
table.remove(context.docontinue)
end
-- break
symbols['break'] = function(state, form, context)
if #form > 1 then
utils.errorx('Too much arguments for break at position %q', form.position)
end
if #context.dobreak == 0 then
utils.errorx('No loop for break at position %q', form.position)
end
local label_end = context.dobreak[#context.dobreak]
-- save form level to free_reg
lisp_x64.gen.unwind_results(state, context)
lisp_x64.gen.jmp(state, context, label_end)
end
-- continue
symbols['continue'] = function(state, form, context)
if #form > 1 then
utils.errorx('Too much arguments for continue %q', form.position)
end
if #context.docontinue == 0 then
utils.errorx('No loop for continue at position %q', form.position)
end
local label = context.docontinue[#context.docontinue]
-- save form level to free_reg
lisp_x64.gen.unwind_results(state, context)
lisp_x64.gen.jmp(state, context, label)
end
symbols['bitnot'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for bitnot at position %q', form.position)
end
compile_form(state, form[2], context)
lisp_x64.gen['not'](state, context)
end
for op, gen in pairs({
['bitand']='and',
['bitor']='or',
['bitxor']='xor',
['shl']='shl',
['shr']='shr'}) do
symbols[op] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for %s at position %q', op, form.position)
end
local old_index = context.reg_index
compile_form(state, form[2], context)
if old_index + 1 ~= context.reg_index then
utils.errorx('No 1st argument result at position %q', form.position)
end
old_index = context.reg_index
compile_form(state, form[3], context)
if old_index + 1 ~= context.reg_index then
utils.errorx('No 2nd argument result at position %q', form.position)
end
lisp_x64.gen[gen](state, context)
end
end
symbols['+'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for plus at position %q', form.position)
end
local iter = 2
compile_form(state, form[iter], context)
iter = iter + 1
while iter <= #form do
compile_form(state, form[iter], context)
iter = iter + 1
lisp_x64.gen.add(state, context)
end
end
symbols['-'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for - at position %q', form.position)
end
local iter = 2
compile_form(state, form[iter], context)
iter = iter + 1
if #form == 2 then
lisp_x64.gen.sub(state, context, 1)
return
end
while iter <= #form do
compile_form(state, form[iter], context)
iter = iter + 1
lisp_x64.gen.sub(state, context, 2)
end
end
symbols['*'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for * at position', form.position)
end
local iter = 2
compile_form(state, form[iter], context)
iter = iter + 1
while iter <= #form do
compile_form(state, form[iter], context)
iter = iter + 1
lisp_x64.gen.imul(state, context)
end
end
symbols['/'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for / at position %q', form.position)
end
local iter = 2
compile_form(state, form[iter], context)
iter = iter + 1
while iter <= #form do
compile_form(state, form[iter], context)
iter = iter + 1
lisp_x64.gen.idiv(state, context)
end
end
symbols['elt64'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Not enough arguments for elt64 at position %q', form.position)
end
compile_form(state, form[2], context)
compile_form(state, form[3], context)
lisp_x64.gen.mov64(state, context)
end
symbols['set-elt64'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Not enough arguments for set-elt64 at position %q', form.position)
end
compile_form(state, form[2], context)
compile_form(state, form[3], context)
lisp_x64.gen.mov64_to_mem(state, context)
end
symbols['int3'] = function(state, form, context)
lisp_x64.gen.int3(state)
end
symbols['progn'] = function(state, form, context, start_from)
start_from = start_from or 2
for i=start_from, #form do
local old_index = context.reg_index
compile_form(state, form[i], context)
-- ignore result for non last forms
if i ~= #form then
discard_result(state, form, context, old_index)
end
end
end
symbols['call'] = function(state, form, context)
if #form == 1 then
utils.errorx('Too few arguments for progn at position %q', form.position)
end
if form[2].atom == nil then
utils.errorx('Second arg has to be atom at position %q', form.position)
end
for i = 1, #form-2 do
if context.registers.args[i] == nil then
utils.errorx('Sorry arguments overflow at position %q', form.position)
end
local old_index = context.old_index
compile_form(state, form[i+2], context)
if context.reg_index == old_index then
utils.errorx('Argument form has no result at position %q', form[i+2].position)
end
lisp_x64.gen.pop_result_to(state, context, context.registers.args[i])
end
lisp_x64.gen.align_stack_to16(state, context)
if form[2].atom then
local rc, res = pcall(function() return ffi.C[form[2].atom] end)
if not rc or not res then
utils.errorx('ffi symbol %q not found at position %q',
form[2].atom,
form.position)
end
end
lisp_x64.gen.call(state, form[2].atom)
lisp_x64.gen.align_stack_back_to16(state, context)
lisp_x64.gen.push_result_from(state, context, context.registers.returns[1])
end
local function is_atom(state, form, context)
if type(form) ~= 'table' then
return false
end
return form.atom ~= nil
end
symbols['set'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for set at position %q', form.position)
end
if #form > 3 then
utils.errorx('Too much arguments for set at position %q', form.position)
end
local subform = form[2]
if not is_atom(state, subform, context) then
utils.errorx('2nd argument of set has to be list with atoms at position', form.position)
end
if context.cells[subform.atom] == nil then
utils.errorx('Symbol %q is not defined at position %q', subform.atom,
form.position)
end
local old_index = context.reg_index
compile_form(state, form[3], context)
if context.reg_index > old_index then
lisp_x64.gen.mov_result_to_mem(state, context, context.cells[subform.atom])
else
utils.errorx('No result for assign at position %q', form.position)
end
end
symbols['let'] = function(state, form, context)
if #form == 1 or #form == 2 then
utils.errorx('Too few arguments for let at position %q', form.position)
end
if #form > 3 then
utils.errorx('Too much arguments for let at position %q', form.position)
end
for i=1,#form[2] do
local subform = form[2][i]
if not is_atom(state, subform, context) then
utils.errorx('2nd argument of let has to be list with atoms at position %q', form.position)
end
if context.cells[subform.atom] ~= nil then
utils.errorx('Symbol %q already defined error position %q',
subform.atom, form.position)
end
context.cells[subform.atom] = ffi.new('uint64_t[1]', 0)
end
compile_form(state, form[3], context)
-- free context cells
-- but save memory from gc to runtime
for name, addr in pairs(context.cells) do
table.insert(context.parking, addr)
context.cells[name] = nil
end
end
compile_form = function(state, form, context)
if type(form) ~= 'table' then
utils.errorx('Can not compile form at position %q', form.position)
end
if is_atom(state, form, context) then
local atomform =
setmetatable({{atom='atom', position=form.position}, form}, {position=form.position})
compile_form(state, atomform, context)
return
end
if #form == 0 then
utils.errorx('Empty form at position', form.position)
end
if form[1].atom == nil then
utils.errorx('form first place have to be atom at position %q', form.position)
end
if symbols[form[1].atom] == nil then
utils.errorx('No special from %q at position %q', form[1].atom, form.position)
end
local old_index = context.reg_index
symbols[form[1].atom](state, form, context)
if old_index ~= context.reg_index then
if old_index + 1 ~= context.reg_index then
utils.errorx("Stack corrupt after form at position %q",
form.position)
end
end
end
local asm_lines = {}
local old_put = dasm.put
dasm.put = function (Dst, ...)
asm_lines[Dst] = asm_lines[Dst] or {}
asm_lines[Dst].step = asm_lines[Dst].step or 0
asm_lines[Dst].step = asm_lines[Dst].step + 1
old_put(Dst, ...)
end
local function compile(ast, register_count)
register_count = register_count or 0
local M = {}
--create a dynasm state with the generated action list
local state, globals = dasm.new(lisp_x64.actions)
local context = lisp_x64.gen.new_context_with_registers(register_count)
prolog(state, ast, context)
local old_index = context.reg_index
symbols.progn(state, ast, context, 1)
-- ignore result for non last forms
if context.reg_index > old_index then
lisp_x64.gen.pop_result_to(state, context,
context.registers.returns[1])
else
lisp_x64.gen.clear_rq0(state, context)
end
epilog(state, ast, context)
--check, link and encode the code
local buf, size = state:build()
--ping allocated atoms parking
M.context = context
--pin buf so it doesn't get collected
M.buf = buf
-- DEBUG
local dump = {}
local function capture_dump(line)
table.insert(dump, line)
end
dasm.dump(buf, size, capture_dump)
M.disasm = table.concat(dump)
return M
end
return {compile=compile}