herb 0.7.1-arm64-darwin → 0.7.3-arm64-darwin
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.
- checksums.yaml +4 -4
- data/Makefile +2 -0
- data/README.md +1 -1
- data/Rakefile +46 -1
- data/config.yml +714 -0
- data/ext/herb/error_helpers.c +27 -27
- data/ext/herb/extconf.rb +2 -1
- data/ext/herb/extension.c +6 -6
- data/ext/herb/extension_helpers.c +3 -3
- data/ext/herb/nodes.c +35 -35
- data/herb.gemspec +3 -0
- data/lib/herb/3.0/herb.bundle +0 -0
- data/lib/herb/3.1/herb.bundle +0 -0
- data/lib/herb/3.2/herb.bundle +0 -0
- data/lib/herb/3.3/herb.bundle +0 -0
- data/lib/herb/3.4/herb.bundle +0 -0
- data/lib/herb/engine/debug_visitor.rb +41 -21
- data/lib/herb/engine.rb +20 -6
- data/lib/herb/version.rb +1 -1
- data/sig/herb/engine/debug_visitor.rbs +3 -3
- data/sig/herb/engine.rbs +5 -0
- data/src/analyze.c +5 -9
- data/src/analyze_helpers.c +17 -6
- data/src/include/pretty_print.h +1 -1
- data/src/include/version.h +1 -1
- data/src/parser.c +6 -9
- data/src/pretty_print.c +1 -1
- data/templates/ext/herb/error_helpers.c.erb +85 -0
- data/templates/ext/herb/error_helpers.h.erb +12 -0
- data/templates/ext/herb/nodes.c.erb +90 -0
- data/templates/ext/herb/nodes.h.erb +9 -0
- data/templates/javascript/packages/core/src/errors.ts.erb +193 -0
- data/templates/javascript/packages/core/src/node-type-guards.ts.erb +325 -0
- data/templates/javascript/packages/core/src/nodes.ts.erb +414 -0
- data/templates/javascript/packages/core/src/visitor.ts.erb +29 -0
- data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +113 -0
- data/templates/javascript/packages/node/extension/error_helpers.h.erb +17 -0
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +111 -0
- data/templates/javascript/packages/node/extension/nodes.h.erb +17 -0
- data/templates/lib/herb/ast/nodes.rb.erb +117 -0
- data/templates/lib/herb/errors.rb.erb +106 -0
- data/templates/lib/herb/visitor.rb.erb +28 -0
- data/templates/sig/serialized_ast_errors.rbs.erb +10 -0
- data/templates/sig/serialized_ast_nodes.rbs.erb +10 -0
- data/templates/src/ast_nodes.c.erb +145 -0
- data/templates/src/ast_pretty_print.c.erb +97 -0
- data/templates/src/errors.c.erb +245 -0
- data/templates/src/include/ast_nodes.h.erb +46 -0
- data/templates/src/include/ast_pretty_print.h.erb +14 -0
- data/templates/src/include/errors.h.erb +58 -0
- data/templates/src/visitor.c.erb +47 -0
- data/templates/template.rb +406 -0
- data/templates/wasm/error_helpers.cpp.erb +93 -0
- data/templates/wasm/error_helpers.h.erb +15 -0
- data/templates/wasm/nodes.cpp.erb +79 -0
- data/templates/wasm/nodes.h.erb +15 -0
- data/vendor/prism/Rakefile +75 -0
- data/vendor/prism/config.yml +4713 -0
- data/vendor/prism/include/prism/ast.h +8190 -0
- data/vendor/prism/include/prism/defines.h +260 -0
- data/vendor/prism/include/prism/diagnostic.h +455 -0
- data/vendor/prism/include/prism/encoding.h +283 -0
- data/vendor/prism/include/prism/node.h +129 -0
- data/vendor/prism/include/prism/options.h +482 -0
- data/vendor/prism/include/prism/pack.h +163 -0
- data/vendor/prism/include/prism/parser.h +933 -0
- data/vendor/prism/include/prism/prettyprint.h +34 -0
- data/vendor/prism/include/prism/regexp.h +43 -0
- data/vendor/prism/include/prism/static_literals.h +121 -0
- data/vendor/prism/include/prism/util/pm_buffer.h +236 -0
- data/vendor/prism/include/prism/util/pm_char.h +204 -0
- data/vendor/prism/include/prism/util/pm_constant_pool.h +218 -0
- data/vendor/prism/include/prism/util/pm_integer.h +130 -0
- data/vendor/prism/include/prism/util/pm_list.h +103 -0
- data/vendor/prism/include/prism/util/pm_memchr.h +29 -0
- data/vendor/prism/include/prism/util/pm_newline_list.h +113 -0
- data/vendor/prism/include/prism/util/pm_string.h +200 -0
- data/vendor/prism/include/prism/util/pm_strncasecmp.h +32 -0
- data/vendor/prism/include/prism/util/pm_strpbrk.h +46 -0
- data/vendor/prism/include/prism/version.h +29 -0
- data/vendor/prism/include/prism.h +408 -0
- data/vendor/prism/src/diagnostic.c +848 -0
- data/vendor/prism/src/encoding.c +5235 -0
- data/vendor/prism/src/node.c +8676 -0
- data/vendor/prism/src/options.c +328 -0
- data/vendor/prism/src/pack.c +509 -0
- data/vendor/prism/src/prettyprint.c +8941 -0
- data/vendor/prism/src/prism.c +23302 -0
- data/vendor/prism/src/regexp.c +790 -0
- data/vendor/prism/src/serialize.c +2268 -0
- data/vendor/prism/src/static_literals.c +617 -0
- data/vendor/prism/src/token_type.c +703 -0
- data/vendor/prism/src/util/pm_buffer.c +357 -0
- data/vendor/prism/src/util/pm_char.c +318 -0
- data/vendor/prism/src/util/pm_constant_pool.c +342 -0
- data/vendor/prism/src/util/pm_integer.c +670 -0
- data/vendor/prism/src/util/pm_list.c +49 -0
- data/vendor/prism/src/util/pm_memchr.c +35 -0
- data/vendor/prism/src/util/pm_newline_list.c +125 -0
- data/vendor/prism/src/util/pm_string.c +383 -0
- data/vendor/prism/src/util/pm_strncasecmp.c +36 -0
- data/vendor/prism/src/util/pm_strpbrk.c +206 -0
- data/vendor/prism/templates/ext/prism/api_node.c.erb +282 -0
- data/vendor/prism/templates/include/prism/ast.h.erb +226 -0
- data/vendor/prism/templates/include/prism/diagnostic.h.erb +130 -0
- data/vendor/prism/templates/java/org/prism/AbstractNodeVisitor.java.erb +22 -0
- data/vendor/prism/templates/java/org/prism/Loader.java.erb +434 -0
- data/vendor/prism/templates/java/org/prism/Nodes.java.erb +403 -0
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +448 -0
- data/vendor/prism/templates/javascript/src/nodes.js.erb +197 -0
- data/vendor/prism/templates/javascript/src/visitor.js.erb +78 -0
- data/vendor/prism/templates/lib/prism/compiler.rb.erb +43 -0
- data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +103 -0
- data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +189 -0
- data/vendor/prism/templates/lib/prism/dsl.rb.erb +133 -0
- data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +131 -0
- data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +19 -0
- data/vendor/prism/templates/lib/prism/node.rb.erb +515 -0
- data/vendor/prism/templates/lib/prism/reflection.rb.erb +136 -0
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +602 -0
- data/vendor/prism/templates/lib/prism/visitor.rb.erb +55 -0
- data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +68 -0
- data/vendor/prism/templates/rbi/prism/node.rbi.erb +164 -0
- data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +18 -0
- data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +45 -0
- data/vendor/prism/templates/sig/prism/dsl.rbs.erb +31 -0
- data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +7 -0
- data/vendor/prism/templates/sig/prism/node.rbs.erb +132 -0
- data/vendor/prism/templates/sig/prism/visitor.rbs.erb +17 -0
- data/vendor/prism/templates/sig/prism.rbs.erb +89 -0
- data/vendor/prism/templates/src/diagnostic.c.erb +523 -0
- data/vendor/prism/templates/src/node.c.erb +333 -0
- data/vendor/prism/templates/src/prettyprint.c.erb +166 -0
- data/vendor/prism/templates/src/serialize.c.erb +406 -0
- data/vendor/prism/templates/src/token_type.c.erb +369 -0
- data/vendor/prism/templates/template.rb +689 -0
- metadata +112 -2
@@ -0,0 +1,448 @@
|
|
1
|
+
import * as nodes from "./nodes.js";
|
2
|
+
|
3
|
+
const MAJOR_VERSION = 1;
|
4
|
+
const MINOR_VERSION = 5;
|
5
|
+
const PATCH_VERSION = 1;
|
6
|
+
|
7
|
+
// The DataView getFloat64 function takes an optional second argument that
|
8
|
+
// specifies whether the number is little-endian or big-endian. It does not
|
9
|
+
// appear to have a native endian mode, so we need to determine the endianness
|
10
|
+
// of the system at runtime.
|
11
|
+
const LITTLE_ENDIAN = (() => {
|
12
|
+
let uint32 = new Uint32Array([0x11223344]);
|
13
|
+
let uint8 = new Uint8Array(uint32.buffer);
|
14
|
+
|
15
|
+
if (uint8[0] === 0x44) {
|
16
|
+
return true;
|
17
|
+
} else if (uInt8[0] === 0x11) {
|
18
|
+
return false;
|
19
|
+
} else {
|
20
|
+
throw new Error("Mixed endianness");
|
21
|
+
}
|
22
|
+
})();
|
23
|
+
|
24
|
+
class SerializationBuffer {
|
25
|
+
FORCED_UTF8_ENCODING_FLAG = 1 << 2;
|
26
|
+
FORCED_BINARY_ENCODING_FLAG = 1 << 3;
|
27
|
+
|
28
|
+
DECODER_MAP = new Map([
|
29
|
+
["ascii-8bit", "ascii"]
|
30
|
+
]);
|
31
|
+
|
32
|
+
constructor(source, array) {
|
33
|
+
this.source = source;
|
34
|
+
this.array = array;
|
35
|
+
this.index = 0;
|
36
|
+
this.fileEncoding = "utf-8";
|
37
|
+
this.decoders = new Map();
|
38
|
+
}
|
39
|
+
|
40
|
+
readByte() {
|
41
|
+
const result = this.array[this.index];
|
42
|
+
this.index += 1;
|
43
|
+
return result;
|
44
|
+
}
|
45
|
+
|
46
|
+
readBytes(length) {
|
47
|
+
const result = this.array.slice(this.index, this.index + length);
|
48
|
+
this.index += length;
|
49
|
+
return result;
|
50
|
+
}
|
51
|
+
|
52
|
+
readString(length, flags) {
|
53
|
+
return this.decodeString(this.readBytes(length), flags).value;
|
54
|
+
}
|
55
|
+
|
56
|
+
// Read a 32-bit unsigned integer in little-endian format.
|
57
|
+
readUint32() {
|
58
|
+
const result = this.scanUint32(this.index);
|
59
|
+
this.index += 4;
|
60
|
+
return result;
|
61
|
+
}
|
62
|
+
|
63
|
+
scanUint32(offset) {
|
64
|
+
const bytes = this.array.slice(offset, offset + 4);
|
65
|
+
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
|
66
|
+
}
|
67
|
+
|
68
|
+
readVarInt() {
|
69
|
+
let result = 0;
|
70
|
+
let shift = 0;
|
71
|
+
|
72
|
+
while (true) {
|
73
|
+
const byte = this.readByte();
|
74
|
+
result += (byte & 0x7f) << shift;
|
75
|
+
shift += 7;
|
76
|
+
|
77
|
+
if ((byte & 0x80) === 0) {
|
78
|
+
break;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
return result;
|
83
|
+
}
|
84
|
+
|
85
|
+
readLocation() {
|
86
|
+
return { startOffset: this.readVarInt(), length: this.readVarInt() };
|
87
|
+
}
|
88
|
+
|
89
|
+
readOptionalLocation() {
|
90
|
+
if (this.readByte() != 0) {
|
91
|
+
return this.readLocation();
|
92
|
+
} else {
|
93
|
+
return null;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
readStringField(flags) {
|
98
|
+
if (flags === undefined) flags = 0;
|
99
|
+
const type = this.readByte();
|
100
|
+
|
101
|
+
switch (type) {
|
102
|
+
case 1: {
|
103
|
+
const startOffset = this.readVarInt();
|
104
|
+
const length = this.readVarInt();
|
105
|
+
return this.decodeString(this.source.slice(startOffset, startOffset + length), flags);
|
106
|
+
}
|
107
|
+
case 2:
|
108
|
+
return this.decodeString(this.readBytes(this.readVarInt()), flags);
|
109
|
+
default:
|
110
|
+
throw new Error(`Unknown serialized string type: ${type}`);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
scanConstant(constantPoolOffset, constantIndex) {
|
115
|
+
const offset = constantPoolOffset + constantIndex * 8;
|
116
|
+
let startOffset = this.scanUint32(offset);
|
117
|
+
const length = this.scanUint32(offset + 4);
|
118
|
+
|
119
|
+
if (startOffset & (1 << 31)) {
|
120
|
+
startOffset &= (1 << 31) - 1;
|
121
|
+
return new TextDecoder().decode(this.array.slice(startOffset, startOffset + length));
|
122
|
+
} else {
|
123
|
+
return new TextDecoder().decode(this.source.slice(startOffset, startOffset + length));
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
readDouble() {
|
128
|
+
const view = new DataView(new ArrayBuffer(8));
|
129
|
+
for (let index = 0; index < 8; index++) {
|
130
|
+
view.setUint8(index, this.readByte());
|
131
|
+
}
|
132
|
+
|
133
|
+
return view.getFloat64(0, LITTLE_ENDIAN);
|
134
|
+
}
|
135
|
+
|
136
|
+
decodeString(bytes, flags) {
|
137
|
+
const forcedBin = (flags & this.FORCED_BINARY_ENCODING_FLAG) !== 0;
|
138
|
+
const forcedUtf8 = (flags & this.FORCED_UTF8_ENCODING_FLAG) !== 0;
|
139
|
+
|
140
|
+
if (forcedBin) {
|
141
|
+
// just use raw bytes
|
142
|
+
return {
|
143
|
+
encoding: "ascii",
|
144
|
+
validEncoding: true,
|
145
|
+
value: this.asciiDecoder.decode(bytes)
|
146
|
+
};
|
147
|
+
} else {
|
148
|
+
const encoding = forcedUtf8 ? "utf-8" : this.fileEncoding.toLowerCase();
|
149
|
+
const decoder = this.getDecoder(encoding);
|
150
|
+
|
151
|
+
try {
|
152
|
+
// decode with encoding
|
153
|
+
return {
|
154
|
+
encoding,
|
155
|
+
validEncoding: true,
|
156
|
+
value: decoder.decode(bytes)
|
157
|
+
};
|
158
|
+
} catch(e) {
|
159
|
+
// just use raw bytes, capture what the encoding should be, set flag saying encoding is invalid
|
160
|
+
if (e.code === "ERR_ENCODING_INVALID_ENCODED_DATA") {
|
161
|
+
return {
|
162
|
+
encoding,
|
163
|
+
validEncoding: false,
|
164
|
+
value: this.asciiDecoder.decode(bytes)
|
165
|
+
};
|
166
|
+
}
|
167
|
+
|
168
|
+
throw e;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
getDecoder(encoding) {
|
174
|
+
encoding = this.DECODER_MAP.get(encoding) || encoding;
|
175
|
+
|
176
|
+
if (!this.decoders.has(encoding)) {
|
177
|
+
this.decoders.set(encoding, new TextDecoder(encoding, {fatal: true}));
|
178
|
+
}
|
179
|
+
|
180
|
+
return this.decoders.get(encoding);
|
181
|
+
}
|
182
|
+
|
183
|
+
get asciiDecoder() {
|
184
|
+
if (!this._asciiDecoder) {
|
185
|
+
this._asciiDecoder = new TextDecoder("ascii");
|
186
|
+
}
|
187
|
+
|
188
|
+
return this._asciiDecoder;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* A location in the source code.
|
194
|
+
*
|
195
|
+
* @typedef {{ startOffset: number, length: number }} Location
|
196
|
+
*/
|
197
|
+
|
198
|
+
/**
|
199
|
+
* A comment in the source code.
|
200
|
+
*
|
201
|
+
* @typedef {{ type: number, location: Location }} Comment
|
202
|
+
*/
|
203
|
+
|
204
|
+
/**
|
205
|
+
* A magic comment in the source code.
|
206
|
+
*
|
207
|
+
* @typedef {{ startLocation: Location, endLocation: Location }} MagicComment
|
208
|
+
*/
|
209
|
+
|
210
|
+
/**
|
211
|
+
* An error in the source code.
|
212
|
+
*
|
213
|
+
* @typedef {{ type: string, message: string, location: Location, level: string }} ParseError
|
214
|
+
*/
|
215
|
+
|
216
|
+
/**
|
217
|
+
* A warning in the source code.
|
218
|
+
*
|
219
|
+
* @typedef {{ type: string, message: string, location: Location, level: string }} ParseWarning
|
220
|
+
*/
|
221
|
+
|
222
|
+
/**
|
223
|
+
* The result of parsing the source code.
|
224
|
+
*
|
225
|
+
* @typedef {{ value: ProgramNode, comments: Comment[], magicComments: MagicComment[], errors: ParseError[], warnings: ParseWarning[] }} ParseResult
|
226
|
+
*/
|
227
|
+
|
228
|
+
/**
|
229
|
+
* The result of calling parse.
|
230
|
+
*/
|
231
|
+
export class ParseResult {
|
232
|
+
/**
|
233
|
+
* @type {nodes.ProgramNode}
|
234
|
+
*/
|
235
|
+
value;
|
236
|
+
|
237
|
+
/**
|
238
|
+
* @type {Comment[]}
|
239
|
+
*/
|
240
|
+
comments;
|
241
|
+
|
242
|
+
/**
|
243
|
+
* @type {MagicComment[]}
|
244
|
+
*/
|
245
|
+
magicComments;
|
246
|
+
|
247
|
+
/**
|
248
|
+
* @type {Location | null}
|
249
|
+
*/
|
250
|
+
|
251
|
+
/**
|
252
|
+
* @type {ParseError[]}
|
253
|
+
*/
|
254
|
+
errors;
|
255
|
+
|
256
|
+
/**
|
257
|
+
* @type {ParseWarning[]}
|
258
|
+
*/
|
259
|
+
warnings;
|
260
|
+
|
261
|
+
/**
|
262
|
+
* @param {nodes.ProgramNode} value
|
263
|
+
* @param {Comment[]} comments
|
264
|
+
* @param {MagicComment[]} magicComments
|
265
|
+
* @param {ParseError[]} errors
|
266
|
+
* @param {ParseWarning[]} warnings
|
267
|
+
*/
|
268
|
+
constructor(value, comments, magicComments, dataLoc, errors, warnings) {
|
269
|
+
this.value = value;
|
270
|
+
this.comments = comments;
|
271
|
+
this.magicComments = magicComments;
|
272
|
+
this.dataLoc = dataLoc;
|
273
|
+
this.errors = errors;
|
274
|
+
this.warnings = warnings;
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
const errorLevels = ["syntax", "argument", "load"];
|
279
|
+
const errorTypes = [
|
280
|
+
<%- errors.each do |error| -%>
|
281
|
+
"<%= error.name.downcase %>",
|
282
|
+
<%- end -%>
|
283
|
+
];
|
284
|
+
|
285
|
+
const warningLevels = ["default", "verbose"];
|
286
|
+
const warningTypes = [
|
287
|
+
<%- warnings.each do |warning| -%>
|
288
|
+
"<%= warning.name.downcase %>",
|
289
|
+
<%- end -%>
|
290
|
+
];
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Accept two Uint8Arrays, one for the source and one for the serialized format.
|
294
|
+
* Return the AST corresponding to the serialized form.
|
295
|
+
*
|
296
|
+
* @param {Uint8Array} source
|
297
|
+
* @param {Uint8Array} array
|
298
|
+
* @returns {ParseResult}
|
299
|
+
* @throws {Error}
|
300
|
+
*/
|
301
|
+
export function deserialize(source, array) {
|
302
|
+
const buffer = new SerializationBuffer(source, array);
|
303
|
+
|
304
|
+
if (buffer.readString(5) !== "PRISM") {
|
305
|
+
throw new Error("Invalid serialization");
|
306
|
+
}
|
307
|
+
|
308
|
+
if ((buffer.readByte() != MAJOR_VERSION) || (buffer.readByte() != MINOR_VERSION) || (buffer.readByte() != PATCH_VERSION)) {
|
309
|
+
throw new Error("Invalid serialization");
|
310
|
+
}
|
311
|
+
|
312
|
+
if (buffer.readByte() != 0) {
|
313
|
+
throw new Error("Invalid serialization (location fields must be included but are not)");
|
314
|
+
}
|
315
|
+
|
316
|
+
// Read the file's encoding.
|
317
|
+
buffer.fileEncoding = buffer.readString(buffer.readVarInt());
|
318
|
+
|
319
|
+
// Skip past the start line, as we don't support that option yet in
|
320
|
+
// JavaScript.
|
321
|
+
buffer.readVarInt();
|
322
|
+
|
323
|
+
// Skip past the line offsets, as there is no Source object yet in JavaScript.
|
324
|
+
// const lineOffsets = Array.from({ length: buffer.readVarInt() }, () => buffer.readVarInt());
|
325
|
+
const lineOffsetsCount = buffer.readVarInt();
|
326
|
+
for (let i = 0; i < lineOffsetsCount; i ++) {
|
327
|
+
buffer.readVarInt();
|
328
|
+
}
|
329
|
+
|
330
|
+
const comments = Array.from({ length: buffer.readVarInt() }, () => ({
|
331
|
+
type: buffer.readVarInt(),
|
332
|
+
location: buffer.readLocation()
|
333
|
+
}));
|
334
|
+
|
335
|
+
const magicComments = Array.from({ length: buffer.readVarInt() }, () => ({
|
336
|
+
startLocation: buffer.readLocation(),
|
337
|
+
endLocation: buffer.readLocation()
|
338
|
+
}));
|
339
|
+
|
340
|
+
const dataLoc = buffer.readOptionalLocation();
|
341
|
+
|
342
|
+
const errors = Array.from({ length: buffer.readVarInt() }, () => ({
|
343
|
+
type: errorTypes[buffer.readVarInt()],
|
344
|
+
message: buffer.readString(buffer.readVarInt()),
|
345
|
+
location: buffer.readLocation(),
|
346
|
+
level: errorLevels[buffer.readByte()]
|
347
|
+
}));
|
348
|
+
|
349
|
+
const warnings = Array.from({ length: buffer.readVarInt() }, () => ({
|
350
|
+
type: warningTypes[buffer.readVarInt()],
|
351
|
+
message: buffer.readString(buffer.readVarInt()),
|
352
|
+
location: buffer.readLocation(),
|
353
|
+
level: warningLevels[buffer.readByte()]
|
354
|
+
}));
|
355
|
+
|
356
|
+
const constantPoolOffset = buffer.readUint32();
|
357
|
+
const constants = Array.from({ length: buffer.readVarInt() }, () => null);
|
358
|
+
|
359
|
+
return new ParseResult(readRequiredNode(), comments, magicComments, dataLoc, errors, warnings);
|
360
|
+
|
361
|
+
function readRequiredNode() {
|
362
|
+
const type = buffer.readByte();
|
363
|
+
const nodeID = buffer.readVarInt();
|
364
|
+
const location = buffer.readLocation();
|
365
|
+
let flags;
|
366
|
+
|
367
|
+
switch (type) {
|
368
|
+
<%- nodes.each.with_index(1) do |node, index| -%>
|
369
|
+
case <%= index %>:
|
370
|
+
<%- if node.needs_serialized_length? -%>
|
371
|
+
buffer.readUint32();
|
372
|
+
<%- end -%>
|
373
|
+
return new nodes.<%= node.name %>(<%= ["nodeID", "location", "flags = buffer.readVarInt()", *node.fields.map { |field|
|
374
|
+
case field
|
375
|
+
when Prism::Template::NodeField then "readRequiredNode()"
|
376
|
+
when Prism::Template::OptionalNodeField then "readOptionalNode()"
|
377
|
+
when Prism::Template::StringField then "buffer.readStringField(flags)"
|
378
|
+
when Prism::Template::NodeListField then "Array.from({ length: buffer.readVarInt() }, readRequiredNode)"
|
379
|
+
when Prism::Template::ConstantField then "readRequiredConstant()"
|
380
|
+
when Prism::Template::OptionalConstantField then "readOptionalConstant()"
|
381
|
+
when Prism::Template::ConstantListField then "Array.from({ length: buffer.readVarInt() }, readRequiredConstant)"
|
382
|
+
when Prism::Template::LocationField then "buffer.readLocation()"
|
383
|
+
when Prism::Template::OptionalLocationField then "buffer.readOptionalLocation()"
|
384
|
+
when Prism::Template::UInt8Field then "buffer.readByte()"
|
385
|
+
when Prism::Template::UInt32Field then "buffer.readVarInt()"
|
386
|
+
when Prism::Template::IntegerField then "readInteger()"
|
387
|
+
when Prism::Template::DoubleField then "buffer.readDouble()"
|
388
|
+
end
|
389
|
+
}].join(", ") -%>);
|
390
|
+
<%- end -%>
|
391
|
+
default:
|
392
|
+
throw new Error(`Unknown node type: ${type}`);
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
function readOptionalNode() {
|
397
|
+
if (buffer.readByte() != 0) {
|
398
|
+
buffer.index -= 1;
|
399
|
+
return readRequiredNode();
|
400
|
+
} else {
|
401
|
+
return null;
|
402
|
+
}
|
403
|
+
}
|
404
|
+
|
405
|
+
function scanConstant(constantIndex) {
|
406
|
+
if (constants[constantIndex] === null) {
|
407
|
+
constants[constantIndex] = buffer.scanConstant(constantPoolOffset, constantIndex);
|
408
|
+
}
|
409
|
+
|
410
|
+
return constants[constantIndex];
|
411
|
+
}
|
412
|
+
|
413
|
+
function readRequiredConstant() {
|
414
|
+
return scanConstant(buffer.readVarInt() - 1);
|
415
|
+
}
|
416
|
+
|
417
|
+
function readOptionalConstant() {
|
418
|
+
const index = buffer.readVarInt();
|
419
|
+
if (index === 0) {
|
420
|
+
return null;
|
421
|
+
} else {
|
422
|
+
return scanConstant(index - 1);
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
function readInteger() {
|
427
|
+
const negative = buffer.readByte() != 0;
|
428
|
+
const length = buffer.readVarInt();
|
429
|
+
|
430
|
+
const firstWord = buffer.readVarInt();
|
431
|
+
if (length == 1) {
|
432
|
+
if (negative && firstWord >= 0x80000000) {
|
433
|
+
return -BigInt(firstWord);
|
434
|
+
} else if (negative) {
|
435
|
+
return -firstWord;
|
436
|
+
} else {
|
437
|
+
return firstWord;
|
438
|
+
}
|
439
|
+
}
|
440
|
+
|
441
|
+
let result = BigInt(firstWord);
|
442
|
+
for (let index = 1; index < length; index++) {
|
443
|
+
result |= (BigInt(buffer.readVarInt()) << BigInt(index * 32));
|
444
|
+
}
|
445
|
+
|
446
|
+
return negative ? -result : result;
|
447
|
+
}
|
448
|
+
}
|
@@ -0,0 +1,197 @@
|
|
1
|
+
<%-
|
2
|
+
|
3
|
+
def camelize(string)
|
4
|
+
string.gsub(/_([a-z])/) { $1.upcase }
|
5
|
+
end
|
6
|
+
|
7
|
+
def prop(field)
|
8
|
+
field.name == "arguments" ? "arguments_" : camelize(field.name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def jstype(field)
|
12
|
+
case field
|
13
|
+
when Prism::Template::NodeField then field.ruby_type
|
14
|
+
when Prism::Template::OptionalNodeField then "#{field.ruby_type} | null"
|
15
|
+
when Prism::Template::NodeListField then "Node[]"
|
16
|
+
when Prism::Template::StringField then "RubyString"
|
17
|
+
when Prism::Template::ConstantField then "string"
|
18
|
+
when Prism::Template::OptionalConstantField then "string | null"
|
19
|
+
when Prism::Template::ConstantListField then "string[]"
|
20
|
+
when Prism::Template::LocationField then "Location"
|
21
|
+
when Prism::Template::OptionalLocationField then "Location | null"
|
22
|
+
when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField, Prism::Template::DoubleField then "number"
|
23
|
+
else raise
|
24
|
+
end
|
25
|
+
end
|
26
|
+
-%>
|
27
|
+
import * as visitors from "./visitor.js"
|
28
|
+
|
29
|
+
<%- flags.each do |flag| -%>
|
30
|
+
|
31
|
+
/**
|
32
|
+
* <%= flag.comment %>
|
33
|
+
*/
|
34
|
+
const <%= flag.name %> = {
|
35
|
+
<%- flag.values.each_with_index do |value, index| -%>
|
36
|
+
<%= value.name %>: 1 << <%= index + Prism::Template::COMMON_FLAGS_COUNT %>,
|
37
|
+
<%- end -%>
|
38
|
+
};
|
39
|
+
<%- end -%>
|
40
|
+
|
41
|
+
/**
|
42
|
+
* A location in the source code.
|
43
|
+
*
|
44
|
+
* @typedef {{ startOffset: number, length: number }} Location
|
45
|
+
*/
|
46
|
+
|
47
|
+
/**
|
48
|
+
* An encoded Ruby string.
|
49
|
+
*
|
50
|
+
* @typedef {{ value: string, encoding: string, validEncoding: boolean }} RubyString
|
51
|
+
*/
|
52
|
+
|
53
|
+
/**
|
54
|
+
* A generic node in the tree.
|
55
|
+
*
|
56
|
+
* @typedef {(<%= nodes.map(&:name).join("|") %>)} Node
|
57
|
+
*/
|
58
|
+
<%- nodes.each do |node| -%>
|
59
|
+
|
60
|
+
/**
|
61
|
+
<%- node.each_comment_line do |line| -%>
|
62
|
+
*<%= line %>
|
63
|
+
<%- end -%>
|
64
|
+
*/
|
65
|
+
export class <%= node.name -%> {
|
66
|
+
/**
|
67
|
+
* @type number
|
68
|
+
*/
|
69
|
+
nodeID;
|
70
|
+
|
71
|
+
/**
|
72
|
+
* @type {Location}
|
73
|
+
*/
|
74
|
+
location;
|
75
|
+
|
76
|
+
/**
|
77
|
+
* @type number
|
78
|
+
*/
|
79
|
+
#flags;
|
80
|
+
|
81
|
+
<%- node.fields.each do |field| -%>
|
82
|
+
/**
|
83
|
+
* @type <%= jstype(field) %>
|
84
|
+
*/
|
85
|
+
<%= prop(field) %>;
|
86
|
+
|
87
|
+
<%- end -%>
|
88
|
+
/**
|
89
|
+
* Construct a new <%= node.name %>.
|
90
|
+
*
|
91
|
+
* @param {number} nodeID
|
92
|
+
* @param {Location} location
|
93
|
+
* @param {number} flags
|
94
|
+
<%- node.fields.each do |field| -%>
|
95
|
+
* @param {<%= jstype(field) %>} <%= prop(field) %>
|
96
|
+
<%- end -%>
|
97
|
+
*/
|
98
|
+
constructor(<%= ["nodeID", "location", "flags", *node.fields.map { |field| prop(field) }].join(", ") %>) {
|
99
|
+
this.nodeID = nodeID;
|
100
|
+
this.location = location;
|
101
|
+
this.#flags = flags;
|
102
|
+
<%- node.fields.each do |field| -%>
|
103
|
+
this.<%= prop(field) %> = <%= prop(field) %>;
|
104
|
+
<%- end -%>
|
105
|
+
}
|
106
|
+
<%- if (node_flags = node.flags) -%>
|
107
|
+
<%- node_flags.values.each do |value| -%>
|
108
|
+
|
109
|
+
/**
|
110
|
+
* True if this node has the <%= value.name %> flag.
|
111
|
+
*
|
112
|
+
* @returns {boolean}
|
113
|
+
*/
|
114
|
+
is<%= value.camelcase %>() {
|
115
|
+
return (this.#flags & <%= node_flags.name %>.<%= value.name %>) !== 0;
|
116
|
+
}
|
117
|
+
<%- end -%>
|
118
|
+
<%- end -%>
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Accept a visitor for this node.
|
122
|
+
*
|
123
|
+
* @param {visitors.Visitor} visitor
|
124
|
+
*/
|
125
|
+
accept(visitor) {
|
126
|
+
visitor.visit<%= camelize(node.name) %>(this)
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Returns all child nodes of the current node.
|
131
|
+
*
|
132
|
+
* @returns {(Node | null)[]} An array of child nodes.
|
133
|
+
*/
|
134
|
+
childNodes() {
|
135
|
+
return [<%= node.fields.map { |field|
|
136
|
+
case field
|
137
|
+
when Prism::Template::NodeField, Prism::Template::OptionalNodeField then "this.#{prop(field)}"
|
138
|
+
when Prism::Template::NodeListField then "...this.#{prop(field)}"
|
139
|
+
end
|
140
|
+
}.compact.join(", ") %>]
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Compact and return an array of child nodes.
|
145
|
+
*
|
146
|
+
* @returns {Node[]} An array of compacted child nodes.
|
147
|
+
*/
|
148
|
+
compactChildNodes() {
|
149
|
+
<%- if node.fields.any? { |field| field.is_a?(Prism::Template::OptionalNodeField) } -%>
|
150
|
+
const compact = [];
|
151
|
+
|
152
|
+
<%- node.fields.each do |field| -%>
|
153
|
+
<%- case field -%>
|
154
|
+
<%- when Prism::Template::NodeField -%>
|
155
|
+
compact.push(this.<%= prop(field) %>);
|
156
|
+
|
157
|
+
<%- when Prism::Template::OptionalNodeField -%>
|
158
|
+
if (this.<%= prop(field) %>) {
|
159
|
+
compact.push(this.<%= prop(field) %>);
|
160
|
+
}
|
161
|
+
<%- when Prism::Template::NodeListField -%>
|
162
|
+
compact.concat(this.<%= prop(field) %>);
|
163
|
+
<%- end -%>
|
164
|
+
<%- end -%>
|
165
|
+
|
166
|
+
return compact;
|
167
|
+
<%- else -%>
|
168
|
+
return [<%= node.fields.map { |field|
|
169
|
+
case field
|
170
|
+
when Prism::Template::NodeField then "this.#{prop(field)}"
|
171
|
+
when Prism::Template::NodeListField then "...this.#{prop(field)}"
|
172
|
+
end
|
173
|
+
}.compact.join(", ") %>];
|
174
|
+
<%- end -%>
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Transforms the Node to a JavaScript object.
|
179
|
+
*
|
180
|
+
* @returns {Object}
|
181
|
+
*/
|
182
|
+
toJSON() {
|
183
|
+
return {
|
184
|
+
type: "<%= node.name %>",
|
185
|
+
location: this.location,
|
186
|
+
flags: this.#flags,
|
187
|
+
<%- node.fields.each do |field| -%>
|
188
|
+
<%- if field.name == "arguments" -%>
|
189
|
+
arguments: this.<%= prop(field) %>,
|
190
|
+
<%- else -%>
|
191
|
+
<%= prop(field) %>: this.<%= prop(field) %>,
|
192
|
+
<%- end -%>
|
193
|
+
<%- end -%>
|
194
|
+
};
|
195
|
+
}
|
196
|
+
}
|
197
|
+
<%- end -%>
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import * as nodes from "./nodes.js";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* A class that knows how to walk down the tree. None of the individual visit
|
5
|
+
* methods are implemented on this visitor, so it forces the consumer to
|
6
|
+
* implement each one that they need. For a default implementation that
|
7
|
+
* continues walking the tree, see the `Visitor` class.
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
export class BasicVisitor {
|
11
|
+
/**
|
12
|
+
* Calls `accept` on the given node if it is not `null`, which in turn should
|
13
|
+
* call back into this visitor by calling the appropriate `visit*` method.
|
14
|
+
*
|
15
|
+
* @param {nodes.Node} node
|
16
|
+
*/
|
17
|
+
visit(node) {
|
18
|
+
node?.accept(this);
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Visits each node in `nodes` by calling `accept` on each one.
|
23
|
+
*
|
24
|
+
* @param {nodes.Node[]} nodes
|
25
|
+
*/
|
26
|
+
visitAll(nodes) {
|
27
|
+
nodes.forEach((node) => {
|
28
|
+
node?.accept(this);
|
29
|
+
});
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Visits the child nodes of `node` by calling `accept` on each one.
|
34
|
+
*
|
35
|
+
* @param {nodes.Node} node
|
36
|
+
*/
|
37
|
+
visitChildNodes(node) {
|
38
|
+
node.compactChildNodes().forEach((childNode) => {
|
39
|
+
childNode.accept(this);
|
40
|
+
});
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* A visitor is a class that provides a default implementation for every accept
|
46
|
+
* method defined on the nodes. This means it can walk a tree without the
|
47
|
+
* caller needing to define any special handling. This allows you to handle a
|
48
|
+
* subset of the tree, while still walking the whole tree.
|
49
|
+
*
|
50
|
+
* For example, to find all of the method calls that call the `foo` method, you
|
51
|
+
* could write:
|
52
|
+
*
|
53
|
+
* @example
|
54
|
+
* class FooCalls extends Visitor {
|
55
|
+
* visitCallNode(node) {
|
56
|
+
* if (node.name === "foo") {
|
57
|
+
* // Do something with the node
|
58
|
+
* }
|
59
|
+
*
|
60
|
+
* // Call super so that the visitor continues walking the tree
|
61
|
+
* super.visitCallNode(node);
|
62
|
+
* }
|
63
|
+
* }
|
64
|
+
*
|
65
|
+
*/
|
66
|
+
export class Visitor extends BasicVisitor {
|
67
|
+
<%- nodes.each_with_index do |node, index| -%>
|
68
|
+
<%= "\n" if index != 0 -%>
|
69
|
+
/**
|
70
|
+
* Visit a <%= node.name %> node.
|
71
|
+
*
|
72
|
+
* @param {nodes.<%= node.name %>} node
|
73
|
+
*/
|
74
|
+
<%= "visit#{node.name.gsub(/_([a-z])/) { $1.upcase }}" %>(node) {
|
75
|
+
this.visitChildNodes(node);
|
76
|
+
}
|
77
|
+
<%- end -%>
|
78
|
+
}
|