cluaupp 0.2.2 → 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 (129) hide show
  1. package/CHANGELOG.md +14 -75
  2. package/README.md +42 -88
  3. package/docs/README.md +25 -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/cpp-advanced.md +6 -3
  9. package/docs/cpp-types.md +2 -0
  10. package/docs/examples/combat.md +19 -227
  11. package/docs/examples/hud.md +13 -81
  12. package/docs/examples/index.md +9 -16
  13. package/docs/examples/leaderstats.md +44 -115
  14. package/docs/getting-started.md +20 -135
  15. package/docs/intellisense.md +5 -42
  16. package/docs/intro.md +16 -15
  17. package/docs/libraries/dataservice.md +4 -1
  18. package/docs/libraries/index.md +2 -1
  19. package/docs/libraries/more.md +3 -1
  20. package/docs/libraries/ui.md +76 -0
  21. package/docs/migration.md +38 -0
  22. package/docs/oop/file-tags.md +12 -36
  23. package/docs/oop/index.md +18 -11
  24. package/docs/roblox-api.md +4 -56
  25. package/docs/syntax.md +54 -179
  26. package/editors/vscode/extension.js +1 -1
  27. package/editors/vscode/package.json +4 -4
  28. package/examples/README.md +5 -7
  29. package/examples/game/.vscode/extensions.json +2 -2
  30. package/examples/game/.vscode/settings.json +6 -23
  31. package/examples/game/src/client/hud.client.clpp +54 -0
  32. package/examples/game/src/client/init.client.clpp +6 -0
  33. package/examples/game/src/server/combat.server.clpp +85 -0
  34. package/examples/game/src/server/leaderstats.server.clpp +28 -0
  35. package/examples/game/src/shared/PlayerData.clh +20 -0
  36. package/examples/game/src/shared/config.clp +6 -0
  37. package/examples/game/src/shared/features.clp +88 -0
  38. package/generated/api.js +15 -1
  39. package/generated/cli.js +40 -24
  40. package/generated/clpp/contract.js +5 -0
  41. package/generated/clpp/index.js +27 -0
  42. package/generated/clpp/paths.js +121 -0
  43. package/generated/clpp/postprocess.js +78 -0
  44. package/generated/clpp/runner.js +102 -0
  45. package/generated/compile.js +26 -138
  46. package/generated/emit.js +79 -3
  47. package/generated/intellisense.js +83 -207
  48. package/generated/lex.js +3 -0
  49. package/generated/libs.js +1 -1
  50. package/generated/lsp.js +1 -8
  51. package/generated/parse.js +119 -24
  52. package/generated/preprocess.js +8 -179
  53. package/generated/system-understander.js +1 -1
  54. package/generated/transpile.js +3 -50
  55. package/generated/utils/project.js +13 -28
  56. package/include/{cluaupp/datatypes.hpp → clpp/datatypes.clh} +5 -1
  57. package/include/{cluaupp/generated/instances.hpp → clpp/generated/instances.clh} +1 -1
  58. package/include/{cluaupp/libs/chrono.hpp → clpp/libs/chrono.clh} +9 -9
  59. package/include/{cluaupp/libs/cmdr.hpp → clpp/libs/cmdr.clh} +4 -4
  60. package/include/{cluaupp/libs/dataservice.hpp → clpp/libs/dataservice.clh} +8 -15
  61. package/include/{cluaupp/libs/display.hpp → clpp/libs/display.clh} +1 -2
  62. package/include/{cluaupp/libs/ezvisual.hpp → clpp/libs/ezvisual.clh} +1 -3
  63. package/include/{cluaupp/libs/formatnumber.hpp → clpp/libs/formatnumber.clh} +2 -3
  64. package/include/{cluaupp/libs/fusion.hpp → clpp/libs/fusion.clh} +5 -5
  65. package/include/{cluaupp/libs/iris.hpp → clpp/libs/iris.clh} +2 -2
  66. package/include/{cluaupp/libs/janitor.hpp → clpp/libs/janitor.clh} +1 -2
  67. package/include/{cluaupp/libs/module3d.hpp → clpp/libs/module3d.clh} +2 -4
  68. package/include/{cluaupp/libs/net.hpp → clpp/libs/net.clh} +2 -4
  69. package/include/{cluaupp/libs/promise.hpp → clpp/libs/promise.clh} +1 -4
  70. package/include/{cluaupp/libs/spring.hpp → clpp/libs/spring.clh} +1 -2
  71. package/include/{cluaupp/libs/statemachine.hpp → clpp/libs/statemachine.clh} +2 -3
  72. package/include/{cluaupp/libs/stickybillboard.hpp → clpp/libs/stickybillboard.clh} +1 -2
  73. package/include/{cluaupp/libs/topbarplus.hpp → clpp/libs/topbarplus.clh} +3 -4
  74. package/include/clpp/libs.clh +24 -0
  75. package/include/clpp/roblox.clh +31 -0
  76. package/package.json +20 -16
  77. package/src/api.ts +15 -1
  78. package/src/cli.ts +39 -25
  79. package/src/clpp/contract.ts +39 -0
  80. package/src/clpp/index.ts +18 -0
  81. package/src/clpp/paths.ts +117 -0
  82. package/src/clpp/postprocess.ts +93 -0
  83. package/src/clpp/runner.ts +102 -0
  84. package/src/compile.ts +26 -149
  85. package/src/intellisense.ts +82 -211
  86. package/src/libs.ts +1 -1
  87. package/src/lsp.ts +1 -5
  88. package/src/preprocess.ts +8 -180
  89. package/src/system-understander.ts +1 -1
  90. package/src/transpile.ts +9 -52
  91. package/src/utils/project.ts +12 -28
  92. package/templates/game/.vscode/extensions.json +2 -2
  93. package/templates/game/.vscode/settings.json +6 -23
  94. package/templates/game/src/client/init.client.clpp +6 -0
  95. package/templates/game/src/server/leaderstats.server.clpp +29 -0
  96. package/templates/game/src/shared/config.clp +6 -0
  97. package/examples/game/.clangd +0 -25
  98. package/examples/game/.vscode/c_cpp_properties.json +0 -27
  99. package/examples/game/compile_flags.txt +0 -6
  100. package/examples/game/src/client/hud.client.cpp +0 -12
  101. package/examples/game/src/client/init.client.cpp +0 -6
  102. package/examples/game/src/server/combat.server.cpp +0 -23
  103. package/examples/game/src/server/leaderstats.server.cpp +0 -29
  104. package/examples/game/src/shared/config.cpp +0 -5
  105. package/examples/game/src/shared/config.h +0 -5
  106. package/include/cluaupp/libs.hpp +0 -25
  107. package/include/cluaupp/roblox.hpp +0 -47
  108. package/src/architecture.ts +0 -500
  109. package/src/ast.ts +0 -37
  110. package/src/emit.ts +0 -531
  111. package/src/emitter/luau-codegen.ts +0 -187
  112. package/src/emitter/translators.ts +0 -168
  113. package/src/headers.ts +0 -233
  114. package/src/layout.ts +0 -114
  115. package/src/lex.ts +0 -155
  116. package/src/parse.ts +0 -630
  117. package/src/parser/collector.ts +0 -148
  118. package/src/parser/index.ts +0 -27
  119. package/src/tree-sitter-cpp.d.ts +0 -4
  120. package/src/understand.ts +0 -322
  121. package/templates/game/.vscode/c_cpp_properties.json +0 -27
  122. package/templates/game/src/client/init.client.cpp +0 -6
  123. package/templates/game/src/server/leaderstats.server.cpp +0 -29
  124. package/templates/game/src/shared/config.cpp +0 -5
  125. package/templates/game/src/shared/config.h +0 -5
  126. /package/include/{cluaupp/generated/enums.hpp → clpp/generated/enums.clh} +0 -0
  127. /package/include/{cluaupp/libs/math.hpp → clpp/libs/math.clh} +0 -0
  128. /package/include/{cluaupp/libs/twinkle.hpp → clpp/libs/twinkle.clh} +0 -0
  129. /package/include/{cluaupp/libs/vfx.hpp → clpp/libs/vfx.clh} +0 -0
@@ -1,168 +0,0 @@
1
- import type Parser from "tree-sitter";
2
- import { compileSource } from "../compile.js";
3
-
4
- type SyntaxNode = Parser.SyntaxNode;
5
-
6
- const TYPE_MAP: Record<string, string> = {
7
- "std::string": "string",
8
- "std::string_view": "string",
9
- string: "string",
10
- string_view: "string",
11
- bool: "boolean",
12
- int: "number",
13
- int8_t: "number",
14
- int16_t: "number",
15
- int32_t: "number",
16
- int64_t: "number",
17
- uint8_t: "number",
18
- uint16_t: "number",
19
- uint32_t: "number",
20
- uint64_t: "number",
21
- float: "number",
22
- double: "number",
23
- size_t: "number",
24
- void: "()",
25
- auto: "any",
26
- };
27
-
28
- export function translateCppType(cppType: string): string {
29
- const trimmed = cppType
30
- .replace(/\b(?:const|constexpr|volatile|static|inline|restrict)\b/g, "")
31
- .replace(/\s+/g, " ")
32
- .replace(/&/g, "")
33
- .replace(/\*/g, "")
34
- .trim();
35
- if (!trimmed) {
36
- return "any";
37
- }
38
- if (TYPE_MAP[trimmed]) {
39
- return TYPE_MAP[trimmed];
40
- }
41
- const generic = trimmed.match(/^(?:std::)?(?:vector|array|span|LuaArray)\s*<\s*([^>]+)\s*>$/);
42
- if (generic) {
43
- return `{${translateCppType(generic[1])}}`;
44
- }
45
- const optional = trimmed.match(/^(?:std::)?optional\s*<\s*([^>]+)\s*>$/);
46
- if (optional) {
47
- return `${translateCppType(optional[1])}?`;
48
- }
49
- return trimmed.replace(/^::/, "");
50
- }
51
-
52
- export function translateGetService(node: SyntaxNode): string | null {
53
- const functionNode = node.childForFieldName("function") || node.namedChildren[0];
54
- if (!functionNode) {
55
- return null;
56
- }
57
- const text = functionNode.text.replace(/\s+/g, "");
58
- const generic = text.match(/GetService<(\w+)>/);
59
- if (generic) {
60
- return generic[1];
61
- }
62
- if (!text.includes("GetService")) {
63
- return null;
64
- }
65
- const args = functionNode.namedChildren.find((child) => child.type === "template_argument_list");
66
- if (args) {
67
- const ident = args.namedChildren.find((child) => child.type === "type_identifier" || child.type === "identifier");
68
- if (ident) {
69
- return ident.text;
70
- }
71
- }
72
- const callText = node.text.replace(/\s+/g, "");
73
- const match = callText.match(/GetService<(\w+)>/);
74
- return match ? match[1] : null;
75
- }
76
-
77
- export function translateConstant(node: SyntaxNode): string | null {
78
- const declarator = node.childForFieldName("declarator");
79
- if (!declarator) {
80
- return null;
81
- }
82
- const name = identifierOf(declarator);
83
- const valueNode =
84
- node.childForFieldName("value") ||
85
- declarator.childForFieldName("value") ||
86
- namedOf(declarator, "initializer") ||
87
- node.namedChildren[node.namedChildren.length - 1];
88
- if (!name || !valueNode || valueNode === declarator) {
89
- return null;
90
- }
91
- let value = valueNode.text.replace(/;$/, "").trim();
92
- if (value.startsWith("=")) {
93
- value = value.slice(1).trim();
94
- }
95
- if (!value) {
96
- return null;
97
- }
98
- return `const ${name} = ${value}`;
99
- }
100
-
101
- export function translateAlias(node: SyntaxNode, mapType: (cppType: string) => string): string | null {
102
- const nameNode = node.childForFieldName("name") || namedOf(node, "type_identifier") || node.namedChildren[1];
103
- const typeNode = node.childForFieldName("type") || namedOf(node, "type_descriptor") || node.namedChildren[0];
104
- if (!nameNode || !typeNode) {
105
- return null;
106
- }
107
- return `type ${nameNode.text} = ${mapType(typeNode.text)}`;
108
- }
109
-
110
- export function translateStruct(node: SyntaxNode, mapType: (cppType: string) => string): string | null {
111
- const nameNode = node.childForFieldName("name") || namedOf(node, "type_identifier");
112
- const body = node.childForFieldName("body") || namedOf(node, "field_declaration_list");
113
- if (!nameNode) {
114
- return null;
115
- }
116
- if (!body) {
117
- return `type ${nameNode.text} = {}`;
118
- }
119
- const fields: string[] = [];
120
- for (const field of body.namedChildren) {
121
- if (field.type !== "field_declaration") {
122
- continue;
123
- }
124
- const fieldType = field.childForFieldName("type");
125
- const fieldDecl = field.childForFieldName("declarator");
126
- const fieldName = fieldDecl ? identifierOf(fieldDecl) : namedOf(field, "field_identifier")?.text;
127
- if (!fieldName) {
128
- continue;
129
- }
130
- fields.push(`\t${fieldName}: ${mapType(fieldType?.text || "any")},`);
131
- }
132
- if (fields.length === 0) {
133
- return `type ${nameNode.text} = {}`;
134
- }
135
- return `type ${nameNode.text} = {\n${fields.join("\n")}\n}`;
136
- }
137
-
138
- export function translateFunction(node: SyntaxNode, fileName: string): string | null {
139
- try {
140
- const luau = compileSource(node.text, fileName, { skipHeader: true });
141
- const trimmed = String(luau || "").trim();
142
- return trimmed || null;
143
- } catch {
144
- const name = identifierOf(node.childForFieldName("declarator") || node) || "fn";
145
- return `-- function ${name} (não traduzida)`;
146
- }
147
- }
148
-
149
- function identifierOf(node: SyntaxNode): string | null {
150
- if (node.type === "identifier" || node.type === "field_identifier" || node.type === "type_identifier") {
151
- return node.text;
152
- }
153
- const direct = node.childForFieldName("declarator") || namedOf(node, "identifier") || namedOf(node, "field_identifier");
154
- if (direct) {
155
- return identifierOf(direct);
156
- }
157
- for (const child of node.namedChildren) {
158
- const found = identifierOf(child);
159
- if (found) {
160
- return found;
161
- }
162
- }
163
- return null;
164
- }
165
-
166
- function namedOf(node: SyntaxNode, type: string): SyntaxNode | null {
167
- return node.namedChildren.find((child) => child.type === type) || null;
168
- }
package/src/headers.ts DELETED
@@ -1,233 +0,0 @@
1
- // @ts-nocheck
2
- import path from "node:path";
3
- import { luauType } from "./api.js";
4
-
5
- function fileStem(fileName) {
6
- return path.basename(String(fileName || "module")).replace(/\.(h|hpp|hh|cpp|cc|cxx|c)$/i, "");
7
- }
8
-
9
- function emitValue(node) {
10
- if (!node) {
11
- return "nil";
12
- }
13
- if (node.type === "number") {
14
- return node.value;
15
- }
16
- if (node.type === "string") {
17
- return `"${node.value}"`;
18
- }
19
- if (node.type === "bool") {
20
- return node.value ? "true" : "false";
21
- }
22
- if (node.type === "null") {
23
- return "nil";
24
- }
25
- if (node.type === "initlist") {
26
- const inner = (node.fields || []).map((field) => `${field.name} = ${emitValue(field.value)}`).join(", ");
27
- return `{ ${inner} }`;
28
- }
29
- return "nil";
30
- }
31
-
32
- function collectHeaderShape(ast, fileName) {
33
- const owners = new Map();
34
- const ensure = (name) => {
35
- if (!owners.has(name)) {
36
- owners.set(name, { fields: [], methods: [] });
37
- }
38
- return owners.get(name);
39
- };
40
- const freeProtos = [];
41
- const consts = [];
42
- for (const decl of ast.body || []) {
43
- if (decl.type === "decl" && decl.owner) {
44
- ensure(decl.owner).fields.push(decl);
45
- continue;
46
- }
47
- if (decl.type === "decl") {
48
- consts.push(decl);
49
- continue;
50
- }
51
- if ((decl.type === "proto" || decl.type === "function") && decl.owner) {
52
- ensure(decl.owner).methods.push(decl);
53
- continue;
54
- }
55
- if (decl.type === "proto") {
56
- freeProtos.push(decl);
57
- }
58
- }
59
- const nested = new Set();
60
- for (const [ownerName, spec] of owners) {
61
- for (const field of spec.fields) {
62
- if (field.valueType && owners.has(field.valueType) && field.valueType !== ownerName) {
63
- nested.add(field.valueType);
64
- }
65
- }
66
- }
67
- const roots = [...owners.keys()].filter((name) => !nested.has(name));
68
- const withMethods = roots.filter((name) => ensure(name).methods.length > 0);
69
- const stem = fileStem(fileName);
70
- let primary = withMethods[0] || (freeProtos.length ? stem : roots[0] || stem);
71
- return { owners, nested, roots, freeProtos, consts, primary, stem };
72
- }
73
-
74
- function primaryHeaderName(ast, fileName) {
75
- return collectHeaderShape(ast, fileName).primary;
76
- }
77
-
78
- function methodType(fn, owner) {
79
- const params = [];
80
- if (owner) {
81
- params.push(`self: ${owner}`);
82
- }
83
- for (const param of fn.params || []) {
84
- if (typeof param === "string") {
85
- params.push(param);
86
- continue;
87
- }
88
- const typeAnn = luauType(param.valueType);
89
- params.push(typeAnn ? `${param.name}: ${typeAnn}` : param.name);
90
- }
91
- const ret = luauType(fn.returnType);
92
- const retAnn = !ret || ret === "()" ? "()" : ret;
93
- return `(${params.join(", ")}) -> ${retAnn}`;
94
- }
95
-
96
- function fieldType(field, owners) {
97
- if (field.valueType && owners.has(field.valueType)) {
98
- return field.valueType;
99
- }
100
- return luauType(field.valueType) || "any";
101
- }
102
-
103
- function emitExportType(lines, name, spec, owners, extraMethods, extraFields) {
104
- lines.push(`export type ${name} = {`);
105
- const seen = new Set();
106
- for (const field of [...((spec && spec.fields) || []), ...(extraFields || [])]) {
107
- if (!field.name || seen.has(field.name)) {
108
- continue;
109
- }
110
- seen.add(field.name);
111
- lines.push(` ${field.name}: ${fieldType(field, owners)},`);
112
- }
113
- for (const method of (spec && spec.methods) || []) {
114
- if (!method.name || seen.has(method.name)) {
115
- continue;
116
- }
117
- seen.add(method.name);
118
- lines.push(` ${method.name}: ${methodType(method, name)},`);
119
- }
120
- for (const method of extraMethods || []) {
121
- if (!method.name || seen.has(method.name)) {
122
- continue;
123
- }
124
- seen.add(method.name);
125
- lines.push(` ${method.name}: ${methodType(method, null)},`);
126
- }
127
- lines.push("}");
128
- lines.push("");
129
- }
130
-
131
- function emitDataConstructor(lines, name, spec, owners) {
132
- const emitField = (field) => {
133
- if (field.valueType && owners.has(field.valueType)) {
134
- const nested = owners.get(field.valueType);
135
- const inner = nested.fields.map((item) => `${item.name} = ${emitValue(item.value)}`).join(", ");
136
- return `{ ${inner} }`;
137
- }
138
- if (field.value && field.value.type === "initlist") {
139
- return emitValue(field.value);
140
- }
141
- return emitValue(field.value);
142
- };
143
- lines.push(`const function ${name}(): ${name}`);
144
- lines.push(" return {");
145
- for (const field of spec.fields) {
146
- lines.push(` ${field.name} = ${emitField(field)},`);
147
- }
148
- lines.push(" }");
149
- lines.push("end");
150
- lines.push("");
151
- }
152
-
153
- function emitHeaderModule(ast, options = {}) {
154
- const strict = options.strict === true;
155
- const fileName = options.relativeName || options.filePath || "header.h";
156
- const shape = collectHeaderShape(ast, fileName);
157
- const lines = [];
158
- if (strict) {
159
- lines.push("--!strict");
160
- }
161
- lines.push("-- Compiled by Cluaupp — C++ × Luau");
162
- lines.push("");
163
-
164
- const typeOrder = [...shape.nested, ...shape.roots];
165
- for (const name of typeOrder) {
166
- const extra = name === shape.primary ? shape.freeProtos : [];
167
- emitExportType(lines, name, shape.owners.get(name), shape.owners, extra);
168
- }
169
- if (shape.freeProtos.length && !shape.owners.has(shape.primary)) {
170
- emitExportType(lines, shape.primary, { fields: [], methods: [] }, shape.owners, shape.freeProtos, shape.consts);
171
- }
172
-
173
- for (const decl of shape.consts) {
174
- const typeAnn = luauType(decl.valueType);
175
- const typed = typeAnn ? `: ${typeAnn}` : "";
176
- lines.push(`const ${decl.name}${typed} = ${emitValue(decl.value)}`);
177
- }
178
- if (shape.consts.length) {
179
- lines.push("");
180
- }
181
-
182
- const hasMethods = shape.roots.some((name) => shape.owners.get(name).methods.length > 0) || shape.freeProtos.length > 0;
183
- const implName = hasMethods ? options.headerImplName : null;
184
-
185
- if (!hasMethods && shape.roots.length > 0) {
186
- for (const name of shape.roots) {
187
- emitDataConstructor(lines, name, shape.owners.get(name), shape.owners);
188
- }
189
- lines.push(`return ${shape.primary}`);
190
- lines.push("");
191
- return lines.join("\n");
192
- }
193
-
194
- if (!hasMethods && shape.consts.length > 0) {
195
- lines.push("return {");
196
- for (const decl of shape.consts) {
197
- lines.push(` ${decl.name} = ${decl.name},`);
198
- }
199
- lines.push("}");
200
- lines.push("");
201
- return lines.join("\n");
202
- }
203
-
204
- const tableName = shape.primary;
205
- const spec = shape.owners.get(tableName) || { fields: [], methods: [] };
206
- const methods = [...spec.methods, ...shape.freeProtos];
207
- lines.push(`const ${tableName}: ${tableName} = {`);
208
- if (implName) {
209
- for (const field of spec.fields) {
210
- if (field.valueType && shape.owners.has(field.valueType) && field.valueType !== tableName) {
211
- continue;
212
- }
213
- lines.push(` ${field.name} = ${implName}.${field.name},`);
214
- }
215
- for (const method of methods) {
216
- lines.push(` ${method.name} = ${implName}.${method.name},`);
217
- }
218
- }
219
- for (const decl of shape.consts) {
220
- lines.push(` ${decl.name} = ${decl.name},`);
221
- }
222
- lines.push("}");
223
- lines.push("");
224
- lines.push(`return ${tableName}`);
225
- lines.push("");
226
- return lines.join("\n");
227
- }
228
-
229
- export {
230
- emitHeaderModule,
231
- primaryHeaderName,
232
- collectHeaderShape,
233
- };
package/src/layout.ts DELETED
@@ -1,114 +0,0 @@
1
- import type { AstNode } from "./ast.js";
2
-
3
- export const SECTION = {
4
- api: "API",
5
- constants: "CONSTANTS",
6
- variables: "VARIABLES",
7
- types: "TYPES",
8
- support: "HELPERS FUNCTIONS",
9
- principal: "MAIN FUNCTIONS",
10
- cleanup: "JANITOR",
11
- returns: "RETURN",
12
- };
13
-
14
- export const CLEANUP_FN = /^(OnClose|OnPlayerRemoving|Cleanup|Shutdown|OnDestroy)$/i;
15
- export const PRINCIPAL_FN = /^(init|start|main|setup)/i;
16
- export const ENTRY_FN = /^(init|main)$/i;
17
-
18
- function isGetService(node: AstNode | null | undefined): boolean {
19
- return Boolean(node && node.type === "getService");
20
- }
21
-
22
- function isDataServiceAlias(node: AstNode | null | undefined): boolean {
23
- let current: AstNode | undefined = node || undefined;
24
- while (current && current.type === "member") {
25
- if (current.object && current.object.type === "ident" && current.object.name === "DataService") {
26
- return true;
27
- }
28
- current = current.object;
29
- }
30
- return false;
31
- }
32
-
33
- export function isJanitorDecl(decl: AstNode | null | undefined): boolean {
34
- if (!decl || decl.type !== "decl") {
35
- return false;
36
- }
37
- const name = String(decl.name || "").toLowerCase();
38
- const typeName = String(decl.valueType || "").toLowerCase();
39
- const created = decl.value && decl.value.type === "new" && decl.value.className === "Janitor";
40
- return name.includes("janitor") || typeName.includes("janitor") || created;
41
- }
42
-
43
- export function isApiDecl(decl: AstNode | null | undefined): boolean {
44
- if (!decl || decl.type !== "decl") {
45
- return false;
46
- }
47
- return isGetService(decl.value) || isDataServiceAlias(decl.value);
48
- }
49
-
50
- export function classifyDecl(decl: AstNode | null | undefined): string | null {
51
- if (!decl || decl.type === "proto") {
52
- return null;
53
- }
54
- if (decl.type === "decl") {
55
- if (isApiDecl(decl)) {
56
- return "api";
57
- }
58
- if (isJanitorDecl(decl)) {
59
- return "variables";
60
- }
61
- if (decl.isConst) {
62
- return "constants";
63
- }
64
- return "variables";
65
- }
66
- if (decl.type === "function") {
67
- if (CLEANUP_FN.test(decl.name || "")) {
68
- return "cleanup";
69
- }
70
- if (PRINCIPAL_FN.test(decl.name || "")) {
71
- return "principal";
72
- }
73
- return "support";
74
- }
75
- return null;
76
- }
77
-
78
- export function organizeDecls(keep: AstNode[]): Record<string, AstNode[]> {
79
- const groups: Record<string, AstNode[]> = {
80
- api: [],
81
- constants: [],
82
- variables: [],
83
- support: [],
84
- principal: [],
85
- cleanup: [],
86
- };
87
- for (const decl of keep) {
88
- const bucket = classifyDecl(decl);
89
- if (bucket && groups[bucket]) {
90
- groups[bucket].push(decl);
91
- }
92
- }
93
- return groups;
94
- }
95
-
96
- export function janitorNames(keep: AstNode[] | null | undefined): string[] {
97
- return (keep || []).filter(isJanitorDecl).map((decl) => decl.name);
98
- }
99
-
100
- export function emitSection(_title: string, body: string): string {
101
- const text = String(body || "").trimEnd();
102
- if (!text) {
103
- return "";
104
- }
105
- return `${text}\n\n`;
106
- }
107
-
108
- export function joinBlocks(parts: Array<string | null | undefined>): string {
109
- return parts.filter((part) => Boolean(part && String(part).trim())).join("\n\n");
110
- }
111
-
112
- export function commentLine(title: string): string {
113
- return `-- ${title}`;
114
- }
package/src/lex.ts DELETED
@@ -1,155 +0,0 @@
1
- import type { Token } from "./ast.js";
2
-
3
- const KEYWORDS = new Set([
4
- "if",
5
- "else",
6
- "for",
7
- "while",
8
- "return",
9
- "new",
10
- "auto",
11
- "const",
12
- "void",
13
- "int",
14
- "bool",
15
- "float",
16
- "double",
17
- "true",
18
- "false",
19
- "nullptr",
20
- "class",
21
- "struct",
22
- "public",
23
- "private",
24
- "protected",
25
- "namespace",
26
- "using",
27
- "template",
28
- "typedef",
29
- "extern",
30
- "enum",
31
- "switch",
32
- "case",
33
- "default",
34
- "break",
35
- ]);
36
-
37
- export function tokenize(source: string): Token[] {
38
- const tokens: Token[] = [];
39
- let i = 0;
40
- let line = 1;
41
- let col = 1;
42
-
43
- const push = (type: string, value: string, startLine: number, startCol: number, start: number, end: number) => {
44
- tokens.push({ type, value, line: startLine, col: startCol, start, end });
45
- };
46
-
47
- while (i < source.length) {
48
- const c = source[i];
49
-
50
- if (c === "\n") {
51
- i += 1;
52
- line += 1;
53
- col = 1;
54
- continue;
55
- }
56
- if (c === " " || c === "\t" || c === "\r") {
57
- i += 1;
58
- col += 1;
59
- continue;
60
- }
61
- if (c === "#") {
62
- while (i < source.length && source[i] !== "\n") {
63
- i += 1;
64
- }
65
- continue;
66
- }
67
- if (c === "/" && source[i + 1] === "/") {
68
- while (i < source.length && source[i] !== "\n") {
69
- i += 1;
70
- }
71
- continue;
72
- }
73
- if (c === "/" && source[i + 1] === "*") {
74
- i += 2;
75
- col += 2;
76
- while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
77
- if (source[i] === "\n") {
78
- line += 1;
79
- col = 1;
80
- } else {
81
- col += 1;
82
- }
83
- i += 1;
84
- }
85
- i += 2;
86
- col += 2;
87
- continue;
88
- }
89
-
90
- const startLine = line;
91
- const startCol = col;
92
-
93
- if (c === '"') {
94
- const start = i;
95
- i += 1;
96
- col += 1;
97
- let value = "";
98
- while (i < source.length && source[i] !== '"') {
99
- if (source[i] === "\\" && i + 1 < source.length) {
100
- value += source[i] + source[i + 1];
101
- i += 2;
102
- col += 2;
103
- continue;
104
- }
105
- value += source[i];
106
- i += 1;
107
- col += 1;
108
- }
109
- i += 1;
110
- col += 1;
111
- push("string", value, startLine, startCol, start, i);
112
- continue;
113
- }
114
-
115
- if (/[0-9]/.test(c)) {
116
- const start = i;
117
- let value = "";
118
- while (i < source.length && /[0-9.]/.test(source[i])) {
119
- value += source[i];
120
- i += 1;
121
- col += 1;
122
- }
123
- push("number", value, startLine, startCol, start, i);
124
- continue;
125
- }
126
-
127
- if (/[A-Za-z_]/.test(c)) {
128
- const start = i;
129
- let value = "";
130
- while (i < source.length && /[A-Za-z0-9_]/.test(source[i])) {
131
- value += source[i];
132
- i += 1;
133
- col += 1;
134
- }
135
- push(KEYWORDS.has(value) ? "kw" : "ident", value, startLine, startCol, start, i);
136
- continue;
137
- }
138
-
139
- const two = source.slice(i, i + 2);
140
- const start = i;
141
- if (["->", "==", "!=", "<=", ">=", "&&", "||", "::", "<<", ">>"].includes(two)) {
142
- push("op", two, startLine, startCol, start, i + 2);
143
- i += 2;
144
- col += 2;
145
- continue;
146
- }
147
-
148
- push("op", c, startLine, startCol, start, i + 1);
149
- i += 1;
150
- col += 1;
151
- }
152
-
153
- push("eof", "", line, col, source.length, source.length);
154
- return tokens;
155
- }