cluaupp 0.2.3 → 1.0.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 (114) hide show
  1. package/CHANGELOG.md +10 -79
  2. package/README.md +42 -88
  3. package/docs/README.md +13 -21
  4. package/docs/architecture.md +13 -76
  5. package/docs/cli.md +12 -64
  6. package/docs/comparison.md +6 -24
  7. package/docs/config.md +6 -7
  8. package/docs/examples/combat.md +19 -227
  9. package/docs/examples/hud.md +13 -81
  10. package/docs/examples/index.md +9 -16
  11. package/docs/getting-started.md +20 -137
  12. package/docs/intellisense.md +5 -41
  13. package/docs/intro.md +16 -15
  14. package/docs/migration.md +38 -0
  15. package/docs/oop/file-tags.md +12 -36
  16. package/docs/oop/index.md +8 -14
  17. package/docs/syntax.md +54 -236
  18. package/editors/vscode/extension.js +1 -1
  19. package/editors/vscode/package.json +4 -4
  20. package/examples/README.md +5 -7
  21. package/examples/game/.vscode/extensions.json +0 -3
  22. package/examples/game/.vscode/settings.json +6 -14
  23. package/examples/game/src/client/hud.client.clpp +54 -0
  24. package/examples/game/src/client/init.client.clpp +6 -0
  25. package/examples/game/src/server/combat.server.clpp +85 -0
  26. package/examples/game/src/server/leaderstats.server.clpp +28 -0
  27. package/examples/game/src/shared/PlayerData.clh +20 -0
  28. package/examples/game/src/shared/config.clp +6 -0
  29. package/examples/game/src/shared/features.clp +88 -0
  30. package/generated/cli.js +40 -24
  31. package/generated/clpp/contract.js +5 -0
  32. package/generated/clpp/index.js +27 -0
  33. package/generated/clpp/paths.js +121 -0
  34. package/generated/clpp/postprocess.js +78 -0
  35. package/generated/clpp/runner.js +102 -0
  36. package/generated/compile.js +26 -138
  37. package/generated/intellisense.js +38 -157
  38. package/generated/libs.js +1 -1
  39. package/generated/lsp.js +1 -8
  40. package/generated/preprocess.js +8 -179
  41. package/generated/system-understander.js +1 -1
  42. package/generated/transpile.js +3 -50
  43. package/generated/utils/project.js +13 -28
  44. package/include/{cluaupp/datatypes.hpp → clpp/datatypes.clh} +1 -1
  45. package/include/{cluaupp/generated/instances.hpp → clpp/generated/instances.clh} +1 -1
  46. package/include/{cluaupp/libs/chrono.hpp → clpp/libs/chrono.clh} +9 -9
  47. package/include/{cluaupp/libs/cmdr.hpp → clpp/libs/cmdr.clh} +4 -4
  48. package/include/{cluaupp/libs/dataservice.hpp → clpp/libs/dataservice.clh} +8 -15
  49. package/include/{cluaupp/libs/display.hpp → clpp/libs/display.clh} +1 -2
  50. package/include/{cluaupp/libs/ezvisual.hpp → clpp/libs/ezvisual.clh} +1 -3
  51. package/include/{cluaupp/libs/formatnumber.hpp → clpp/libs/formatnumber.clh} +2 -3
  52. package/include/{cluaupp/libs/fusion.hpp → clpp/libs/fusion.clh} +5 -5
  53. package/include/{cluaupp/libs/iris.hpp → clpp/libs/iris.clh} +2 -2
  54. package/include/{cluaupp/libs/janitor.hpp → clpp/libs/janitor.clh} +1 -2
  55. package/include/{cluaupp/libs/module3d.hpp → clpp/libs/module3d.clh} +2 -4
  56. package/include/{cluaupp/libs/net.hpp → clpp/libs/net.clh} +2 -4
  57. package/include/{cluaupp/libs/promise.hpp → clpp/libs/promise.clh} +1 -4
  58. package/include/{cluaupp/libs/spring.hpp → clpp/libs/spring.clh} +1 -2
  59. package/include/{cluaupp/libs/statemachine.hpp → clpp/libs/statemachine.clh} +2 -3
  60. package/include/{cluaupp/libs/stickybillboard.hpp → clpp/libs/stickybillboard.clh} +1 -2
  61. package/include/{cluaupp/libs/topbarplus.hpp → clpp/libs/topbarplus.clh} +3 -4
  62. package/include/clpp/libs.clh +24 -0
  63. package/include/clpp/roblox.clh +31 -0
  64. package/package.json +7 -14
  65. package/src/cli.ts +39 -25
  66. package/src/clpp/contract.ts +39 -0
  67. package/src/clpp/index.ts +18 -0
  68. package/src/clpp/paths.ts +117 -0
  69. package/src/clpp/postprocess.ts +93 -0
  70. package/src/clpp/runner.ts +102 -0
  71. package/src/compile.ts +26 -149
  72. package/src/intellisense.ts +36 -165
  73. package/src/libs.ts +1 -1
  74. package/src/lsp.ts +1 -5
  75. package/src/preprocess.ts +8 -180
  76. package/src/system-understander.ts +1 -1
  77. package/src/transpile.ts +9 -52
  78. package/src/utils/project.ts +12 -28
  79. package/templates/game/.vscode/extensions.json +0 -3
  80. package/templates/game/.vscode/settings.json +6 -14
  81. package/templates/game/src/client/init.client.clpp +6 -0
  82. package/templates/game/src/server/leaderstats.server.clpp +29 -0
  83. package/templates/game/src/shared/config.clp +6 -0
  84. package/examples/game/.clangd +0 -25
  85. package/examples/game/compile_flags.txt +0 -6
  86. package/examples/game/src/client/hud.client.cpp +0 -12
  87. package/examples/game/src/client/init.client.cpp +0 -6
  88. package/examples/game/src/server/combat.server.cpp +0 -23
  89. package/examples/game/src/server/leaderstats.server.cpp +0 -29
  90. package/examples/game/src/shared/config.cpp +0 -5
  91. package/examples/game/src/shared/config.h +0 -5
  92. package/include/cluaupp/libs.hpp +0 -25
  93. package/include/cluaupp/roblox.hpp +0 -47
  94. package/src/architecture.ts +0 -500
  95. package/src/ast.ts +0 -37
  96. package/src/emit.ts +0 -609
  97. package/src/emitter/luau-codegen.ts +0 -187
  98. package/src/emitter/translators.ts +0 -168
  99. package/src/headers.ts +0 -233
  100. package/src/layout.ts +0 -114
  101. package/src/lex.ts +0 -158
  102. package/src/parse.ts +0 -729
  103. package/src/parser/collector.ts +0 -148
  104. package/src/parser/index.ts +0 -27
  105. package/src/tree-sitter-cpp.d.ts +0 -4
  106. package/src/understand.ts +0 -322
  107. package/templates/game/src/client/init.client.cpp +0 -6
  108. package/templates/game/src/server/leaderstats.server.cpp +0 -29
  109. package/templates/game/src/shared/config.cpp +0 -5
  110. package/templates/game/src/shared/config.h +0 -5
  111. /package/include/{cluaupp/generated/enums.hpp → clpp/generated/enums.clh} +0 -0
  112. /package/include/{cluaupp/libs/math.hpp → clpp/libs/math.clh} +0 -0
  113. /package/include/{cluaupp/libs/twinkle.hpp → clpp/libs/twinkle.clh} +0 -0
  114. /package/include/{cluaupp/libs/vfx.hpp → clpp/libs/vfx.clh} +0 -0
package/src/parse.ts DELETED
@@ -1,729 +0,0 @@
1
- // @ts-nocheck
2
- import { tokenize } from "./lex.js";
3
-
4
- function parse(source, fileName) {
5
- const tokens = tokenize(source);
6
- let i = 0;
7
-
8
- const peek = (offset = 0) => tokens[i + offset] || tokens[tokens.length - 1];
9
- const at = (type, value) => {
10
- const token = peek();
11
- if (type && token.type !== type) {
12
- return false;
13
- }
14
- if (value !== undefined && token.value !== value) {
15
- return false;
16
- }
17
- return true;
18
- };
19
- const eat = (type, value) => {
20
- if (!at(type, value)) {
21
- const token = peek();
22
- throw error(`expected ${value || type}, got '${token.value}'`, token);
23
- }
24
- const token = peek();
25
- i += 1;
26
- return token;
27
- };
28
- const error = (message, token = peek()) => {
29
- const err = new Error(`${fileName}:${token.line}:${token.col}: ${message}`);
30
- err.line = token.line;
31
- err.col = token.col;
32
- return err;
33
- };
34
-
35
- const skipSemicolons = () => {
36
- while (at("op", ";")) {
37
- i += 1;
38
- }
39
- };
40
-
41
- const skipTemplate = () => {
42
- if (!at("op", "<")) {
43
- return null;
44
- }
45
- eat("op", "<");
46
- const name = eat("ident").value;
47
- eat("op", ">");
48
- return name;
49
- };
50
-
51
- const CAST_NAMES = new Set(["static_cast", "const_cast", "reinterpret_cast", "dynamic_cast"]);
52
-
53
- function skipSpecifiers() {
54
- let isConst = false;
55
- while (at("kw", "static") || at("kw", "inline") || at("kw", "constexpr") || at("kw", "const")) {
56
- if (at("kw", "constexpr") || at("kw", "const")) {
57
- isConst = true;
58
- }
59
- i += 1;
60
- }
61
- return isConst;
62
- }
63
-
64
- function looksLikePrimaryStart() {
65
- if (at("ident") || at("number") || at("string")) {
66
- return true;
67
- }
68
- if (at("kw", "true") || at("kw", "false") || at("kw", "nullptr") || at("kw", "new")) {
69
- return true;
70
- }
71
- return at("op", "(") || at("op", "{") || at("op", "[") || at("op", "-") || at("op", "!");
72
- }
73
-
74
- function parseLambda() {
75
- eat("op", "[");
76
- while (!at("eof") && !at("op", "]")) {
77
- i += 1;
78
- }
79
- eat("op", "]");
80
- const params = [];
81
- if (at("op", "(")) {
82
- i += 1;
83
- if (!at("op", ")")) {
84
- params.push(parseParam());
85
- while (at("op", ",")) {
86
- i += 1;
87
- params.push(parseParam());
88
- }
89
- }
90
- eat("op", ")");
91
- }
92
- const body = parseBlock();
93
- return { type: "lambda", params, body };
94
- }
95
-
96
- function parseNamedCast() {
97
- i += 1;
98
- eat("op", "<");
99
- const valueType = parseType() || "any";
100
- eat("op", ">");
101
- const args = parseArgs();
102
- return { type: "cast", valueType, argument: args[0] || { type: "null" } };
103
- }
104
-
105
- function parseParenPrimary() {
106
- eat("op", "(");
107
- const saved = i;
108
- skipSpecifiers();
109
- const valueType = parseType();
110
- if (valueType && at("op", ")")) {
111
- i += 1;
112
- if (looksLikePrimaryStart()) {
113
- return { type: "cast", valueType, argument: parseUnary() };
114
- }
115
- }
116
- i = saved;
117
- const expr = parseExpr();
118
- eat("op", ")");
119
- return expr;
120
- }
121
-
122
- function eatGeneric() {
123
- let text = "";
124
- let depth = 0;
125
- while (!at("eof")) {
126
- const token = peek();
127
- if (at("op", "<")) {
128
- depth += 1;
129
- text += "<";
130
- i += 1;
131
- continue;
132
- }
133
- if (at("op", ">")) {
134
- depth -= 1;
135
- text += ">";
136
- i += 1;
137
- if (depth === 0) {
138
- return text;
139
- }
140
- continue;
141
- }
142
- if (token.type === "string") {
143
- text += `"${token.value}"`;
144
- } else {
145
- text += token.value;
146
- }
147
- i += 1;
148
- }
149
- return text;
150
- }
151
-
152
- function parseType() {
153
- if (at("kw", "const")) {
154
- i += 1;
155
- }
156
- let name = null;
157
- if (at("kw", "auto") || at("kw", "void") || at("kw", "int") || at("kw", "bool") || at("kw", "float") || at("kw", "double")) {
158
- name = eat("kw").value;
159
- } else if (at("ident")) {
160
- name = eat("ident").value;
161
- while (at("op", "::")) {
162
- i += 1;
163
- if (at("ident")) {
164
- name += `::${eat("ident").value}`;
165
- }
166
- }
167
- } else {
168
- return null;
169
- }
170
- if (at("op", "<")) {
171
- name += eatGeneric();
172
- }
173
- while (at("op", "*")) {
174
- i += 1;
175
- }
176
- return name;
177
- }
178
-
179
- function parseArgs() {
180
- eat("op", "(");
181
- const args = [];
182
- if (!at("op", ")")) {
183
- args.push(parseExpr());
184
- while (at("op", ",")) {
185
- i += 1;
186
- args.push(parseExpr());
187
- }
188
- }
189
- eat("op", ")");
190
- return args;
191
- }
192
-
193
- function parseInitList() {
194
- eat("op", "{");
195
- const fields = [];
196
- while (!at("op", "}") && !at("eof")) {
197
- if (at("op", ".")) {
198
- i += 1;
199
- const name = eat("ident").value;
200
- eat("op", "=");
201
- fields.push({ name, value: parseExpr() });
202
- } else {
203
- throw error("expected designated initializer .Field = value");
204
- }
205
- if (at("op", ",")) {
206
- i += 1;
207
- } else {
208
- break;
209
- }
210
- }
211
- eat("op", "}");
212
- return { type: "initlist", fields };
213
- }
214
-
215
- function parsePrimary() {
216
- if (at("kw", "true") || at("kw", "false")) {
217
- return { type: "bool", value: eat("kw").value === "true" };
218
- }
219
- if (at("kw", "nullptr")) {
220
- i += 1;
221
- return { type: "null" };
222
- }
223
- if (at("number")) {
224
- return { type: "number", value: eat("number").value };
225
- }
226
- if (at("string")) {
227
- return { type: "string", value: eat("string").value };
228
- }
229
- if (at("kw", "new")) {
230
- i += 1;
231
- const className = eat("ident").value;
232
- const args = at("op", "(") ? parseArgs() : [];
233
- return { type: "new", className, args };
234
- }
235
- if (at("ident", "GetService") && peek(1).value === "<") {
236
- i += 1;
237
- const service = skipTemplate();
238
- eat("op", "(");
239
- eat("op", ")");
240
- return { type: "getService", service };
241
- }
242
- if (at("ident") && CAST_NAMES.has(peek().value) && peek(1).value === "<") {
243
- return parseNamedCast();
244
- }
245
- if (at("ident")) {
246
- return { type: "ident", name: eat("ident").value };
247
- }
248
- if (at("op", "[")) {
249
- return parseLambda();
250
- }
251
- if (at("op", "(")) {
252
- return parseParenPrimary();
253
- }
254
- if (at("op", "{")) {
255
- return parseInitList();
256
- }
257
- throw error("invalid expression");
258
- }
259
-
260
- function parseUnary() {
261
- if (at("op", "-") || at("op", "!")) {
262
- const op = eat("op").value;
263
- return { type: "unary", op, argument: parseUnary() };
264
- }
265
- return parsePostfix();
266
- }
267
-
268
- function parsePostfix() {
269
- let node = parsePrimary();
270
- for (;;) {
271
- if (at("op", "->") || at("op", ".") || at("op", "::")) {
272
- const access = eat("op").value;
273
- const name = eat("ident").value;
274
- if (name === "GetService" && at("op", "<")) {
275
- const service = skipTemplate();
276
- eat("op", "(");
277
- eat("op", ")");
278
- node = { type: "getService", service };
279
- continue;
280
- }
281
- if (at("op", "(")) {
282
- node = { type: "call", object: node, name, args: parseArgs(), access };
283
- } else {
284
- node = { type: "member", object: node, name, access };
285
- }
286
- continue;
287
- }
288
- if (at("op", "(") && node.type === "ident") {
289
- node = { type: "call", object: null, name: node.name, args: parseArgs(), access: "." };
290
- continue;
291
- }
292
- if (at("op", "{") && (node.type === "ident" || node.type === "member")) {
293
- node = parseInitList();
294
- continue;
295
- }
296
- break;
297
- }
298
- return node;
299
- }
300
-
301
- function parseBinary() {
302
- let left = parseUnary();
303
- while (["==", "!=", "<=", ">=", "<", ">", "&&", "||", "+", "-", "*", "/"].includes(peek().value)) {
304
- const op = eat("op").value;
305
- const right = parseUnary();
306
- left = { type: "binary", op, left, right };
307
- }
308
- return left;
309
- }
310
-
311
- function parseShift() {
312
- let left = parseBinary();
313
- while (at("op", "<<")) {
314
- eat("op", "<<");
315
- const right = parseBinary();
316
- left = { type: "binary", op: "<<", left, right };
317
- }
318
- return left;
319
- }
320
-
321
- function parseExpr() {
322
- const left = parseShift();
323
- if (at("op", "=")) {
324
- i += 1;
325
- return { type: "assign", left, right: parseExpr() };
326
- }
327
- return left;
328
- }
329
-
330
- function parseBlock() {
331
- eat("op", "{");
332
- const body = [];
333
- while (!at("op", "}") && !at("eof")) {
334
- skipSemicolons();
335
- if (at("op", "}")) {
336
- break;
337
- }
338
- body.push(parseStmt());
339
- skipSemicolons();
340
- }
341
- eat("op", "}");
342
- return body;
343
- }
344
-
345
- function parseSwitch() {
346
- eat("kw", "switch");
347
- eat("op", "(");
348
- const discriminant = parseExpr();
349
- eat("op", ")");
350
- eat("op", "{");
351
- const cases = [];
352
- let current = null;
353
- const flush = () => {
354
- if (current) {
355
- cases.push(current);
356
- current = null;
357
- }
358
- };
359
- while (!at("eof") && !at("op", "}")) {
360
- skipSemicolons();
361
- if (at("op", "}")) {
362
- break;
363
- }
364
- if (at("kw", "case")) {
365
- i += 1;
366
- const value = parseExpr();
367
- eat("op", ":");
368
- if (current && current.body.length === 0 && !current.isDefault) {
369
- current.values.push(value);
370
- } else {
371
- flush();
372
- current = { values: [value], body: [], isDefault: false };
373
- }
374
- continue;
375
- }
376
- if (at("kw", "default")) {
377
- i += 1;
378
- eat("op", ":");
379
- flush();
380
- current = { values: [], body: [], isDefault: true };
381
- continue;
382
- }
383
- if (!current) {
384
- throw error("switch body needs case or default before statements");
385
- }
386
- if (at("op", "{")) {
387
- current.body.push(...parseBlock());
388
- continue;
389
- }
390
- current.body.push(parseStmt());
391
- }
392
- flush();
393
- eat("op", "}");
394
- return { type: "switch", discriminant, cases };
395
- }
396
-
397
- function parseIf() {
398
- eat("kw", "if");
399
- eat("op", "(");
400
- const test = parseExpr();
401
- eat("op", ")");
402
- const consequent = at("op", "{") ? parseBlock() : [parseStmt()];
403
- let alternate = null;
404
- if (at("kw", "else")) {
405
- i += 1;
406
- alternate = at("op", "{") ? parseBlock() : [parseStmt()];
407
- }
408
- return { type: "if", test, consequent, alternate };
409
- }
410
-
411
- function parseFor() {
412
- eat("kw", "for");
413
- eat("op", "(");
414
- if (at("kw", "auto") || at("ident") || at("kw", "int") || at("kw", "const")) {
415
- const isConst = at("kw", "const");
416
- if (isConst) {
417
- i += 1;
418
- }
419
- parseType();
420
- const name = eat("ident").value;
421
- if (at("op", ":")) {
422
- i += 1;
423
- const iter = parseExpr();
424
- eat("op", ")");
425
- const body = at("op", "{") ? parseBlock() : [parseStmt()];
426
- return { type: "foreach", name, iter, body, isConst };
427
- }
428
- }
429
- throw error("only range-for is supported: for (auto* x : list)");
430
- }
431
-
432
- function parseDeclOrExpr() {
433
- const saved = i;
434
- const isConst = skipSpecifiers();
435
- const maybeType = parseType();
436
- if (maybeType && at("ident") && (peek(1).value === "=" || peek(1).value === ";")) {
437
- const name = eat("ident").value;
438
- let value = null;
439
- if (at("op", "=")) {
440
- i += 1;
441
- value = parseExpr();
442
- }
443
- return { type: "decl", name, valueType: maybeType, value, isConst: isConst || maybeType === "const" };
444
- }
445
- i = saved;
446
- return { type: "expr", expr: parseExpr() };
447
- }
448
-
449
- function parseStmt() {
450
- if (at("kw", "if")) {
451
- return parseIf();
452
- }
453
- if (at("kw", "switch")) {
454
- return parseSwitch();
455
- }
456
- if (at("kw", "for")) {
457
- return parseFor();
458
- }
459
- if (at("kw", "return")) {
460
- i += 1;
461
- const value = at("op", ";") || at("op", "}") ? null : parseExpr();
462
- return { type: "return", value };
463
- }
464
- if (at("kw", "break")) {
465
- i += 1;
466
- skipSemicolons();
467
- return { type: "break" };
468
- }
469
- if (at("kw", "while")) {
470
- i += 1;
471
- eat("op", "(");
472
- const test = parseExpr();
473
- eat("op", ")");
474
- const body = at("op", "{") ? parseBlock() : [parseStmt()];
475
- return { type: "while", test, body };
476
- }
477
- return parseDeclOrExpr();
478
- }
479
-
480
- function parseParam() {
481
- const valueType = parseType();
482
- if (at("ident")) {
483
- return { name: eat("ident").value, valueType };
484
- }
485
- return { name: "arg", valueType };
486
- }
487
-
488
- function parseQualifiedName() {
489
- const parts = [eat("ident").value];
490
- while (at("op", "::")) {
491
- i += 1;
492
- parts.push(eat("ident").value);
493
- }
494
- return {
495
- parts,
496
- name: parts[parts.length - 1],
497
- owner: parts.length > 1 ? parts.slice(0, -1).join("::") : null,
498
- };
499
- }
500
-
501
- function parseFunction() {
502
- skipSpecifiers();
503
- const returnType = parseType();
504
- if (!returnType || !at("ident")) {
505
- throw error("invalid function declaration");
506
- }
507
- const qualified = parseQualifiedName();
508
- eat("op", "(");
509
- const params = [];
510
- if (!at("op", ")")) {
511
- params.push(parseParam());
512
- while (at("op", ",")) {
513
- i += 1;
514
- params.push(parseParam());
515
- }
516
- }
517
- eat("op", ")");
518
- if (at("op", ";")) {
519
- i += 1;
520
- return { type: "proto", name: qualified.name, owner: qualified.owner, returnType, params };
521
- }
522
- const body = parseBlock();
523
- return { type: "function", name: qualified.name, owner: qualified.owner, returnType, params, body };
524
- }
525
-
526
- function parseStruct() {
527
- eat("kw");
528
- const name = at("ident") ? eat("ident").value : "_anon";
529
- if (at("op", ";")) {
530
- i += 1;
531
- return { type: "struct", name, fields: [], methods: [] };
532
- }
533
- eat("op", "{");
534
- const fields = [];
535
- const methods = [];
536
- while (!at("eof") && !at("op", "}")) {
537
- skipSemicolons();
538
- if (at("op", "}")) {
539
- break;
540
- }
541
- if (at("kw", "public") || at("kw", "private") || at("kw", "protected")) {
542
- i += 1;
543
- if (at("op", ":")) {
544
- i += 1;
545
- }
546
- continue;
547
- }
548
- if (at("kw", "struct") || at("kw", "class")) {
549
- const nested = parseStruct();
550
- if (nested.instance) {
551
- fields.push({
552
- type: "decl",
553
- name: nested.instance.name,
554
- valueType: nested.name,
555
- value:
556
- nested.instance.value || {
557
- type: "initlist",
558
- fields: nested.fields.map((field) => ({
559
- name: field.name,
560
- value: field.value || { type: "null" },
561
- })),
562
- },
563
- isConst: false,
564
- owner: name,
565
- });
566
- } else {
567
- fields.push(...nested.fields);
568
- methods.push(...nested.methods);
569
- }
570
- continue;
571
- }
572
- const saved = i;
573
- const isConst = skipSpecifiers();
574
- const valueType = parseType();
575
- if (valueType && at("ident")) {
576
- if (peek(1).value === "(") {
577
- i = saved;
578
- const method = parseFunction();
579
- method.owner = method.owner || name;
580
- methods.push(method);
581
- continue;
582
- }
583
- const fieldName = eat("ident").value;
584
- let value = null;
585
- if (at("op", "=")) {
586
- i += 1;
587
- value = parseExpr();
588
- }
589
- skipSemicolons();
590
- fields.push({ type: "decl", name: fieldName, valueType, value, isConst, owner: name });
591
- continue;
592
- }
593
- i = saved;
594
- skipBalanced();
595
- }
596
- eat("op", "}");
597
- let instance = null;
598
- if (at("ident")) {
599
- instance = { name: eat("ident").value, value: null };
600
- if (at("op", "=")) {
601
- i += 1;
602
- instance.value = parseExpr();
603
- }
604
- }
605
- skipSemicolons();
606
- return { type: "struct", name, fields, methods, instance };
607
- }
608
-
609
- function parseTopLevelDecl(isConst, valueType) {
610
- const name = eat("ident").value;
611
- let value = null;
612
- if (at("op", "=")) {
613
- i += 1;
614
- value = parseExpr();
615
- }
616
- skipSemicolons();
617
- return { type: "decl", name, valueType, value, isConst };
618
- }
619
-
620
- function skipBalanced() {
621
- if (at("op", "{")) {
622
- let depth = 0;
623
- while (!at("eof")) {
624
- if (at("op", "{")) {
625
- depth += 1;
626
- } else if (at("op", "}")) {
627
- depth -= 1;
628
- i += 1;
629
- if (depth === 0) {
630
- break;
631
- }
632
- continue;
633
- }
634
- i += 1;
635
- }
636
- skipSemicolons();
637
- return;
638
- }
639
- while (!at("op", ";") && !at("eof") && !at("op", "}")) {
640
- i += 1;
641
- }
642
- skipSemicolons();
643
- }
644
-
645
- function skipTypeDecl() {
646
- while (at("kw", "template") || at("kw", "struct") || at("kw", "class") || at("kw", "enum") || at("kw", "typedef") || at("kw", "extern")) {
647
- i += 1;
648
- }
649
- while (!at("eof") && !at("op", "{") && !at("op", ";")) {
650
- i += 1;
651
- }
652
- skipBalanced();
653
- }
654
-
655
- function skipNamespace() {
656
- eat("kw", "namespace");
657
- if (at("ident")) {
658
- i += 1;
659
- }
660
- if (at("op", "{")) {
661
- const inner = parseProgramInside();
662
- eat("op", "}");
663
- return inner;
664
- }
665
- eat("op", ";");
666
- return [];
667
- }
668
-
669
- function parseProgramInside() {
670
- const decls = [];
671
- while (!at("eof") && !at("op", "}")) {
672
- skipSemicolons();
673
- if (at("eof") || at("op", "}")) {
674
- break;
675
- }
676
- if (at("kw", "namespace")) {
677
- decls.push(...skipNamespace());
678
- continue;
679
- }
680
- if (at("kw", "using")) {
681
- while (!at("op", ";") && !at("eof")) {
682
- i += 1;
683
- }
684
- skipSemicolons();
685
- continue;
686
- }
687
- if (at("kw", "struct") || at("kw", "class")) {
688
- const parsed = parseStruct();
689
- decls.push(...parsed.fields);
690
- decls.push(...parsed.methods);
691
- continue;
692
- }
693
- if (at("kw", "enum") || at("kw", "template") || at("kw", "typedef") || at("kw", "extern")) {
694
- skipTypeDecl();
695
- continue;
696
- }
697
- const saved = i;
698
- const isConst = skipSpecifiers();
699
- const maybeType = parseType();
700
- if (maybeType && at("ident")) {
701
- const next = peek(1);
702
- if (next.value === "(" || next.value === "::") {
703
- i = saved;
704
- decls.push(parseFunction());
705
- skipSemicolons();
706
- continue;
707
- }
708
- if (next.value === "=" || next.value === ";") {
709
- decls.push(parseTopLevelDecl(isConst, maybeType));
710
- continue;
711
- }
712
- }
713
- i = saved;
714
- if (at("ident") && (peek(1).value === "::" || peek(1).value === "(" || peek(1).value === "." || peek(1).value === "->")) {
715
- const expr = parseExpr();
716
- skipSemicolons();
717
- decls.push({ type: "expr", expr });
718
- continue;
719
- }
720
- decls.push(parseFunction());
721
- skipSemicolons();
722
- }
723
- return decls;
724
- }
725
-
726
- return { type: "program", body: parseProgramInside(), fileName };
727
- }
728
-
729
- export { parse };