cluaupp 0.1.4 → 0.2.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 (113) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +12 -7
  3. package/bin/cluau.js +1 -1
  4. package/bin/cluaupp.js +1 -1
  5. package/docs/README.md +1 -1
  6. package/docs/architecture.md +52 -77
  7. package/docs/cli.md +29 -9
  8. package/docs/comparison.md +3 -3
  9. package/docs/config.md +6 -6
  10. package/docs/cpp-organization.md +2 -2
  11. package/docs/examples/index.md +2 -0
  12. package/docs/getting-started.md +7 -5
  13. package/docs/intellisense.md +49 -31
  14. package/docs/intro.md +2 -2
  15. package/docs/oop/file-tags.md +16 -17
  16. package/docs/oop/index.md +2 -2
  17. package/docs/oop/modules.md +3 -1
  18. package/docs/oop/services.md +7 -14
  19. package/docs/syntax.md +6 -5
  20. package/editors/vscode/extension.js +97 -146
  21. package/editors/vscode/package.json +6 -6
  22. package/examples/README.md +18 -0
  23. package/examples/game/.clangd +25 -0
  24. package/examples/game/.vscode/c_cpp_properties.json +27 -0
  25. package/examples/game/.vscode/extensions.json +5 -0
  26. package/examples/game/.vscode/settings.json +27 -0
  27. package/examples/game/Packages/.gitkeep +0 -0
  28. package/examples/game/cluaupp.config.json +6 -0
  29. package/examples/game/compile_flags.txt +6 -0
  30. package/examples/game/default.project.json +39 -0
  31. package/examples/game/package.json +9 -0
  32. package/examples/game/rokit.toml +5 -0
  33. package/examples/game/src/client/hud.client.cpp +12 -0
  34. package/{src → examples/game/src}/client/init.client.cpp +1 -0
  35. package/examples/game/src/server/combat.server.cpp +23 -0
  36. package/{src → examples/game/src}/server/leaderstats.server.cpp +1 -0
  37. package/examples/game/wally.toml +11 -0
  38. package/generated/api.js +51 -0
  39. package/generated/architecture.js +473 -0
  40. package/generated/ast.js +2 -0
  41. package/generated/cli.js +191 -0
  42. package/generated/compile.js +115 -0
  43. package/generated/editor-install.js +170 -0
  44. package/generated/emit.js +503 -0
  45. package/generated/emitter/luau-codegen.js +167 -0
  46. package/generated/emitter/translators.js +164 -0
  47. package/generated/headers.js +220 -0
  48. package/generated/index.html +49 -0
  49. package/generated/intellisense.js +277 -0
  50. package/generated/layout.js +112 -0
  51. package/generated/lex.js +146 -0
  52. package/generated/libs.js +484 -0
  53. package/generated/lsp.js +55 -0
  54. package/generated/package-info.js +11 -0
  55. package/generated/parse.js +607 -0
  56. package/generated/parser/collector.js +100 -0
  57. package/generated/parser/index.js +30 -0
  58. package/generated/preprocess.js +181 -0
  59. package/generated/system-understander.js +443 -0
  60. package/generated/transpile.js +66 -0
  61. package/generated/types.js +2 -0
  62. package/generated/understand.js +298 -0
  63. package/generated/utils/process-orchestrator.js +63 -0
  64. package/generated/utils/project.js +491 -0
  65. package/generated/utils/rojo-mapper.js +181 -0
  66. package/generated/utils/safe-paths.js +104 -0
  67. package/package.json +25 -8
  68. package/src/api.ts +53 -0
  69. package/src/{architecture.js → architecture.ts} +80 -33
  70. package/src/ast.ts +37 -0
  71. package/src/cli.ts +214 -0
  72. package/src/compile.ts +118 -0
  73. package/src/editor-install.ts +182 -0
  74. package/src/{emit.js → emit.ts} +5 -6
  75. package/src/emitter/luau-codegen.ts +187 -0
  76. package/src/emitter/translators.ts +168 -0
  77. package/src/headers.ts +233 -0
  78. package/src/intellisense.ts +286 -0
  79. package/src/{layout.js → layout.ts} +114 -129
  80. package/src/{lex.js → lex.ts} +4 -6
  81. package/src/{libs.js → libs.ts} +119 -46
  82. package/src/lsp.ts +60 -0
  83. package/src/package-info.ts +7 -0
  84. package/src/{parse.js → parse.ts} +3 -4
  85. package/src/parser/collector.ts +115 -0
  86. package/src/parser/index.ts +27 -0
  87. package/src/{preprocess.js → preprocess.ts} +64 -32
  88. package/src/system-understander.ts +501 -0
  89. package/src/transpile.ts +64 -0
  90. package/src/tree-sitter-cpp.d.ts +4 -0
  91. package/src/types.ts +81 -0
  92. package/src/{understand.js → understand.ts} +24 -213
  93. package/src/utils/process-orchestrator.ts +72 -0
  94. package/src/utils/project.ts +506 -0
  95. package/src/utils/rojo-mapper.ts +190 -0
  96. package/src/utils/safe-paths.ts +98 -0
  97. package/templates/game/.clangd +9 -0
  98. package/templates/game/.vscode/c_cpp_properties.json +1 -1
  99. package/templates/game/.vscode/extensions.json +5 -6
  100. package/templates/game/.vscode/settings.json +7 -6
  101. package/templates/game/cluaupp.config.json +2 -2
  102. package/templates/game/compile_flags.txt +1 -0
  103. package/templates/game/rokit.toml +5 -0
  104. package/templates/game/src/client/init.client.cpp +1 -0
  105. package/templates/game/src/server/leaderstats.server.cpp +1 -0
  106. package/src/api.js +0 -57
  107. package/src/cli.js +0 -481
  108. package/src/compile.js +0 -56
  109. package/src/editor-install.js +0 -218
  110. package/src/intellisense.js +0 -1368
  111. package/src/lsp.js +0 -227
  112. /package/{src → examples/game/src}/shared/config.cpp +0 -0
  113. /package/{src → examples/game/src}/shared/config.h +0 -0
@@ -0,0 +1,607 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parse = parse;
4
+ // @ts-nocheck
5
+ const lex_js_1 = require("./lex.js");
6
+ function parse(source, fileName) {
7
+ const tokens = (0, lex_js_1.tokenize)(source);
8
+ let i = 0;
9
+ const peek = (offset = 0) => tokens[i + offset] || tokens[tokens.length - 1];
10
+ const at = (type, value) => {
11
+ const token = peek();
12
+ if (type && token.type !== type) {
13
+ return false;
14
+ }
15
+ if (value !== undefined && token.value !== value) {
16
+ return false;
17
+ }
18
+ return true;
19
+ };
20
+ const eat = (type, value) => {
21
+ if (!at(type, value)) {
22
+ const token = peek();
23
+ throw error(`expected ${value || type}, got '${token.value}'`, token);
24
+ }
25
+ const token = peek();
26
+ i += 1;
27
+ return token;
28
+ };
29
+ const error = (message, token = peek()) => {
30
+ const err = new Error(`${fileName}:${token.line}:${token.col}: ${message}`);
31
+ err.line = token.line;
32
+ err.col = token.col;
33
+ return err;
34
+ };
35
+ const skipSemicolons = () => {
36
+ while (at("op", ";")) {
37
+ i += 1;
38
+ }
39
+ };
40
+ const skipTemplate = () => {
41
+ if (!at("op", "<")) {
42
+ return null;
43
+ }
44
+ eat("op", "<");
45
+ const name = eat("ident").value;
46
+ eat("op", ">");
47
+ return name;
48
+ };
49
+ function parseType() {
50
+ if (at("kw", "const")) {
51
+ i += 1;
52
+ }
53
+ if (at("kw", "auto") || at("kw", "void") || at("kw", "int") || at("kw", "bool") || at("kw", "float") || at("kw", "double")) {
54
+ const name = eat("kw").value;
55
+ while (at("op", "*")) {
56
+ i += 1;
57
+ }
58
+ return name;
59
+ }
60
+ if (!at("ident")) {
61
+ return null;
62
+ }
63
+ const name = eat("ident").value;
64
+ while (at("op", "*")) {
65
+ i += 1;
66
+ }
67
+ return name;
68
+ }
69
+ function parseArgs() {
70
+ eat("op", "(");
71
+ const args = [];
72
+ if (!at("op", ")")) {
73
+ args.push(parseExpr());
74
+ while (at("op", ",")) {
75
+ i += 1;
76
+ args.push(parseExpr());
77
+ }
78
+ }
79
+ eat("op", ")");
80
+ return args;
81
+ }
82
+ function parseInitList() {
83
+ eat("op", "{");
84
+ const fields = [];
85
+ while (!at("op", "}") && !at("eof")) {
86
+ if (at("op", ".")) {
87
+ i += 1;
88
+ const name = eat("ident").value;
89
+ eat("op", "=");
90
+ fields.push({ name, value: parseExpr() });
91
+ }
92
+ else {
93
+ throw error("expected designated initializer .Field = value");
94
+ }
95
+ if (at("op", ",")) {
96
+ i += 1;
97
+ }
98
+ else {
99
+ break;
100
+ }
101
+ }
102
+ eat("op", "}");
103
+ return { type: "initlist", fields };
104
+ }
105
+ function parsePrimary() {
106
+ if (at("kw", "true") || at("kw", "false")) {
107
+ return { type: "bool", value: eat("kw").value === "true" };
108
+ }
109
+ if (at("kw", "nullptr")) {
110
+ i += 1;
111
+ return { type: "null" };
112
+ }
113
+ if (at("number")) {
114
+ return { type: "number", value: eat("number").value };
115
+ }
116
+ if (at("string")) {
117
+ return { type: "string", value: eat("string").value };
118
+ }
119
+ if (at("kw", "new")) {
120
+ i += 1;
121
+ const className = eat("ident").value;
122
+ const args = at("op", "(") ? parseArgs() : [];
123
+ return { type: "new", className, args };
124
+ }
125
+ if (at("ident", "GetService") && peek(1).value === "<") {
126
+ i += 1;
127
+ const service = skipTemplate();
128
+ eat("op", "(");
129
+ eat("op", ")");
130
+ return { type: "getService", service };
131
+ }
132
+ if (at("ident")) {
133
+ return { type: "ident", name: eat("ident").value };
134
+ }
135
+ if (at("op", "(")) {
136
+ i += 1;
137
+ const expr = parseExpr();
138
+ eat("op", ")");
139
+ return expr;
140
+ }
141
+ if (at("op", "{")) {
142
+ return parseInitList();
143
+ }
144
+ throw error("invalid expression");
145
+ }
146
+ function parseUnary() {
147
+ if (at("op", "-") || at("op", "!")) {
148
+ const op = eat("op").value;
149
+ return { type: "unary", op, argument: parseUnary() };
150
+ }
151
+ return parsePostfix();
152
+ }
153
+ function parsePostfix() {
154
+ let node = parsePrimary();
155
+ for (;;) {
156
+ if (at("op", "->") || at("op", ".") || at("op", "::")) {
157
+ const access = eat("op").value;
158
+ const name = eat("ident").value;
159
+ if (name === "GetService" && at("op", "<")) {
160
+ const service = skipTemplate();
161
+ eat("op", "(");
162
+ eat("op", ")");
163
+ node = { type: "getService", service };
164
+ continue;
165
+ }
166
+ if (at("op", "(")) {
167
+ node = { type: "call", object: node, name, args: parseArgs(), access };
168
+ }
169
+ else {
170
+ node = { type: "member", object: node, name, access };
171
+ }
172
+ continue;
173
+ }
174
+ if (at("op", "(") && node.type === "ident") {
175
+ node = { type: "call", object: null, name: node.name, args: parseArgs(), access: "." };
176
+ continue;
177
+ }
178
+ if (at("op", "{") && (node.type === "ident" || node.type === "member")) {
179
+ node = parseInitList();
180
+ continue;
181
+ }
182
+ break;
183
+ }
184
+ return node;
185
+ }
186
+ function parseBinary() {
187
+ let left = parseUnary();
188
+ while (["==", "!=", "<=", ">=", "<", ">", "&&", "||", "+", "-", "*", "/"].includes(peek().value)) {
189
+ const op = eat("op").value;
190
+ const right = parseUnary();
191
+ left = { type: "binary", op, left, right };
192
+ }
193
+ return left;
194
+ }
195
+ function parseShift() {
196
+ let left = parseBinary();
197
+ while (at("op", "<<")) {
198
+ eat("op", "<<");
199
+ const right = parseBinary();
200
+ left = { type: "binary", op: "<<", left, right };
201
+ }
202
+ return left;
203
+ }
204
+ function parseExpr() {
205
+ const left = parseShift();
206
+ if (at("op", "=")) {
207
+ i += 1;
208
+ return { type: "assign", left, right: parseExpr() };
209
+ }
210
+ return left;
211
+ }
212
+ function parseBlock() {
213
+ eat("op", "{");
214
+ const body = [];
215
+ while (!at("op", "}") && !at("eof")) {
216
+ skipSemicolons();
217
+ if (at("op", "}")) {
218
+ break;
219
+ }
220
+ body.push(parseStmt());
221
+ skipSemicolons();
222
+ }
223
+ eat("op", "}");
224
+ return body;
225
+ }
226
+ function parseSwitch() {
227
+ eat("kw", "switch");
228
+ eat("op", "(");
229
+ const discriminant = parseExpr();
230
+ eat("op", ")");
231
+ eat("op", "{");
232
+ const cases = [];
233
+ let current = null;
234
+ const flush = () => {
235
+ if (current) {
236
+ cases.push(current);
237
+ current = null;
238
+ }
239
+ };
240
+ while (!at("eof") && !at("op", "}")) {
241
+ skipSemicolons();
242
+ if (at("op", "}")) {
243
+ break;
244
+ }
245
+ if (at("kw", "case")) {
246
+ i += 1;
247
+ const value = parseExpr();
248
+ eat("op", ":");
249
+ if (current && current.body.length === 0 && !current.isDefault) {
250
+ current.values.push(value);
251
+ }
252
+ else {
253
+ flush();
254
+ current = { values: [value], body: [], isDefault: false };
255
+ }
256
+ continue;
257
+ }
258
+ if (at("kw", "default")) {
259
+ i += 1;
260
+ eat("op", ":");
261
+ flush();
262
+ current = { values: [], body: [], isDefault: true };
263
+ continue;
264
+ }
265
+ if (!current) {
266
+ throw error("switch body needs case or default before statements");
267
+ }
268
+ if (at("op", "{")) {
269
+ current.body.push(...parseBlock());
270
+ continue;
271
+ }
272
+ current.body.push(parseStmt());
273
+ }
274
+ flush();
275
+ eat("op", "}");
276
+ return { type: "switch", discriminant, cases };
277
+ }
278
+ function parseIf() {
279
+ eat("kw", "if");
280
+ eat("op", "(");
281
+ const test = parseExpr();
282
+ eat("op", ")");
283
+ const consequent = at("op", "{") ? parseBlock() : [parseStmt()];
284
+ let alternate = null;
285
+ if (at("kw", "else")) {
286
+ i += 1;
287
+ alternate = at("op", "{") ? parseBlock() : [parseStmt()];
288
+ }
289
+ return { type: "if", test, consequent, alternate };
290
+ }
291
+ function parseFor() {
292
+ eat("kw", "for");
293
+ eat("op", "(");
294
+ if (at("kw", "auto") || at("ident") || at("kw", "int") || at("kw", "const")) {
295
+ const isConst = at("kw", "const");
296
+ if (isConst) {
297
+ i += 1;
298
+ }
299
+ parseType();
300
+ const name = eat("ident").value;
301
+ if (at("op", ":")) {
302
+ i += 1;
303
+ const iter = parseExpr();
304
+ eat("op", ")");
305
+ const body = at("op", "{") ? parseBlock() : [parseStmt()];
306
+ return { type: "foreach", name, iter, body, isConst };
307
+ }
308
+ }
309
+ throw error("only range-for is supported: for (auto* x : list)");
310
+ }
311
+ function parseDeclOrExpr() {
312
+ const isConst = at("kw", "const");
313
+ if (isConst) {
314
+ i += 1;
315
+ }
316
+ const saved = i;
317
+ const maybeType = parseType();
318
+ if (maybeType && at("ident") && (peek(1).value === "=" || peek(1).value === ";")) {
319
+ const name = eat("ident").value;
320
+ let value = null;
321
+ if (at("op", "=")) {
322
+ i += 1;
323
+ value = parseExpr();
324
+ }
325
+ return { type: "decl", name, valueType: maybeType, value, isConst: isConst || maybeType === "const" };
326
+ }
327
+ i = saved;
328
+ if (isConst) {
329
+ i = saved - 1;
330
+ }
331
+ return { type: "expr", expr: parseExpr() };
332
+ }
333
+ function parseStmt() {
334
+ if (at("kw", "if")) {
335
+ return parseIf();
336
+ }
337
+ if (at("kw", "switch")) {
338
+ return parseSwitch();
339
+ }
340
+ if (at("kw", "for")) {
341
+ return parseFor();
342
+ }
343
+ if (at("kw", "return")) {
344
+ i += 1;
345
+ const value = at("op", ";") || at("op", "}") ? null : parseExpr();
346
+ return { type: "return", value };
347
+ }
348
+ if (at("kw", "break")) {
349
+ i += 1;
350
+ skipSemicolons();
351
+ return { type: "break" };
352
+ }
353
+ if (at("kw", "while")) {
354
+ i += 1;
355
+ eat("op", "(");
356
+ const test = parseExpr();
357
+ eat("op", ")");
358
+ const body = at("op", "{") ? parseBlock() : [parseStmt()];
359
+ return { type: "while", test, body };
360
+ }
361
+ return parseDeclOrExpr();
362
+ }
363
+ function parseParam() {
364
+ const valueType = parseType();
365
+ if (at("ident")) {
366
+ return { name: eat("ident").value, valueType };
367
+ }
368
+ return { name: "arg", valueType };
369
+ }
370
+ function parseQualifiedName() {
371
+ const parts = [eat("ident").value];
372
+ while (at("op", "::")) {
373
+ i += 1;
374
+ parts.push(eat("ident").value);
375
+ }
376
+ return {
377
+ parts,
378
+ name: parts[parts.length - 1],
379
+ owner: parts.length > 1 ? parts.slice(0, -1).join("::") : null,
380
+ };
381
+ }
382
+ function parseFunction() {
383
+ const returnType = parseType();
384
+ if (!returnType || !at("ident")) {
385
+ throw error("invalid function declaration");
386
+ }
387
+ const qualified = parseQualifiedName();
388
+ eat("op", "(");
389
+ const params = [];
390
+ if (!at("op", ")")) {
391
+ params.push(parseParam());
392
+ while (at("op", ",")) {
393
+ i += 1;
394
+ params.push(parseParam());
395
+ }
396
+ }
397
+ eat("op", ")");
398
+ if (at("op", ";")) {
399
+ i += 1;
400
+ return { type: "proto", name: qualified.name, owner: qualified.owner, returnType, params };
401
+ }
402
+ const body = parseBlock();
403
+ return { type: "function", name: qualified.name, owner: qualified.owner, returnType, params, body };
404
+ }
405
+ function parseStruct() {
406
+ eat("kw");
407
+ const name = at("ident") ? eat("ident").value : "_anon";
408
+ if (at("op", ";")) {
409
+ i += 1;
410
+ return { type: "struct", name, fields: [], methods: [] };
411
+ }
412
+ eat("op", "{");
413
+ const fields = [];
414
+ const methods = [];
415
+ while (!at("eof") && !at("op", "}")) {
416
+ skipSemicolons();
417
+ if (at("op", "}")) {
418
+ break;
419
+ }
420
+ if (at("kw", "public") || at("kw", "private") || at("kw", "protected")) {
421
+ i += 1;
422
+ if (at("op", ":")) {
423
+ i += 1;
424
+ }
425
+ continue;
426
+ }
427
+ if (at("kw", "struct") || at("kw", "class")) {
428
+ const nested = parseStruct();
429
+ if (nested.instance) {
430
+ fields.push({
431
+ type: "decl",
432
+ name: nested.instance.name,
433
+ valueType: nested.name,
434
+ value: nested.instance.value || {
435
+ type: "initlist",
436
+ fields: nested.fields.map((field) => ({
437
+ name: field.name,
438
+ value: field.value || { type: "null" },
439
+ })),
440
+ },
441
+ isConst: false,
442
+ owner: name,
443
+ });
444
+ }
445
+ else {
446
+ fields.push(...nested.fields);
447
+ methods.push(...nested.methods);
448
+ }
449
+ continue;
450
+ }
451
+ const saved = i;
452
+ const isConst = at("kw", "const");
453
+ if (isConst) {
454
+ i += 1;
455
+ }
456
+ const valueType = parseType();
457
+ if (valueType && at("ident")) {
458
+ if (peek(1).value === "(") {
459
+ i = saved;
460
+ const method = parseFunction();
461
+ method.owner = method.owner || name;
462
+ methods.push(method);
463
+ continue;
464
+ }
465
+ const fieldName = eat("ident").value;
466
+ let value = null;
467
+ if (at("op", "=")) {
468
+ i += 1;
469
+ value = parseExpr();
470
+ }
471
+ skipSemicolons();
472
+ fields.push({ type: "decl", name: fieldName, valueType, value, isConst, owner: name });
473
+ continue;
474
+ }
475
+ i = saved;
476
+ skipBalanced();
477
+ }
478
+ eat("op", "}");
479
+ let instance = null;
480
+ if (at("ident")) {
481
+ instance = { name: eat("ident").value, value: null };
482
+ if (at("op", "=")) {
483
+ i += 1;
484
+ instance.value = parseExpr();
485
+ }
486
+ }
487
+ skipSemicolons();
488
+ return { type: "struct", name, fields, methods, instance };
489
+ }
490
+ function parseTopLevelDecl(isConst, valueType) {
491
+ const name = eat("ident").value;
492
+ let value = null;
493
+ if (at("op", "=")) {
494
+ i += 1;
495
+ value = parseExpr();
496
+ }
497
+ skipSemicolons();
498
+ return { type: "decl", name, valueType, value, isConst };
499
+ }
500
+ function skipBalanced() {
501
+ if (at("op", "{")) {
502
+ let depth = 0;
503
+ while (!at("eof")) {
504
+ if (at("op", "{")) {
505
+ depth += 1;
506
+ }
507
+ else if (at("op", "}")) {
508
+ depth -= 1;
509
+ i += 1;
510
+ if (depth === 0) {
511
+ break;
512
+ }
513
+ continue;
514
+ }
515
+ i += 1;
516
+ }
517
+ skipSemicolons();
518
+ return;
519
+ }
520
+ while (!at("op", ";") && !at("eof") && !at("op", "}")) {
521
+ i += 1;
522
+ }
523
+ skipSemicolons();
524
+ }
525
+ function skipTypeDecl() {
526
+ while (at("kw", "template") || at("kw", "struct") || at("kw", "class") || at("kw", "enum") || at("kw", "typedef") || at("kw", "extern")) {
527
+ i += 1;
528
+ }
529
+ while (!at("eof") && !at("op", "{") && !at("op", ";")) {
530
+ i += 1;
531
+ }
532
+ skipBalanced();
533
+ }
534
+ function skipNamespace() {
535
+ eat("kw", "namespace");
536
+ if (at("ident")) {
537
+ i += 1;
538
+ }
539
+ if (at("op", "{")) {
540
+ const inner = parseProgramInside();
541
+ eat("op", "}");
542
+ return inner;
543
+ }
544
+ eat("op", ";");
545
+ return [];
546
+ }
547
+ function parseProgramInside() {
548
+ const decls = [];
549
+ while (!at("eof") && !at("op", "}")) {
550
+ skipSemicolons();
551
+ if (at("eof") || at("op", "}")) {
552
+ break;
553
+ }
554
+ if (at("kw", "namespace")) {
555
+ decls.push(...skipNamespace());
556
+ continue;
557
+ }
558
+ if (at("kw", "using")) {
559
+ while (!at("op", ";") && !at("eof")) {
560
+ i += 1;
561
+ }
562
+ skipSemicolons();
563
+ continue;
564
+ }
565
+ if (at("kw", "struct") || at("kw", "class")) {
566
+ const parsed = parseStruct();
567
+ decls.push(...parsed.fields);
568
+ decls.push(...parsed.methods);
569
+ continue;
570
+ }
571
+ if (at("kw", "enum") || at("kw", "template") || at("kw", "typedef") || at("kw", "extern")) {
572
+ skipTypeDecl();
573
+ continue;
574
+ }
575
+ const saved = i;
576
+ const isConst = at("kw", "const");
577
+ if (isConst) {
578
+ i += 1;
579
+ }
580
+ const maybeType = parseType();
581
+ if (maybeType && at("ident")) {
582
+ const next = peek(1);
583
+ if (next.value === "(" || next.value === "::") {
584
+ i = saved;
585
+ decls.push(parseFunction());
586
+ skipSemicolons();
587
+ continue;
588
+ }
589
+ if (next.value === "=" || next.value === ";") {
590
+ decls.push(parseTopLevelDecl(isConst, maybeType));
591
+ continue;
592
+ }
593
+ }
594
+ i = saved;
595
+ if (at("ident") && (peek(1).value === "::" || peek(1).value === "(" || peek(1).value === "." || peek(1).value === "->")) {
596
+ const expr = parseExpr();
597
+ skipSemicolons();
598
+ decls.push({ type: "expr", expr });
599
+ continue;
600
+ }
601
+ decls.push(parseFunction());
602
+ skipSemicolons();
603
+ }
604
+ return decls;
605
+ }
606
+ return { type: "program", body: parseProgramInside(), fileName };
607
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ASTCollector = void 0;
4
+ exports.emptyState = emptyState;
5
+ const translators_js_1 = require("../emitter/translators.js");
6
+ function emptyState(strict = false) {
7
+ return {
8
+ robloxServices: new Set(),
9
+ moduleRequires: new Map(),
10
+ customTypes: [],
11
+ constants: [],
12
+ functions: [],
13
+ headerLines: [],
14
+ strict,
15
+ };
16
+ }
17
+ class ASTCollector {
18
+ mapper;
19
+ state;
20
+ constructor(mapper, context = { fileName: "input.cpp" }) {
21
+ this.mapper = mapper;
22
+ this.state = emptyState(context.strict === true);
23
+ }
24
+ collect(rootNode) {
25
+ this.visit(rootNode, false);
26
+ return this.state;
27
+ }
28
+ visit(node, inFunction) {
29
+ if (node.type === "preproc_include") {
30
+ this.collectInclude(node);
31
+ }
32
+ else if (node.type === "call_expression") {
33
+ this.collectGetService(node);
34
+ }
35
+ else if (!inFunction && node.type === "function_definition") {
36
+ this.visitChildren(node, true);
37
+ return;
38
+ }
39
+ else if (!inFunction && node.type === "declaration") {
40
+ this.collectDeclaration(node);
41
+ }
42
+ else if (!inFunction && (node.type === "type_definition" || node.type === "alias_declaration")) {
43
+ this.collectAlias(node);
44
+ }
45
+ else if (!inFunction && (node.type === "struct_specifier" || node.type === "class_specifier")) {
46
+ this.collectStruct(node);
47
+ }
48
+ else if (!inFunction && node.type === "preproc_call" && /pragma\s+strict/.test(node.text)) {
49
+ this.state.strict = true;
50
+ }
51
+ this.visitChildren(node, inFunction);
52
+ }
53
+ visitChildren(node, inFunction) {
54
+ for (let i = 0; i < node.childCount; i += 1) {
55
+ const child = node.child(i);
56
+ if (child) {
57
+ this.visit(child, inFunction);
58
+ }
59
+ }
60
+ }
61
+ collectInclude(node) {
62
+ const pathNode = node.childForFieldName("path") || node.namedChildren[0] || node.child(1);
63
+ if (!pathNode) {
64
+ return;
65
+ }
66
+ const resolved = this.mapper.resolveIncludeToRequire(pathNode.text);
67
+ if (resolved) {
68
+ this.state.moduleRequires.set(resolved.alias, resolved.path);
69
+ }
70
+ }
71
+ collectGetService(node) {
72
+ const service = (0, translators_js_1.translateGetService)(node);
73
+ if (service) {
74
+ this.state.robloxServices.add(service);
75
+ }
76
+ }
77
+ collectDeclaration(node) {
78
+ const text = node.text.trimStart();
79
+ if (!(text.startsWith("const") || text.startsWith("constexpr"))) {
80
+ return;
81
+ }
82
+ const translated = (0, translators_js_1.translateConstant)(node);
83
+ if (translated) {
84
+ this.state.constants.push(translated);
85
+ }
86
+ }
87
+ collectAlias(node) {
88
+ const translated = (0, translators_js_1.translateAlias)(node, translators_js_1.translateCppType);
89
+ if (translated) {
90
+ this.state.customTypes.push(translated);
91
+ }
92
+ }
93
+ collectStruct(node) {
94
+ const translated = (0, translators_js_1.translateStruct)(node, translators_js_1.translateCppType);
95
+ if (translated) {
96
+ this.state.customTypes.push(translated);
97
+ }
98
+ }
99
+ }
100
+ exports.ASTCollector = ASTCollector;