@sie-js/vkp 1.0.6 → 2.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/src/parser.js DELETED
@@ -1,515 +0,0 @@
1
- import iconv from 'iconv-lite';
2
- import { LEXER, TOKEN } from './lexer.js';
3
- import { VkpParseError } from './VkpParseError.js';
4
-
5
- const UINT_PARSER_DATA = [
6
- [3, 0xFF, 8], // 1b
7
- [5, 0xFFFF, 16], // 2b
8
- [8, 0xFFFFFF, 24], // 3b
9
- [10, 0xFFFFFFFF, 32], // 4b
10
- [13, 0xFFFFFFFFFF, 40], // 5b
11
- [15, 0xFFFFFFFFFFFF, 48], // 6b
12
- [17, 0xFFFFFFFFFFFFFFn, 56], // 7b
13
- [20, 0xFFFFFFFFFFFFFFFFn, 64], // 8b
14
- ];
15
-
16
- const SINT_PARSER_DATA = [
17
- [3, 0x7F, 8], // 1b
18
- [5, 0x7FFF, 16], // 2b
19
- [8, 0x7FFFFF, 24], // 3b
20
- [10, 0x7FFFFFFF, 32], // 4b
21
- [13, 0x7FFFFFFFFF, 40], // 5b
22
- [15, 0x7FFFFFFFFFFF, 48], // 6b
23
- [17, 0x7FFFFFFFFFFFFFn, 56], // 7b
24
- [20, 0x7FFFFFFFFFFFFFFFn, 64], // 8b
25
- ];
26
-
27
- const STR_ESCAPE_TABLE = {
28
- "a": "\x07",
29
- "b": "\b",
30
- "t": "\t",
31
- "r": "\r",
32
- "n": "\n",
33
- "v": "\v",
34
- "f": "\f",
35
- "e": "\x1B",
36
- "\\": "\\",
37
- "/": "/",
38
- "*": "*",
39
- "\"": "\"",
40
- "'": "'",
41
- "`": "`",
42
- " ": " ",
43
- };
44
-
45
- let state;
46
-
47
- function noop() {
48
-
49
- }
50
-
51
- function vkpRawParser(text, options = {}) {
52
- options = {
53
- onPragma: noop,
54
- onPatchData: noop,
55
- onOffset: noop,
56
- onComments: noop,
57
- onWarning: noop,
58
- onError: noop,
59
- ...options
60
- };
61
-
62
- state = {
63
- token: null,
64
- prevToken: null,
65
- warnings: [],
66
- onPragma: options.onPragma,
67
- onPatchData: options.onPatchData,
68
- onOffset: options.onOffset,
69
- onComments: options.onComments,
70
- onWarning: options.onWarning,
71
- onError: options.onError,
72
- };
73
-
74
- LEXER.reset(text);
75
-
76
- let token;
77
- while ((token = peekToken())) {
78
- try {
79
- if (token.type == TOKEN.ADDRESS) {
80
- let loc = getLocation();
81
- state.onPatchData(parsePatchRecord(), loc);
82
- } else if (token.type == TOKEN.PRAGMA) {
83
- let loc = getLocation();
84
- state.onPragma(parsePatchPragma(), loc);
85
- } else if (token.type == TOKEN.OFFSET) {
86
- let loc = getLocation();
87
- state.onOffset(parsePatchOffsetCorrector(), loc);
88
- } else if (token.type == TOKEN.NEWLINE || token.type == TOKEN.WHITESPACE) {
89
- nextToken();
90
- } else if (token.type == TOKEN.COMMENT || token.type == TOKEN.MULTILINE_COMMENT || token.type == TOKEN.UNFINISHED_COMMENT) {
91
- let loc = getLocation();
92
- state.onComments(parseComments(), loc);
93
- } else if (token.type == TOKEN.TRAILING_COMMENT_END) {
94
- state.onWarning(new VkpParseError(`Trailing multiline comment end`, getLocation()));
95
- nextToken();
96
- } else {
97
- throw new VkpParseError("Syntax error", getLocation());
98
- }
99
- } catch (e) {
100
- if (!(e instanceof VkpParseError))
101
- throw e;
102
-
103
- let loc = getLocation();
104
- let token;
105
- while ((token = nextToken())) {
106
- if (token.type == TOKEN.NEWLINE)
107
- break;
108
- }
109
- state.onError(e, loc);
110
- }
111
- }
112
-
113
- state = null;
114
- }
115
-
116
- function parseComments() {
117
- let comments = [];
118
- let token;
119
- while ((token = peekToken())) {
120
- if (token.type == TOKEN.NEWLINE) {
121
- nextToken();
122
- break;
123
- } else if (token.type == TOKEN.WHITESPACE) {
124
- nextToken();
125
- } else if (token.type == TOKEN.COMMENT || token.type == TOKEN.MULTILINE_COMMENT || token.type == TOKEN.UNFINISHED_COMMENT) {
126
- if (token.type == TOKEN.UNFINISHED_COMMENT)
127
- state.onWarning(new VkpParseError(`Unfinished multiline comment`, getLocation()));
128
- comments.push(parseCommentValue(token.value));
129
- nextToken();
130
- } else {
131
- break;
132
- }
133
- }
134
- return comments;
135
- }
136
-
137
- function parsePatchPragma() {
138
- let pragma = parsePragmaValue(peekToken().value);
139
- nextToken();
140
- let comment = parseCommentsAfterExpr();
141
- return { pragma, comment };
142
- }
143
-
144
- function parsePatchOffsetCorrector() {
145
- let text = peekToken().value;
146
- let offset = parseOffsetValue(text);
147
- nextToken();
148
- let comment = parseCommentsAfterExpr();
149
- return { text, offset, comment };
150
- }
151
-
152
- function parsePatchRecord() {
153
- let address = parsePatchRecordAddress();
154
-
155
- let data = [];
156
- for (let i = 0; i < 2; i++) {
157
- if (!parsePatchRecordSeparator())
158
- break;
159
- let loc = getLocation();
160
- let [buffer, placeholders] = parsePatchData();
161
- data.push({ loc, buffer: mergeBuffers(buffer), placeholders });
162
- }
163
-
164
- if (!data.length)
165
- throw new VkpParseError(`Empty patch data record!`, getLocation());
166
-
167
- let comment = parseCommentsAfterExpr();
168
- return {
169
- address,
170
- comment,
171
- old: data.length == 2 ? data[0] : null,
172
- new: data.length == 2 ? data[1] : data[0],
173
- };
174
- }
175
-
176
- function mergeBuffers(buffers) {
177
- return buffers.length > 1 ? Buffer.concat(buffers) : buffers[0];
178
- }
179
-
180
- function parsePatchRecordAddress() {
181
- let value = parseAddressValue(peekToken().value);
182
- nextToken();
183
- return value;
184
- }
185
-
186
- function parsePatchData() {
187
- let data = [];
188
- let token;
189
- let placeholders = 0;
190
- while ((token = peekToken())) {
191
- if (token.type == TOKEN.COMMA) {
192
- nextToken();
193
- } else if (token.type == TOKEN.DATA) {
194
- data.push(parseHexDataValue(peekToken().value));
195
- nextToken();
196
- } else if (token.type == TOKEN.PLACEHOLDER) {
197
- data.push(parsePlaceholderValue(peekToken().value));
198
- nextToken();
199
- placeholders++;
200
- } else if (token.type == TOKEN.NUMBER) {
201
- data.push(parseAnyNumberValue(peekToken().value));
202
- nextToken();
203
- } else if (token.type == TOKEN.STRING) {
204
- data.push(parseStringValue(peekToken().value));
205
- nextToken();
206
- } else if (token.type == TOKEN.LINE_ESCAPE) {
207
- nextToken();
208
- } else if (token.type == TOKEN.WHITESPACE || token.type == TOKEN.NEWLINE) {
209
- break;
210
- } else if (token.type == TOKEN.COMMENT || token.type == TOKEN.MULTILINE_COMMENT || token.type == TOKEN.UNFINISHED_COMMENT) {
211
- if (prevToken().type == TOKEN.NUMBER)
212
- throw new VkpParseError(`No whitespace between number and comment`, getLocation());
213
- break;
214
- } else {
215
- throw new VkpParseError("Syntax error", getLocation());
216
- }
217
- }
218
- return [data, placeholders];
219
- }
220
-
221
- function parsePatchRecordSeparator() {
222
- let token;
223
- while ((token = peekToken())) {
224
- if (token.type == TOKEN.NEWLINE) {
225
- return false;
226
- } else if (token.type == TOKEN.DATA || token.type == TOKEN.PLACEHOLDER || token.type == TOKEN.NUMBER || token.type == TOKEN.STRING) {
227
- return true;
228
- } else if (token.type == TOKEN.COMMENT || token.type == TOKEN.MULTILINE_COMMENT || token.type == TOKEN.UNFINISHED_COMMENT) {
229
- return false;
230
- } else if (token.type == TOKEN.WHITESPACE || token.type == TOKEN.LINE_ESCAPE) {
231
- nextToken();
232
- } else {
233
- throw new VkpParseError("Syntax error", getLocation());
234
- }
235
- }
236
- return false;
237
- }
238
-
239
- function parseCommentsAfterExpr() {
240
- let comments = [];
241
- let token;
242
- while ((token = peekToken())) {
243
- if (token.type == TOKEN.NEWLINE) {
244
- nextToken();
245
- break;
246
- } else if (token.type == TOKEN.WHITESPACE) {
247
- nextToken();
248
- } else if (token.type == TOKEN.COMMENT || token.type == TOKEN.MULTILINE_COMMENT || token.type == TOKEN.UNFINISHED_COMMENT) {
249
- if (token.type == TOKEN.UNFINISHED_COMMENT)
250
- state.onWarning(new VkpParseError(`Unfinished multiline comment`, getLocation()));
251
- comments.push(parseCommentValue(token.value));
252
- nextToken();
253
- } else {
254
- throw new VkpParseError("Syntax error", getLocation());
255
- }
256
- }
257
- return comments.join(" ");
258
- }
259
-
260
- function nextToken() {
261
- state.prevToken = state.token;
262
- let token = state.token ? state.token : LEXER.next();
263
- state.token = LEXER.next();
264
- return token;
265
- }
266
-
267
- function peekToken() {
268
- if (state.token == null)
269
- state.token = LEXER.next();
270
- return state.token;
271
- }
272
-
273
- function prevToken() {
274
- return state.prevToken;
275
- }
276
-
277
- /**
278
- * Token value parsers
279
- * */
280
- function parseCommentValue(v) {
281
- if (v.startsWith(';')) {
282
- return v.substr(1);
283
- } else if (v.startsWith('#')) {
284
- return v.substr(1);
285
- } else if (v.startsWith('//')) {
286
- return v.substr(2);
287
- } else if (v.startsWith('/*')) {
288
- return v.slice(2, -2);
289
- }
290
- throw new VkpParseError(`Invalid comment type`, getLocation());
291
- }
292
-
293
- function parseAnyNumberValue(v) {
294
- let m;
295
- let tmpBuffer = Buffer.allocUnsafe(8);
296
-
297
- if ((m = v.match(/^0i([+-]\d+)$/i))) { // dec signed
298
- let num = m[1];
299
- for (let d of SINT_PARSER_DATA) {
300
- if ((num.length - 1) <= d[0]) {
301
- let parsedNum = BigInt(num);
302
- if (parsedNum < -d[1] || parsedNum > d[1])
303
- throw new VkpParseError(`Number ${v} exceeds allowed range -${d[1].toString(10)} ... +${d[1].toString(10)}`, getLocation());
304
- if ((num.length - 1) < d[0] && d[0] > 3) {
305
- throw new VkpParseError(`The wrong number of digits in integer (${v})`, getLocation(),
306
- "Must be: 3 (for BYTE), 5 (for WORD), 8 (for 3 BYTES), 10 (for DWORD), 13 (for 5 BYTES), 15 (for 6 BYTES), 17 (for 7 BYTES), 20 (for 8 BYTES)." +
307
- "Use leading zeroes to match the number of digits.");
308
- }
309
- tmpBuffer.writeBigUInt64LE(BigInt.asUintN(d[2], parsedNum), 0);
310
- return tmpBuffer.subarray(0, d[2] / 8);
311
- }
312
- }
313
- } else if ((m = v.match(/^0i(\d+)$/i))) { // dec unsigned
314
- let num = m[1];
315
- for (let d of UINT_PARSER_DATA) {
316
- if (num.length <= d[0]) {
317
- let parsedNum = d[2] <= 32 ? parseInt(num, 10) : BigInt(num);
318
- if (parsedNum < 0 || parsedNum > d[1])
319
- throw new VkpParseError(`Number ${v} exceeds allowed range 0 ... ${d[1].toString(10)}`, getLocation());
320
- if (num.length < d[0] && d[0] > 3) {
321
- throw new VkpParseError(`The wrong number of digits in integer (${v})`, getLocation(),
322
- "Must be: 3 (for BYTE), 5 (for WORD), 8 (for 3 BYTES), 10 (for DWORD), 13 (for 5 BYTES), 15 (for 6 BYTES), 17 (for 7 BYTES), 20 (for 8 BYTES)." +
323
- "Use leading zeroes to match the number of digits.");
324
- }
325
- if (d[2] <= 32) {
326
- tmpBuffer.writeUInt32LE(parsedNum, 0);
327
- return tmpBuffer.subarray(0, d[2] / 8);
328
- } else {
329
- tmpBuffer.writeBigUInt64LE(parsedNum, 0);
330
- return tmpBuffer.subarray(0, d[2] / 8);
331
- }
332
- }
333
- }
334
- } else if ((m = v.match(/^0x([a-f0-9]+)$/i))) { // hex unsigned
335
- let hexnum = m[1];
336
- if (hexnum.length % 2)
337
- hexnum = "0" + hexnum;
338
- if (hexnum.length > 8)
339
- throw new VkpParseError(`Number ${v} exceeds allowed range 0x00000000 ... 0xFFFFFFFF`, getLocation());
340
- let number = parseInt(`0x${hexnum}`, 16);
341
- tmpBuffer.writeUInt32LE(number, 0);
342
- return tmpBuffer.subarray(0, Math.ceil(hexnum.length / 2));
343
- } else if ((m = v.match(/^0n([10]+)$/i))) { // binary unsigned
344
- if (m[1].length > 32)
345
- throw new VkpParseError(`Number ${v} exceeds allowed range 0n0 ... 0n11111111111111111111111111111111`, getLocation());
346
- let number = parseInt(m[1], 2);
347
- tmpBuffer.writeUInt32LE(number, 0);
348
- return tmpBuffer.subarray(0, Math.ceil(m[1].length / 8));
349
- }
350
-
351
- throw new VkpParseError(`Invalid number: ${v}`, getLocation());
352
- }
353
-
354
- function parsePlaceholderValue(value) {
355
- let m;
356
- if ((m = value.match(/^(0i|0x|0n)(.*?)$/i))) {
357
- return parseAnyNumberValue(m[1] + m[2].replace(/[^0-9a-f]/gi, '0'));
358
- } else {
359
- return parseHexDataValue(value.replace(/[^0-9a-f]/gi, '0'));
360
- }
361
- }
362
-
363
- function parseStringValue(value) {
364
- let m;
365
- if ((m = value.match(/(\/\*|\*\/|\/\/)/))) {
366
- throw new VkpParseError(`Unescaped ${m[1]} is not allowed in string: ${value}`, getLocation(),
367
- `Escape these ambiguous characters like this: \\/* or \\/\\/.`);
368
- }
369
-
370
- let text = value.slice(1, -1);
371
-
372
- let offset = 0;
373
- let parts = [];
374
- let tmp = "";
375
- let unicode = (value.charAt(0) == "'");
376
- let escape = false;
377
-
378
- /*
379
- if (!unicode && value.match(/[^\u0000-\u007F]/)) {
380
- throw new VkpParseError(`ASCII string with non-ASCII characters`, getLocation(),
381
- `Please use only ASCII-safe characters from the range U+0000-U+007F or \\xNN escape sequences.`);
382
- }
383
- */
384
-
385
- let breakpoint = () => {
386
- if (tmp.length) {
387
- parts.push(tmp);
388
- tmp = "";
389
- }
390
- };
391
-
392
- let getStrLocation = (i) => {
393
- let loc = getLocation();
394
- loc.column += i;
395
- return loc;
396
- };
397
-
398
- for (let i = 0; i < text.length; i++) {
399
- let c = text.charAt(i);
400
- if (escape) {
401
- if (c == "\r") {
402
- if (text.charAt(i + 1) == "\n")
403
- i++;
404
- } else if (c == "\n") {
405
- // Ignore
406
- } else if (c == "x") {
407
- let hex = text.substr(i + 1, 2);
408
- if (hex.length == 2) {
409
- breakpoint();
410
- let hexnum = parseInt(`0x${hex}`);
411
- if (unicode) {
412
- parts.push(Buffer.from([ hexnum, 0x00 ]));
413
- } else {
414
- if (hexnum >= 0x7F && !unicode)
415
- throw new VkpParseError(`Bad escape sequence (\\x${hex})`, getStrLocation(i), `Allowed range: \\x00-\\x7F.`);
416
- parts.push(Buffer.from([ hexnum ]));
417
- }
418
- i += 2;
419
- } else {
420
- throw new VkpParseError(`Unknown escape sequence (\\x${hex})`, getStrLocation(i));
421
- }
422
- } else if (c == "u") {
423
- let hex = text.substr(i + 1, 4);
424
- if (hex.length == 4) {
425
- breakpoint();
426
- let hexnum = parseInt(`0x${hex}`);
427
- if (unicode) {
428
- parts.push(Buffer.from([ hexnum & 0xFF, (hexnum >> 8) & 0xFF ]));
429
- } else {
430
- throw new VkpParseError(`Unknown escape sequence (\\u${hex})`, getStrLocation(i));
431
- }
432
- i += 4;
433
- } else {
434
- throw new VkpParseError(`Unknown escape sequence (\\u${hex})`, getStrLocation(i));
435
- }
436
- } else if (c.match(/[0-7]/)) {
437
- let ocatlLen = 1;
438
- for (let j = 1; j < 3; j++) {
439
- if (!text.charAt(i + j).match(/[0-7]/))
440
- break;
441
- ocatlLen++;
442
- }
443
-
444
- let oct = parseInt(text.substr(i, ocatlLen), 8);
445
- if (oct > 0xFF)
446
- throw new VkpParseError(`Unknown escape sequence (\\${text.substr(i, ocatlLen)})`, getStrLocation(i));
447
-
448
- breakpoint();
449
- if (unicode) {
450
- parts.push(Buffer.from([ oct, 0x00 ]));
451
- } else {
452
- parts.push(Buffer.from([ oct ]));
453
- }
454
-
455
- i += ocatlLen - 1;
456
- } else if ((c in STR_ESCAPE_TABLE)) {
457
- tmp += STR_ESCAPE_TABLE[c];
458
- } else {
459
- throw new VkpParseError(`Unknown escape sequence (\\${c})`, getStrLocation(i));
460
- }
461
- escape = false;
462
- } else {
463
- if (c == '\\') {
464
- escape = true;
465
- } else {
466
- tmp += c;
467
- }
468
- }
469
- }
470
-
471
- breakpoint();
472
-
473
- if (unicode) {
474
- return Buffer.concat(parts.map((p) => typeof p === "string" ? iconv.encode(p, 'utf-16', { addBOM: false }) : p));
475
- } else {
476
- return Buffer.concat(parts.map((p) => typeof p === "string" ? iconv.encode(p, 'windows-1251') : p));
477
- }
478
- }
479
-
480
- function parseOffsetValue(v) {
481
- let result = parseInt(v.replace(/^([+-])(0x)?/i, '$10x'), 16);
482
- if (isNaN(result))
483
- throw new VkpParseError(`Invalid offset: ${v}`, getLocation());
484
- if (result > 0xFFFFFFFF)
485
- throw new VkpParseError(`Offset ${v} exceeds allowed range 00000000 ... FFFFFFFF`, getLocation());
486
- return result;
487
- }
488
-
489
- function parseAddressValue(v) {
490
- let result = parseInt(v.replace(/^(0x)?(.*?):$/i, '0x$2'), 16);
491
- if (isNaN(result))
492
- throw new VkpParseError(`Invalid address: ${v}`, getLocation());
493
- if (result > 0xFFFFFFFF)
494
- throw new VkpParseError(`Address ${v} exceeds allowed range 00000000 ... FFFFFFFF`, getLocation());
495
- return result;
496
- }
497
-
498
- function parseHexDataValue(v) {
499
- if (v.length % 2 != 0)
500
- throw new VkpParseError(`Hex data (${v}) must be even length`, getLocation());
501
- return Buffer.from(v, "hex");
502
- }
503
-
504
- function parsePragmaValue(v) {
505
- let m;
506
- if (!(m = v.trim().match(/^#pragma\s+(enable|disable)\s+(warn_no_old_on_apply|warn_if_new_exist_on_apply|warn_if_old_exist_on_undo|undo|old_equal_ff)$/)))
507
- throw new VkpParseError(`Invalid PRAGMA: ${v}`, getLocation());
508
- return { name: m[2], action: m[1] };
509
- }
510
-
511
- function getLocation() {
512
- return state.token ? { line: state.token.line, column: state.token.col } : { line: 1, column: 1 };
513
- }
514
-
515
- export { vkpRawParser };