swf-toolkit 0.0.0-stage → 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.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -2
  3. package/README.tr.md +126 -0
  4. package/dist/abc/abc-file.d.ts +168 -0
  5. package/dist/abc/abc-file.js +656 -0
  6. package/dist/abc/analysis.d.ts +36 -0
  7. package/dist/abc/analysis.js +213 -0
  8. package/dist/abc/code.d.ts +59 -0
  9. package/dist/abc/code.js +379 -0
  10. package/dist/abc/constants.d.ts +80 -0
  11. package/dist/abc/constants.js +93 -0
  12. package/dist/abc/interner.d.ts +46 -0
  13. package/dist/abc/interner.js +168 -0
  14. package/dist/abc/model.d.ts +40 -0
  15. package/dist/abc/model.js +146 -0
  16. package/dist/abc/opcodes.d.ts +39 -0
  17. package/dist/abc/opcodes.js +196 -0
  18. package/dist/abc/pcode/assembler.d.ts +42 -0
  19. package/dist/abc/pcode/assembler.js +635 -0
  20. package/dist/abc/pcode/disassembler.d.ts +17 -0
  21. package/dist/abc/pcode/disassembler.js +147 -0
  22. package/dist/abc/pcode/format.d.ts +23 -0
  23. package/dist/abc/pcode/format.js +131 -0
  24. package/dist/abc/pcode/lexer.d.ts +34 -0
  25. package/dist/abc/pcode/lexer.js +183 -0
  26. package/dist/assets/assets.d.ts +26 -0
  27. package/dist/assets/assets.js +151 -0
  28. package/dist/assets/images.d.ts +31 -0
  29. package/dist/assets/images.js +238 -0
  30. package/dist/assets/png.d.ts +12 -0
  31. package/dist/assets/png.js +196 -0
  32. package/dist/assets/sounds.d.ts +37 -0
  33. package/dist/assets/sounds.js +236 -0
  34. package/dist/avm1/avm1.d.ts +55 -0
  35. package/dist/avm1/avm1.js +642 -0
  36. package/dist/avm1/project.d.ts +16 -0
  37. package/dist/avm1/project.js +81 -0
  38. package/dist/cjs/abc/abc-file.d.ts +168 -0
  39. package/dist/cjs/abc/abc-file.js +663 -0
  40. package/dist/cjs/abc/analysis.d.ts +36 -0
  41. package/dist/cjs/abc/analysis.js +221 -0
  42. package/dist/cjs/abc/code.d.ts +59 -0
  43. package/dist/cjs/abc/code.js +387 -0
  44. package/dist/cjs/abc/constants.d.ts +80 -0
  45. package/dist/cjs/abc/constants.js +97 -0
  46. package/dist/cjs/abc/interner.d.ts +46 -0
  47. package/dist/cjs/abc/interner.js +172 -0
  48. package/dist/cjs/abc/model.d.ts +40 -0
  49. package/dist/cjs/abc/model.js +155 -0
  50. package/dist/cjs/abc/opcodes.d.ts +39 -0
  51. package/dist/cjs/abc/opcodes.js +201 -0
  52. package/dist/cjs/abc/pcode/assembler.d.ts +42 -0
  53. package/dist/cjs/abc/pcode/assembler.js +640 -0
  54. package/dist/cjs/abc/pcode/disassembler.d.ts +17 -0
  55. package/dist/cjs/abc/pcode/disassembler.js +152 -0
  56. package/dist/cjs/abc/pcode/format.d.ts +23 -0
  57. package/dist/cjs/abc/pcode/format.js +141 -0
  58. package/dist/cjs/abc/pcode/lexer.d.ts +34 -0
  59. package/dist/cjs/abc/pcode/lexer.js +188 -0
  60. package/dist/cjs/assets/assets.d.ts +26 -0
  61. package/dist/cjs/assets/assets.js +159 -0
  62. package/dist/cjs/assets/images.d.ts +31 -0
  63. package/dist/cjs/assets/images.js +246 -0
  64. package/dist/cjs/assets/png.d.ts +12 -0
  65. package/dist/cjs/assets/png.js +202 -0
  66. package/dist/cjs/assets/sounds.d.ts +37 -0
  67. package/dist/cjs/assets/sounds.js +246 -0
  68. package/dist/cjs/avm1/avm1.d.ts +55 -0
  69. package/dist/cjs/avm1/avm1.js +649 -0
  70. package/dist/cjs/avm1/project.d.ts +16 -0
  71. package/dist/cjs/avm1/project.js +88 -0
  72. package/dist/cjs/compiler/ast.d.ts +285 -0
  73. package/dist/cjs/compiler/ast.js +3 -0
  74. package/dist/cjs/compiler/class-compiler.d.ts +34 -0
  75. package/dist/cjs/compiler/class-compiler.js +440 -0
  76. package/dist/cjs/compiler/codegen.d.ts +144 -0
  77. package/dist/cjs/compiler/codegen.js +1589 -0
  78. package/dist/cjs/compiler/lexer.d.ts +27 -0
  79. package/dist/cjs/compiler/lexer.js +254 -0
  80. package/dist/cjs/compiler/parser.d.ts +58 -0
  81. package/dist/cjs/compiler/parser.js +989 -0
  82. package/dist/cjs/compiler/program.d.ts +29 -0
  83. package/dist/cjs/compiler/program.js +36 -0
  84. package/dist/cjs/compiler/scope.d.ts +22 -0
  85. package/dist/cjs/compiler/scope.js +242 -0
  86. package/dist/cjs/compression/lzma.d.ts +37 -0
  87. package/dist/cjs/compression/lzma.js +575 -0
  88. package/dist/cjs/compression/swf-container.d.ts +19 -0
  89. package/dist/cjs/compression/swf-container.js +92 -0
  90. package/dist/cjs/decompiler/ast.d.ts +228 -0
  91. package/dist/cjs/decompiler/ast.js +455 -0
  92. package/dist/cjs/decompiler/cfg.d.ts +40 -0
  93. package/dist/cjs/decompiler/cfg.js +236 -0
  94. package/dist/cjs/decompiler/class-printer.d.ts +9 -0
  95. package/dist/cjs/decompiler/class-printer.js +173 -0
  96. package/dist/cjs/decompiler/method.d.ts +41 -0
  97. package/dist/cjs/decompiler/method.js +436 -0
  98. package/dist/cjs/decompiler/simulate.d.ts +55 -0
  99. package/dist/cjs/decompiler/simulate.js +591 -0
  100. package/dist/cjs/decompiler/structure.d.ts +48 -0
  101. package/dist/cjs/decompiler/structure.js +764 -0
  102. package/dist/cjs/errors.d.ts +11 -0
  103. package/dist/cjs/errors.js +25 -0
  104. package/dist/cjs/index.d.ts +41 -0
  105. package/dist/cjs/index.js +82 -0
  106. package/dist/cjs/io/reader.d.ts +48 -0
  107. package/dist/cjs/io/reader.js +180 -0
  108. package/dist/cjs/io/writer.d.ts +37 -0
  109. package/dist/cjs/io/writer.js +175 -0
  110. package/dist/cjs/package.json +1 -0
  111. package/dist/cjs/project/pcode-project.d.ts +32 -0
  112. package/dist/cjs/project/pcode-project.js +210 -0
  113. package/dist/cjs/project/source-export.d.ts +12 -0
  114. package/dist/cjs/project/source-export.js +51 -0
  115. package/dist/cjs/project/source-import.d.ts +13 -0
  116. package/dist/cjs/project/source-import.js +73 -0
  117. package/dist/cjs/swf/swf.d.ts +63 -0
  118. package/dist/cjs/swf/swf.js +147 -0
  119. package/dist/cjs/swf/tag-codes.d.ts +77 -0
  120. package/dist/cjs/swf/tag-codes.js +109 -0
  121. package/dist/cjs/swf/tags.d.ts +118 -0
  122. package/dist/cjs/swf/tags.js +330 -0
  123. package/dist/cli.d.ts +2 -0
  124. package/dist/cli.js +296 -0
  125. package/dist/compiler/ast.d.ts +285 -0
  126. package/dist/compiler/ast.js +2 -0
  127. package/dist/compiler/class-compiler.d.ts +34 -0
  128. package/dist/compiler/class-compiler.js +437 -0
  129. package/dist/compiler/codegen.d.ts +144 -0
  130. package/dist/compiler/codegen.js +1585 -0
  131. package/dist/compiler/lexer.d.ts +27 -0
  132. package/dist/compiler/lexer.js +249 -0
  133. package/dist/compiler/parser.d.ts +58 -0
  134. package/dist/compiler/parser.js +984 -0
  135. package/dist/compiler/program.d.ts +29 -0
  136. package/dist/compiler/program.js +32 -0
  137. package/dist/compiler/scope.d.ts +22 -0
  138. package/dist/compiler/scope.js +238 -0
  139. package/dist/compression/lzma.d.ts +37 -0
  140. package/dist/compression/lzma.js +569 -0
  141. package/dist/compression/swf-container.d.ts +19 -0
  142. package/dist/compression/swf-container.js +87 -0
  143. package/dist/decompiler/ast.d.ts +228 -0
  144. package/dist/decompiler/ast.js +441 -0
  145. package/dist/decompiler/cfg.d.ts +40 -0
  146. package/dist/decompiler/cfg.js +231 -0
  147. package/dist/decompiler/class-printer.d.ts +9 -0
  148. package/dist/decompiler/class-printer.js +170 -0
  149. package/dist/decompiler/method.d.ts +41 -0
  150. package/dist/decompiler/method.js +427 -0
  151. package/dist/decompiler/simulate.d.ts +55 -0
  152. package/dist/decompiler/simulate.js +586 -0
  153. package/dist/decompiler/structure.d.ts +48 -0
  154. package/dist/decompiler/structure.js +760 -0
  155. package/dist/errors.d.ts +11 -0
  156. package/dist/errors.js +20 -0
  157. package/dist/index.d.ts +41 -0
  158. package/dist/index.js +49 -0
  159. package/dist/io/reader.d.ts +48 -0
  160. package/dist/io/reader.js +176 -0
  161. package/dist/io/writer.d.ts +37 -0
  162. package/dist/io/writer.js +169 -0
  163. package/dist/project/pcode-project.d.ts +32 -0
  164. package/dist/project/pcode-project.js +203 -0
  165. package/dist/project/source-export.d.ts +12 -0
  166. package/dist/project/source-export.js +47 -0
  167. package/dist/project/source-import.d.ts +13 -0
  168. package/dist/project/source-import.js +70 -0
  169. package/dist/swf/swf.d.ts +63 -0
  170. package/dist/swf/swf.js +143 -0
  171. package/dist/swf/tag-codes.d.ts +77 -0
  172. package/dist/swf/tag-codes.js +105 -0
  173. package/dist/swf/tags.d.ts +118 -0
  174. package/dist/swf/tags.js +316 -0
  175. package/package.json +71 -4
@@ -0,0 +1,989 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Parser = void 0;
4
+ exports.parseAs3 = parseAs3;
5
+ const lexer_js_1 = require("./lexer.js");
6
+ const BINARY_PREC = {
7
+ '||': 1,
8
+ '&&': 2,
9
+ '|': 3,
10
+ '^': 4,
11
+ '&': 5,
12
+ '==': 6,
13
+ '!=': 6,
14
+ '===': 6,
15
+ '!==': 6,
16
+ '<': 7,
17
+ '>': 7,
18
+ '<=': 7,
19
+ '>=': 7,
20
+ instanceof: 7,
21
+ is: 7,
22
+ as: 7,
23
+ in: 7,
24
+ '<<': 8,
25
+ '>>': 8,
26
+ '>>>': 8,
27
+ '+': 9,
28
+ '-': 9,
29
+ '*': 10,
30
+ '/': 10,
31
+ '%': 10,
32
+ };
33
+ const ASSIGN_OPS = new Set(['=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', '&=', '|=', '^=', '&&=', '||=']);
34
+ const MODIFIERS = new Set(['public', 'private', 'protected', 'internal', 'static', 'override', 'final', 'native', 'dynamic', 'virtual']);
35
+ class Parser {
36
+ src;
37
+ source;
38
+ toks;
39
+ p = 0;
40
+ constructor(src, source) {
41
+ this.src = src;
42
+ this.source = source;
43
+ this.toks = (0, lexer_js_1.tokenize)(src, source);
44
+ }
45
+ // -------------------------------------------------------------------------
46
+ // Token helpers
47
+ // -------------------------------------------------------------------------
48
+ get t() {
49
+ return this.toks[this.p];
50
+ }
51
+ peek(o = 1) {
52
+ return this.toks[Math.min(this.p + o, this.toks.length - 1)];
53
+ }
54
+ pos(t = this.t) {
55
+ return { line: t.line, col: t.col };
56
+ }
57
+ error(msg, t = this.t) {
58
+ return new lexer_js_1.CompileError(msg, t.line, t.col, this.source);
59
+ }
60
+ is(value) {
61
+ const t = this.t;
62
+ return (t.kind === 'punct' || t.kind === 'kw') && t.value === value;
63
+ }
64
+ isId(value) {
65
+ return this.t.kind === 'id' && (value === undefined || this.t.value === value);
66
+ }
67
+ next() {
68
+ const t = this.t;
69
+ if (t.kind !== 'eof')
70
+ this.p++;
71
+ return t;
72
+ }
73
+ eat(value) {
74
+ if (this.is(value)) {
75
+ this.p++;
76
+ return true;
77
+ }
78
+ return false;
79
+ }
80
+ expect(value) {
81
+ // Split '>>' / '>>>' when closing type parameter lists.
82
+ if (value === '>' && this.t.kind === 'punct' && (this.t.value === '>>' || this.t.value === '>>>' || this.t.value === '>=' || this.t.value === '>>=')) {
83
+ const t = this.t;
84
+ const rest = t.value.slice(1);
85
+ this.toks.splice(this.p + 1, 0, { ...t, value: rest, col: t.col + 1, start: t.start + 1, nl: false });
86
+ this.p++;
87
+ return { ...t, value: '>' };
88
+ }
89
+ if (!this.is(value))
90
+ throw this.error(`Expected '${value}' but found '${this.t.value || 'end of file'}'`);
91
+ return this.next();
92
+ }
93
+ /** Identifier (keywords allowed where `allowKeyword` is set, e.g. after '.'). */
94
+ ident(allowKeyword = false) {
95
+ const t = this.t;
96
+ if (t.kind === 'id' || (allowKeyword && t.kind === 'kw')) {
97
+ this.p++;
98
+ return t.value;
99
+ }
100
+ throw this.error(`Expected identifier but found '${t.value || 'end of file'}'`);
101
+ }
102
+ semicolon() {
103
+ if (this.eat(';'))
104
+ return;
105
+ if (this.is('}') || this.t.kind === 'eof' || this.t.nl)
106
+ return;
107
+ throw this.error(`Expected ';' but found '${this.t.value}'`);
108
+ }
109
+ // -------------------------------------------------------------------------
110
+ // Compilation unit
111
+ // -------------------------------------------------------------------------
112
+ parseUnit() {
113
+ const unit = { package: '', imports: [], useNamespaces: [], classes: [], functions: [] };
114
+ if (this.is('package')) {
115
+ this.next();
116
+ if (!this.is('{'))
117
+ unit.package = this.dottedName();
118
+ this.expect('{');
119
+ this.parseDirectives(unit, '}');
120
+ this.expect('}');
121
+ }
122
+ this.parseDirectives(unit, '');
123
+ return unit;
124
+ }
125
+ dottedName(allowStar = false) {
126
+ let name = this.ident(true);
127
+ while (this.is('.')) {
128
+ this.next();
129
+ if (allowStar && this.eat('*'))
130
+ return name + '.*';
131
+ name += '.' + this.ident(true);
132
+ }
133
+ return name;
134
+ }
135
+ parseDirectives(unit, end) {
136
+ while (this.t.kind !== 'eof' && !(end && this.is(end))) {
137
+ if (this.eat(';'))
138
+ continue;
139
+ if (this.is('import')) {
140
+ this.next();
141
+ unit.imports.push(this.dottedName(true));
142
+ this.semicolon();
143
+ continue;
144
+ }
145
+ if (this.is('use')) {
146
+ this.next();
147
+ if (!this.isId('namespace') && !this.is('namespace'))
148
+ throw this.error("Expected 'namespace'");
149
+ this.next();
150
+ unit.useNamespaces.push(this.dottedName());
151
+ this.semicolon();
152
+ continue;
153
+ }
154
+ const metadata = this.parseMetadataList();
155
+ const mods = this.parseModifiers();
156
+ if (this.is('class') || this.is('interface')) {
157
+ unit.classes.push(this.parseClass(mods, metadata));
158
+ continue;
159
+ }
160
+ if (this.is('function')) {
161
+ const start = this.t.start;
162
+ const m = this.parseMethodMember(mods, metadata, '', start);
163
+ unit.functions.push(m);
164
+ continue;
165
+ }
166
+ if (this.isId('namespace')) {
167
+ // namespace declarations at package level: skip
168
+ this.next();
169
+ this.ident();
170
+ if (this.eat('='))
171
+ this.parseAssign();
172
+ this.semicolon();
173
+ continue;
174
+ }
175
+ throw this.error(`Unexpected '${this.t.value}' at package level`);
176
+ }
177
+ }
178
+ parseMetadataList() {
179
+ const list = [];
180
+ while (this.is('[') && this.peek().kind === 'id') {
181
+ this.next();
182
+ const name = this.ident();
183
+ const items = [];
184
+ if (this.eat('(')) {
185
+ while (!this.is(')')) {
186
+ if ((this.t.kind === 'id' || this.t.kind === 'kw') && this.peek().value === '=') {
187
+ const key = this.next().value;
188
+ this.next();
189
+ items.push({ key, value: this.metaValue() });
190
+ }
191
+ else
192
+ items.push({ value: this.metaValue() });
193
+ if (!this.eat(','))
194
+ break;
195
+ }
196
+ this.expect(')');
197
+ }
198
+ this.expect(']');
199
+ list.push({ name, items });
200
+ }
201
+ return list;
202
+ }
203
+ metaValue() {
204
+ const t = this.next();
205
+ return t.value;
206
+ }
207
+ parseModifiers() {
208
+ const mods = [];
209
+ for (;;) {
210
+ const t = this.t;
211
+ if ((t.kind === 'kw' || t.kind === 'id') && MODIFIERS.has(t.value)) {
212
+ mods.push(t.value);
213
+ this.next();
214
+ continue;
215
+ }
216
+ // Custom namespace modifier: `mx_internal function ...`
217
+ if (t.kind === 'id' && !this.peek().nl && (this.peek().value === 'function' || this.peek().value === 'var' || this.peek().value === 'const' || MODIFIERS.has(this.peek().value))) {
218
+ mods.push(`ns:${t.value}`);
219
+ this.next();
220
+ continue;
221
+ }
222
+ return mods;
223
+ }
224
+ }
225
+ accessOf(mods) {
226
+ for (const m of mods)
227
+ if (m === 'public' || m === 'private' || m === 'protected' || m === 'internal' || m.startsWith('ns:'))
228
+ return m;
229
+ return 'internal';
230
+ }
231
+ parseType() {
232
+ const pos = this.pos();
233
+ if (this.eat('*'))
234
+ return { name: '*', pos };
235
+ if (this.is('void')) {
236
+ this.next();
237
+ return { name: 'void', pos };
238
+ }
239
+ let name = this.ident(true);
240
+ while (this.is('.') && (this.peek().kind === 'id' || this.peek().kind === 'kw')) {
241
+ this.next();
242
+ name += '.' + this.ident(true);
243
+ }
244
+ if (this.eat('.<')) {
245
+ const params = [this.parseType()];
246
+ while (this.eat(','))
247
+ params.push(this.parseType());
248
+ this.expect('>');
249
+ return { name, params, pos };
250
+ }
251
+ return { name, pos };
252
+ }
253
+ parseClass(mods, metadata) {
254
+ const pos = this.pos();
255
+ const isInterface = this.next().value === 'interface';
256
+ const name = this.ident();
257
+ const cls = {
258
+ name,
259
+ isInterface,
260
+ isDynamic: mods.includes('dynamic'),
261
+ isFinal: mods.includes('final'),
262
+ access: this.accessOf(mods),
263
+ implements: [],
264
+ members: [],
265
+ metadata,
266
+ pos,
267
+ };
268
+ if (this.eat('extends')) {
269
+ if (isInterface) {
270
+ cls.implements.push(this.parseType());
271
+ while (this.eat(','))
272
+ cls.implements.push(this.parseType());
273
+ }
274
+ else
275
+ cls.extends = this.parseType();
276
+ }
277
+ if (this.eat('implements')) {
278
+ cls.implements.push(this.parseType());
279
+ while (this.eat(','))
280
+ cls.implements.push(this.parseType());
281
+ }
282
+ this.expect('{');
283
+ while (!this.is('}')) {
284
+ if (this.t.kind === 'eof')
285
+ throw this.error('Unexpected end of file in class body');
286
+ cls.members.push(...this.parseMember(name));
287
+ }
288
+ this.expect('}');
289
+ return cls;
290
+ }
291
+ parseMember(className) {
292
+ if (this.eat(';'))
293
+ return [];
294
+ const start = this.t.start;
295
+ const metadata = this.parseMetadataList();
296
+ const mods = this.parseModifiers();
297
+ if (this.is('function'))
298
+ return [this.parseMethodMember(mods, metadata, className, start)];
299
+ if (this.is('var') || this.is('const')) {
300
+ const isConst = this.next().value === 'const';
301
+ const out = [];
302
+ do {
303
+ const pos = this.pos();
304
+ const name = this.ident(true);
305
+ const type = this.eat(':') ? this.parseType() : undefined;
306
+ const init = this.eat('=') ? this.parseAssign() : undefined;
307
+ out.push({ k: 'field', name, isStatic: mods.includes('static'), isConst, access: this.accessOf(mods), type, init, metadata, pos, span: [start, 0] });
308
+ } while (this.eat(','));
309
+ this.semicolon();
310
+ for (const f of out)
311
+ f.span[1] = this.toks[this.p - 1].end;
312
+ return out;
313
+ }
314
+ if (this.is('{') && mods.length === 0) {
315
+ const pos = this.pos();
316
+ const body = this.parseBlock();
317
+ return [{ k: 'static', body, pos, span: [start, this.toks[this.p - 1].end] }];
318
+ }
319
+ if (this.isId('namespace')) {
320
+ this.next();
321
+ this.ident();
322
+ if (this.eat('='))
323
+ this.parseAssign();
324
+ this.semicolon();
325
+ return [];
326
+ }
327
+ // Loose statements in a class body run in the static initialiser.
328
+ const pos = this.pos();
329
+ const stmt = this.parseStatement();
330
+ return [{ k: 'static', body: [stmt], pos, span: [start, this.toks[this.p - 1].end] }];
331
+ }
332
+ parseMethodMember(mods, metadata, className, start) {
333
+ const pos = this.pos();
334
+ this.expect('function');
335
+ let kind = 'method';
336
+ if ((this.isId('get') || this.isId('set')) && (this.peek().kind === 'id' || this.peek().kind === 'kw') && this.peek().value !== '(') {
337
+ kind = this.next().value;
338
+ }
339
+ const name = this.ident(true);
340
+ if (name === className && kind === 'method' && !mods.includes('static'))
341
+ kind = 'constructor';
342
+ const fn = this.parseFunctionRest(name, mods.includes('native'));
343
+ return {
344
+ k: 'method',
345
+ name,
346
+ kind,
347
+ isStatic: mods.includes('static'),
348
+ isOverride: mods.includes('override'),
349
+ isFinal: mods.includes('final'),
350
+ isNative: mods.includes('native'),
351
+ access: this.accessOf(mods),
352
+ fn,
353
+ metadata,
354
+ pos,
355
+ span: [start, this.toks[this.p - 1].end],
356
+ };
357
+ }
358
+ /** Parses `(params):Type { body }` (body optional for interfaces / native). */
359
+ parseFunctionRest(name, noBody = false) {
360
+ const pos = this.pos();
361
+ this.expect('(');
362
+ const params = [];
363
+ let rest;
364
+ while (!this.is(')')) {
365
+ if (this.eat('...')) {
366
+ const rname = this.ident(true);
367
+ rest = { name: rname, type: this.eat(':') ? this.parseType() : undefined };
368
+ break;
369
+ }
370
+ const pname = this.ident(true);
371
+ const type = this.eat(':') ? this.parseType() : undefined;
372
+ const init = this.eat('=') ? this.parseAssign() : undefined;
373
+ params.push({ name: pname, type, init });
374
+ if (!this.eat(','))
375
+ break;
376
+ }
377
+ this.expect(')');
378
+ const returnType = this.eat(':') ? this.parseType() : undefined;
379
+ let body;
380
+ if (this.is('{') && !noBody)
381
+ body = this.parseBlock();
382
+ else
383
+ this.semicolon();
384
+ return { name, params, rest, returnType, body, pos };
385
+ }
386
+ // -------------------------------------------------------------------------
387
+ // Statements
388
+ // -------------------------------------------------------------------------
389
+ parseBlock() {
390
+ this.expect('{');
391
+ const body = [];
392
+ while (!this.is('}')) {
393
+ if (this.t.kind === 'eof')
394
+ throw this.error("Expected '}'");
395
+ body.push(this.parseStatement());
396
+ }
397
+ this.expect('}');
398
+ return body;
399
+ }
400
+ parseVarDecls(noIn = false) {
401
+ const decls = [];
402
+ do {
403
+ const pos = this.pos();
404
+ const name = this.ident(true);
405
+ const type = this.eat(':') ? this.parseType() : undefined;
406
+ const init = this.eat('=') ? this.parseAssign(noIn) : undefined;
407
+ decls.push({ name, type, init, pos });
408
+ } while (this.eat(','));
409
+ return decls;
410
+ }
411
+ parseStatement() {
412
+ const pos = this.pos();
413
+ const t = this.t;
414
+ if (t.kind === 'punct') {
415
+ if (t.value === '{')
416
+ return { k: 'block', body: this.parseBlock(), pos };
417
+ if (t.value === ';') {
418
+ this.next();
419
+ return { k: 'empty', pos };
420
+ }
421
+ if (t.value === '[') {
422
+ // Metadata inside function bodies is ignored; otherwise an array expression.
423
+ const save = this.p;
424
+ if (this.peek().kind === 'id') {
425
+ try {
426
+ this.parseMetadataList();
427
+ if (this.is('function') || this.is('var'))
428
+ return this.parseStatement();
429
+ }
430
+ catch {
431
+ /* not metadata */
432
+ }
433
+ this.p = save;
434
+ }
435
+ }
436
+ }
437
+ if (t.kind === 'kw') {
438
+ switch (t.value) {
439
+ case 'var':
440
+ case 'const': {
441
+ this.next();
442
+ const decls = this.parseVarDecls();
443
+ this.semicolon();
444
+ return { k: 'var', isConst: t.value === 'const', decls, pos };
445
+ }
446
+ case 'function': {
447
+ if (this.peek().kind === 'id') {
448
+ this.next();
449
+ const name = this.ident();
450
+ return { k: 'function', fn: this.parseFunctionRest(name), pos };
451
+ }
452
+ break;
453
+ }
454
+ case 'if': {
455
+ this.next();
456
+ this.expect('(');
457
+ const c = this.parseExpr();
458
+ this.expect(')');
459
+ const then = this.parseStatement();
460
+ const els = this.eat('else') ? this.parseStatement() : undefined;
461
+ return { k: 'if', c, then, else: els, pos };
462
+ }
463
+ case 'while': {
464
+ this.next();
465
+ this.expect('(');
466
+ const c = this.parseExpr();
467
+ this.expect(')');
468
+ return { k: 'while', c, body: this.parseStatement(), pos };
469
+ }
470
+ case 'do': {
471
+ this.next();
472
+ const body = this.parseStatement();
473
+ this.expect('while');
474
+ this.expect('(');
475
+ const c = this.parseExpr();
476
+ this.expect(')');
477
+ this.eat(';');
478
+ return { k: 'dowhile', c, body, pos };
479
+ }
480
+ case 'for':
481
+ return this.parseFor();
482
+ case 'switch':
483
+ return this.parseSwitch();
484
+ case 'try':
485
+ return this.parseTry();
486
+ case 'return': {
487
+ this.next();
488
+ let e;
489
+ if (!this.is(';') && !this.is('}') && !this.t.nl && this.t.kind !== 'eof')
490
+ e = this.parseExpr();
491
+ this.semicolon();
492
+ return { k: 'return', e, pos };
493
+ }
494
+ case 'throw': {
495
+ this.next();
496
+ const e = this.parseExpr();
497
+ this.semicolon();
498
+ return { k: 'throw', e, pos };
499
+ }
500
+ case 'break':
501
+ case 'continue': {
502
+ this.next();
503
+ let label;
504
+ if (this.t.kind === 'id' && !this.t.nl)
505
+ label = this.next().value;
506
+ this.semicolon();
507
+ return { k: t.value, label, pos };
508
+ }
509
+ case 'with': {
510
+ this.next();
511
+ this.expect('(');
512
+ const obj = this.parseExpr();
513
+ this.expect(')');
514
+ return { k: 'with', obj, body: this.parseStatement(), pos };
515
+ }
516
+ case 'default': {
517
+ // default xml namespace = expr
518
+ if (this.peek().value === 'xml') {
519
+ this.next();
520
+ this.next();
521
+ if (!this.isId('namespace') && !this.is('namespace'))
522
+ throw this.error("Expected 'namespace'");
523
+ this.next();
524
+ this.expect('=');
525
+ const e = this.parseAssign();
526
+ this.semicolon();
527
+ return { k: 'dxns', e, pos };
528
+ }
529
+ break;
530
+ }
531
+ case 'use': {
532
+ this.next();
533
+ this.next();
534
+ this.dottedName();
535
+ this.semicolon();
536
+ return { k: 'empty', pos };
537
+ }
538
+ case 'import': {
539
+ this.next();
540
+ this.dottedName(true);
541
+ this.semicolon();
542
+ return { k: 'empty', pos };
543
+ }
544
+ }
545
+ }
546
+ if (t.kind === 'id' && this.peek().value === ':' && this.peek().kind === 'punct') {
547
+ this.next();
548
+ this.next();
549
+ return { k: 'labeled', label: t.value, body: this.parseStatement(), pos };
550
+ }
551
+ const e = this.parseExpr();
552
+ this.semicolon();
553
+ return { k: 'expr', e, pos };
554
+ }
555
+ parseFor() {
556
+ const pos = this.pos();
557
+ this.expect('for');
558
+ let each = false;
559
+ if (this.isId('each')) {
560
+ this.next();
561
+ each = true;
562
+ }
563
+ this.expect('(');
564
+ let init;
565
+ let decl;
566
+ let target;
567
+ if (this.is('var') || this.is('const')) {
568
+ const kpos = this.pos();
569
+ const isConst = this.next().value === 'const';
570
+ const decls = this.parseVarDecls(true);
571
+ if (this.is('in') && decls.length === 1)
572
+ decl = decls[0];
573
+ else
574
+ init = { k: 'var', isConst, decls, pos: kpos };
575
+ }
576
+ else if (!this.is(';')) {
577
+ const e = this.parseExpr(true);
578
+ if (this.is('in'))
579
+ target = e;
580
+ else
581
+ init = { k: 'expr', e, pos: e.pos };
582
+ }
583
+ if (this.eat('in')) {
584
+ const obj = this.parseExpr();
585
+ this.expect(')');
586
+ return { k: 'forin', each, decl, target, obj, body: this.parseStatement(), pos };
587
+ }
588
+ if (each)
589
+ throw this.error("Expected 'in' in for each");
590
+ this.expect(';');
591
+ const c = this.is(';') ? undefined : this.parseExpr();
592
+ this.expect(';');
593
+ const update = this.is(')') ? undefined : this.parseExpr();
594
+ this.expect(')');
595
+ return { k: 'for', init, c, update, body: this.parseStatement(), pos };
596
+ }
597
+ parseSwitch() {
598
+ const pos = this.pos();
599
+ this.expect('switch');
600
+ this.expect('(');
601
+ const disc = this.parseExpr();
602
+ this.expect(')');
603
+ this.expect('{');
604
+ const cases = [];
605
+ while (!this.is('}')) {
606
+ let test;
607
+ if (this.eat('default'))
608
+ test = null;
609
+ else {
610
+ this.expect('case');
611
+ test = this.parseExpr();
612
+ }
613
+ this.expect(':');
614
+ const body = [];
615
+ while (!this.is('case') && !this.is('default') && !this.is('}'))
616
+ body.push(this.parseStatement());
617
+ cases.push({ test, body });
618
+ }
619
+ this.expect('}');
620
+ return { k: 'switch', disc, cases, pos };
621
+ }
622
+ parseTry() {
623
+ const pos = this.pos();
624
+ this.expect('try');
625
+ const body = this.parseBlock();
626
+ const catches = [];
627
+ while (this.eat('catch')) {
628
+ this.expect('(');
629
+ const name = this.ident(true);
630
+ const type = this.eat(':') ? this.parseType() : undefined;
631
+ this.expect(')');
632
+ catches.push({ name, type, body: this.parseBlock() });
633
+ }
634
+ const fin = this.eat('finally') ? this.parseBlock() : undefined;
635
+ if (!catches.length && !fin)
636
+ throw this.error("Expected 'catch' or 'finally'");
637
+ return { k: 'try', body, catches, finally: fin, pos };
638
+ }
639
+ // -------------------------------------------------------------------------
640
+ // Expressions
641
+ // -------------------------------------------------------------------------
642
+ parseExpr(noIn = false) {
643
+ const first = this.parseAssign(noIn);
644
+ if (!this.is(','))
645
+ return first;
646
+ const list = [first];
647
+ while (this.eat(','))
648
+ list.push(this.parseAssign(noIn));
649
+ return { k: 'comma', list, pos: first.pos };
650
+ }
651
+ parseAssign(noIn = false) {
652
+ const pos = this.pos();
653
+ const left = this.parseConditional(noIn);
654
+ if (this.t.kind === 'punct' && ASSIGN_OPS.has(this.t.value)) {
655
+ const op = this.next().value;
656
+ if (!isAssignable(left))
657
+ throw new lexer_js_1.CompileError('Invalid assignment target', pos.line, pos.col, this.source);
658
+ const v = this.parseAssign(noIn);
659
+ return { k: 'assign', op: op === '=' ? '' : op.slice(0, -1), target: left, v, pos };
660
+ }
661
+ return left;
662
+ }
663
+ parseConditional(noIn) {
664
+ const c = this.parseBinary(0, noIn);
665
+ if (!this.eat('?'))
666
+ return c;
667
+ const t = this.parseAssign();
668
+ this.expect(':');
669
+ const f = this.parseAssign(noIn);
670
+ return { k: 'cond', c, t, f, pos: c.pos };
671
+ }
672
+ binaryOp(noIn) {
673
+ const t = this.t;
674
+ if (t.kind !== 'punct' && t.kind !== 'kw')
675
+ return undefined;
676
+ if (noIn && t.value === 'in')
677
+ return undefined;
678
+ return BINARY_PREC[t.value] !== undefined ? t.value : undefined;
679
+ }
680
+ parseBinary(minPrec, noIn) {
681
+ let left = this.parseUnary();
682
+ for (;;) {
683
+ const op = this.binaryOp(noIn);
684
+ if (!op)
685
+ return left;
686
+ const prec = BINARY_PREC[op];
687
+ if (prec <= minPrec)
688
+ return left;
689
+ this.next();
690
+ if (op === 'as' || op === 'is') {
691
+ // Right side is a type expression (may be '*' or a Vector type).
692
+ if (this.is('*') || this.is('void')) {
693
+ const tpos = this.pos();
694
+ this.next();
695
+ left = { k: 'binary', op, a: left, b: { k: 'id', name: '*', pos: tpos }, pos: left.pos };
696
+ continue;
697
+ }
698
+ }
699
+ const right = this.parseBinary(prec, noIn);
700
+ left = { k: 'binary', op, a: left, b: right, pos: left.pos };
701
+ }
702
+ }
703
+ parseUnary() {
704
+ const pos = this.pos();
705
+ const t = this.t;
706
+ if (t.kind === 'punct' && (t.value === '!' || t.value === '~' || t.value === '-' || t.value === '+')) {
707
+ this.next();
708
+ const e = this.parseUnary();
709
+ if (t.value === '-' && e.k === 'num')
710
+ return { k: 'num', v: -e.v, pos };
711
+ return { k: 'unary', op: t.value, e, pos };
712
+ }
713
+ if (t.kind === 'punct' && (t.value === '++' || t.value === '--')) {
714
+ this.next();
715
+ const e = this.parseUnary();
716
+ return { k: 'update', op: t.value, prefix: true, e, pos };
717
+ }
718
+ if (t.kind === 'kw' && (t.value === 'typeof' || t.value === 'void' || t.value === 'delete')) {
719
+ this.next();
720
+ return { k: 'unary', op: t.value, e: this.parseUnary(), pos };
721
+ }
722
+ const e = this.parsePostfix();
723
+ return e;
724
+ }
725
+ parsePostfix() {
726
+ const e = this.parseLeftHandSide();
727
+ if ((this.is('++') || this.is('--')) && !this.t.nl) {
728
+ const op = this.next().value;
729
+ return { k: 'update', op, prefix: false, e, pos: e.pos };
730
+ }
731
+ return e;
732
+ }
733
+ parseArgs() {
734
+ this.expect('(');
735
+ const args = [];
736
+ while (!this.is(')')) {
737
+ args.push(this.parseAssign());
738
+ if (!this.eat(','))
739
+ break;
740
+ }
741
+ this.expect(')');
742
+ return args;
743
+ }
744
+ parseLeftHandSide() {
745
+ let e;
746
+ const pos = this.pos();
747
+ if (this.is('new')) {
748
+ this.next();
749
+ if (this.is('<')) {
750
+ // Vector literal: new <T>[a, b]
751
+ this.next();
752
+ const type = this.parseType();
753
+ this.expect('>');
754
+ const arr = this.parsePrimary();
755
+ if (arr.k !== 'array')
756
+ throw this.error('Expected array literal after vector type');
757
+ e = { k: 'vector', type, items: arr.items.map((x) => x ?? { k: 'undefined', pos }), pos };
758
+ }
759
+ else {
760
+ const ctor = this.parseMemberOnly();
761
+ const args = this.is('(') ? this.parseArgs() : [];
762
+ e = { k: 'new', ctor, args, pos };
763
+ }
764
+ }
765
+ else {
766
+ e = this.parsePrimary();
767
+ }
768
+ return this.parseSuffixes(e, true);
769
+ }
770
+ /** Member expression without call suffixes (for `new X.Y(args)`). */
771
+ parseMemberOnly() {
772
+ if (this.is('new'))
773
+ return this.parseLeftHandSide();
774
+ return this.parseSuffixes(this.parsePrimary(), false);
775
+ }
776
+ parseSuffixes(e, allowCalls) {
777
+ for (;;) {
778
+ const pos = e.pos;
779
+ if (this.is('.')) {
780
+ this.next();
781
+ if (this.eat('@')) {
782
+ if (this.eat('[')) {
783
+ const index = this.parseExpr();
784
+ this.expect(']');
785
+ e = { k: 'index', obj: e, index, attr: true, pos };
786
+ }
787
+ else
788
+ e = { k: 'member', obj: e, name: this.is('*') ? (this.next(), '*') : this.ident(true), attr: true, pos };
789
+ continue;
790
+ }
791
+ if (this.is('(')) {
792
+ this.next();
793
+ const cond = this.parseExpr();
794
+ this.expect(')');
795
+ e = { k: 'filter', obj: e, cond, pos };
796
+ continue;
797
+ }
798
+ const name = this.is('*') ? (this.next(), '*') : this.ident(true);
799
+ if (this.eat('::')) {
800
+ const local = this.ident(true);
801
+ e = { k: 'member', obj: e, name: local, ns: name, pos };
802
+ }
803
+ else
804
+ e = { k: 'member', obj: e, name, pos };
805
+ continue;
806
+ }
807
+ if (this.is('..')) {
808
+ this.next();
809
+ const attr = this.eat('@');
810
+ e = { k: 'descendants', obj: e, name: this.is('*') ? (this.next(), '*') : this.ident(true), attr, pos };
811
+ continue;
812
+ }
813
+ if (this.is('.<')) {
814
+ this.next();
815
+ const params = [this.parseType()];
816
+ while (this.eat(','))
817
+ params.push(this.parseType());
818
+ this.expect('>');
819
+ e = { k: 'typeapply', base: e, params, pos };
820
+ continue;
821
+ }
822
+ if (this.is('[')) {
823
+ this.next();
824
+ const index = this.parseExpr();
825
+ this.expect(']');
826
+ e = { k: 'index', obj: e, index, pos };
827
+ continue;
828
+ }
829
+ if (allowCalls && this.is('(')) {
830
+ e = { k: 'call', fn: e, args: this.parseArgs(), pos };
831
+ continue;
832
+ }
833
+ return e;
834
+ }
835
+ }
836
+ parsePrimary() {
837
+ const t = this.t;
838
+ const pos = this.pos();
839
+ switch (t.kind) {
840
+ case 'num':
841
+ this.next();
842
+ return { k: 'num', v: t.num, pos };
843
+ case 'str':
844
+ this.next();
845
+ return { k: 'str', v: t.value, pos };
846
+ case 'regex':
847
+ this.next();
848
+ return { k: 'regex', pattern: t.value, flags: t.flags ?? '', pos };
849
+ case 'id': {
850
+ this.next();
851
+ if (t.value === 'undefined')
852
+ return { k: 'undefined', pos };
853
+ if (this.is('::')) {
854
+ this.next();
855
+ return { k: 'id', name: this.ident(true), ns: t.value, pos };
856
+ }
857
+ return { k: 'id', name: t.value, pos };
858
+ }
859
+ case 'kw':
860
+ switch (t.value) {
861
+ case 'this':
862
+ this.next();
863
+ return { k: 'this', pos };
864
+ case 'super':
865
+ this.next();
866
+ return { k: 'super', pos };
867
+ case 'true':
868
+ case 'false':
869
+ this.next();
870
+ return { k: 'bool', v: t.value === 'true', pos };
871
+ case 'null':
872
+ this.next();
873
+ return { k: 'null', pos };
874
+ case 'function': {
875
+ this.next();
876
+ const name = this.t.kind === 'id' ? this.next().value : undefined;
877
+ return { k: 'function', fn: this.parseFunctionRest(name), pos };
878
+ }
879
+ case 'public':
880
+ case 'private':
881
+ case 'protected':
882
+ case 'internal':
883
+ if (this.peek().value === '::') {
884
+ this.next();
885
+ this.next();
886
+ return { k: 'id', name: this.ident(true), ns: t.value, pos };
887
+ }
888
+ break;
889
+ }
890
+ break;
891
+ case 'punct':
892
+ switch (t.value) {
893
+ case '(': {
894
+ this.next();
895
+ const e = this.parseExpr();
896
+ this.expect(')');
897
+ return e;
898
+ }
899
+ case '[': {
900
+ this.next();
901
+ const items = [];
902
+ while (!this.is(']')) {
903
+ if (this.is(',')) {
904
+ this.next();
905
+ items.push(null);
906
+ continue;
907
+ }
908
+ items.push(this.parseAssign());
909
+ if (!this.eat(','))
910
+ break;
911
+ }
912
+ this.expect(']');
913
+ return { k: 'array', items, pos };
914
+ }
915
+ case '{': {
916
+ this.next();
917
+ const props = [];
918
+ while (!this.is('}')) {
919
+ const kt = this.next();
920
+ let key;
921
+ if (kt.kind === 'str' || kt.kind === 'id' || kt.kind === 'kw')
922
+ key = kt.value;
923
+ else if (kt.kind === 'num')
924
+ key = kt.num;
925
+ else
926
+ throw this.error('Expected property name', kt);
927
+ this.expect(':');
928
+ props.push({ key, value: this.parseAssign() });
929
+ if (!this.eat(','))
930
+ break;
931
+ }
932
+ this.expect('}');
933
+ return { k: 'object', props, pos };
934
+ }
935
+ case '@': {
936
+ this.next();
937
+ const name = this.is('*') ? (this.next(), '*') : this.ident(true);
938
+ return { k: 'member', obj: { k: 'this', pos }, name, attr: true, pos };
939
+ }
940
+ case '<':
941
+ return this.parseXmlLiteral();
942
+ }
943
+ break;
944
+ }
945
+ throw this.error(`Unexpected '${t.value || 'end of file'}'`);
946
+ }
947
+ /** Very small XML literal support: the literal is kept as text and parsed at runtime with `new XML(...)`. */
948
+ parseXmlLiteral() {
949
+ const pos = this.pos();
950
+ const start = this.t.start;
951
+ // Scan the raw source for the matching close tag.
952
+ let depth = 0;
953
+ let i = start;
954
+ const src = this.src;
955
+ while (i < src.length) {
956
+ if (src.startsWith('<!--', i)) {
957
+ i = src.indexOf('-->', i) + 3;
958
+ continue;
959
+ }
960
+ if (src[i] === '<') {
961
+ const close = src.indexOf('>', i);
962
+ if (close < 0)
963
+ break;
964
+ const tag = src.slice(i, close + 1);
965
+ if (tag.startsWith('</'))
966
+ depth--;
967
+ else if (!tag.endsWith('/>') && !tag.startsWith('<?'))
968
+ depth++;
969
+ i = close + 1;
970
+ if (depth === 0)
971
+ break;
972
+ continue;
973
+ }
974
+ i++;
975
+ }
976
+ if (depth !== 0)
977
+ throw this.error('Unterminated XML literal');
978
+ while (this.t.kind !== 'eof' && this.t.start < i)
979
+ this.next();
980
+ return { k: 'xml', text: src.slice(start, i), pos };
981
+ }
982
+ }
983
+ exports.Parser = Parser;
984
+ function isAssignable(e) {
985
+ return e.k === 'id' || e.k === 'member' || e.k === 'index' || e.k === 'descendants';
986
+ }
987
+ function parseAs3(src, source) {
988
+ return new Parser(src, source).parseUnit();
989
+ }