toilscript 0.1.6 → 0.1.7
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/dist/cli.js +526 -3
- package/dist/cli.js.map +2 -2
- package/dist/importmap.json +2 -2
- package/dist/web.js +3 -3
- package/package.json +3 -2
- package/std/assembly/json.ts +523 -0
- package/std/assembly/toilscript.d.ts +2 -2
package/dist/importmap.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"imports": {
|
|
3
|
-
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
4
|
-
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
3
|
+
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.7/dist/toilscript.js",
|
|
4
|
+
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.7/dist/cli.js",
|
|
5
5
|
"binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/index.js",
|
|
6
6
|
"long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
|
|
7
7
|
}
|
package/dist/web.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var ASSEMBLYSCRIPT_VERSION = "0.1.
|
|
1
|
+
var ASSEMBLYSCRIPT_VERSION = "0.1.7";
|
|
2
2
|
var ASSEMBLYSCRIPT_IMPORTMAP = {
|
|
3
3
|
"imports": {
|
|
4
|
-
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
5
|
-
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
4
|
+
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.7/dist/toilscript.js",
|
|
5
|
+
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.7/dist/cli.js",
|
|
6
6
|
"binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/index.js",
|
|
7
7
|
"long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
|
|
8
8
|
}
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"toilscript",
|
|
9
9
|
"wasm"
|
|
10
10
|
],
|
|
11
|
-
"version": "0.1.
|
|
11
|
+
"version": "0.1.7",
|
|
12
12
|
"author": "Daniel Wirtz <dcode+assemblyscript@dcode.io>",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"homepage": "https://github.com/dacely-cloud/toilscript",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"prepublishOnly": "node scripts/build",
|
|
81
81
|
"watch": "node scripts/build --watch",
|
|
82
82
|
"coverage": "npx c8 -- npm test",
|
|
83
|
-
"test": "npm run test:parser && npm run test:compiler -- --parallel && npm run test:browser && npm run test:toilconfig && npm run test:transform && npm run test:cli",
|
|
83
|
+
"test": "npm run test:parser && npm run test:compiler -- --parallel && npm run test:browser && npm run test:toilconfig && npm run test:transform && npm run test:cli && npm run test:json",
|
|
84
84
|
"test:parser": "node --enable-source-maps tests/parser",
|
|
85
85
|
"test:compiler": "node --enable-source-maps --no-warnings tests/compiler",
|
|
86
86
|
"test:browser": "node --enable-source-maps tests/browser",
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
"test:transform:esm": "node bin/toilscript tests/compiler/empty --transform ./tests/transform/index.js --noEmit && node bin/toilscript tests/compiler/empty --transform ./tests/transform/simple.js --noEmit",
|
|
90
90
|
"test:transform:cjs": "node bin/toilscript tests/compiler/empty --transform ./tests/transform/cjs/index.js --noEmit && node bin/toilscript tests/compiler/empty --transform ./tests/transform/cjs/simple.js --noEmit",
|
|
91
91
|
"test:cli": "node tests/cli/options.js",
|
|
92
|
+
"test:json": "node tests/json/run.mjs",
|
|
92
93
|
"asbuild": "npm run asbuild:debug && npm run asbuild:release",
|
|
93
94
|
"asbuild:debug": "node bin/toilscript --config src/toilconfig.json --target debug",
|
|
94
95
|
"asbuild:release": "node bin/toilscript --config src/toilconfig.json --target release",
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON for the ToilScript standard library.
|
|
3
|
+
*
|
|
4
|
+
* `JSON` is a zero-import global (this is a top-level library entry). It is both
|
|
5
|
+
* a dynamic value tree (`JSON.obj().set(...)`, `JSON.arr().push(...)`, returned
|
|
6
|
+
* from a `toJSON(): JSON` method, or produced by `JSON.parse`), and the home of
|
|
7
|
+
* the typed serializer `JSON.stringify<T>`.
|
|
8
|
+
*
|
|
9
|
+
* Booleans, integers (signed i64 and unsigned u64), floats (NaN/±Infinity emit
|
|
10
|
+
* `null`), strings, null, arrays and objects are supported. Malformed `parse`
|
|
11
|
+
* input yields an error value (`isError()`), never a trap.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export class JSON {
|
|
15
|
+
// ---- value kinds ----
|
|
16
|
+
private static readonly NULL: u8 = 0;
|
|
17
|
+
private static readonly BOOL: u8 = 1;
|
|
18
|
+
private static readonly NUM: u8 = 2;
|
|
19
|
+
private static readonly STR: u8 = 3;
|
|
20
|
+
private static readonly ARR: u8 = 4;
|
|
21
|
+
private static readonly OBJ: u8 = 5;
|
|
22
|
+
private static readonly ERR: u8 = 6;
|
|
23
|
+
|
|
24
|
+
// number sub-kinds
|
|
25
|
+
private static readonly N_I64: u8 = 0;
|
|
26
|
+
private static readonly N_U64: u8 = 1;
|
|
27
|
+
private static readonly N_F64: u8 = 2;
|
|
28
|
+
|
|
29
|
+
kind: u8 = JSON.NULL;
|
|
30
|
+
private b: bool = false;
|
|
31
|
+
private inum: i64 = 0;
|
|
32
|
+
private unum: u64 = 0;
|
|
33
|
+
private fnum: f64 = 0;
|
|
34
|
+
private numKind: u8 = JSON.N_I64;
|
|
35
|
+
private str: string = "";
|
|
36
|
+
private items: Array<JSON> | null = null; // ARR
|
|
37
|
+
private keys: Array<string> | null = null; // OBJ
|
|
38
|
+
private vals: Array<JSON> | null = null; // OBJ
|
|
39
|
+
|
|
40
|
+
// ---- value factories ----
|
|
41
|
+
|
|
42
|
+
static nul(): JSON {
|
|
43
|
+
return new JSON();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Wrap a scalar (boolean, signed/unsigned integer, float or string). */
|
|
47
|
+
static of<T>(value: T): JSON {
|
|
48
|
+
const j = new JSON();
|
|
49
|
+
if (isBoolean<T>()) {
|
|
50
|
+
j.kind = JSON.BOOL;
|
|
51
|
+
j.b = value;
|
|
52
|
+
} else if (isInteger<T>()) {
|
|
53
|
+
j.kind = JSON.NUM;
|
|
54
|
+
if (isSigned<T>()) {
|
|
55
|
+
j.numKind = JSON.N_I64;
|
|
56
|
+
j.inum = <i64>value;
|
|
57
|
+
} else {
|
|
58
|
+
j.numKind = JSON.N_U64;
|
|
59
|
+
j.unum = <u64>value;
|
|
60
|
+
}
|
|
61
|
+
} else if (isFloat<T>()) {
|
|
62
|
+
j.kind = JSON.NUM;
|
|
63
|
+
j.numKind = JSON.N_F64;
|
|
64
|
+
j.fnum = <f64>value;
|
|
65
|
+
} else if (isString<T>()) {
|
|
66
|
+
j.kind = JSON.STR;
|
|
67
|
+
j.str = changetype<string>(value);
|
|
68
|
+
} else {
|
|
69
|
+
ERROR("JSON.of: expected a boolean, number or string");
|
|
70
|
+
}
|
|
71
|
+
return j;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static arr(): JSON {
|
|
75
|
+
const j = new JSON();
|
|
76
|
+
j.kind = JSON.ARR;
|
|
77
|
+
j.items = new Array<JSON>();
|
|
78
|
+
return j;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static obj(): JSON {
|
|
82
|
+
const j = new JSON();
|
|
83
|
+
j.kind = JSON.OBJ;
|
|
84
|
+
j.keys = new Array<string>();
|
|
85
|
+
j.vals = new Array<JSON>();
|
|
86
|
+
return j;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---- builders (chainable) ----
|
|
90
|
+
|
|
91
|
+
push(value: JSON): JSON {
|
|
92
|
+
this.items!.push(value);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
set(key: string, value: JSON): JSON {
|
|
97
|
+
this.keys!.push(key);
|
|
98
|
+
this.vals!.push(value);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ---- kind predicates ----
|
|
103
|
+
isNull(): bool { return this.kind == JSON.NULL; }
|
|
104
|
+
isBool(): bool { return this.kind == JSON.BOOL; }
|
|
105
|
+
isNumber(): bool { return this.kind == JSON.NUM; }
|
|
106
|
+
isString(): bool { return this.kind == JSON.STR; }
|
|
107
|
+
isArray(): bool { return this.kind == JSON.ARR; }
|
|
108
|
+
isObject(): bool { return this.kind == JSON.OBJ; }
|
|
109
|
+
isError(): bool { return this.kind == JSON.ERR; }
|
|
110
|
+
|
|
111
|
+
/** The message of an error value, else "". */
|
|
112
|
+
errorMessage(): string {
|
|
113
|
+
return this.kind == JSON.ERR ? this.str : "";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ---- scalar accessors (0 / "" off-kind) ----
|
|
117
|
+
asBool(): bool {
|
|
118
|
+
return this.kind == JSON.BOOL ? this.b : false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
asF64(): f64 {
|
|
122
|
+
if (this.kind != JSON.NUM) return 0;
|
|
123
|
+
if (this.numKind == JSON.N_I64) return <f64>this.inum;
|
|
124
|
+
if (this.numKind == JSON.N_U64) return <f64>this.unum;
|
|
125
|
+
return this.fnum;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
asI64(): i64 {
|
|
129
|
+
if (this.kind != JSON.NUM) return 0;
|
|
130
|
+
if (this.numKind == JSON.N_I64) return this.inum;
|
|
131
|
+
if (this.numKind == JSON.N_U64) return <i64>this.unum;
|
|
132
|
+
return <i64>this.fnum;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
asU64(): u64 {
|
|
136
|
+
if (this.kind != JSON.NUM) return 0;
|
|
137
|
+
if (this.numKind == JSON.N_U64) return this.unum;
|
|
138
|
+
if (this.numKind == JSON.N_I64) return <u64>this.inum;
|
|
139
|
+
return <u64>this.fnum;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
asString(): string {
|
|
143
|
+
return this.kind == JSON.STR ? this.str : "";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ---- container accessors ----
|
|
147
|
+
length(): i32 {
|
|
148
|
+
if (this.kind == JSON.ARR) return this.items!.length;
|
|
149
|
+
if (this.kind == JSON.OBJ) return this.keys!.length;
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Element of an array value, or a null value if out of range / not an array. */
|
|
154
|
+
at(index: i32): JSON {
|
|
155
|
+
if (this.kind == JSON.ARR) {
|
|
156
|
+
const items = this.items!;
|
|
157
|
+
if (index >= 0 && index < items.length) return items[index];
|
|
158
|
+
}
|
|
159
|
+
return JSON.nul();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
has(key: string): bool {
|
|
163
|
+
if (this.kind == JSON.OBJ) {
|
|
164
|
+
const keys = this.keys!;
|
|
165
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
166
|
+
if (keys[i] == key) return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Value of an object key, or a null value if absent / not an object. */
|
|
173
|
+
get(key: string): JSON {
|
|
174
|
+
if (this.kind == JSON.OBJ) {
|
|
175
|
+
const keys = this.keys!;
|
|
176
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
177
|
+
if (keys[i] == key) return this.vals![i];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return JSON.nul();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Keys of an object value, else an empty array. */
|
|
184
|
+
objectKeys(): Array<string> {
|
|
185
|
+
return this.kind == JSON.OBJ ? this.keys! : new Array<string>();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---- serialize this value to JSON text ----
|
|
189
|
+
toString(): string {
|
|
190
|
+
switch (this.kind) {
|
|
191
|
+
case JSON.BOOL:
|
|
192
|
+
return this.b ? "true" : "false";
|
|
193
|
+
case JSON.NUM:
|
|
194
|
+
if (this.numKind == JSON.N_I64) return this.inum.toString();
|
|
195
|
+
if (this.numKind == JSON.N_U64) return this.unum.toString();
|
|
196
|
+
return isFinite(this.fnum) ? this.fnum.toString() : "null";
|
|
197
|
+
case JSON.STR:
|
|
198
|
+
return JSON.quote(this.str);
|
|
199
|
+
case JSON.ARR: {
|
|
200
|
+
const items = this.items!;
|
|
201
|
+
const parts = new Array<string>(items.length);
|
|
202
|
+
for (let i = 0, n = items.length; i < n; i++) parts[i] = items[i].toString();
|
|
203
|
+
return "[" + parts.join(",") + "]";
|
|
204
|
+
}
|
|
205
|
+
case JSON.OBJ: {
|
|
206
|
+
const keys = this.keys!;
|
|
207
|
+
const vals = this.vals!;
|
|
208
|
+
const parts = new Array<string>(keys.length);
|
|
209
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
210
|
+
parts[i] = JSON.quote(keys[i]) + ":" + vals[i].toString();
|
|
211
|
+
}
|
|
212
|
+
return "{" + parts.join(",") + "}";
|
|
213
|
+
}
|
|
214
|
+
default:
|
|
215
|
+
// NULL and ERR both serialize to null.
|
|
216
|
+
return "null";
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ---- typed serializer: AS value -> JSON text, by compile-time type ----
|
|
221
|
+
static stringify<T>(value: T): string {
|
|
222
|
+
if (isBoolean<T>()) {
|
|
223
|
+
return value ? "true" : "false";
|
|
224
|
+
}
|
|
225
|
+
if (isInteger<T>()) {
|
|
226
|
+
return value.toString();
|
|
227
|
+
}
|
|
228
|
+
if (isFloat<T>()) {
|
|
229
|
+
return isFinite(value) ? value.toString() : "null";
|
|
230
|
+
}
|
|
231
|
+
if (isString<T>()) {
|
|
232
|
+
if (changetype<usize>(value) == 0) return "null";
|
|
233
|
+
return JSON.quote(changetype<string>(value));
|
|
234
|
+
}
|
|
235
|
+
if (isArray<T>()) {
|
|
236
|
+
if (changetype<usize>(value) == 0) return "null";
|
|
237
|
+
const arr = changetype<Array<valueof<T>>>(value);
|
|
238
|
+
const parts = new Array<string>(arr.length);
|
|
239
|
+
for (let i = 0, n = arr.length; i < n; i++) parts[i] = JSON.stringify<valueof<T>>(arr[i]);
|
|
240
|
+
return "[" + parts.join(",") + "]";
|
|
241
|
+
}
|
|
242
|
+
ERROR("JSON.stringify: unsupported type, booleans, numbers, strings, null and arrays only");
|
|
243
|
+
return unreachable();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ---- parse: JSON text -> value tree ----
|
|
247
|
+
static parse(text: string): JSON {
|
|
248
|
+
const p = new Parser(text);
|
|
249
|
+
const v = p.parseValue();
|
|
250
|
+
if (p.err.length != 0) return JSON.errorValue(p.err);
|
|
251
|
+
p.skipWs();
|
|
252
|
+
if (!p.atEnd()) return JSON.errorValue("unexpected trailing characters");
|
|
253
|
+
return v;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
static errorValue(message: string): JSON {
|
|
257
|
+
const j = new JSON();
|
|
258
|
+
j.kind = JSON.ERR;
|
|
259
|
+
j.str = message;
|
|
260
|
+
return j;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// internal builders used by the parser to keep number sub-kind exact
|
|
264
|
+
static fromI64(value: i64): JSON {
|
|
265
|
+
const j = new JSON();
|
|
266
|
+
j.kind = JSON.NUM;
|
|
267
|
+
j.numKind = JSON.N_I64;
|
|
268
|
+
j.inum = value;
|
|
269
|
+
return j;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
static fromU64(value: u64): JSON {
|
|
273
|
+
const j = new JSON();
|
|
274
|
+
j.kind = JSON.NUM;
|
|
275
|
+
j.numKind = JSON.N_U64;
|
|
276
|
+
j.unum = value;
|
|
277
|
+
return j;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static fromF64(value: f64): JSON {
|
|
281
|
+
const j = new JSON();
|
|
282
|
+
j.kind = JSON.NUM;
|
|
283
|
+
j.numKind = JSON.N_F64;
|
|
284
|
+
j.fnum = value;
|
|
285
|
+
return j;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
static fromString(value: string): JSON {
|
|
289
|
+
const j = new JSON();
|
|
290
|
+
j.kind = JSON.STR;
|
|
291
|
+
j.str = value;
|
|
292
|
+
return j;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ---- shared string escaping ----
|
|
296
|
+
|
|
297
|
+
private static quote(s: string): string {
|
|
298
|
+
let out = "\"";
|
|
299
|
+
for (let i = 0, len = s.length; i < len; i++) {
|
|
300
|
+
const c = s.charCodeAt(i);
|
|
301
|
+
if (c == 0x22) out += "\\\"";
|
|
302
|
+
else if (c == 0x5c) out += "\\\\";
|
|
303
|
+
else if (c == 0x08) out += "\\b";
|
|
304
|
+
else if (c == 0x0c) out += "\\f";
|
|
305
|
+
else if (c == 0x0a) out += "\\n";
|
|
306
|
+
else if (c == 0x0d) out += "\\r";
|
|
307
|
+
else if (c == 0x09) out += "\\t";
|
|
308
|
+
else if (c < 0x20) out += "\\u" + JSON.hex4(c);
|
|
309
|
+
else out += String.fromCharCode(c);
|
|
310
|
+
}
|
|
311
|
+
return out + "\"";
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private static hex4(c: i32): string {
|
|
315
|
+
const digits = "0123456789abcdef";
|
|
316
|
+
let r = "";
|
|
317
|
+
for (let shift = 12; shift >= 0; shift -= 4) r += digits.charAt((c >> shift) & 0xf);
|
|
318
|
+
return r;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Recursive-descent parser producing a `JSON` value tree. Internal. */
|
|
323
|
+
class Parser {
|
|
324
|
+
private src: string;
|
|
325
|
+
private pos: i32 = 0;
|
|
326
|
+
err: string = "";
|
|
327
|
+
|
|
328
|
+
constructor(src: string) {
|
|
329
|
+
this.src = src;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
atEnd(): bool {
|
|
333
|
+
return this.pos >= this.src.length;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Always called after an atEnd() guard, so pos is in range.
|
|
337
|
+
private peek(): i32 {
|
|
338
|
+
return this.src.charCodeAt(this.pos);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private take(): i32 {
|
|
342
|
+
return this.pos < this.src.length ? this.src.charCodeAt(this.pos++) : -1;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
skipWs(): void {
|
|
346
|
+
while (this.pos < this.src.length) {
|
|
347
|
+
const c = this.src.charCodeAt(this.pos);
|
|
348
|
+
if (c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d) this.pos++;
|
|
349
|
+
else break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Callers return as soon as err is set, so fail is only reached with err empty.
|
|
354
|
+
private fail(message: string): JSON {
|
|
355
|
+
this.err = message;
|
|
356
|
+
return JSON.nul();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
parseValue(): JSON {
|
|
360
|
+
this.skipWs();
|
|
361
|
+
if (this.atEnd()) return this.fail("unexpected end of input");
|
|
362
|
+
const c = this.peek();
|
|
363
|
+
if (c == 0x7b) return this.parseObject(); // {
|
|
364
|
+
if (c == 0x5b) return this.parseArray(); // [
|
|
365
|
+
if (c == 0x22) return JSON.fromString(this.readString()); // "
|
|
366
|
+
if (c == 0x74) return this.parseLiteral("true", JSON.of<bool>(true)); // t
|
|
367
|
+
if (c == 0x66) return this.parseLiteral("false", JSON.of<bool>(false)); // f
|
|
368
|
+
if (c == 0x6e) return this.parseLiteral("null", JSON.nul()); // n
|
|
369
|
+
if (c == 0x2d || (c >= 0x30 && c <= 0x39)) return this.parseNumber(); // - or digit
|
|
370
|
+
return this.fail("unexpected character");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private parseLiteral(word: string, value: JSON): JSON {
|
|
374
|
+
for (let i = 0, n = word.length; i < n; i++) {
|
|
375
|
+
if (this.take() != word.charCodeAt(i)) return this.fail("invalid literal");
|
|
376
|
+
}
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Read a quoted string starting at the current `"`. Sets err on failure. */
|
|
381
|
+
private readString(): string {
|
|
382
|
+
this.pos++; // opening "
|
|
383
|
+
let out = "";
|
|
384
|
+
while (true) {
|
|
385
|
+
if (this.atEnd()) {
|
|
386
|
+
this.fail("unterminated string");
|
|
387
|
+
return "";
|
|
388
|
+
}
|
|
389
|
+
const c = this.take();
|
|
390
|
+
if (c == 0x22) break; // closing "
|
|
391
|
+
if (c == 0x5c) {
|
|
392
|
+
if (this.atEnd()) {
|
|
393
|
+
this.fail("unterminated escape");
|
|
394
|
+
return "";
|
|
395
|
+
}
|
|
396
|
+
const e = this.take();
|
|
397
|
+
if (e == 0x22) out += "\"";
|
|
398
|
+
else if (e == 0x5c) out += "\\";
|
|
399
|
+
else if (e == 0x2f) out += "/";
|
|
400
|
+
else if (e == 0x62) out += String.fromCharCode(0x08);
|
|
401
|
+
else if (e == 0x66) out += String.fromCharCode(0x0c);
|
|
402
|
+
else if (e == 0x6e) out += "\n";
|
|
403
|
+
else if (e == 0x72) out += "\r";
|
|
404
|
+
else if (e == 0x74) out += "\t";
|
|
405
|
+
else if (e == 0x75) {
|
|
406
|
+
let code = 0;
|
|
407
|
+
for (let k = 0; k < 4; k++) {
|
|
408
|
+
const d = hexVal(this.take());
|
|
409
|
+
if (d < 0) {
|
|
410
|
+
this.fail("invalid \\u escape");
|
|
411
|
+
return "";
|
|
412
|
+
}
|
|
413
|
+
code = (code << 4) | d;
|
|
414
|
+
}
|
|
415
|
+
out += String.fromCharCode(code);
|
|
416
|
+
} else {
|
|
417
|
+
this.fail("invalid escape");
|
|
418
|
+
return "";
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
out += String.fromCharCode(c);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return out;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private parseNumber(): JSON {
|
|
428
|
+
const start = this.pos;
|
|
429
|
+
let isFloating = false;
|
|
430
|
+
if (this.peek() == 0x2d) this.pos++; // -
|
|
431
|
+
let intDigits = 0;
|
|
432
|
+
while (!this.atEnd() && this.peek() >= 0x30 && this.peek() <= 0x39) {
|
|
433
|
+
this.pos++;
|
|
434
|
+
intDigits++;
|
|
435
|
+
}
|
|
436
|
+
if (intDigits == 0) return this.fail("invalid number");
|
|
437
|
+
if (!this.atEnd() && this.peek() == 0x2e) {
|
|
438
|
+
isFloating = true;
|
|
439
|
+
this.pos++;
|
|
440
|
+
let frac = 0;
|
|
441
|
+
while (!this.atEnd() && this.peek() >= 0x30 && this.peek() <= 0x39) {
|
|
442
|
+
this.pos++;
|
|
443
|
+
frac++;
|
|
444
|
+
}
|
|
445
|
+
if (frac == 0) return this.fail("invalid number");
|
|
446
|
+
}
|
|
447
|
+
if (!this.atEnd() && (this.peek() == 0x65 || this.peek() == 0x45)) {
|
|
448
|
+
isFloating = true;
|
|
449
|
+
this.pos++;
|
|
450
|
+
if (!this.atEnd() && (this.peek() == 0x2b || this.peek() == 0x2d)) this.pos++;
|
|
451
|
+
let exp = 0;
|
|
452
|
+
while (!this.atEnd() && this.peek() >= 0x30 && this.peek() <= 0x39) {
|
|
453
|
+
this.pos++;
|
|
454
|
+
exp++;
|
|
455
|
+
}
|
|
456
|
+
if (exp == 0) return this.fail("invalid number");
|
|
457
|
+
}
|
|
458
|
+
const slice = this.src.substring(start, this.pos);
|
|
459
|
+
if (isFloating) return JSON.fromF64(parseFloat(slice));
|
|
460
|
+
if (slice.charCodeAt(0) == 0x2d) {
|
|
461
|
+
let v: i64 = 0;
|
|
462
|
+
for (let i = 1, n = slice.length; i < n; i++) v = v * 10 - <i64>(slice.charCodeAt(i) - 0x30);
|
|
463
|
+
return JSON.fromI64(v);
|
|
464
|
+
}
|
|
465
|
+
let v: u64 = 0;
|
|
466
|
+
for (let i = 0, n = slice.length; i < n; i++) v = v * 10 + <u64>(slice.charCodeAt(i) - 0x30);
|
|
467
|
+
return JSON.fromU64(v);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private parseArray(): JSON {
|
|
471
|
+
this.pos++; // [
|
|
472
|
+
const j = JSON.arr();
|
|
473
|
+
this.skipWs();
|
|
474
|
+
if (!this.atEnd() && this.peek() == 0x5d) {
|
|
475
|
+
this.pos++;
|
|
476
|
+
return j;
|
|
477
|
+
}
|
|
478
|
+
while (true) {
|
|
479
|
+
const v = this.parseValue();
|
|
480
|
+
if (this.err.length != 0) return j;
|
|
481
|
+
j.push(v);
|
|
482
|
+
this.skipWs();
|
|
483
|
+
const c = this.take();
|
|
484
|
+
if (c == 0x5d) break; // ]
|
|
485
|
+
if (c != 0x2c) return this.fail("expected ',' or ']'"); // ,
|
|
486
|
+
}
|
|
487
|
+
return j;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private parseObject(): JSON {
|
|
491
|
+
this.pos++; // {
|
|
492
|
+
const j = JSON.obj();
|
|
493
|
+
this.skipWs();
|
|
494
|
+
if (!this.atEnd() && this.peek() == 0x7d) {
|
|
495
|
+
this.pos++;
|
|
496
|
+
return j;
|
|
497
|
+
}
|
|
498
|
+
while (true) {
|
|
499
|
+
this.skipWs();
|
|
500
|
+
if (this.atEnd() || this.peek() != 0x22) return this.fail("expected string key");
|
|
501
|
+
const key = this.readString();
|
|
502
|
+
if (this.err.length != 0) return j;
|
|
503
|
+
this.skipWs();
|
|
504
|
+
if (this.take() != 0x3a) return this.fail("expected ':'"); // :
|
|
505
|
+
const v = this.parseValue();
|
|
506
|
+
if (this.err.length != 0) return j;
|
|
507
|
+
j.set(key, v);
|
|
508
|
+
this.skipWs();
|
|
509
|
+
const c = this.take();
|
|
510
|
+
if (c == 0x7d) break; // }
|
|
511
|
+
if (c != 0x2c) return this.fail("expected ',' or '}'"); // ,
|
|
512
|
+
}
|
|
513
|
+
return j;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Hex digit value, or -1. Internal. */
|
|
518
|
+
function hexVal(c: i32): i32 {
|
|
519
|
+
if (c >= 0x30 && c <= 0x39) return c - 0x30;
|
|
520
|
+
if (c >= 0x61 && c <= 0x66) return c - 0x61 + 10;
|
|
521
|
+
if (c >= 0x41 && c <= 0x46) return c - 0x41 + 10;
|
|
522
|
+
return -1;
|
|
523
|
+
}
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Marks a single top-level function as the module entry point. The compiler
|
|
13
|
-
* exports it as the WebAssembly export `main
|
|
13
|
+
* exports it as the WebAssembly export `main`, no `export` keyword needed.
|
|
14
14
|
* Exactly one `@main` is allowed per module.
|
|
15
15
|
*/
|
|
16
16
|
declare function main(): void;
|
|
17
17
|
|
|
18
|
-
// Big integers
|
|
18
|
+
// Big integers, native globals implemented in std/assembly/bignum. The
|
|
19
19
|
// arithmetic/bitwise/comparison operators
|
|
20
20
|
// (+ - * / % & | ^ << >> == != < > <= >=) are operator overloads resolved by
|
|
21
21
|
// the compiler; the equivalent static methods are listed for tooling.
|