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
package/src/parser/collector.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import type Parser from "tree-sitter";
|
|
3
|
-
import type { CollectorContext, TranspilerState } from "../types.js";
|
|
4
|
-
import type { RojoMapper } from "../utils/rojo-mapper.js";
|
|
5
|
-
import { siblingHeader } from "../preprocess.js";
|
|
6
|
-
import {
|
|
7
|
-
translateAlias,
|
|
8
|
-
translateConstant,
|
|
9
|
-
translateCppType,
|
|
10
|
-
translateGetService,
|
|
11
|
-
translateStruct,
|
|
12
|
-
} from "../emitter/translators.js";
|
|
13
|
-
|
|
14
|
-
type SyntaxNode = Parser.SyntaxNode;
|
|
15
|
-
|
|
16
|
-
function emptyState(strict = false): TranspilerState {
|
|
17
|
-
return {
|
|
18
|
-
robloxServices: new Set<string>(),
|
|
19
|
-
moduleRequires: new Map<string, string>(),
|
|
20
|
-
customTypes: [],
|
|
21
|
-
constants: [],
|
|
22
|
-
functions: [],
|
|
23
|
-
headerLines: [],
|
|
24
|
-
strict,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class ASTCollector {
|
|
29
|
-
private state: TranspilerState;
|
|
30
|
-
|
|
31
|
-
constructor(
|
|
32
|
-
private mapper: RojoMapper,
|
|
33
|
-
private context: CollectorContext = { fileName: "input.cpp" },
|
|
34
|
-
) {
|
|
35
|
-
this.state = emptyState(context.strict === true);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public collect(rootNode: SyntaxNode): TranspilerState {
|
|
39
|
-
this.visit(rootNode, false);
|
|
40
|
-
return this.state;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private visit(node: SyntaxNode, inFunction: boolean): void {
|
|
44
|
-
if (node.type === "preproc_include") {
|
|
45
|
-
this.collectInclude(node);
|
|
46
|
-
} else if (node.type === "call_expression") {
|
|
47
|
-
this.collectGetService(node);
|
|
48
|
-
} else if (!inFunction && node.type === "function_definition") {
|
|
49
|
-
this.visitChildren(node, true);
|
|
50
|
-
return;
|
|
51
|
-
} else if (!inFunction && node.type === "declaration") {
|
|
52
|
-
this.collectDeclaration(node);
|
|
53
|
-
} else if (!inFunction && (node.type === "type_definition" || node.type === "alias_declaration")) {
|
|
54
|
-
this.collectAlias(node);
|
|
55
|
-
} else if (!inFunction && (node.type === "struct_specifier" || node.type === "class_specifier")) {
|
|
56
|
-
this.collectStruct(node);
|
|
57
|
-
} else if (!inFunction && node.type === "preproc_call" && /pragma\s+strict/.test(node.text)) {
|
|
58
|
-
this.state.strict = true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.visitChildren(node, inFunction);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private visitChildren(node: SyntaxNode, inFunction: boolean): void {
|
|
65
|
-
for (let i = 0; i < node.childCount; i += 1) {
|
|
66
|
-
const child = node.child(i);
|
|
67
|
-
if (child) {
|
|
68
|
-
this.visit(child, inFunction);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private includeTarget(raw: string): string | null {
|
|
74
|
-
const from = this.context.sourcePath;
|
|
75
|
-
if (!from) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
const cleaned = raw.replace(/[<>'"]/g, "").trim();
|
|
79
|
-
if (!cleaned || raw.includes("<")) {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
return path.resolve(path.dirname(from), cleaned);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private isOwnHeader(raw: string): boolean {
|
|
86
|
-
const from = this.context.sourcePath;
|
|
87
|
-
if (!from) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
const target = this.includeTarget(raw);
|
|
91
|
-
if (!target) {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
if (path.resolve(from) === target) {
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
const header = siblingHeader(from);
|
|
98
|
-
return Boolean(header && path.resolve(header) === target);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private collectInclude(node: SyntaxNode): void {
|
|
102
|
-
const pathNode = node.childForFieldName("path") || node.namedChildren[0] || node.child(1);
|
|
103
|
-
if (!pathNode) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (this.isOwnHeader(pathNode.text)) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const resolved = this.mapper.resolveIncludeToRequire(pathNode.text, this.context.sourcePath);
|
|
110
|
-
if (resolved) {
|
|
111
|
-
this.state.moduleRequires.set(resolved.alias, resolved.path);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private collectGetService(node: SyntaxNode): void {
|
|
116
|
-
const service = translateGetService(node);
|
|
117
|
-
if (service) {
|
|
118
|
-
this.state.robloxServices.add(service);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private collectDeclaration(node: SyntaxNode): void {
|
|
123
|
-
const text = node.text.trimStart();
|
|
124
|
-
if (!(text.startsWith("const") || text.startsWith("constexpr"))) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const translated = translateConstant(node);
|
|
128
|
-
if (translated) {
|
|
129
|
-
this.state.constants.push(translated);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private collectAlias(node: SyntaxNode): void {
|
|
134
|
-
const translated = translateAlias(node, translateCppType);
|
|
135
|
-
if (translated) {
|
|
136
|
-
this.state.customTypes.push(translated);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private collectStruct(node: SyntaxNode): void {
|
|
141
|
-
const translated = translateStruct(node, translateCppType);
|
|
142
|
-
if (translated) {
|
|
143
|
-
this.state.customTypes.push(translated);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export { emptyState };
|
package/src/parser/index.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import Parser from "tree-sitter";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { projectRoot } from "../package-info.js";
|
|
5
|
-
|
|
6
|
-
const nodeRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
7
|
-
|
|
8
|
-
export type CppParser = Parser;
|
|
9
|
-
|
|
10
|
-
export function createCppParserSync(): CppParser | null {
|
|
11
|
-
try {
|
|
12
|
-
const Cpp = nodeRequire("tree-sitter-cpp");
|
|
13
|
-
const parser = new Parser();
|
|
14
|
-
parser.setLanguage(Cpp);
|
|
15
|
-
parser.parse("int main() { return 0; }");
|
|
16
|
-
return parser;
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function parseCpp(source: string, parser = createCppParserSync()): Parser.Tree | null {
|
|
23
|
-
if (!parser) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
return parser.parse(source);
|
|
27
|
-
}
|
package/src/tree-sitter-cpp.d.ts
DELETED
package/src/understand.ts
DELETED
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { luauType } from "./api.js";
|
|
4
|
-
import {
|
|
5
|
-
INTENTS,
|
|
6
|
-
walk,
|
|
7
|
-
namesIn,
|
|
8
|
-
parseFileTag,
|
|
9
|
-
scoreIntents,
|
|
10
|
-
determineArchitecture,
|
|
11
|
-
} from "./system-understander.js";
|
|
12
|
-
|
|
13
|
-
const VALUE_CLASSES = {
|
|
14
|
-
IntValue: { luau: "number", instance: "IntValue", fallback: "0" },
|
|
15
|
-
NumberValue: { luau: "number", instance: "NumberValue", fallback: "0" },
|
|
16
|
-
StringValue: { luau: "string", instance: "StringValue", fallback: '""' },
|
|
17
|
-
BoolValue: { luau: "boolean", instance: "BoolValue", fallback: "false" },
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const EXT = "(cpp|cc|cxx|c|h|hpp|hh)";
|
|
21
|
-
|
|
22
|
-
function capitalize(word) {
|
|
23
|
-
if (!word) {
|
|
24
|
-
return word;
|
|
25
|
-
}
|
|
26
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function toPascalServiceName(fileName) {
|
|
30
|
-
let base = path.basename(fileName).replace(new RegExp(`\\.${EXT}$`, "i"), "");
|
|
31
|
-
base = base.replace(/\.legacy\.(server|client)$/i, "");
|
|
32
|
-
base = base.replace(/\.(server|client)$/i, "");
|
|
33
|
-
if (/^init$/i.test(base)) {
|
|
34
|
-
const folder = path.basename(path.dirname(fileName) || "");
|
|
35
|
-
if (folder && folder !== "." && folder !== "src") {
|
|
36
|
-
return capitalize(folder);
|
|
37
|
-
}
|
|
38
|
-
return "Init";
|
|
39
|
-
}
|
|
40
|
-
if (/stats$/i.test(base) && !/Stats$/.test(base)) {
|
|
41
|
-
return capitalize(base.slice(0, -5)) + "Stats";
|
|
42
|
-
}
|
|
43
|
-
if (/[-_]/.test(base)) {
|
|
44
|
-
return base
|
|
45
|
-
.split(/[-_]+/)
|
|
46
|
-
.filter(Boolean)
|
|
47
|
-
.map(capitalize)
|
|
48
|
-
.join("");
|
|
49
|
-
}
|
|
50
|
-
return capitalize(base);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function literal(node) {
|
|
54
|
-
if (!node) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
if (node.type === "number") {
|
|
58
|
-
return node.value;
|
|
59
|
-
}
|
|
60
|
-
if (node.type === "string") {
|
|
61
|
-
return `"${node.value}"`;
|
|
62
|
-
}
|
|
63
|
-
if (node.type === "bool") {
|
|
64
|
-
return node.value ? "true" : "false";
|
|
65
|
-
}
|
|
66
|
-
if (node.type === "null") {
|
|
67
|
-
return "nil";
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function collectStats(ast) {
|
|
73
|
-
const stats = [];
|
|
74
|
-
let folderName = null;
|
|
75
|
-
const seen = new Set();
|
|
76
|
-
|
|
77
|
-
for (const decl of ast.body || []) {
|
|
78
|
-
if (decl.type !== "function" || !decl.body) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const locals = new Map();
|
|
82
|
-
for (const stmt of decl.body) {
|
|
83
|
-
if (stmt.type === "decl" && stmt.value && stmt.value.type === "new") {
|
|
84
|
-
locals.set(stmt.name, {
|
|
85
|
-
className: stmt.value.className,
|
|
86
|
-
statName: null,
|
|
87
|
-
defaultValue: null,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (stmt.type !== "expr" || !stmt.expr || stmt.expr.type !== "assign") {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
const left = stmt.expr.left;
|
|
94
|
-
if (!left || left.type !== "member" || !left.object || left.object.type !== "ident") {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const record = locals.get(left.object.name);
|
|
98
|
-
if (!record) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
if (left.name === "Name" && stmt.expr.right && stmt.expr.right.type === "string") {
|
|
102
|
-
record.statName = stmt.expr.right.value;
|
|
103
|
-
if (record.className === "Folder") {
|
|
104
|
-
folderName = record.statName;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (left.name === "Value") {
|
|
108
|
-
record.defaultValue = literal(stmt.expr.right);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
for (const record of locals.values()) {
|
|
112
|
-
const spec = VALUE_CLASSES[record.className];
|
|
113
|
-
if (!spec || !record.statName || seen.has(record.statName)) {
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
seen.add(record.statName);
|
|
117
|
-
stats.push({
|
|
118
|
-
name: record.statName,
|
|
119
|
-
instance: spec.instance,
|
|
120
|
-
luau: spec.luau,
|
|
121
|
-
defaultValue: record.defaultValue != null ? record.defaultValue : spec.fallback,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return { stats, folderName: folderName || (stats.length > 0 ? "leaderstats" : null) };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function collectConsts(ast) {
|
|
130
|
-
const consts = [];
|
|
131
|
-
for (const decl of ast.body || []) {
|
|
132
|
-
if (decl.type === "decl" && decl.isConst) {
|
|
133
|
-
consts.push({
|
|
134
|
-
name: decl.name,
|
|
135
|
-
luau: luauType(decl.valueType) || "number",
|
|
136
|
-
value: literal(decl.value) || "nil",
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return consts;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function onlyConsts(ast) {
|
|
144
|
-
const body = ast.body || [];
|
|
145
|
-
if (body.length === 0) {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
return body.every((node) => node.type === "decl" || node.type === "proto");
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function looksLikeCacheSetup(fn) {
|
|
152
|
-
if (!fn || !fn.body) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
const named = namesIn(fn);
|
|
156
|
-
return [...Object.keys(VALUE_CLASSES), "Folder"].some((name) => named.has(name));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function isTrivial(ast) {
|
|
160
|
-
const fns = (ast.body || []).filter((node) => node.type === "function");
|
|
161
|
-
if (fns.length === 0) {
|
|
162
|
-
return (ast.body || []).every((node) => node.type === "proto" || node.type === "decl");
|
|
163
|
-
}
|
|
164
|
-
if (fns.some((fn) => fn.name !== "init")) {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
const init = fns.find((fn) => fn.name === "init");
|
|
168
|
-
const named = namesIn(init || { body: [] }, { strings: false });
|
|
169
|
-
const interesting = [...named].filter((name) => !["print", "warn", "error"].includes(name) && !/^[0-9.]+$/.test(name));
|
|
170
|
-
return interesting.length === 0;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function classifyFunctions(ast, intents) {
|
|
174
|
-
const primary = intents.find((item) => item.name !== "players" && item.name !== "cache");
|
|
175
|
-
const classified = [];
|
|
176
|
-
for (const decl of ast.body || []) {
|
|
177
|
-
if (decl.type !== "function" || decl.name === "init") {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
const named = namesIn(decl);
|
|
181
|
-
const localIntents = scoreIntents(named);
|
|
182
|
-
let absorb = "none";
|
|
183
|
-
if (looksLikeCacheSetup(decl)) {
|
|
184
|
-
absorb = "cache";
|
|
185
|
-
}
|
|
186
|
-
const top = localIntents[0];
|
|
187
|
-
classified.push({
|
|
188
|
-
name: decl.name,
|
|
189
|
-
decl,
|
|
190
|
-
intent: top ? top.name : primary ? primary.name : "domain",
|
|
191
|
-
absorb,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
keepReferencedFunctions(ast, classified);
|
|
195
|
-
return classified;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function keepReferencedFunctions(ast, classified) {
|
|
199
|
-
const kept = new Set(["init"]);
|
|
200
|
-
for (const item of classified) {
|
|
201
|
-
if (item.absorb !== "cache") {
|
|
202
|
-
kept.add(item.name);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let changed = true;
|
|
207
|
-
while (changed) {
|
|
208
|
-
changed = false;
|
|
209
|
-
for (const decl of ast.body || []) {
|
|
210
|
-
if (decl.type !== "function" || !kept.has(decl.name)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
const named = namesIn(decl);
|
|
214
|
-
for (const item of classified) {
|
|
215
|
-
if (item.absorb === "cache" && named.has(item.name)) {
|
|
216
|
-
item.absorb = "none";
|
|
217
|
-
kept.add(item.name);
|
|
218
|
-
changed = true;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function analyze(ast, fileName) {
|
|
226
|
-
const report = determineArchitecture(fileName, ast);
|
|
227
|
-
const tag = report.fileTag;
|
|
228
|
-
const named = namesIn(ast);
|
|
229
|
-
const { stats, folderName } = collectStats(ast);
|
|
230
|
-
const consts = collectConsts(ast);
|
|
231
|
-
const intents = scoreIntents(named, tag.runtime);
|
|
232
|
-
const functions = classifyFunctions(ast, intents);
|
|
233
|
-
const trivial = isTrivial(ast);
|
|
234
|
-
const constsOnly = onlyConsts(ast);
|
|
235
|
-
const hasPlayers = intents.some((item) => item.name === "players");
|
|
236
|
-
const hasCache = intents.some((item) => item.name === "cache") || stats.length > 0;
|
|
237
|
-
const primaryDomain = intents.find((item) => item.name !== "players" && item.name !== "cache") || null;
|
|
238
|
-
const serviceName = toPascalServiceName(fileName);
|
|
239
|
-
const configLike =
|
|
240
|
-
tag.emit === "module" &&
|
|
241
|
-
(constsOnly || (consts.length > 0 && !hasPlayers && stats.length === 0 && !primaryDomain));
|
|
242
|
-
|
|
243
|
-
let kind = "flat";
|
|
244
|
-
if (tag.emit === "legacy") {
|
|
245
|
-
kind = "legacy";
|
|
246
|
-
} else if (tag.emit === "module") {
|
|
247
|
-
kind = configLike ? "config" : "module";
|
|
248
|
-
} else if (tag.emit === "service") {
|
|
249
|
-
kind = trivial ? "flat" : "service";
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const roles = {
|
|
253
|
-
players: kind === "service" && hasPlayers,
|
|
254
|
-
cache: kind === "service" && (hasCache || stats.length > 0),
|
|
255
|
-
domain: null,
|
|
256
|
-
};
|
|
257
|
-
if (kind === "service" && primaryDomain) {
|
|
258
|
-
roles.domain = primaryDomain.module;
|
|
259
|
-
} else if (kind === "service" && !roles.cache && !roles.players) {
|
|
260
|
-
roles.domain = `${serviceName}Controller`;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const reasoning = [
|
|
264
|
-
...report.reasoning,
|
|
265
|
-
kind === "service"
|
|
266
|
-
? `roles ${["Main", roles.players ? "PlayersManager" : null, roles.cache ? "CacheController" : null, roles.domain, `${serviceName}Types`].filter(Boolean).join(", ")}`
|
|
267
|
-
: `kind ${kind}`,
|
|
268
|
-
];
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
tag,
|
|
272
|
-
kind,
|
|
273
|
-
onlyConsts: constsOnly,
|
|
274
|
-
trivial,
|
|
275
|
-
serviceName,
|
|
276
|
-
typesName: `${serviceName}Types`,
|
|
277
|
-
isClient: tag.runtime === "client",
|
|
278
|
-
runContext: tag.runContext || null,
|
|
279
|
-
intents,
|
|
280
|
-
primaryDomain,
|
|
281
|
-
roles,
|
|
282
|
-
stats,
|
|
283
|
-
folderName,
|
|
284
|
-
consts,
|
|
285
|
-
functions,
|
|
286
|
-
fileName,
|
|
287
|
-
understand: report,
|
|
288
|
-
reasoning,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function modernScriptOutName(relativeName) {
|
|
293
|
-
const rel = String(relativeName).replace(/\\/g, "/");
|
|
294
|
-
if (/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i.test(rel)) {
|
|
295
|
-
return rel.replace(/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
296
|
-
}
|
|
297
|
-
return rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function legacyOutName(relativeName) {
|
|
301
|
-
return String(relativeName)
|
|
302
|
-
.replace(/\\/g, "/")
|
|
303
|
-
.replace(/\.legacy\.plugin\./i, ".")
|
|
304
|
-
.replace(/\.legacy\.(server|client)\./i, ".$1.")
|
|
305
|
-
.replace(/\.legacy\./i, ".server.")
|
|
306
|
-
.replace(new RegExp(`\\.${EXT}$`, "i"), ".luau");
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export {
|
|
310
|
-
VALUE_CLASSES,
|
|
311
|
-
INTENTS,
|
|
312
|
-
walk,
|
|
313
|
-
namesIn,
|
|
314
|
-
parseFileTag,
|
|
315
|
-
toPascalServiceName,
|
|
316
|
-
scoreIntents,
|
|
317
|
-
analyze,
|
|
318
|
-
legacyOutName,
|
|
319
|
-
modernScriptOutName,
|
|
320
|
-
looksLikeCacheSetup,
|
|
321
|
-
determineArchitecture,
|
|
322
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"configurations": [
|
|
3
|
-
{
|
|
4
|
-
"name": "Cluaupp",
|
|
5
|
-
"compilerPath": "clang++",
|
|
6
|
-
"cStandard": "c17",
|
|
7
|
-
"cppStandard": "c++20",
|
|
8
|
-
"intelliSenseMode": "windows-clang-x64",
|
|
9
|
-
"includePath": [
|
|
10
|
-
"${workspaceFolder}/include",
|
|
11
|
-
"${workspaceFolder}/src"
|
|
12
|
-
],
|
|
13
|
-
"forcedInclude": [
|
|
14
|
-
"${workspaceFolder}/include/cluaupp/roblox.hpp"
|
|
15
|
-
],
|
|
16
|
-
"compileCommands": "${workspaceFolder}/compile_commands.json",
|
|
17
|
-
"browse": {
|
|
18
|
-
"path": [
|
|
19
|
-
"${workspaceFolder}/include",
|
|
20
|
-
"${workspaceFolder}/src"
|
|
21
|
-
],
|
|
22
|
-
"limitSymbolsToIncludedHeaders": true
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
],
|
|
26
|
-
"version": 4
|
|
27
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#pragma strict
|
|
2
|
-
#include <cluaupp/roblox.hpp>
|
|
3
|
-
|
|
4
|
-
void CreateLeaderstats(Player* player) {
|
|
5
|
-
if (player->FindFirstChild("leaderstats") != nullptr) {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
auto* leaderstats = new Folder(player);
|
|
10
|
-
leaderstats->Name = "leaderstats";
|
|
11
|
-
|
|
12
|
-
auto* coins = new IntValue(leaderstats);
|
|
13
|
-
coins->Name = "Coins";
|
|
14
|
-
coins->Value = 0;
|
|
15
|
-
|
|
16
|
-
auto* level = new IntValue(leaderstats);
|
|
17
|
-
level->Name = "Level";
|
|
18
|
-
level->Value = 0;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
void init() {
|
|
22
|
-
auto* players = GetService<Players>();
|
|
23
|
-
|
|
24
|
-
for (auto* player : players->GetPlayers()) {
|
|
25
|
-
CreateLeaderstats(player);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
players->PlayerAdded.Connect(CreateLeaderstats);
|
|
29
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|