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.
- package/CHANGELOG.md +14 -75
- package/README.md +42 -88
- package/docs/README.md +25 -21
- package/docs/architecture.md +13 -76
- package/docs/cli.md +12 -64
- package/docs/comparison.md +6 -24
- package/docs/config.md +6 -7
- package/docs/cpp-advanced.md +6 -3
- package/docs/cpp-types.md +2 -0
- package/docs/examples/combat.md +19 -227
- package/docs/examples/hud.md +13 -81
- package/docs/examples/index.md +9 -16
- package/docs/examples/leaderstats.md +44 -115
- package/docs/getting-started.md +20 -135
- package/docs/intellisense.md +5 -42
- package/docs/intro.md +16 -15
- package/docs/libraries/dataservice.md +4 -1
- package/docs/libraries/index.md +2 -1
- package/docs/libraries/more.md +3 -1
- package/docs/libraries/ui.md +76 -0
- package/docs/migration.md +38 -0
- package/docs/oop/file-tags.md +12 -36
- package/docs/oop/index.md +18 -11
- package/docs/roblox-api.md +4 -56
- package/docs/syntax.md +54 -179
- package/editors/vscode/extension.js +1 -1
- package/editors/vscode/package.json +4 -4
- package/examples/README.md +5 -7
- package/examples/game/.vscode/extensions.json +2 -2
- package/examples/game/.vscode/settings.json +6 -23
- package/examples/game/src/client/hud.client.clpp +54 -0
- package/examples/game/src/client/init.client.clpp +6 -0
- package/examples/game/src/server/combat.server.clpp +85 -0
- package/examples/game/src/server/leaderstats.server.clpp +28 -0
- package/examples/game/src/shared/PlayerData.clh +20 -0
- package/examples/game/src/shared/config.clp +6 -0
- package/examples/game/src/shared/features.clp +88 -0
- package/generated/api.js +15 -1
- package/generated/cli.js +40 -24
- package/generated/clpp/contract.js +5 -0
- package/generated/clpp/index.js +27 -0
- package/generated/clpp/paths.js +121 -0
- package/generated/clpp/postprocess.js +78 -0
- package/generated/clpp/runner.js +102 -0
- package/generated/compile.js +26 -138
- package/generated/emit.js +79 -3
- package/generated/intellisense.js +83 -207
- package/generated/lex.js +3 -0
- package/generated/libs.js +1 -1
- package/generated/lsp.js +1 -8
- package/generated/parse.js +119 -24
- package/generated/preprocess.js +8 -179
- package/generated/system-understander.js +1 -1
- package/generated/transpile.js +3 -50
- package/generated/utils/project.js +13 -28
- package/include/{cluaupp/datatypes.hpp → clpp/datatypes.clh} +5 -1
- package/include/{cluaupp/generated/instances.hpp → clpp/generated/instances.clh} +1 -1
- package/include/{cluaupp/libs/chrono.hpp → clpp/libs/chrono.clh} +9 -9
- package/include/{cluaupp/libs/cmdr.hpp → clpp/libs/cmdr.clh} +4 -4
- package/include/{cluaupp/libs/dataservice.hpp → clpp/libs/dataservice.clh} +8 -15
- package/include/{cluaupp/libs/display.hpp → clpp/libs/display.clh} +1 -2
- package/include/{cluaupp/libs/ezvisual.hpp → clpp/libs/ezvisual.clh} +1 -3
- package/include/{cluaupp/libs/formatnumber.hpp → clpp/libs/formatnumber.clh} +2 -3
- package/include/{cluaupp/libs/fusion.hpp → clpp/libs/fusion.clh} +5 -5
- package/include/{cluaupp/libs/iris.hpp → clpp/libs/iris.clh} +2 -2
- package/include/{cluaupp/libs/janitor.hpp → clpp/libs/janitor.clh} +1 -2
- package/include/{cluaupp/libs/module3d.hpp → clpp/libs/module3d.clh} +2 -4
- package/include/{cluaupp/libs/net.hpp → clpp/libs/net.clh} +2 -4
- package/include/{cluaupp/libs/promise.hpp → clpp/libs/promise.clh} +1 -4
- package/include/{cluaupp/libs/spring.hpp → clpp/libs/spring.clh} +1 -2
- package/include/{cluaupp/libs/statemachine.hpp → clpp/libs/statemachine.clh} +2 -3
- package/include/{cluaupp/libs/stickybillboard.hpp → clpp/libs/stickybillboard.clh} +1 -2
- package/include/{cluaupp/libs/topbarplus.hpp → clpp/libs/topbarplus.clh} +3 -4
- package/include/clpp/libs.clh +24 -0
- package/include/clpp/roblox.clh +31 -0
- package/package.json +20 -16
- package/src/api.ts +15 -1
- package/src/cli.ts +39 -25
- package/src/clpp/contract.ts +39 -0
- package/src/clpp/index.ts +18 -0
- package/src/clpp/paths.ts +117 -0
- package/src/clpp/postprocess.ts +93 -0
- package/src/clpp/runner.ts +102 -0
- package/src/compile.ts +26 -149
- package/src/intellisense.ts +82 -211
- package/src/libs.ts +1 -1
- package/src/lsp.ts +1 -5
- package/src/preprocess.ts +8 -180
- package/src/system-understander.ts +1 -1
- package/src/transpile.ts +9 -52
- package/src/utils/project.ts +12 -28
- package/templates/game/.vscode/extensions.json +2 -2
- package/templates/game/.vscode/settings.json +6 -23
- package/templates/game/src/client/init.client.clpp +6 -0
- package/templates/game/src/server/leaderstats.server.clpp +29 -0
- package/templates/game/src/shared/config.clp +6 -0
- package/examples/game/.clangd +0 -25
- package/examples/game/.vscode/c_cpp_properties.json +0 -27
- package/examples/game/compile_flags.txt +0 -6
- package/examples/game/src/client/hud.client.cpp +0 -12
- package/examples/game/src/client/init.client.cpp +0 -6
- package/examples/game/src/server/combat.server.cpp +0 -23
- package/examples/game/src/server/leaderstats.server.cpp +0 -29
- package/examples/game/src/shared/config.cpp +0 -5
- package/examples/game/src/shared/config.h +0 -5
- package/include/cluaupp/libs.hpp +0 -25
- package/include/cluaupp/roblox.hpp +0 -47
- package/src/architecture.ts +0 -500
- package/src/ast.ts +0 -37
- package/src/emit.ts +0 -531
- package/src/emitter/luau-codegen.ts +0 -187
- package/src/emitter/translators.ts +0 -168
- package/src/headers.ts +0 -233
- package/src/layout.ts +0 -114
- package/src/lex.ts +0 -155
- package/src/parse.ts +0 -630
- package/src/parser/collector.ts +0 -148
- package/src/parser/index.ts +0 -27
- package/src/tree-sitter-cpp.d.ts +0 -4
- package/src/understand.ts +0 -322
- package/templates/game/.vscode/c_cpp_properties.json +0 -27
- package/templates/game/src/client/init.client.cpp +0 -6
- package/templates/game/src/server/leaderstats.server.cpp +0 -29
- package/templates/game/src/shared/config.cpp +0 -5
- package/templates/game/src/shared/config.h +0 -5
- /package/include/{cluaupp/generated/enums.hpp → clpp/generated/enums.clh} +0 -0
- /package/include/{cluaupp/libs/math.hpp → clpp/libs/math.clh} +0 -0
- /package/include/{cluaupp/libs/twinkle.hpp → clpp/libs/twinkle.clh} +0 -0
- /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
|
-
}
|