@port-labs/jq-node-bindings 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +5 -0
- package/.jshintignore +1 -0
- package/.jshintrc +23 -0
- package/binding.gyp +56 -0
- package/configure +26 -0
- package/deps/jq/.gitattributes +2 -0
- package/deps/jq/.travis.yml +53 -0
- package/deps/jq/AUTHORS +73 -0
- package/deps/jq/COPYING +70 -0
- package/deps/jq/ChangeLog +1349 -0
- package/deps/jq/Makefile.am +198 -0
- package/deps/jq/NEWS +88 -0
- package/deps/jq/README.md +64 -0
- package/deps/jq/builtin.c +1684 -0
- package/deps/jq/builtin.h +10 -0
- package/deps/jq/bytecode.c +161 -0
- package/deps/jq/bytecode.h +92 -0
- package/deps/jq/compile-ios.sh +102 -0
- package/deps/jq/compile.c +1210 -0
- package/deps/jq/compile.h +101 -0
- package/deps/jq/config/m4/check-math-func.m4 +4 -0
- package/deps/jq/config/m4/find-func-no-libs.m4 +8 -0
- package/deps/jq/config/m4/find-func-no-libs2.m4 +62 -0
- package/deps/jq/config/m4/find-func.m4 +9 -0
- package/deps/jq/config/m4/misc.m4 +3 -0
- package/deps/jq/configure.ac +221 -0
- package/deps/jq/docs/Gemfile +7 -0
- package/deps/jq/docs/Gemfile.lock +63 -0
- package/deps/jq/docs/README.md +25 -0
- package/deps/jq/docs/Rakefile +145 -0
- package/deps/jq/docs/content/1.tutorial/default.yml +327 -0
- package/deps/jq/docs/content/2.download/default.yml +117 -0
- package/deps/jq/docs/content/3.manual/manual.yml +2878 -0
- package/deps/jq/docs/content/3.manual/v1.3/manual.yml +1270 -0
- package/deps/jq/docs/content/3.manual/v1.4/manual.yml +1672 -0
- package/deps/jq/docs/content/index/index.yml +51 -0
- package/deps/jq/docs/default_manpage.md +22 -0
- package/deps/jq/docs/public/.htaccess +28 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap-responsive.css +1058 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap-responsive.min.css +9 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap.css +5224 -0
- package/deps/jq/docs/public/bootstrap/css/bootstrap.min.css +9 -0
- package/deps/jq/docs/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
- package/deps/jq/docs/public/bootstrap/img/glyphicons-halflings.png +0 -0
- package/deps/jq/docs/public/bootstrap/js/bootstrap.js +2027 -0
- package/deps/jq/docs/public/bootstrap/js/bootstrap.min.js +6 -0
- package/deps/jq/docs/public/css/base.scss +99 -0
- package/deps/jq/docs/public/jq.png +0 -0
- package/deps/jq/docs/public/robots.txt +2 -0
- package/deps/jq/docs/site.yml +18 -0
- package/deps/jq/docs/templates/default.liquid +34 -0
- package/deps/jq/docs/templates/index.liquid +60 -0
- package/deps/jq/docs/templates/manual.liquid +122 -0
- package/deps/jq/docs/templates/shared/_footer.liquid +5 -0
- package/deps/jq/docs/templates/shared/_head.liquid +12 -0
- package/deps/jq/docs/templates/shared/_header.liquid +26 -0
- package/deps/jq/exec_stack.h +112 -0
- package/deps/jq/execute.c +1155 -0
- package/deps/jq/inject_errors.c +112 -0
- package/deps/jq/jq.1.default +39 -0
- package/deps/jq/jq.1.prebuilt +3075 -0
- package/deps/jq/jq.h +60 -0
- package/deps/jq/jq.spec +70 -0
- package/deps/jq/jq_parser.h +9 -0
- package/deps/jq/jq_test.c +346 -0
- package/deps/jq/jv.c +1333 -0
- package/deps/jq/jv.h +240 -0
- package/deps/jq/jv_alloc.c +179 -0
- package/deps/jq/jv_alloc.h +27 -0
- package/deps/jq/jv_aux.c +619 -0
- package/deps/jq/jv_dtoa.c +4275 -0
- package/deps/jq/jv_dtoa.h +22 -0
- package/deps/jq/jv_file.c +49 -0
- package/deps/jq/jv_parse.c +852 -0
- package/deps/jq/jv_print.c +348 -0
- package/deps/jq/jv_unicode.c +96 -0
- package/deps/jq/jv_unicode.h +11 -0
- package/deps/jq/jv_utf8_tables.h +37 -0
- package/deps/jq/lexer.c +2442 -0
- package/deps/jq/lexer.h +362 -0
- package/deps/jq/lexer.l +184 -0
- package/deps/jq/libm.h +160 -0
- package/deps/jq/linker.c +393 -0
- package/deps/jq/linker.h +7 -0
- package/deps/jq/locfile.c +91 -0
- package/deps/jq/locfile.h +29 -0
- package/deps/jq/m4/ax_compare_version.m4 +177 -0
- package/deps/jq/m4/ax_prog_bison_version.m4 +68 -0
- package/deps/jq/main.c +566 -0
- package/deps/jq/opcode_list.h +44 -0
- package/deps/jq/parser.c +3914 -0
- package/deps/jq/parser.h +193 -0
- package/deps/jq/parser.y +923 -0
- package/deps/jq/scripts/crosscompile +42 -0
- package/deps/jq/scripts/gen_utf8_tables.py +32 -0
- package/deps/jq/scripts/version +5 -0
- package/deps/jq/setup.sh +33 -0
- package/deps/jq/tests/jq.test +1235 -0
- package/deps/jq/tests/jqtest +5 -0
- package/deps/jq/tests/mantest +7 -0
- package/deps/jq/tests/modules/.jq +5 -0
- package/deps/jq/tests/modules/a.jq +2 -0
- package/deps/jq/tests/modules/b/b.jq +2 -0
- package/deps/jq/tests/modules/c/c.jq +16 -0
- package/deps/jq/tests/modules/c/d.jq +1 -0
- package/deps/jq/tests/modules/data.json +4 -0
- package/deps/jq/tests/modules/lib/jq/e/e.jq +1 -0
- package/deps/jq/tests/modules/lib/jq/f.jq +1 -0
- package/deps/jq/tests/modules/syntaxerror/syntaxerror.jq +1 -0
- package/deps/jq/tests/modules/test_bind_order.jq +4 -0
- package/deps/jq/tests/modules/test_bind_order0.jq +1 -0
- package/deps/jq/tests/modules/test_bind_order1.jq +2 -0
- package/deps/jq/tests/modules/test_bind_order2.jq +2 -0
- package/deps/jq/tests/onig.supp +21 -0
- package/deps/jq/tests/onig.test +85 -0
- package/deps/jq/tests/onigtest +5 -0
- package/deps/jq/tests/setup +36 -0
- package/deps/jq/tests/shtest +205 -0
- package/deps/jq/tests/torture/input0.json +7 -0
- package/deps/jq/util.c +462 -0
- package/deps/jq/util.h +64 -0
- package/deps/jq.gyp +35 -0
- package/index.d.ts +3 -0
- package/jest.config.js +10 -0
- package/lib/index.js +14 -0
- package/package.json +48 -0
- package/reports/jest-port-api.xml +35 -0
- package/src/binding.cc +177 -0
- package/src/binding.h +13 -0
- package/test/santiy.test.js +122 -0
- package/util/configure.js +27 -0
|
@@ -0,0 +1,1210 @@
|
|
|
1
|
+
#ifndef _GNU_SOURCE
|
|
2
|
+
#define _GNU_SOURCE // for strdup
|
|
3
|
+
#endif
|
|
4
|
+
#include <assert.h>
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
#include <stdlib.h>
|
|
8
|
+
#include "compile.h"
|
|
9
|
+
#include "bytecode.h"
|
|
10
|
+
#include "locfile.h"
|
|
11
|
+
#include "jv_alloc.h"
|
|
12
|
+
#include "linker.h"
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
The intermediate representation for jq filters is as a sequence of
|
|
16
|
+
struct inst, which form a doubly-linked list via the next and prev
|
|
17
|
+
pointers.
|
|
18
|
+
|
|
19
|
+
A "block" represents a sequence of "struct inst", which may be
|
|
20
|
+
empty.
|
|
21
|
+
|
|
22
|
+
Blocks are generated by the parser bottom-up, so may have free
|
|
23
|
+
variables (refer to things not defined). See inst.bound_by and
|
|
24
|
+
inst.symbol.
|
|
25
|
+
*/
|
|
26
|
+
struct inst {
|
|
27
|
+
struct inst* next;
|
|
28
|
+
struct inst* prev;
|
|
29
|
+
|
|
30
|
+
opcode op;
|
|
31
|
+
|
|
32
|
+
struct {
|
|
33
|
+
uint16_t intval;
|
|
34
|
+
struct inst* target;
|
|
35
|
+
jv constant;
|
|
36
|
+
const struct cfunction* cfunc;
|
|
37
|
+
} imm;
|
|
38
|
+
|
|
39
|
+
struct locfile* locfile;
|
|
40
|
+
location source;
|
|
41
|
+
|
|
42
|
+
// Binding
|
|
43
|
+
// An instruction requiring binding (for parameters/variables/functions)
|
|
44
|
+
// is in one of three states:
|
|
45
|
+
// inst->bound_by = NULL - Unbound free variable
|
|
46
|
+
// inst->bound_by = inst - This instruction binds a variable
|
|
47
|
+
// inst->bound_by = other - Uses variable bound by other instruction
|
|
48
|
+
// Unbound instructions (references to other things that may or may not
|
|
49
|
+
// exist) are created by "gen_foo_unbound", and bindings are created by
|
|
50
|
+
// block_bind(definition, body), which binds all instructions in
|
|
51
|
+
// body which are unboudn and refer to "definition" by name.
|
|
52
|
+
struct inst* bound_by;
|
|
53
|
+
char* symbol;
|
|
54
|
+
|
|
55
|
+
int nformals;
|
|
56
|
+
int nactuals;
|
|
57
|
+
|
|
58
|
+
block subfn; // used by CLOSURE_CREATE (body of function)
|
|
59
|
+
block arglist; // used by CLOSURE_CREATE (formals) and CALL_JQ (arguments)
|
|
60
|
+
|
|
61
|
+
// This instruction is compiled as part of which function?
|
|
62
|
+
// (only used during block_compile)
|
|
63
|
+
struct bytecode* compiled;
|
|
64
|
+
|
|
65
|
+
int bytecode_pos; // position just after this insn
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
static inst* inst_new(opcode op) {
|
|
69
|
+
inst* i = jv_mem_alloc(sizeof(inst));
|
|
70
|
+
i->next = i->prev = 0;
|
|
71
|
+
i->op = op;
|
|
72
|
+
i->bytecode_pos = -1;
|
|
73
|
+
i->bound_by = 0;
|
|
74
|
+
i->symbol = 0;
|
|
75
|
+
i->nformals = -1;
|
|
76
|
+
i->nactuals = -1;
|
|
77
|
+
i->subfn = gen_noop();
|
|
78
|
+
i->arglist = gen_noop();
|
|
79
|
+
i->source = UNKNOWN_LOCATION;
|
|
80
|
+
i->locfile = 0;
|
|
81
|
+
return i;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static void inst_free(struct inst* i) {
|
|
85
|
+
jv_mem_free(i->symbol);
|
|
86
|
+
block_free(i->subfn);
|
|
87
|
+
block_free(i->arglist);
|
|
88
|
+
if (i->locfile)
|
|
89
|
+
locfile_free(i->locfile);
|
|
90
|
+
if (opcode_describe(i->op)->flags & OP_HAS_CONSTANT) {
|
|
91
|
+
jv_free(i->imm.constant);
|
|
92
|
+
}
|
|
93
|
+
jv_mem_free(i);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static block inst_block(inst* i) {
|
|
97
|
+
block b = {i,i};
|
|
98
|
+
return b;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
int block_is_single(block b) {
|
|
102
|
+
return b.first && b.first == b.last;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static inst* block_take(block* b) {
|
|
106
|
+
if (b->first == 0) return 0;
|
|
107
|
+
inst* i = b->first;
|
|
108
|
+
if (i->next) {
|
|
109
|
+
i->next->prev = 0;
|
|
110
|
+
b->first = i->next;
|
|
111
|
+
i->next = 0;
|
|
112
|
+
} else {
|
|
113
|
+
b->first = 0;
|
|
114
|
+
b->last = 0;
|
|
115
|
+
}
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
block gen_location(location loc, struct locfile* l, block b) {
|
|
120
|
+
for (inst* i = b.first; i; i = i->next) {
|
|
121
|
+
if (i->source.start == UNKNOWN_LOCATION.start &&
|
|
122
|
+
i->source.end == UNKNOWN_LOCATION.end) {
|
|
123
|
+
i->source = loc;
|
|
124
|
+
i->locfile = locfile_retain(l);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return b;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
block gen_noop() {
|
|
131
|
+
block b = {0,0};
|
|
132
|
+
return b;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
int block_is_noop(block b) {
|
|
136
|
+
return (b.first == 0 && b.last == 0);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
block gen_op_simple(opcode op) {
|
|
140
|
+
assert(opcode_describe(op)->length == 1);
|
|
141
|
+
return inst_block(inst_new(op));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
block gen_const(jv constant) {
|
|
146
|
+
assert(opcode_describe(LOADK)->flags & OP_HAS_CONSTANT);
|
|
147
|
+
inst* i = inst_new(LOADK);
|
|
148
|
+
i->imm.constant = constant;
|
|
149
|
+
return inst_block(i);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
block gen_const_global(jv constant, const char *name) {
|
|
153
|
+
assert((opcode_describe(STORE_GLOBAL)->flags & (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING)) ==
|
|
154
|
+
(OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING));
|
|
155
|
+
inst* i = inst_new(STORE_GLOBAL);
|
|
156
|
+
i->imm.constant = constant;
|
|
157
|
+
i->symbol = strdup(name);
|
|
158
|
+
return inst_block(i);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
int block_is_const(block b) {
|
|
162
|
+
return (block_is_single(b) && b.first->op == LOADK);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
int block_is_const_inf(block b) {
|
|
166
|
+
return (block_is_single(b) && b.first->op == LOADK &&
|
|
167
|
+
jv_get_kind(b.first->imm.constant) == JV_KIND_NUMBER &&
|
|
168
|
+
isinf(jv_number_value(b.first->imm.constant)));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
jv_kind block_const_kind(block b) {
|
|
172
|
+
assert(block_is_const(b));
|
|
173
|
+
return jv_get_kind(b.first->imm.constant);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
jv block_const(block b) {
|
|
177
|
+
assert(block_is_const(b));
|
|
178
|
+
return jv_copy(b.first->imm.constant);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
block gen_op_target(opcode op, block target) {
|
|
182
|
+
assert(opcode_describe(op)->flags & OP_HAS_BRANCH);
|
|
183
|
+
assert(target.last);
|
|
184
|
+
inst* i = inst_new(op);
|
|
185
|
+
i->imm.target = target.last;
|
|
186
|
+
return inst_block(i);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
block gen_op_targetlater(opcode op) {
|
|
190
|
+
assert(opcode_describe(op)->flags & OP_HAS_BRANCH);
|
|
191
|
+
inst* i = inst_new(op);
|
|
192
|
+
i->imm.target = 0;
|
|
193
|
+
return inst_block(i);
|
|
194
|
+
}
|
|
195
|
+
void inst_set_target(block b, block target) {
|
|
196
|
+
assert(block_is_single(b));
|
|
197
|
+
assert(opcode_describe(b.first->op)->flags & OP_HAS_BRANCH);
|
|
198
|
+
assert(target.last);
|
|
199
|
+
b.first->imm.target = target.last;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
block gen_op_unbound(opcode op, const char* name) {
|
|
203
|
+
assert(opcode_describe(op)->flags & OP_HAS_BINDING);
|
|
204
|
+
inst* i = inst_new(op);
|
|
205
|
+
i->symbol = strdup(name);
|
|
206
|
+
return inst_block(i);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
block gen_op_var_fresh(opcode op, const char* name) {
|
|
210
|
+
assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);
|
|
211
|
+
return block_bind(gen_op_unbound(op, name),
|
|
212
|
+
gen_noop(), OP_HAS_VARIABLE);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
block gen_op_bound(opcode op, block binder) {
|
|
216
|
+
assert(block_is_single(binder));
|
|
217
|
+
block b = gen_op_unbound(op, binder.first->symbol);
|
|
218
|
+
b.first->bound_by = binder.first;
|
|
219
|
+
return b;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
static void inst_join(inst* a, inst* b) {
|
|
224
|
+
assert(a && b);
|
|
225
|
+
assert(!a->next);
|
|
226
|
+
assert(!b->prev);
|
|
227
|
+
a->next = b;
|
|
228
|
+
b->prev = a;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void block_append(block* b, block b2) {
|
|
232
|
+
if (b2.first) {
|
|
233
|
+
if (b->last) {
|
|
234
|
+
inst_join(b->last, b2.first);
|
|
235
|
+
} else {
|
|
236
|
+
b->first = b2.first;
|
|
237
|
+
}
|
|
238
|
+
b->last = b2.last;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
block block_join(block a, block b) {
|
|
243
|
+
block c = a;
|
|
244
|
+
block_append(&c, b);
|
|
245
|
+
return c;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
int block_has_only_binders_and_imports(block binders, int bindflags) {
|
|
249
|
+
bindflags |= OP_HAS_BINDING;
|
|
250
|
+
for (inst* curr = binders.first; curr; curr = curr->next) {
|
|
251
|
+
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != DEPS && curr->op != MODULEMETA) {
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static int inst_is_binder(inst *i, int bindflags) {
|
|
259
|
+
return !((opcode_describe(i->op)->flags & bindflags) != bindflags && i->op != MODULEMETA);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
int block_has_only_binders(block binders, int bindflags) {
|
|
263
|
+
bindflags |= OP_HAS_BINDING;
|
|
264
|
+
bindflags &= ~OP_BIND_WILDCARD;
|
|
265
|
+
for (inst* curr = binders.first; curr; curr = curr->next) {
|
|
266
|
+
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != MODULEMETA) {
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return 1;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Count a binder's (function) formal params
|
|
274
|
+
static int block_count_formals(block b) {
|
|
275
|
+
int args = 0;
|
|
276
|
+
if (b.first->op == CLOSURE_CREATE_C)
|
|
277
|
+
return b.first->imm.cfunc->nargs - 1;
|
|
278
|
+
for (inst* i = b.first->arglist.first; i; i = i->next) {
|
|
279
|
+
assert(i->op == CLOSURE_PARAM);
|
|
280
|
+
args++;
|
|
281
|
+
}
|
|
282
|
+
return args;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Count a call site's actual params
|
|
286
|
+
static int block_count_actuals(block b) {
|
|
287
|
+
int args = 0;
|
|
288
|
+
for (inst* i = b.first; i; i = i->next) {
|
|
289
|
+
switch (i->op) {
|
|
290
|
+
default: assert(0 && "Unknown function type"); break;
|
|
291
|
+
case CLOSURE_CREATE:
|
|
292
|
+
case CLOSURE_PARAM:
|
|
293
|
+
case CLOSURE_CREATE_C:
|
|
294
|
+
args++;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return args;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
static int block_count_refs(block binder, block body) {
|
|
302
|
+
int nrefs = 0;
|
|
303
|
+
for (inst* i = body.first; i; i = i->next) {
|
|
304
|
+
if (i != binder.first && i->bound_by == binder.first) {
|
|
305
|
+
nrefs++;
|
|
306
|
+
}
|
|
307
|
+
// counting recurses into closures
|
|
308
|
+
nrefs += block_count_refs(binder, i->subfn);
|
|
309
|
+
// counting recurses into argument list
|
|
310
|
+
nrefs += block_count_refs(binder, i->arglist);
|
|
311
|
+
}
|
|
312
|
+
return nrefs;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static int block_bind_subblock(block binder, block body, int bindflags, int break_distance) {
|
|
316
|
+
assert(block_is_single(binder));
|
|
317
|
+
assert((opcode_describe(binder.first->op)->flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD));
|
|
318
|
+
assert(binder.first->symbol);
|
|
319
|
+
assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first);
|
|
320
|
+
assert(break_distance >= 0);
|
|
321
|
+
|
|
322
|
+
binder.first->bound_by = binder.first;
|
|
323
|
+
if (binder.first->nformals == -1)
|
|
324
|
+
binder.first->nformals = block_count_formals(binder);
|
|
325
|
+
int nrefs = 0;
|
|
326
|
+
for (inst* i = body.first; i; i = i->next) {
|
|
327
|
+
int flags = opcode_describe(i->op)->flags;
|
|
328
|
+
if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by == 0 &&
|
|
329
|
+
(!strcmp(i->symbol, binder.first->symbol) ||
|
|
330
|
+
// Check for break/break2/break3; see parser.y
|
|
331
|
+
((bindflags & OP_BIND_WILDCARD) && i->symbol[0] == '*' &&
|
|
332
|
+
break_distance <= 3 && (i->symbol[1] == '1' + break_distance) &&
|
|
333
|
+
i->symbol[2] == '\0'))) {
|
|
334
|
+
// bind this instruction
|
|
335
|
+
if (i->op == CALL_JQ && i->nactuals == -1)
|
|
336
|
+
i->nactuals = block_count_actuals(i->arglist);
|
|
337
|
+
if (i->nactuals == -1 || i->nactuals == binder.first->nformals) {
|
|
338
|
+
i->bound_by = binder.first;
|
|
339
|
+
nrefs++;
|
|
340
|
+
}
|
|
341
|
+
} else if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by != 0 &&
|
|
342
|
+
!strncmp(binder.first->symbol, "*anonlabel", sizeof("*anonlabel") - 1) &&
|
|
343
|
+
!strncmp(i->symbol, "*anonlabel", sizeof("*anonlabel") - 1)) {
|
|
344
|
+
// Increment the break distance required for this binder to match
|
|
345
|
+
// a break whenever we come across a STOREV of *anonlabel...
|
|
346
|
+
break_distance++;
|
|
347
|
+
}
|
|
348
|
+
// binding recurses into closures
|
|
349
|
+
nrefs += block_bind_subblock(binder, i->subfn, bindflags, break_distance);
|
|
350
|
+
// binding recurses into argument list
|
|
351
|
+
nrefs += block_bind_subblock(binder, i->arglist, bindflags, break_distance);
|
|
352
|
+
}
|
|
353
|
+
return nrefs;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
static int block_bind_each(block binder, block body, int bindflags) {
|
|
357
|
+
assert(block_has_only_binders(binder, bindflags));
|
|
358
|
+
bindflags |= OP_HAS_BINDING;
|
|
359
|
+
int nrefs = 0;
|
|
360
|
+
for (inst* curr = binder.first; curr; curr = curr->next) {
|
|
361
|
+
nrefs += block_bind_subblock(inst_block(curr), body, bindflags, 0);
|
|
362
|
+
}
|
|
363
|
+
return nrefs;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
block block_bind(block binder, block body, int bindflags) {
|
|
367
|
+
block_bind_each(binder, body, bindflags);
|
|
368
|
+
return block_join(binder, body);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
block block_bind_library(block binder, block body, int bindflags, const char *libname) {
|
|
372
|
+
bindflags |= OP_HAS_BINDING;
|
|
373
|
+
int nrefs = 0;
|
|
374
|
+
int matchlen = (libname == NULL) ? 0 : strlen(libname);
|
|
375
|
+
char *matchname = jv_mem_alloc(matchlen+2+1);
|
|
376
|
+
matchname[0] = '\0';
|
|
377
|
+
if (libname != NULL && libname[0] != '\0') {
|
|
378
|
+
strcpy(matchname,libname);
|
|
379
|
+
strcpy(matchname+matchlen, "::");
|
|
380
|
+
matchlen += 2;
|
|
381
|
+
}
|
|
382
|
+
assert(block_has_only_binders(binder, bindflags));
|
|
383
|
+
for (inst *curr = binder.first; curr; curr = curr->next) {
|
|
384
|
+
int bindflags2 = bindflags;
|
|
385
|
+
char* cname = curr->symbol;
|
|
386
|
+
char* tname = jv_mem_alloc(strlen(curr->symbol)+matchlen+1);
|
|
387
|
+
strcpy(tname, matchname);
|
|
388
|
+
strcpy(tname+matchlen, curr->symbol);
|
|
389
|
+
|
|
390
|
+
// Ew
|
|
391
|
+
if ((opcode_describe(curr->op)->flags & (OP_HAS_VARIABLE | OP_HAS_CONSTANT)))
|
|
392
|
+
bindflags2 = OP_HAS_VARIABLE | OP_HAS_BINDING;
|
|
393
|
+
|
|
394
|
+
// This mutation is ugly, even if we undo it
|
|
395
|
+
curr->symbol = tname;
|
|
396
|
+
nrefs += block_bind_subblock(inst_block(curr), body, bindflags2, 0);
|
|
397
|
+
curr->symbol = cname;
|
|
398
|
+
free(tname);
|
|
399
|
+
}
|
|
400
|
+
free(matchname);
|
|
401
|
+
return body; // We don't return a join because we don't want those sticking around...
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Bind binder to body and throw away any defs in binder not referenced
|
|
405
|
+
// (directly or indirectly) from body.
|
|
406
|
+
block block_bind_referenced(block binder, block body, int bindflags) {
|
|
407
|
+
assert(block_has_only_binders(binder, bindflags));
|
|
408
|
+
bindflags |= OP_HAS_BINDING;
|
|
409
|
+
block refd = gen_noop();
|
|
410
|
+
block unrefd = gen_noop();
|
|
411
|
+
int nrefs;
|
|
412
|
+
for (int last_kept = 0, kept = 0; ; ) {
|
|
413
|
+
for (inst* curr; (curr = block_take(&binder));) {
|
|
414
|
+
block b = inst_block(curr);
|
|
415
|
+
nrefs = block_bind_each(b, body, bindflags);
|
|
416
|
+
// Check if this binder is referenced from any of the ones we
|
|
417
|
+
// already know are referenced by body.
|
|
418
|
+
nrefs += block_count_refs(b, refd);
|
|
419
|
+
nrefs += block_count_refs(b, body);
|
|
420
|
+
if (nrefs) {
|
|
421
|
+
refd = BLOCK(refd, b);
|
|
422
|
+
kept++;
|
|
423
|
+
} else {
|
|
424
|
+
unrefd = BLOCK(unrefd, b);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (kept == last_kept)
|
|
428
|
+
break;
|
|
429
|
+
last_kept = kept;
|
|
430
|
+
binder = unrefd;
|
|
431
|
+
unrefd = gen_noop();
|
|
432
|
+
}
|
|
433
|
+
block_free(unrefd);
|
|
434
|
+
return block_join(refd, body);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
block block_drop_unreferenced(block body) {
|
|
438
|
+
inst* curr;
|
|
439
|
+
block refd = gen_noop();
|
|
440
|
+
block unrefd = gen_noop();
|
|
441
|
+
int drop;
|
|
442
|
+
do {
|
|
443
|
+
drop = 0;
|
|
444
|
+
while ((curr = block_take(&body)) && curr->op != TOP) {
|
|
445
|
+
block b = inst_block(curr);
|
|
446
|
+
if (block_count_refs(b,refd) + block_count_refs(b,body) == 0) {
|
|
447
|
+
unrefd = BLOCK(unrefd, b);
|
|
448
|
+
drop++;
|
|
449
|
+
} else {
|
|
450
|
+
refd = BLOCK(refd, b);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (curr && curr->op == TOP) {
|
|
454
|
+
body = BLOCK(inst_block(curr),body);
|
|
455
|
+
}
|
|
456
|
+
body = BLOCK(refd, body);
|
|
457
|
+
refd = gen_noop();
|
|
458
|
+
} while (drop != 0);
|
|
459
|
+
block_free(unrefd);
|
|
460
|
+
return body;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
jv block_take_imports(block* body) {
|
|
464
|
+
jv imports = jv_array();
|
|
465
|
+
|
|
466
|
+
inst* top = NULL;
|
|
467
|
+
if (body->first && body->first->op == TOP) {
|
|
468
|
+
top = block_take(body);
|
|
469
|
+
}
|
|
470
|
+
while (body->first && (body->first->op == MODULEMETA || body->first->op == DEPS)) {
|
|
471
|
+
inst* dep = block_take(body);
|
|
472
|
+
if (dep->op == DEPS) {
|
|
473
|
+
imports = jv_array_append(imports, jv_copy(dep->imm.constant));
|
|
474
|
+
}
|
|
475
|
+
inst_free(dep);
|
|
476
|
+
}
|
|
477
|
+
if (top) {
|
|
478
|
+
*body = block_join(inst_block(top),*body);
|
|
479
|
+
}
|
|
480
|
+
return imports;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
block gen_module(block metadata) {
|
|
484
|
+
inst* i = inst_new(MODULEMETA);
|
|
485
|
+
i->imm.constant = block_const(metadata);
|
|
486
|
+
if (jv_get_kind(i->imm.constant) != JV_KIND_OBJECT)
|
|
487
|
+
i->imm.constant = jv_object_set(jv_object(), jv_string("metadata"), i->imm.constant);
|
|
488
|
+
block_free(metadata);
|
|
489
|
+
return inst_block(i);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
jv block_module_meta(block b) {
|
|
493
|
+
if (b.first != NULL && b.first->op == MODULEMETA)
|
|
494
|
+
return jv_copy(b.first->imm.constant);
|
|
495
|
+
return jv_null();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
block gen_import(const char* name, block metadata, const char* as, int is_data) {
|
|
499
|
+
assert(metadata.first == NULL || block_is_const(metadata));
|
|
500
|
+
inst* i = inst_new(DEPS);
|
|
501
|
+
jv meta;
|
|
502
|
+
if (block_is_const(metadata))
|
|
503
|
+
meta = block_const(metadata);
|
|
504
|
+
else
|
|
505
|
+
meta = jv_object();
|
|
506
|
+
if (as != NULL)
|
|
507
|
+
meta = jv_object_set(meta, jv_string("as"), jv_string(as));
|
|
508
|
+
meta = jv_object_set(meta, jv_string("is_data"), is_data ? jv_true() : jv_false());
|
|
509
|
+
meta = jv_object_set(meta, jv_string("relpath"), jv_string(name));
|
|
510
|
+
i->imm.constant = meta;
|
|
511
|
+
block_free(metadata);
|
|
512
|
+
return inst_block(i);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
block gen_function(const char* name, block formals, block body) {
|
|
516
|
+
inst* i = inst_new(CLOSURE_CREATE);
|
|
517
|
+
for (inst* i = formals.last; i; i = i->prev) {
|
|
518
|
+
if (i->op == CLOSURE_PARAM_REGULAR) {
|
|
519
|
+
i->op = CLOSURE_PARAM;
|
|
520
|
+
body = gen_var_binding(gen_call(i->symbol, gen_noop()), i->symbol, body);
|
|
521
|
+
}
|
|
522
|
+
block_bind_subblock(inst_block(i), body, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);
|
|
523
|
+
}
|
|
524
|
+
i->subfn = body;
|
|
525
|
+
i->symbol = strdup(name);
|
|
526
|
+
i->arglist = formals;
|
|
527
|
+
block b = inst_block(i);
|
|
528
|
+
block_bind_subblock(b, b, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);
|
|
529
|
+
return b;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
block gen_param_regular(const char* name) {
|
|
533
|
+
return gen_op_unbound(CLOSURE_PARAM_REGULAR, name);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
block gen_param(const char* name) {
|
|
537
|
+
return gen_op_unbound(CLOSURE_PARAM, name);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
block gen_lambda(block body) {
|
|
541
|
+
return gen_function("@lambda", gen_noop(), body);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
block gen_call(const char* name, block args) {
|
|
545
|
+
block b = gen_op_unbound(CALL_JQ, name);
|
|
546
|
+
b.first->arglist = args;
|
|
547
|
+
return b;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
block gen_subexp(block a) {
|
|
553
|
+
return BLOCK(gen_op_simple(SUBEXP_BEGIN), a, gen_op_simple(SUBEXP_END));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
block gen_both(block a, block b) {
|
|
557
|
+
block jump = gen_op_targetlater(JUMP);
|
|
558
|
+
block fork = gen_op_target(FORK, jump);
|
|
559
|
+
block c = BLOCK(fork, a, jump, b);
|
|
560
|
+
inst_set_target(jump, c);
|
|
561
|
+
return c;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
block gen_const_object(block expr) {
|
|
565
|
+
int is_const = 1;
|
|
566
|
+
jv o = jv_object();
|
|
567
|
+
jv k = jv_null();
|
|
568
|
+
jv v = jv_null();
|
|
569
|
+
for (inst *i = expr.first; i; i = i->next) {
|
|
570
|
+
if (i->op != SUBEXP_BEGIN ||
|
|
571
|
+
i->next == NULL ||
|
|
572
|
+
i->next->op != LOADK ||
|
|
573
|
+
i->next->next == NULL ||
|
|
574
|
+
i->next->next->op != SUBEXP_END) {
|
|
575
|
+
is_const = 0;
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
k = jv_copy(i->next->imm.constant);
|
|
579
|
+
i = i->next->next->next;
|
|
580
|
+
if (i == NULL ||
|
|
581
|
+
i->op != SUBEXP_BEGIN ||
|
|
582
|
+
i->next == NULL ||
|
|
583
|
+
i->next->op != LOADK ||
|
|
584
|
+
i->next->next == NULL ||
|
|
585
|
+
i->next->next->op != SUBEXP_END) {
|
|
586
|
+
is_const = 0;
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
v = jv_copy(i->next->imm.constant);
|
|
590
|
+
i = i->next->next->next;
|
|
591
|
+
if (i == NULL || i->op != INSERT) {
|
|
592
|
+
is_const = 0;
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
o = jv_object_set(o, k, v);
|
|
596
|
+
k = jv_null();
|
|
597
|
+
v = jv_null();
|
|
598
|
+
}
|
|
599
|
+
if (!is_const) {
|
|
600
|
+
jv_free(o);
|
|
601
|
+
jv_free(k);
|
|
602
|
+
jv_free(v);
|
|
603
|
+
block b = {0,0};
|
|
604
|
+
return b;
|
|
605
|
+
}
|
|
606
|
+
block_free(expr);
|
|
607
|
+
return gen_const(o);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
static block gen_const_array(block expr) {
|
|
611
|
+
/*
|
|
612
|
+
* An expr of all constant elements looks like this:
|
|
613
|
+
*
|
|
614
|
+
* 0009 FORK 0027
|
|
615
|
+
* 0011 FORK 0023
|
|
616
|
+
* 0013 FORK 0019
|
|
617
|
+
* 0015 LOADK 1
|
|
618
|
+
* 0017 JUMP 0021
|
|
619
|
+
* 0019 LOADK 2
|
|
620
|
+
* 0021 JUMP 0025
|
|
621
|
+
* 0023 LOADK 3
|
|
622
|
+
* 0025 JUMP 0029
|
|
623
|
+
* 0027 LOADK 4
|
|
624
|
+
*
|
|
625
|
+
* That's: N-1 commas for N elements, N LOADKs, and a JUMP between
|
|
626
|
+
* every LOADK. The sequence ends in a LOADK. Any deviation and it's
|
|
627
|
+
* not a list of constants.
|
|
628
|
+
*
|
|
629
|
+
* Here we check for this pattern almost exactly. We don't check that
|
|
630
|
+
* the targets of the FORK and JUMP instructions are in the right
|
|
631
|
+
* sequence.
|
|
632
|
+
*/
|
|
633
|
+
int all_const = 1;
|
|
634
|
+
int commas = 0;
|
|
635
|
+
int normal = 1;
|
|
636
|
+
jv a = jv_array();
|
|
637
|
+
for (inst *i = expr.first; i; i = i->next) {
|
|
638
|
+
if (i->op == FORK) {
|
|
639
|
+
commas++;
|
|
640
|
+
if (i->imm.target == NULL || i->imm.target->op != JUMP ||
|
|
641
|
+
jv_array_length(jv_copy(a)) > 0) {
|
|
642
|
+
normal = 0;
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
} else if (all_const && i->op == LOADK) {
|
|
646
|
+
if (i->next != NULL && i->next->op != JUMP) {
|
|
647
|
+
normal = 0;
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
a = jv_array_append(a, jv_copy(i->imm.constant));
|
|
651
|
+
} else if (i->op != JUMP || i->imm.target == NULL ||
|
|
652
|
+
i->imm.target->op != LOADK) {
|
|
653
|
+
all_const = 0;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (all_const && normal &&
|
|
658
|
+
(expr.last == NULL || expr.last->op == LOADK) &&
|
|
659
|
+
jv_array_length(jv_copy(a)) == commas + 1) {
|
|
660
|
+
block_free(expr);
|
|
661
|
+
return gen_const(a);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
jv_free(a);
|
|
665
|
+
block b = {0,0};
|
|
666
|
+
return b;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
block gen_collect(block expr) {
|
|
670
|
+
block const_array = gen_const_array(expr);
|
|
671
|
+
if (const_array.first != NULL)
|
|
672
|
+
return const_array;
|
|
673
|
+
|
|
674
|
+
block array_var = gen_op_var_fresh(STOREV, "collect");
|
|
675
|
+
block c = BLOCK(gen_op_simple(DUP), gen_const(jv_array()), array_var);
|
|
676
|
+
|
|
677
|
+
block tail = BLOCK(gen_op_bound(APPEND, array_var),
|
|
678
|
+
gen_op_simple(BACKTRACK));
|
|
679
|
+
|
|
680
|
+
return BLOCK(c,
|
|
681
|
+
gen_op_target(FORK, tail),
|
|
682
|
+
expr,
|
|
683
|
+
tail,
|
|
684
|
+
gen_op_bound(LOADVN, array_var));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
static block bind_matcher(block matcher, block body) {
|
|
688
|
+
// cannot call block_bind(matcher, body) because that requires
|
|
689
|
+
// block_has_only_binders(matcher), which is not true here as matchers
|
|
690
|
+
// may also contain code to extract the correct elements
|
|
691
|
+
for (inst* i = matcher.first; i; i = i->next) {
|
|
692
|
+
if (i->op == STOREV && !i->bound_by)
|
|
693
|
+
block_bind_subblock(inst_block(i), body, OP_HAS_VARIABLE, 0);
|
|
694
|
+
}
|
|
695
|
+
return BLOCK(matcher, body);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
block gen_reduce(block source, block matcher, block init, block body) {
|
|
699
|
+
block res_var = gen_op_var_fresh(STOREV, "reduce");
|
|
700
|
+
block loop = BLOCK(gen_op_simple(DUPN),
|
|
701
|
+
source,
|
|
702
|
+
bind_matcher(matcher,
|
|
703
|
+
BLOCK(gen_op_bound(LOADVN, res_var),
|
|
704
|
+
body,
|
|
705
|
+
gen_op_bound(STOREV, res_var))),
|
|
706
|
+
gen_op_simple(BACKTRACK));
|
|
707
|
+
return BLOCK(gen_op_simple(DUP),
|
|
708
|
+
init,
|
|
709
|
+
res_var,
|
|
710
|
+
gen_op_target(FORK, loop),
|
|
711
|
+
loop,
|
|
712
|
+
gen_op_bound(LOADVN, res_var));
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
block gen_foreach(block source, block matcher, block init, block update, block extract) {
|
|
716
|
+
block output = gen_op_targetlater(JUMP);
|
|
717
|
+
block state_var = gen_op_var_fresh(STOREV, "foreach");
|
|
718
|
+
block loop = BLOCK(gen_op_simple(DUPN),
|
|
719
|
+
// get a value from the source expression:
|
|
720
|
+
source,
|
|
721
|
+
// destructure the value into variable(s) for all the code
|
|
722
|
+
// in the body to see
|
|
723
|
+
bind_matcher(matcher,
|
|
724
|
+
// load the loop state variable
|
|
725
|
+
BLOCK(gen_op_bound(LOADVN, state_var),
|
|
726
|
+
// generate updated state
|
|
727
|
+
update,
|
|
728
|
+
// save the updated state for value extraction
|
|
729
|
+
gen_op_simple(DUP),
|
|
730
|
+
// save new state
|
|
731
|
+
gen_op_bound(STOREV, state_var),
|
|
732
|
+
// extract an output...
|
|
733
|
+
extract,
|
|
734
|
+
// ...and output it by jumping
|
|
735
|
+
// past the BACKTRACK that comes
|
|
736
|
+
// right after the loop body,
|
|
737
|
+
// which in turn is there
|
|
738
|
+
// because...
|
|
739
|
+
//
|
|
740
|
+
// (Incidentally, extract can also
|
|
741
|
+
// backtrack, e.g., if it calls
|
|
742
|
+
// empty, in which case we don't
|
|
743
|
+
// get here.)
|
|
744
|
+
output)));
|
|
745
|
+
block foreach = BLOCK(gen_op_simple(DUP),
|
|
746
|
+
init,
|
|
747
|
+
state_var,
|
|
748
|
+
gen_op_target(FORK, loop),
|
|
749
|
+
loop,
|
|
750
|
+
// ...at this point `foreach`'s original input
|
|
751
|
+
// will be on top of the stack, and we don't
|
|
752
|
+
// want to output it, so we backtrack.
|
|
753
|
+
gen_op_simple(BACKTRACK));
|
|
754
|
+
inst_set_target(output, foreach); // make that JUMP go bast the BACKTRACK at the end of the loop
|
|
755
|
+
return foreach;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
block gen_definedor(block a, block b) {
|
|
759
|
+
// var found := false
|
|
760
|
+
block found_var = gen_op_var_fresh(STOREV, "found");
|
|
761
|
+
block init = BLOCK(gen_op_simple(DUP), gen_const(jv_false()), found_var);
|
|
762
|
+
|
|
763
|
+
// if found, backtrack. Otherwise execute b
|
|
764
|
+
block backtrack = gen_op_simple(BACKTRACK);
|
|
765
|
+
block tail = BLOCK(gen_op_simple(DUP),
|
|
766
|
+
gen_op_bound(LOADV, found_var),
|
|
767
|
+
gen_op_target(JUMP_F, backtrack),
|
|
768
|
+
backtrack,
|
|
769
|
+
gen_op_simple(POP),
|
|
770
|
+
b);
|
|
771
|
+
|
|
772
|
+
// try again
|
|
773
|
+
block if_notfound = gen_op_simple(BACKTRACK);
|
|
774
|
+
|
|
775
|
+
// found := true, produce result
|
|
776
|
+
block if_found = BLOCK(gen_op_simple(DUP),
|
|
777
|
+
gen_const(jv_true()),
|
|
778
|
+
gen_op_bound(STOREV, found_var),
|
|
779
|
+
gen_op_target(JUMP, tail));
|
|
780
|
+
|
|
781
|
+
return BLOCK(init,
|
|
782
|
+
gen_op_target(FORK, if_notfound),
|
|
783
|
+
a,
|
|
784
|
+
gen_op_target(JUMP_F, if_found),
|
|
785
|
+
if_found,
|
|
786
|
+
if_notfound,
|
|
787
|
+
tail);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
int block_has_main(block top) {
|
|
791
|
+
for (inst *c = top.first; c; c = c->next) {
|
|
792
|
+
if (c->op == TOP)
|
|
793
|
+
return 1;
|
|
794
|
+
}
|
|
795
|
+
return 0;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
int block_is_funcdef(block b) {
|
|
799
|
+
if (b.first != NULL && b.first->op == CLOSURE_CREATE)
|
|
800
|
+
return 1;
|
|
801
|
+
return 0;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
block gen_condbranch(block iftrue, block iffalse) {
|
|
805
|
+
iftrue = BLOCK(iftrue, gen_op_target(JUMP, iffalse));
|
|
806
|
+
return BLOCK(gen_op_target(JUMP_F, iftrue), iftrue, iffalse);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
block gen_and(block a, block b) {
|
|
810
|
+
// a and b = if a then (if b then true else false) else false
|
|
811
|
+
return BLOCK(gen_op_simple(DUP), a,
|
|
812
|
+
gen_condbranch(BLOCK(gen_op_simple(POP),
|
|
813
|
+
b,
|
|
814
|
+
gen_condbranch(gen_const(jv_true()),
|
|
815
|
+
gen_const(jv_false()))),
|
|
816
|
+
BLOCK(gen_op_simple(POP), gen_const(jv_false()))));
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
block gen_or(block a, block b) {
|
|
820
|
+
// a or b = if a then true else (if b then true else false)
|
|
821
|
+
return BLOCK(gen_op_simple(DUP), a,
|
|
822
|
+
gen_condbranch(BLOCK(gen_op_simple(POP), gen_const(jv_true())),
|
|
823
|
+
BLOCK(gen_op_simple(POP),
|
|
824
|
+
b,
|
|
825
|
+
gen_condbranch(gen_const(jv_true()),
|
|
826
|
+
gen_const(jv_false())))));
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
block gen_var_binding(block var, const char* name, block body) {
|
|
830
|
+
return gen_destructure(var, gen_op_unbound(STOREV, name), body);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
block gen_array_matcher(block left, block curr) {
|
|
834
|
+
int index;
|
|
835
|
+
if (block_is_noop(left))
|
|
836
|
+
index = 0;
|
|
837
|
+
else {
|
|
838
|
+
// `left` was returned by this function, so the third inst is the
|
|
839
|
+
// constant containing the previously used index
|
|
840
|
+
assert(left.first->op == DUP);
|
|
841
|
+
assert(left.first->next->op == SUBEXP_BEGIN);
|
|
842
|
+
assert(left.first->next->next->op == LOADK);
|
|
843
|
+
index = 1 + (int) jv_number_value(left.first->next->next->imm.constant);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// `left` goes at the end so that the const index is in a predictable place
|
|
847
|
+
return BLOCK(gen_op_simple(DUP), gen_subexp(gen_const(jv_number(index))),
|
|
848
|
+
gen_op_simple(INDEX), curr, left);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
block gen_object_matcher(block name, block curr) {
|
|
852
|
+
return BLOCK(gen_op_simple(DUP), gen_subexp(name), gen_op_simple(INDEX),
|
|
853
|
+
curr);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
block gen_destructure(block var, block matcher, block body) {
|
|
857
|
+
// var bindings can be added after coding the program; leave the TOP first.
|
|
858
|
+
block top = gen_noop();
|
|
859
|
+
if (body.first && body.first->op == TOP)
|
|
860
|
+
top = inst_block(block_take(&body));
|
|
861
|
+
|
|
862
|
+
return BLOCK(top, gen_op_simple(DUP), var, bind_matcher(matcher, body));
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Like gen_var_binding(), but bind `break`'s wildcard unbound variable
|
|
866
|
+
static block gen_wildvar_binding(block var, const char* name, block body) {
|
|
867
|
+
return BLOCK(gen_op_simple(DUP), var,
|
|
868
|
+
block_bind(gen_op_unbound(STOREV, name),
|
|
869
|
+
body, OP_HAS_VARIABLE | OP_BIND_WILDCARD));
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
block gen_cond(block cond, block iftrue, block iffalse) {
|
|
873
|
+
return BLOCK(gen_op_simple(DUP), cond,
|
|
874
|
+
gen_condbranch(BLOCK(gen_op_simple(POP), iftrue),
|
|
875
|
+
BLOCK(gen_op_simple(POP), iffalse)));
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
block gen_try_handler(block handler) {
|
|
879
|
+
// Quite a pain just to hide jq's internal errors.
|
|
880
|
+
return gen_cond(// `if type=="object" and .__jq
|
|
881
|
+
gen_and(gen_call("_equal",
|
|
882
|
+
BLOCK(gen_lambda(gen_const(jv_string("object"))),
|
|
883
|
+
gen_lambda(gen_noop()))),
|
|
884
|
+
BLOCK(gen_subexp(gen_const(jv_string("__jq"))),
|
|
885
|
+
gen_noop(),
|
|
886
|
+
gen_op_simple(INDEX))),
|
|
887
|
+
// `then error`
|
|
888
|
+
gen_call("error", gen_noop()),
|
|
889
|
+
// `else HANDLER end`
|
|
890
|
+
handler);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
block gen_try(block exp, block handler) {
|
|
894
|
+
/*
|
|
895
|
+
* Produce something like:
|
|
896
|
+
* FORK_OPT <address of handler>
|
|
897
|
+
* <exp>
|
|
898
|
+
* JUMP <end of handler>
|
|
899
|
+
* <handler>
|
|
900
|
+
*
|
|
901
|
+
* If this is not an internal try/catch, then catch and re-raise
|
|
902
|
+
* internal errors to prevent them from leaking.
|
|
903
|
+
*
|
|
904
|
+
* The handler will only execute if we backtrack to the FORK_OPT with
|
|
905
|
+
* an error (exception). If <exp> produces no value then FORK_OPT
|
|
906
|
+
* will backtrack (propagate the `empty`, as it were. If <exp>
|
|
907
|
+
* produces a value then we'll execute whatever bytecode follows this
|
|
908
|
+
* sequence.
|
|
909
|
+
*/
|
|
910
|
+
if (!handler.first && !handler.last)
|
|
911
|
+
// A hack to deal with `.` as the handler; we could use a real NOOP here
|
|
912
|
+
handler = BLOCK(gen_op_simple(DUP), gen_op_simple(POP), handler);
|
|
913
|
+
exp = BLOCK(exp, gen_op_target(JUMP, handler));
|
|
914
|
+
return BLOCK(gen_op_target(FORK_OPT, exp), exp, handler);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
block gen_label(const char *label, block exp) {
|
|
918
|
+
block cond = gen_call("_equal",
|
|
919
|
+
BLOCK(gen_lambda(gen_noop()),
|
|
920
|
+
gen_lambda(gen_op_unbound(LOADV, label))));
|
|
921
|
+
return gen_wildvar_binding(gen_op_simple(GENLABEL), label,
|
|
922
|
+
BLOCK(gen_op_simple(POP),
|
|
923
|
+
// try exp catch if . == $label
|
|
924
|
+
// then empty
|
|
925
|
+
// else error end
|
|
926
|
+
//
|
|
927
|
+
// Can't use gen_binop(), as that's firmly
|
|
928
|
+
// stuck in parser.y as it refers to things
|
|
929
|
+
// like EQ.
|
|
930
|
+
gen_try(exp,
|
|
931
|
+
gen_cond(cond,
|
|
932
|
+
gen_op_simple(BACKTRACK),
|
|
933
|
+
gen_call("error", gen_noop())))));
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
block gen_cbinding(const struct cfunction* cfunctions, int ncfunctions, block code) {
|
|
937
|
+
for (int cfunc=0; cfunc<ncfunctions; cfunc++) {
|
|
938
|
+
inst* i = inst_new(CLOSURE_CREATE_C);
|
|
939
|
+
i->imm.cfunc = &cfunctions[cfunc];
|
|
940
|
+
i->symbol = strdup(i->imm.cfunc->name);
|
|
941
|
+
code = block_bind(inst_block(i), code, OP_IS_CALL_PSEUDO);
|
|
942
|
+
}
|
|
943
|
+
return code;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
static uint16_t nesting_level(struct bytecode* bc, inst* target) {
|
|
947
|
+
uint16_t level = 0;
|
|
948
|
+
assert(bc && target && target->compiled);
|
|
949
|
+
while (bc && target->compiled != bc) {
|
|
950
|
+
level++;
|
|
951
|
+
bc = bc->parent;
|
|
952
|
+
}
|
|
953
|
+
assert(bc && bc == target->compiled);
|
|
954
|
+
return level;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
static int count_cfunctions(block b) {
|
|
958
|
+
int n = 0;
|
|
959
|
+
for (inst* i = b.first; i; i = i->next) {
|
|
960
|
+
if (i->op == CLOSURE_CREATE_C) n++;
|
|
961
|
+
n += count_cfunctions(i->subfn);
|
|
962
|
+
}
|
|
963
|
+
return n;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
// Expands call instructions into a calling sequence
|
|
968
|
+
static int expand_call_arglist(block* b) {
|
|
969
|
+
int errors = 0;
|
|
970
|
+
block ret = gen_noop();
|
|
971
|
+
for (inst* curr; (curr = block_take(b));) {
|
|
972
|
+
if (opcode_describe(curr->op)->flags & OP_HAS_BINDING) {
|
|
973
|
+
if (!curr->bound_by) {
|
|
974
|
+
if (curr->symbol[0] == '*' && curr->symbol[1] >= '1' && curr->symbol[1] <= '3' && curr->symbol[2] == '\0')
|
|
975
|
+
locfile_locate(curr->locfile, curr->source, "jq: error: break used outside labeled control structure");
|
|
976
|
+
else
|
|
977
|
+
locfile_locate(curr->locfile, curr->source, "jq: error: %s/%d is not defined", curr->symbol, block_count_actuals(curr->arglist));
|
|
978
|
+
errors++;
|
|
979
|
+
// don't process this instruction if it's not well-defined
|
|
980
|
+
ret = BLOCK(ret, inst_block(curr));
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
block prelude = gen_noop();
|
|
986
|
+
if (curr->op == CALL_JQ) {
|
|
987
|
+
int actual_args = 0, desired_args = 0;
|
|
988
|
+
// We expand the argument list as a series of instructions
|
|
989
|
+
switch (curr->bound_by->op) {
|
|
990
|
+
default: assert(0 && "Unknown function type"); break;
|
|
991
|
+
case CLOSURE_CREATE:
|
|
992
|
+
case CLOSURE_PARAM: {
|
|
993
|
+
block callargs = gen_noop();
|
|
994
|
+
for (inst* i; (i = block_take(&curr->arglist));) {
|
|
995
|
+
assert(opcode_describe(i->op)->flags & OP_IS_CALL_PSEUDO);
|
|
996
|
+
block b = inst_block(i);
|
|
997
|
+
switch (i->op) {
|
|
998
|
+
default: assert(0 && "Unknown type of parameter"); break;
|
|
999
|
+
case CLOSURE_REF:
|
|
1000
|
+
block_append(&callargs, b);
|
|
1001
|
+
break;
|
|
1002
|
+
case CLOSURE_CREATE:
|
|
1003
|
+
block_append(&prelude, b);
|
|
1004
|
+
block_append(&callargs, gen_op_bound(CLOSURE_REF, b));
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
actual_args++;
|
|
1008
|
+
}
|
|
1009
|
+
curr->imm.intval = actual_args;
|
|
1010
|
+
curr->arglist = callargs;
|
|
1011
|
+
|
|
1012
|
+
if (curr->bound_by->op == CLOSURE_CREATE) {
|
|
1013
|
+
for (inst* i = curr->bound_by->arglist.first; i; i = i->next) {
|
|
1014
|
+
assert(i->op == CLOSURE_PARAM);
|
|
1015
|
+
desired_args++;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
case CLOSURE_CREATE_C: {
|
|
1022
|
+
for (inst* i; (i = block_take(&curr->arglist)); ) {
|
|
1023
|
+
assert(i->op == CLOSURE_CREATE); // FIXME
|
|
1024
|
+
block body = i->subfn;
|
|
1025
|
+
i->subfn = gen_noop();
|
|
1026
|
+
inst_free(i);
|
|
1027
|
+
// arguments should be pushed in reverse order, prepend them to prelude
|
|
1028
|
+
errors += expand_call_arglist(&body);
|
|
1029
|
+
prelude = BLOCK(gen_subexp(body), prelude);
|
|
1030
|
+
actual_args++;
|
|
1031
|
+
}
|
|
1032
|
+
assert(curr->op == CALL_JQ);
|
|
1033
|
+
curr->op = CALL_BUILTIN;
|
|
1034
|
+
curr->imm.intval = actual_args + 1 /* include the implicit input in arg count */;
|
|
1035
|
+
assert(curr->bound_by->op == CLOSURE_CREATE_C);
|
|
1036
|
+
desired_args = curr->bound_by->imm.cfunc->nargs - 1;
|
|
1037
|
+
assert(!curr->arglist.first);
|
|
1038
|
+
break;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
assert(actual_args == desired_args); // because now handle this above
|
|
1043
|
+
}
|
|
1044
|
+
ret = BLOCK(ret, prelude, inst_block(curr));
|
|
1045
|
+
}
|
|
1046
|
+
*b = ret;
|
|
1047
|
+
return errors;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
static int compile(struct bytecode* bc, block b, struct locfile* lf) {
|
|
1051
|
+
int errors = 0;
|
|
1052
|
+
int pos = 0;
|
|
1053
|
+
int var_frame_idx = 0;
|
|
1054
|
+
bc->nsubfunctions = 0;
|
|
1055
|
+
errors += expand_call_arglist(&b);
|
|
1056
|
+
b = BLOCK(b, gen_op_simple(RET));
|
|
1057
|
+
jv localnames = jv_array();
|
|
1058
|
+
for (inst* curr = b.first; curr; curr = curr->next) {
|
|
1059
|
+
if (!curr->next) assert(curr == b.last);
|
|
1060
|
+
int length = opcode_describe(curr->op)->length;
|
|
1061
|
+
if (curr->op == CALL_JQ) {
|
|
1062
|
+
for (inst* arg = curr->arglist.first; arg; arg = arg->next) {
|
|
1063
|
+
length += 2;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
pos += length;
|
|
1067
|
+
curr->bytecode_pos = pos;
|
|
1068
|
+
curr->compiled = bc;
|
|
1069
|
+
|
|
1070
|
+
assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);
|
|
1071
|
+
|
|
1072
|
+
if ((opcode_describe(curr->op)->flags & OP_HAS_VARIABLE) &&
|
|
1073
|
+
curr->bound_by == curr) {
|
|
1074
|
+
curr->imm.intval = var_frame_idx++;
|
|
1075
|
+
localnames = jv_array_append(localnames, jv_string(curr->symbol));
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (curr->op == CLOSURE_CREATE) {
|
|
1079
|
+
assert(curr->bound_by == curr);
|
|
1080
|
+
curr->imm.intval = bc->nsubfunctions++;
|
|
1081
|
+
}
|
|
1082
|
+
if (curr->op == CLOSURE_CREATE_C) {
|
|
1083
|
+
assert(curr->bound_by == curr);
|
|
1084
|
+
int idx = bc->globals->ncfunctions++;
|
|
1085
|
+
bc->globals->cfunc_names = jv_array_append(bc->globals->cfunc_names,
|
|
1086
|
+
jv_string(curr->symbol));
|
|
1087
|
+
bc->globals->cfunctions[idx] = *curr->imm.cfunc;
|
|
1088
|
+
curr->imm.intval = idx;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (pos > 0xFFFF) {
|
|
1092
|
+
// too long for program counter to fit in uint16_t
|
|
1093
|
+
locfile_locate(lf, UNKNOWN_LOCATION,
|
|
1094
|
+
"function compiled to %d bytes which is too long", pos);
|
|
1095
|
+
errors++;
|
|
1096
|
+
}
|
|
1097
|
+
bc->codelen = pos;
|
|
1098
|
+
bc->debuginfo = jv_object_set(bc->debuginfo, jv_string("locals"), localnames);
|
|
1099
|
+
if (bc->nsubfunctions) {
|
|
1100
|
+
bc->subfunctions = jv_mem_alloc(sizeof(struct bytecode*) * bc->nsubfunctions);
|
|
1101
|
+
for (inst* curr = b.first; curr; curr = curr->next) {
|
|
1102
|
+
if (curr->op == CLOSURE_CREATE) {
|
|
1103
|
+
struct bytecode* subfn = jv_mem_alloc(sizeof(struct bytecode));
|
|
1104
|
+
bc->subfunctions[curr->imm.intval] = subfn;
|
|
1105
|
+
subfn->globals = bc->globals;
|
|
1106
|
+
subfn->parent = bc;
|
|
1107
|
+
subfn->nclosures = 0;
|
|
1108
|
+
subfn->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_string(curr->symbol));
|
|
1109
|
+
jv params = jv_array();
|
|
1110
|
+
for (inst* param = curr->arglist.first; param; param = param->next) {
|
|
1111
|
+
assert(param->op == CLOSURE_PARAM);
|
|
1112
|
+
assert(param->bound_by == param);
|
|
1113
|
+
param->imm.intval = subfn->nclosures++;
|
|
1114
|
+
param->compiled = subfn;
|
|
1115
|
+
params = jv_array_append(params, jv_string(param->symbol));
|
|
1116
|
+
}
|
|
1117
|
+
subfn->debuginfo = jv_object_set(subfn->debuginfo, jv_string("params"), params);
|
|
1118
|
+
errors += compile(subfn, curr->subfn, lf);
|
|
1119
|
+
curr->subfn = gen_noop();
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
} else {
|
|
1123
|
+
bc->subfunctions = 0;
|
|
1124
|
+
}
|
|
1125
|
+
uint16_t* code = jv_mem_alloc(sizeof(uint16_t) * bc->codelen);
|
|
1126
|
+
bc->code = code;
|
|
1127
|
+
pos = 0;
|
|
1128
|
+
jv constant_pool = jv_array();
|
|
1129
|
+
int maxvar = -1;
|
|
1130
|
+
if (!errors) for (inst* curr = b.first; curr; curr = curr->next) {
|
|
1131
|
+
const struct opcode_description* op = opcode_describe(curr->op);
|
|
1132
|
+
if (op->length == 0)
|
|
1133
|
+
continue;
|
|
1134
|
+
code[pos++] = curr->op;
|
|
1135
|
+
assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);
|
|
1136
|
+
if (curr->op == CALL_BUILTIN) {
|
|
1137
|
+
assert(curr->bound_by->op == CLOSURE_CREATE_C);
|
|
1138
|
+
assert(!curr->arglist.first);
|
|
1139
|
+
code[pos++] = (uint16_t)curr->imm.intval;
|
|
1140
|
+
code[pos++] = curr->bound_by->imm.intval;
|
|
1141
|
+
} else if (curr->op == CALL_JQ) {
|
|
1142
|
+
assert(curr->bound_by->op == CLOSURE_CREATE ||
|
|
1143
|
+
curr->bound_by->op == CLOSURE_PARAM);
|
|
1144
|
+
code[pos++] = (uint16_t)curr->imm.intval;
|
|
1145
|
+
code[pos++] = nesting_level(bc, curr->bound_by);
|
|
1146
|
+
code[pos++] = curr->bound_by->imm.intval |
|
|
1147
|
+
(curr->bound_by->op == CLOSURE_CREATE ? ARG_NEWCLOSURE : 0);
|
|
1148
|
+
for (inst* arg = curr->arglist.first; arg; arg = arg->next) {
|
|
1149
|
+
assert(arg->op == CLOSURE_REF && arg->bound_by->op == CLOSURE_CREATE);
|
|
1150
|
+
code[pos++] = nesting_level(bc, arg->bound_by);
|
|
1151
|
+
code[pos++] = arg->bound_by->imm.intval | ARG_NEWCLOSURE;
|
|
1152
|
+
}
|
|
1153
|
+
} else if ((op->flags & OP_HAS_CONSTANT) && (op->flags & OP_HAS_VARIABLE)) {
|
|
1154
|
+
// STORE_GLOBAL: constant global, basically
|
|
1155
|
+
code[pos++] = jv_array_length(jv_copy(constant_pool));
|
|
1156
|
+
constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));
|
|
1157
|
+
code[pos++] = nesting_level(bc, curr->bound_by);
|
|
1158
|
+
uint16_t var = (uint16_t)curr->bound_by->imm.intval;
|
|
1159
|
+
code[pos++] = var;
|
|
1160
|
+
} else if (op->flags & OP_HAS_CONSTANT) {
|
|
1161
|
+
code[pos++] = jv_array_length(jv_copy(constant_pool));
|
|
1162
|
+
constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));
|
|
1163
|
+
} else if (op->flags & OP_HAS_VARIABLE) {
|
|
1164
|
+
code[pos++] = nesting_level(bc, curr->bound_by);
|
|
1165
|
+
uint16_t var = (uint16_t)curr->bound_by->imm.intval;
|
|
1166
|
+
code[pos++] = var;
|
|
1167
|
+
if (var > maxvar) maxvar = var;
|
|
1168
|
+
} else if (op->flags & OP_HAS_BRANCH) {
|
|
1169
|
+
assert(curr->imm.target->bytecode_pos != -1);
|
|
1170
|
+
assert(curr->imm.target->bytecode_pos > pos); // only forward branches
|
|
1171
|
+
code[pos] = curr->imm.target->bytecode_pos - (pos + 1);
|
|
1172
|
+
pos++;
|
|
1173
|
+
} else if (op->length > 1) {
|
|
1174
|
+
assert(0 && "codegen not implemented for this operation");
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
bc->constants = constant_pool;
|
|
1178
|
+
bc->nlocals = maxvar + 2; // FIXME: frames of size zero?
|
|
1179
|
+
block_free(b);
|
|
1180
|
+
return errors;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
int block_compile(block b, struct bytecode** out, struct locfile* lf) {
|
|
1184
|
+
struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode));
|
|
1185
|
+
bc->parent = 0;
|
|
1186
|
+
bc->nclosures = 0;
|
|
1187
|
+
bc->globals = jv_mem_alloc(sizeof(struct symbol_table));
|
|
1188
|
+
int ncfunc = count_cfunctions(b);
|
|
1189
|
+
bc->globals->ncfunctions = 0;
|
|
1190
|
+
bc->globals->cfunctions = jv_mem_alloc(sizeof(struct cfunction) * ncfunc);
|
|
1191
|
+
bc->globals->cfunc_names = jv_array();
|
|
1192
|
+
bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null());
|
|
1193
|
+
int nerrors = compile(bc, b, lf);
|
|
1194
|
+
assert(bc->globals->ncfunctions == ncfunc);
|
|
1195
|
+
if (nerrors > 0) {
|
|
1196
|
+
bytecode_free(bc);
|
|
1197
|
+
*out = 0;
|
|
1198
|
+
} else {
|
|
1199
|
+
*out = bc;
|
|
1200
|
+
}
|
|
1201
|
+
return nerrors;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
void block_free(block b) {
|
|
1205
|
+
struct inst* next;
|
|
1206
|
+
for (struct inst* curr = b.first; curr; curr = next) {
|
|
1207
|
+
next = curr->next;
|
|
1208
|
+
inst_free(curr);
|
|
1209
|
+
}
|
|
1210
|
+
}
|