xbintsc 0.1.0
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/LICENSE +21 -0
- package/README.md +191 -0
- package/bin/xbintsc.js +26 -0
- package/dist/src/ast/factory.d.ts +11 -0
- package/dist/src/ast/factory.js +36 -0
- package/dist/src/ast/factory.js.map +1 -0
- package/dist/src/ast/nodes.d.ts +848 -0
- package/dist/src/ast/nodes.js +247 -0
- package/dist/src/ast/nodes.js.map +1 -0
- package/dist/src/ast/visitor.d.ts +18 -0
- package/dist/src/ast/visitor.js +52 -0
- package/dist/src/ast/visitor.js.map +1 -0
- package/dist/src/binder/binder.d.ts +82 -0
- package/dist/src/binder/binder.js +443 -0
- package/dist/src/binder/binder.js.map +1 -0
- package/dist/src/cli/main.d.ts +16 -0
- package/dist/src/cli/main.js +191 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/codegen/llvm.d.ts +31 -0
- package/dist/src/codegen/llvm.js +1178 -0
- package/dist/src/codegen/llvm.js.map +1 -0
- package/dist/src/codegen/values.d.ts +23 -0
- package/dist/src/codegen/values.js +32 -0
- package/dist/src/codegen/values.js.map +1 -0
- package/dist/src/diagnostics/diagnostic.d.ts +69 -0
- package/dist/src/diagnostics/diagnostic.js +104 -0
- package/dist/src/diagnostics/diagnostic.js.map +1 -0
- package/dist/src/diagnostics/source.d.ts +43 -0
- package/dist/src/diagnostics/source.js +73 -0
- package/dist/src/diagnostics/source.js.map +1 -0
- package/dist/src/driver/cache.d.ts +37 -0
- package/dist/src/driver/cache.js +82 -0
- package/dist/src/driver/cache.js.map +1 -0
- package/dist/src/driver/compiler.d.ts +41 -0
- package/dist/src/driver/compiler.js +128 -0
- package/dist/src/driver/compiler.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +5 -0
- package/dist/src/driver/index.js.map +1 -0
- package/dist/src/driver/paths.d.ts +4 -0
- package/dist/src/driver/paths.js +22 -0
- package/dist/src/driver/paths.js.map +1 -0
- package/dist/src/driver/toolchain.d.ts +43 -0
- package/dist/src/driver/toolchain.js +76 -0
- package/dist/src/driver/toolchain.js.map +1 -0
- package/dist/src/extensions/node.d.ts +9 -0
- package/dist/src/extensions/node.js +20 -0
- package/dist/src/extensions/node.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +45 -0
- package/dist/src/extensions/registry.js +76 -0
- package/dist/src/extensions/registry.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lexer/scanner.d.ts +48 -0
- package/dist/src/lexer/scanner.js +650 -0
- package/dist/src/lexer/scanner.js.map +1 -0
- package/dist/src/lexer/token.d.ts +170 -0
- package/dist/src/lexer/token.js +250 -0
- package/dist/src/lexer/token.js.map +1 -0
- package/dist/src/parser/parser.d.ts +127 -0
- package/dist/src/parser/parser.js +2334 -0
- package/dist/src/parser/parser.js.map +1 -0
- package/dist/tests/binder/binder.test.d.ts +1 -0
- package/dist/tests/binder/binder.test.js +89 -0
- package/dist/tests/binder/binder.test.js.map +1 -0
- package/dist/tests/cli/main.test.d.ts +1 -0
- package/dist/tests/cli/main.test.js +68 -0
- package/dist/tests/cli/main.test.js.map +1 -0
- package/dist/tests/cli/process.test.d.ts +9 -0
- package/dist/tests/cli/process.test.js +57 -0
- package/dist/tests/cli/process.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.d.ts +1 -0
- package/dist/tests/codegen/llvm.test.js +93 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -0
- package/dist/tests/driver/cache.test.d.ts +1 -0
- package/dist/tests/driver/cache.test.js +66 -0
- package/dist/tests/driver/cache.test.js.map +1 -0
- package/dist/tests/driver/compiler.test.d.ts +1 -0
- package/dist/tests/driver/compiler.test.js +80 -0
- package/dist/tests/driver/compiler.test.js.map +1 -0
- package/dist/tests/driver/toolchain.test.d.ts +1 -0
- package/dist/tests/driver/toolchain.test.js +60 -0
- package/dist/tests/driver/toolchain.test.js.map +1 -0
- package/dist/tests/e2e/compile.test.d.ts +5 -0
- package/dist/tests/e2e/compile.test.js +93 -0
- package/dist/tests/e2e/compile.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.d.ts +1 -0
- package/dist/tests/extensions/registry.test.js +47 -0
- package/dist/tests/extensions/registry.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +23 -0
- package/dist/tests/helpers.js +41 -0
- package/dist/tests/helpers.js.map +1 -0
- package/dist/tests/lexer/scanner.test.d.ts +1 -0
- package/dist/tests/lexer/scanner.test.js +95 -0
- package/dist/tests/lexer/scanner.test.js.map +1 -0
- package/dist/tests/parser/parser.test.d.ts +1 -0
- package/dist/tests/parser/parser.test.js +137 -0
- package/dist/tests/parser/parser.test.js.map +1 -0
- package/package.json +49 -0
- package/runtime/ext_node.c +42 -0
- package/runtime/rt.h +177 -0
- package/runtime/xt_runtime.c +682 -0
- package/scripts/build-runtime.mjs +34 -0
- package/src/ast/factory.ts +37 -0
- package/src/ast/nodes.ts +1111 -0
- package/src/ast/visitor.ts +60 -0
- package/src/binder/binder.ts +549 -0
- package/src/cli/main.ts +210 -0
- package/src/codegen/llvm.ts +1324 -0
- package/src/codegen/values.ts +36 -0
- package/src/diagnostics/diagnostic.ts +132 -0
- package/src/diagnostics/source.ts +97 -0
- package/src/driver/cache.ts +96 -0
- package/src/driver/compiler.ts +197 -0
- package/src/driver/index.ts +4 -0
- package/src/driver/paths.ts +24 -0
- package/src/driver/toolchain.ts +103 -0
- package/src/extensions/node.ts +23 -0
- package/src/extensions/registry.ts +101 -0
- package/src/index.ts +33 -0
- package/src/lexer/scanner.ts +650 -0
- package/src/lexer/token.ts +266 -0
- package/src/parser/parser.ts +2396 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc runtime implementation.
|
|
3
|
+
*
|
|
4
|
+
* Design notes
|
|
5
|
+
* ------------
|
|
6
|
+
* * Values are 64-bit NaN-boxed words (`xt_value`). Numbers travel unboxed;
|
|
7
|
+
* everything else is a tagged pointer into and allocation arena.
|
|
8
|
+
* * The allocator is a bump arena. It never frees, which keeps the generated
|
|
9
|
+
* code free of write barriers and makes the first implementation small and
|
|
10
|
+
* predictable. Replacing it with a precise/conservative collector is an
|
|
11
|
+
* isolated change behind `xt_alloc` plus root registration.
|
|
12
|
+
* * Strings are UTF-8. JavaScript measures string length in UTF-16 code
|
|
13
|
+
* units; `xt_string_length` currently reports code points (see README).
|
|
14
|
+
* * Objects use a small linear property list. Fine for the language subset
|
|
15
|
+
* xbintsc targets today; swap for a hash map when the profile demands it.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "rt.h"
|
|
19
|
+
|
|
20
|
+
#include <math.h>
|
|
21
|
+
#include <stdio.h>
|
|
22
|
+
#include <stdlib.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
#include <stdarg.h>
|
|
25
|
+
|
|
26
|
+
/* ------------------------------------------------------------------------- */
|
|
27
|
+
/* Allocation */
|
|
28
|
+
/* ------------------------------------------------------------------------- */
|
|
29
|
+
|
|
30
|
+
#define XT_OBJECT_KIND_STRING 1
|
|
31
|
+
#define XT_OBJECT_KIND_OBJECT 2
|
|
32
|
+
#define XT_OBJECT_KIND_ARRAY 3
|
|
33
|
+
#define XT_OBJECT_KIND_FUNCTION 4
|
|
34
|
+
|
|
35
|
+
typedef struct xt_header {
|
|
36
|
+
uint8_t kind;
|
|
37
|
+
uint8_t flags;
|
|
38
|
+
uint16_t reserved;
|
|
39
|
+
uint32_t size;
|
|
40
|
+
struct xt_header *gc_next;
|
|
41
|
+
} xt_header;
|
|
42
|
+
|
|
43
|
+
typedef struct {
|
|
44
|
+
xt_header header;
|
|
45
|
+
uint32_t length;
|
|
46
|
+
uint32_t capacity;
|
|
47
|
+
char *data;
|
|
48
|
+
} xt_string;
|
|
49
|
+
|
|
50
|
+
typedef struct {
|
|
51
|
+
xt_string *key;
|
|
52
|
+
xt_value value;
|
|
53
|
+
} xt_property;
|
|
54
|
+
|
|
55
|
+
typedef struct {
|
|
56
|
+
xt_header header;
|
|
57
|
+
uint32_t count;
|
|
58
|
+
uint32_t capacity;
|
|
59
|
+
xt_property *properties;
|
|
60
|
+
} xt_object;
|
|
61
|
+
|
|
62
|
+
typedef struct {
|
|
63
|
+
xt_header header;
|
|
64
|
+
uint32_t length;
|
|
65
|
+
uint32_t capacity;
|
|
66
|
+
xt_value *items;
|
|
67
|
+
} xt_array;
|
|
68
|
+
|
|
69
|
+
typedef struct {
|
|
70
|
+
xt_header header;
|
|
71
|
+
xt_code_fn code;
|
|
72
|
+
xt_value *environment;
|
|
73
|
+
int32_t environment_count;
|
|
74
|
+
int32_t arity;
|
|
75
|
+
xt_string *name;
|
|
76
|
+
xt_object *properties;
|
|
77
|
+
} xt_function;
|
|
78
|
+
|
|
79
|
+
static size_t g_heap_bytes = 0;
|
|
80
|
+
static size_t g_heap_allocations = 0;
|
|
81
|
+
static xt_header *g_heap_head = NULL;
|
|
82
|
+
|
|
83
|
+
static void *xt_alloc(size_t size, int kind) {
|
|
84
|
+
xt_header *header = (xt_header *)calloc(1, size);
|
|
85
|
+
if (!header) {
|
|
86
|
+
fprintf(stderr, "xbintsc: out of memory allocating %zu bytes\n", size);
|
|
87
|
+
abort();
|
|
88
|
+
}
|
|
89
|
+
header->kind = (uint8_t)kind;
|
|
90
|
+
header->size = (uint32_t)size;
|
|
91
|
+
header->gc_next = g_heap_head;
|
|
92
|
+
g_heap_head = header;
|
|
93
|
+
g_heap_bytes += size;
|
|
94
|
+
g_heap_allocations++;
|
|
95
|
+
return header;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
size_t xt_heap_bytes(void) { return g_heap_bytes; }
|
|
99
|
+
size_t xt_heap_allocations(void) { return g_heap_allocations; }
|
|
100
|
+
|
|
101
|
+
/* ------------------------------------------------------------------------- */
|
|
102
|
+
/* Constructors */
|
|
103
|
+
/* ------------------------------------------------------------------------- */
|
|
104
|
+
|
|
105
|
+
xt_value xt_undefined(void) { return XT_UNDEFINED; }
|
|
106
|
+
xt_value xt_null(void) { return XT_NULL; }
|
|
107
|
+
xt_value xt_bool(int b) { return b ? XT_TRUE : XT_FALSE; }
|
|
108
|
+
xt_value xt_number(double d) { return xt_from_double(d); }
|
|
109
|
+
|
|
110
|
+
static xt_string *xt_string_alloc(size_t length) {
|
|
111
|
+
xt_string *s = (xt_string *)xt_alloc(sizeof(xt_string), XT_OBJECT_KIND_STRING);
|
|
112
|
+
s->length = (uint32_t)length;
|
|
113
|
+
s->capacity = (uint32_t)length + 1;
|
|
114
|
+
s->data = (char *)malloc(s->capacity);
|
|
115
|
+
if (!s->data) {
|
|
116
|
+
fprintf(stderr, "xbintsc: out of memory allocating string\n");
|
|
117
|
+
abort();
|
|
118
|
+
}
|
|
119
|
+
s->data[length] = '\0';
|
|
120
|
+
return s;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
xt_value xt_string_new(const char *data, size_t len) {
|
|
124
|
+
xt_string *s = xt_string_alloc(len);
|
|
125
|
+
if (len > 0) memcpy(s->data, data, len);
|
|
126
|
+
return XT_FROM_PTR(XT_TAG_STRING, s);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
xt_value xt_string_from_cstr(const char *data) { return xt_string_new(data, strlen(data)); }
|
|
130
|
+
|
|
131
|
+
static xt_string *xt_as_string(xt_value v);
|
|
132
|
+
|
|
133
|
+
const char *xt_string_data(xt_value value) {
|
|
134
|
+
xt_string *s = xt_as_string(value);
|
|
135
|
+
return s ? s->data : NULL;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
int32_t xt_string_length_value(xt_value value) {
|
|
139
|
+
xt_string *s = xt_as_string(value);
|
|
140
|
+
return s ? (int32_t)s->length : -1;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ------------------------------------------------------------------------- */
|
|
144
|
+
/* Strings */
|
|
145
|
+
/* ------------------------------------------------------------------------- */
|
|
146
|
+
|
|
147
|
+
static xt_string *xt_as_string(xt_value v) {
|
|
148
|
+
if (XT_IS_STRING(v)) return (xt_string *)XT_GET_PTR(v);
|
|
149
|
+
return NULL;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static int32_t xt_string_length(xt_string *s) { return (int32_t)s->length; }
|
|
153
|
+
|
|
154
|
+
static xt_value xt_string_concat(xt_string *a, xt_string *b) {
|
|
155
|
+
size_t total = (size_t)a->length + (size_t)b->length;
|
|
156
|
+
xt_string *out = xt_string_alloc(total);
|
|
157
|
+
memcpy(out->data, a->data, a->length);
|
|
158
|
+
memcpy(out->data + a->length, b->data, b->length);
|
|
159
|
+
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static int xt_string_equals(xt_string *a, xt_string *b) {
|
|
163
|
+
return a->length == b->length && memcmp(a->data, b->data, a->length) == 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/*
|
|
167
|
+
* Format a double the way JavaScript's ToString does for the common cases:
|
|
168
|
+
* integers print without a decimal point, everything else uses the shortest
|
|
169
|
+
* representation that round-trips.
|
|
170
|
+
*/
|
|
171
|
+
static void xt_format_double(double d, char *buffer, size_t size) {
|
|
172
|
+
if (isnan(d)) {
|
|
173
|
+
snprintf(buffer, size, "NaN");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (isinf(d)) {
|
|
177
|
+
snprintf(buffer, size, d < 0 ? "-Infinity" : "Infinity");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (d == 0.0) {
|
|
181
|
+
snprintf(buffer, size, "0");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (d == floor(d) && fabs(d) < 1e21) {
|
|
185
|
+
snprintf(buffer, size, "%.0f", d);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (int precision = 1; precision <= 17; precision++) {
|
|
189
|
+
snprintf(buffer, size, "%.*g", precision, d);
|
|
190
|
+
if (strtod(buffer, NULL) == d) return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* ------------------------------------------------------------------------- */
|
|
195
|
+
/* Conversions */
|
|
196
|
+
/* ------------------------------------------------------------------------- */
|
|
197
|
+
|
|
198
|
+
int xt_truthy(xt_value v) {
|
|
199
|
+
if (XT_IS_NUMBER(v)) {
|
|
200
|
+
double d = xt_to_double(v);
|
|
201
|
+
return d != 0.0 && !isnan(d);
|
|
202
|
+
}
|
|
203
|
+
if (v == XT_UNDEFINED || v == XT_NULL) return 0;
|
|
204
|
+
if (v == XT_FALSE) return 0;
|
|
205
|
+
if (v == XT_TRUE) return 1;
|
|
206
|
+
return 1; /* objects, arrays, functions and non-empty strings are truthy */
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
double xt_to_number(xt_value v) {
|
|
210
|
+
if (XT_IS_NUMBER(v)) return xt_to_double(v);
|
|
211
|
+
if (v == XT_TRUE) return 1.0;
|
|
212
|
+
if (v == XT_FALSE) return 0.0;
|
|
213
|
+
if (v == XT_UNDEFINED) return NAN;
|
|
214
|
+
if (v == XT_NULL) return 0.0;
|
|
215
|
+
if (XT_IS_STRING(v)) {
|
|
216
|
+
xt_string *s = xt_as_string(v);
|
|
217
|
+
if (s->length == 0) return 0.0;
|
|
218
|
+
char *end = NULL;
|
|
219
|
+
double parsed = strtod(s->data, &end);
|
|
220
|
+
if (end != s->data + s->length) return NAN;
|
|
221
|
+
return parsed;
|
|
222
|
+
}
|
|
223
|
+
return NAN;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static xt_value xt_number_to_string_value(double d) {
|
|
227
|
+
char buffer[64];
|
|
228
|
+
xt_format_double(d, buffer, sizeof(buffer));
|
|
229
|
+
return xt_string_from_cstr(buffer);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
xt_value xt_to_string(xt_value v) {
|
|
233
|
+
if (XT_IS_STRING(v)) return v;
|
|
234
|
+
if (XT_IS_NUMBER(v)) return xt_number_to_string_value(xt_to_double(v));
|
|
235
|
+
if (v == XT_UNDEFINED) return xt_string_from_cstr("undefined");
|
|
236
|
+
if (v == XT_NULL) return xt_string_from_cstr("null");
|
|
237
|
+
if (v == XT_TRUE) return xt_string_from_cstr("true");
|
|
238
|
+
if (v == XT_FALSE) return xt_string_from_cstr("false");
|
|
239
|
+
if (XT_IS_ARRAY(v)) {
|
|
240
|
+
xt_array *a = (xt_array *)XT_GET_PTR(v);
|
|
241
|
+
/* Join elements with commas, like Array.prototype.toString. */
|
|
242
|
+
size_t capacity = 32;
|
|
243
|
+
char *out = (char *)malloc(capacity);
|
|
244
|
+
size_t length = 0;
|
|
245
|
+
out[0] = '\0';
|
|
246
|
+
for (uint32_t i = 0; i < a->length; i++) {
|
|
247
|
+
xt_value s = xt_to_string(a->items[i]);
|
|
248
|
+
xt_string *str = xt_as_string(s);
|
|
249
|
+
if (length + str->length + 2 > capacity) {
|
|
250
|
+
capacity = (length + str->length + 2) * 2;
|
|
251
|
+
out = (char *)realloc(out, capacity);
|
|
252
|
+
}
|
|
253
|
+
if (i > 0) out[length++] = ',';
|
|
254
|
+
memcpy(out + length, str->data, str->length);
|
|
255
|
+
length += str->length;
|
|
256
|
+
out[length] = '\0';
|
|
257
|
+
}
|
|
258
|
+
xt_value result = xt_string_new(out, length);
|
|
259
|
+
free(out);
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
if (XT_IS_FUNCTION(v)) return xt_string_from_cstr("function () { [native code] }");
|
|
263
|
+
return xt_string_from_cstr("[object Object]");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
xt_value xt_typeof(xt_value v) {
|
|
267
|
+
if (XT_IS_NUMBER(v)) return xt_string_from_cstr("number");
|
|
268
|
+
if (XT_IS_STRING(v)) return xt_string_from_cstr("string");
|
|
269
|
+
if (v == XT_TRUE || v == XT_FALSE) return xt_string_from_cstr("boolean");
|
|
270
|
+
if (v == XT_UNDEFINED) return xt_string_from_cstr("undefined");
|
|
271
|
+
if (XT_IS_FUNCTION(v)) return xt_string_from_cstr("function");
|
|
272
|
+
if (v == XT_NULL) return xt_string_from_cstr("object");
|
|
273
|
+
return xt_string_from_cstr("object");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ------------------------------------------------------------------------- */
|
|
277
|
+
/* Arithmetic */
|
|
278
|
+
/* ------------------------------------------------------------------------- */
|
|
279
|
+
|
|
280
|
+
xt_value xt_add(xt_value a, xt_value b) {
|
|
281
|
+
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) {
|
|
282
|
+
return xt_number(xt_to_double(a) + xt_to_double(b));
|
|
283
|
+
}
|
|
284
|
+
/* JavaScript's `+` concatenates when either operand is a string. */
|
|
285
|
+
if (XT_IS_STRING(a) || XT_IS_STRING(b)) {
|
|
286
|
+
xt_value sa = xt_to_string(a);
|
|
287
|
+
xt_value sb = xt_to_string(b);
|
|
288
|
+
return xt_string_concat(xt_as_string(sa), xt_as_string(sb));
|
|
289
|
+
}
|
|
290
|
+
return xt_number(xt_to_number(a) + xt_to_number(b));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
xt_value xt_sub(xt_value a, xt_value b) { return xt_number(xt_to_number(a) - xt_to_number(b)); }
|
|
294
|
+
xt_value xt_mul(xt_value a, xt_value b) { return xt_number(xt_to_number(a) * xt_to_number(b)); }
|
|
295
|
+
xt_value xt_div(xt_value a, xt_value b) { return xt_number(xt_to_number(a) / xt_to_number(b)); }
|
|
296
|
+
|
|
297
|
+
xt_value xt_mod(xt_value a, xt_value b) {
|
|
298
|
+
double x = xt_to_number(a);
|
|
299
|
+
double y = xt_to_number(b);
|
|
300
|
+
return xt_number(fmod(x, y));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
xt_value xt_pow(xt_value a, xt_value b) { return xt_number(pow(xt_to_number(a), xt_to_number(b))); }
|
|
304
|
+
xt_value xt_neg(xt_value a) { return xt_number(-xt_to_number(a)); }
|
|
305
|
+
xt_value xt_pos(xt_value a) { return xt_number(xt_to_number(a)); }
|
|
306
|
+
|
|
307
|
+
static int32_t xt_to_int32(xt_value v) {
|
|
308
|
+
double d = xt_to_number(v);
|
|
309
|
+
if (isnan(d) || isinf(d)) return 0;
|
|
310
|
+
return (int32_t)(uint32_t)(int64_t)d;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
xt_value xt_bit_and(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) & xt_to_int32(b))); }
|
|
314
|
+
xt_value xt_bit_or(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) | xt_to_int32(b))); }
|
|
315
|
+
xt_value xt_bit_xor(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) ^ xt_to_int32(b))); }
|
|
316
|
+
xt_value xt_bit_not(xt_value a) { return xt_number((double)(~xt_to_int32(a))); }
|
|
317
|
+
xt_value xt_shl(xt_value a, xt_value b) { return xt_number((double)(int32_t)((uint32_t)xt_to_int32(a) << (xt_to_int32(b) & 31))); }
|
|
318
|
+
xt_value xt_shr(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) >> (xt_to_int32(b) & 31))); }
|
|
319
|
+
xt_value xt_ushr(xt_value a, xt_value b) {
|
|
320
|
+
return xt_number((double)(uint32_t)((uint32_t)xt_to_int32(a) >> (xt_to_int32(b) & 31)));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* ------------------------------------------------------------------------- */
|
|
324
|
+
/* Comparison */
|
|
325
|
+
/* ------------------------------------------------------------------------- */
|
|
326
|
+
|
|
327
|
+
xt_value xt_lt(xt_value a, xt_value b) {
|
|
328
|
+
if (XT_IS_STRING(a) && XT_IS_STRING(b)) {
|
|
329
|
+
xt_string *x = xt_as_string(a);
|
|
330
|
+
xt_string *y = xt_as_string(b);
|
|
331
|
+
size_t n = x->length < y->length ? x->length : y->length;
|
|
332
|
+
int cmp = memcmp(x->data, y->data, n);
|
|
333
|
+
if (cmp == 0) return xt_bool(x->length < y->length);
|
|
334
|
+
return xt_bool(cmp < 0);
|
|
335
|
+
}
|
|
336
|
+
return xt_bool(xt_to_number(a) < xt_to_number(b));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
xt_value xt_le(xt_value a, xt_value b) {
|
|
340
|
+
if (XT_IS_STRING(a) && XT_IS_STRING(b)) {
|
|
341
|
+
xt_string *x = xt_as_string(a);
|
|
342
|
+
xt_string *y = xt_as_string(b);
|
|
343
|
+
size_t n = x->length < y->length ? x->length : y->length;
|
|
344
|
+
int cmp = memcmp(x->data, y->data, n);
|
|
345
|
+
if (cmp == 0) return xt_bool(x->length <= y->length);
|
|
346
|
+
return xt_bool(cmp < 0);
|
|
347
|
+
}
|
|
348
|
+
return xt_bool(xt_to_number(a) <= xt_to_number(b));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
xt_value xt_gt(xt_value a, xt_value b) { return xt_lt(b, a); }
|
|
352
|
+
xt_value xt_ge(xt_value a, xt_value b) { return xt_le(b, a); }
|
|
353
|
+
|
|
354
|
+
static int xt_loose_equals(xt_value a, xt_value b) {
|
|
355
|
+
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) return xt_to_double(a) == xt_to_double(b);
|
|
356
|
+
if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_string_equals(xt_as_string(a), xt_as_string(b));
|
|
357
|
+
if (a == b) return 1;
|
|
358
|
+
if ((a == XT_NULL && b == XT_UNDEFINED) || (a == XT_UNDEFINED && b == XT_NULL)) return 1;
|
|
359
|
+
if (XT_IS_BOOL(a)) return xt_loose_equals(xt_number(xt_to_number(a)), b);
|
|
360
|
+
if (XT_IS_BOOL(b)) return xt_loose_equals(a, xt_number(xt_to_number(b)));
|
|
361
|
+
if (XT_IS_STRING(a) && XT_IS_NUMBER(b)) return xt_to_number(a) == xt_to_double(b);
|
|
362
|
+
if (XT_IS_NUMBER(a) && XT_IS_STRING(b)) return xt_to_double(a) == xt_to_number(b);
|
|
363
|
+
return 0;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
xt_value xt_eq(xt_value a, xt_value b) { return xt_bool(xt_loose_equals(a, b)); }
|
|
367
|
+
xt_value xt_ne(xt_value a, xt_value b) { return xt_bool(!xt_loose_equals(a, b)); }
|
|
368
|
+
|
|
369
|
+
xt_value xt_seq(xt_value a, xt_value b) {
|
|
370
|
+
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) return xt_bool(xt_to_double(a) == xt_to_double(b));
|
|
371
|
+
if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_bool(xt_string_equals(xt_as_string(a), xt_as_string(b)));
|
|
372
|
+
return xt_bool(a == b);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
xt_value xt_sne(xt_value a, xt_value b) { return xt_bool(!xt_truthy(xt_seq(a, b))); }
|
|
376
|
+
xt_value xt_not(xt_value a) { return xt_bool(!xt_truthy(a)); }
|
|
377
|
+
|
|
378
|
+
/* ------------------------------------------------------------------------- */
|
|
379
|
+
/* Objects */
|
|
380
|
+
/* ------------------------------------------------------------------------- */
|
|
381
|
+
|
|
382
|
+
static void xt_object_reserve(xt_object *obj, uint32_t needed) {
|
|
383
|
+
if (needed <= obj->capacity) return;
|
|
384
|
+
uint32_t capacity = obj->capacity ? obj->capacity * 2 : 8;
|
|
385
|
+
while (capacity < needed) capacity *= 2;
|
|
386
|
+
obj->properties = (xt_property *)realloc(obj->properties, sizeof(xt_property) * capacity);
|
|
387
|
+
if (!obj->properties) {
|
|
388
|
+
fprintf(stderr, "xbintsc: out of memory growing object\n");
|
|
389
|
+
abort();
|
|
390
|
+
}
|
|
391
|
+
obj->capacity = capacity;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
xt_value xt_object_new(void) {
|
|
395
|
+
xt_object *obj = (xt_object *)xt_alloc(sizeof(xt_object), XT_OBJECT_KIND_OBJECT);
|
|
396
|
+
obj->count = 0;
|
|
397
|
+
obj->capacity = 0;
|
|
398
|
+
obj->properties = NULL;
|
|
399
|
+
return XT_FROM_PTR(XT_TAG_OBJECT, obj);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
|
|
403
|
+
for (uint32_t i = 0; i < obj->count; i++) {
|
|
404
|
+
if (xt_string_equals(obj->properties[i].key, key)) return &obj->properties[i];
|
|
405
|
+
}
|
|
406
|
+
return NULL;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
xt_value xt_object_get(xt_value value, xt_value key) {
|
|
410
|
+
if (!XT_IS_OBJECT(value)) return XT_UNDEFINED;
|
|
411
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
412
|
+
xt_value keyString = xt_to_string(key);
|
|
413
|
+
xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
|
|
414
|
+
return prop ? prop->value : XT_UNDEFINED;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
418
|
+
if (!XT_IS_OBJECT(value)) return newValue;
|
|
419
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
420
|
+
xt_value keyString = xt_to_string(key);
|
|
421
|
+
xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
|
|
422
|
+
if (prop) {
|
|
423
|
+
prop->value = newValue;
|
|
424
|
+
return newValue;
|
|
425
|
+
}
|
|
426
|
+
xt_object_reserve(obj, obj->count + 1);
|
|
427
|
+
obj->properties[obj->count].key = xt_as_string(keyString);
|
|
428
|
+
obj->properties[obj->count].value = newValue;
|
|
429
|
+
obj->count++;
|
|
430
|
+
return newValue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
xt_value xt_object_has(xt_value value, xt_value key) {
|
|
434
|
+
if (!XT_IS_OBJECT(value)) return XT_FALSE;
|
|
435
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
436
|
+
xt_value keyString = xt_to_string(key);
|
|
437
|
+
return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
xt_value xt_object_keys(xt_value value) {
|
|
441
|
+
if (!XT_IS_OBJECT(value)) return xt_array_new(0, NULL);
|
|
442
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
443
|
+
xt_array *array = (xt_array *)XT_GET_PTR(xt_array_new(0, NULL));
|
|
444
|
+
for (uint32_t i = 0; i < obj->count; i++) {
|
|
445
|
+
array->items[array->length++] = XT_FROM_PTR(XT_TAG_STRING, obj->properties[i].key);
|
|
446
|
+
}
|
|
447
|
+
return XT_FROM_PTR(XT_TAG_ARRAY, array);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Convenience used by codegen for `obj.prop`. */
|
|
451
|
+
xt_value xt_object_get_cstr(xt_value value, const char *key) {
|
|
452
|
+
xt_value k = xt_string_from_cstr(key);
|
|
453
|
+
return xt_object_get(value, k);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* ------------------------------------------------------------------------- */
|
|
457
|
+
/* Arrays */
|
|
458
|
+
/* ------------------------------------------------------------------------- */
|
|
459
|
+
|
|
460
|
+
static void xt_array_reserve(xt_array *array, uint32_t needed) {
|
|
461
|
+
if (needed <= array->capacity) return;
|
|
462
|
+
uint32_t capacity = array->capacity ? array->capacity * 2 : 8;
|
|
463
|
+
while (capacity < needed) capacity *= 2;
|
|
464
|
+
array->items = (xt_value *)realloc(array->items, sizeof(xt_value) * capacity);
|
|
465
|
+
if (!array->items) {
|
|
466
|
+
fprintf(stderr, "xbintsc: out of memory growing array\n");
|
|
467
|
+
abort();
|
|
468
|
+
}
|
|
469
|
+
for (uint32_t i = array->capacity; i < capacity; i++) array->items[i] = XT_UNDEFINED;
|
|
470
|
+
array->capacity = capacity;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
xt_value xt_array_new(int32_t count, xt_value *items) {
|
|
474
|
+
xt_array *array = (xt_array *)xt_alloc(sizeof(xt_array), XT_OBJECT_KIND_ARRAY);
|
|
475
|
+
array->length = 0;
|
|
476
|
+
array->capacity = 0;
|
|
477
|
+
array->items = NULL;
|
|
478
|
+
if (count > 0) {
|
|
479
|
+
xt_array_reserve(array, (uint32_t)count);
|
|
480
|
+
for (int32_t i = 0; i < count; i++) array->items[i] = items[i];
|
|
481
|
+
array->length = (uint32_t)count;
|
|
482
|
+
}
|
|
483
|
+
return XT_FROM_PTR(XT_TAG_ARRAY, array);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
xt_value xt_array_get(xt_value value, xt_value index) {
|
|
487
|
+
if (!XT_IS_ARRAY(value)) return XT_UNDEFINED;
|
|
488
|
+
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
489
|
+
int32_t i = xt_to_int32(index);
|
|
490
|
+
if (i < 0 || (uint32_t)i >= array->length) return XT_UNDEFINED;
|
|
491
|
+
return array->items[i];
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
xt_value xt_array_set(xt_value value, xt_value index, xt_value newValue) {
|
|
495
|
+
if (!XT_IS_ARRAY(value)) return newValue;
|
|
496
|
+
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
497
|
+
int32_t i = xt_to_int32(index);
|
|
498
|
+
if (i < 0) return newValue;
|
|
499
|
+
xt_array_reserve(array, (uint32_t)i + 1);
|
|
500
|
+
for (uint32_t j = array->length; j < (uint32_t)i; j++) array->items[j] = XT_UNDEFINED;
|
|
501
|
+
array->items[i] = newValue;
|
|
502
|
+
if ((uint32_t)i >= array->length) array->length = (uint32_t)i + 1;
|
|
503
|
+
return newValue;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
xt_value xt_array_push(xt_value value, xt_value newValue) {
|
|
507
|
+
if (!XT_IS_ARRAY(value)) return xt_number(0);
|
|
508
|
+
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
509
|
+
xt_array_reserve(array, array->length + 1);
|
|
510
|
+
array->items[array->length++] = newValue;
|
|
511
|
+
return xt_number((double)array->length);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
xt_value xt_array_length(xt_value value) {
|
|
515
|
+
if (XT_IS_ARRAY(value)) return xt_number((double)((xt_array *)XT_GET_PTR(value))->length);
|
|
516
|
+
if (XT_IS_STRING(value)) return xt_number((double)xt_string_length(xt_as_string(value)));
|
|
517
|
+
return XT_UNDEFINED;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
xt_value xt_array_spread(xt_value target, xt_value source) {
|
|
521
|
+
if (!XT_IS_ARRAY(target) || !XT_IS_ARRAY(source)) return target;
|
|
522
|
+
xt_array *dst = (xt_array *)XT_GET_PTR(target);
|
|
523
|
+
xt_array *src = (xt_array *)XT_GET_PTR(source);
|
|
524
|
+
xt_array_reserve(dst, dst->length + src->length);
|
|
525
|
+
for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
|
|
526
|
+
return target;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* ------------------------------------------------------------------------- */
|
|
530
|
+
/* Generic member access */
|
|
531
|
+
/* ------------------------------------------------------------------------- */
|
|
532
|
+
|
|
533
|
+
xt_value xt_get(xt_value target, xt_value key) {
|
|
534
|
+
if (XT_IS_ARRAY(target)) return xt_array_get(target, key);
|
|
535
|
+
if (XT_IS_OBJECT(target)) return xt_object_get(target, key);
|
|
536
|
+
if (XT_IS_STRING(target)) {
|
|
537
|
+
/* Property access on a string: `.length` and numeric indexing. */
|
|
538
|
+
xt_string *s = xt_as_string(target);
|
|
539
|
+
if (XT_IS_STRING(key)) {
|
|
540
|
+
xt_string *k = xt_as_string(key);
|
|
541
|
+
if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return xt_number((double)s->length);
|
|
542
|
+
}
|
|
543
|
+
int32_t index = xt_to_int32(key);
|
|
544
|
+
if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
|
|
545
|
+
return XT_UNDEFINED;
|
|
546
|
+
}
|
|
547
|
+
return XT_UNDEFINED;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
551
|
+
if (XT_IS_ARRAY(target)) return xt_array_set(target, key, value);
|
|
552
|
+
if (XT_IS_OBJECT(target)) return xt_object_set(target, key, value);
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* ------------------------------------------------------------------------- */
|
|
557
|
+
/* Boxes */
|
|
558
|
+
/* ------------------------------------------------------------------------- */
|
|
559
|
+
|
|
560
|
+
xt_value xt_box_new(xt_value value) { return xt_array_new(1, &value); }
|
|
561
|
+
|
|
562
|
+
xt_value xt_box_get(xt_value box) { return xt_array_get(box, xt_number(0)); }
|
|
563
|
+
|
|
564
|
+
xt_value xt_box_set(xt_value box, xt_value value) { return xt_array_set(box, xt_number(0), value); }
|
|
565
|
+
|
|
566
|
+
xt_value xt_is_nullish(xt_value value) { return xt_bool(value == XT_UNDEFINED || value == XT_NULL); }
|
|
567
|
+
|
|
568
|
+
/* ------------------------------------------------------------------------- */
|
|
569
|
+
/* Functions */
|
|
570
|
+
/* ------------------------------------------------------------------------- */
|
|
571
|
+
|
|
572
|
+
xt_value xt_arg(int32_t argc, xt_value *argv, int32_t index) {
|
|
573
|
+
if (index < 0 || index >= argc) return XT_UNDEFINED;
|
|
574
|
+
return argv[index];
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
xt_value xt_closure_new(void *fn, int32_t env_count, xt_value *env) {
|
|
578
|
+
xt_function *function = (xt_function *)xt_alloc(sizeof(xt_function), XT_OBJECT_KIND_FUNCTION);
|
|
579
|
+
function->code = (xt_code_fn)fn;
|
|
580
|
+
function->environment_count = env_count;
|
|
581
|
+
function->arity = -1;
|
|
582
|
+
function->properties = NULL;
|
|
583
|
+
function->environment = NULL;
|
|
584
|
+
if (env_count > 0) {
|
|
585
|
+
function->environment = (xt_value *)malloc(sizeof(xt_value) * (size_t)env_count);
|
|
586
|
+
for (int32_t i = 0; i < env_count; i++) function->environment[i] = env[i];
|
|
587
|
+
}
|
|
588
|
+
return XT_FROM_PTR(XT_TAG_FUNCTION, function);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
xt_value xt_closure_call(xt_value value, int32_t argc, xt_value *argv) {
|
|
592
|
+
if (!XT_IS_FUNCTION(value)) {
|
|
593
|
+
xt_throw(xt_string_from_cstr("TypeError: value is not a function"));
|
|
594
|
+
return XT_UNDEFINED;
|
|
595
|
+
}
|
|
596
|
+
xt_function *function = (xt_function *)XT_GET_PTR(value);
|
|
597
|
+
/* The closure value itself is threaded through as `env` so the callee can
|
|
598
|
+
* read its captures with xt_closure_env(env, i). */
|
|
599
|
+
return function->code(value, argc, argv);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
xt_value xt_closure_env(xt_value value, int32_t index) {
|
|
603
|
+
if (!XT_IS_FUNCTION(value)) return XT_UNDEFINED;
|
|
604
|
+
xt_function *function = (xt_function *)XT_GET_PTR(value);
|
|
605
|
+
if (index < 0 || index >= function->environment_count) return XT_UNDEFINED;
|
|
606
|
+
return function->environment[index];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
int32_t xt_closure_arity(xt_value value) {
|
|
610
|
+
if (!XT_IS_FUNCTION(value)) return -1;
|
|
611
|
+
return ((xt_function *)XT_GET_PTR(value))->arity;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/* ------------------------------------------------------------------------- */
|
|
615
|
+
/* Exceptions and output */
|
|
616
|
+
/* ------------------------------------------------------------------------- */
|
|
617
|
+
|
|
618
|
+
void xt_throw(xt_value v) {
|
|
619
|
+
xt_value message = xt_to_string(v);
|
|
620
|
+
xt_string *s = xt_as_string(message);
|
|
621
|
+
fprintf(stderr, "Uncaught %s\n", s->data);
|
|
622
|
+
exit(1);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
static void xt_print_value(xt_value v, FILE *out) {
|
|
626
|
+
if (XT_IS_STRING(v)) {
|
|
627
|
+
xt_string *s = xt_as_string(v);
|
|
628
|
+
fwrite(s->data, 1, s->length, out);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
xt_value text = xt_to_string(v);
|
|
632
|
+
xt_string *s = xt_as_string(text);
|
|
633
|
+
fwrite(s->data, 1, s->length, out);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
void xt_print(xt_value v) { xt_print_value(v, stdout); }
|
|
637
|
+
void xt_println(xt_value v) {
|
|
638
|
+
xt_print_value(v, stdout);
|
|
639
|
+
fputc('\n', stdout);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/*
|
|
643
|
+
* console.log applies a Node-like inspection format: strings unquoted at the
|
|
644
|
+
* top level, arrays as `[ a, b ]` and objects as `{ key: value }`.
|
|
645
|
+
*/
|
|
646
|
+
static void xt_inspect(xt_value v, FILE *out) {
|
|
647
|
+
if (XT_IS_ARRAY(v)) {
|
|
648
|
+
xt_array *array = (xt_array *)XT_GET_PTR(v);
|
|
649
|
+
fputc('[', out);
|
|
650
|
+
for (uint32_t i = 0; i < array->length; i++) {
|
|
651
|
+
if (i > 0) fputs(", ", out);
|
|
652
|
+
else fputc(' ', out);
|
|
653
|
+
xt_inspect(array->items[i], out);
|
|
654
|
+
}
|
|
655
|
+
if (array->length > 0) fputc(' ', out);
|
|
656
|
+
fputc(']', out);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (XT_IS_OBJECT(v)) {
|
|
660
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(v);
|
|
661
|
+
fputc('{', out);
|
|
662
|
+
for (uint32_t i = 0; i < obj->count; i++) {
|
|
663
|
+
if (i > 0) fputs(", ", out);
|
|
664
|
+
else fputc(' ', out);
|
|
665
|
+
fwrite(obj->properties[i].key->data, 1, obj->properties[i].key->length, out);
|
|
666
|
+
fputs(": ", out);
|
|
667
|
+
xt_inspect(obj->properties[i].value, out);
|
|
668
|
+
}
|
|
669
|
+
if (obj->count > 0) fputc(' ', out);
|
|
670
|
+
fputc('}', out);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
xt_print_value(v, out);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
void xt_console_log(int32_t argc, xt_value *argv) {
|
|
677
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
678
|
+
if (i > 0) fputc(' ', stdout);
|
|
679
|
+
xt_inspect(argv[i], stdout);
|
|
680
|
+
}
|
|
681
|
+
fputc('\n', stdout);
|
|
682
|
+
}
|