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/intellisense.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import { preprocess } from "./preprocess.js";
|
|
6
|
-
import { findClangPlusPlus, installMarketplaceExtension, ensureLlvm, CLANGD_ID, reportInstall } from "./editor-install.js";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
7
5
|
import { pkg, projectRoot as PACKAGE_ROOT } from "./package-info.js";
|
|
8
6
|
import type { ProjectConfig } from "./types.js";
|
|
9
|
-
import
|
|
7
|
+
import { CLPP_INSTALL_HINT } from "./clpp/contract.js";
|
|
8
|
+
import { compileViaClpp, hasClpp } from "./clpp/runner.js";
|
|
9
|
+
import { isSourceFile } from "./clpp/paths.js";
|
|
10
10
|
|
|
11
11
|
export type CluauppDiagnostic = {
|
|
12
12
|
line: number;
|
|
@@ -15,83 +15,6 @@ export type CluauppDiagnostic = {
|
|
|
15
15
|
severity: "error";
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
function posix(file: string): string {
|
|
19
|
-
return String(file).replace(/\\/g, "/");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function collectFiles(dir: string, files: string[] = []): string[] {
|
|
23
|
-
if (!fs.existsSync(dir)) {
|
|
24
|
-
return files;
|
|
25
|
-
}
|
|
26
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
27
|
-
const full = path.join(dir, entry.name);
|
|
28
|
-
if (entry.isDirectory()) {
|
|
29
|
-
collectFiles(full, files);
|
|
30
|
-
} else {
|
|
31
|
-
files.push(full);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return files;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function isCppFile(file: string): boolean {
|
|
38
|
-
return /\.(cpp|cc|cxx|c|h|hpp|hh)$/i.test(file);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function clangCompileArgs(root: string): string[] {
|
|
42
|
-
const absRoot = posix(path.resolve(root));
|
|
43
|
-
const absInclude = posix(path.join(absRoot, "include"));
|
|
44
|
-
const absSrc = posix(path.join(absRoot, "src"));
|
|
45
|
-
const roblox = posix(path.join(absInclude, "cluaupp", "roblox.hpp"));
|
|
46
|
-
return [
|
|
47
|
-
"-xc++",
|
|
48
|
-
"-std=c++20",
|
|
49
|
-
"-ferror-limit=0",
|
|
50
|
-
"-I" + absInclude,
|
|
51
|
-
"-I" + absSrc,
|
|
52
|
-
"-Iinclude",
|
|
53
|
-
"-Isrc",
|
|
54
|
-
"-include",
|
|
55
|
-
roblox,
|
|
56
|
-
];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function compileCommandEntry(root: string, file: string) {
|
|
60
|
-
const absRoot = posix(path.resolve(root));
|
|
61
|
-
const absFile = posix(path.resolve(file));
|
|
62
|
-
return {
|
|
63
|
-
directory: absRoot,
|
|
64
|
-
file: absFile,
|
|
65
|
-
arguments: [clangPath(), ...clangCompileArgs(root), "-c", absFile],
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function mergeJson(file: string, patch: Record<string, unknown>): boolean {
|
|
70
|
-
let current: Record<string, unknown> = {};
|
|
71
|
-
if (fs.existsSync(file)) {
|
|
72
|
-
try {
|
|
73
|
-
current = JSON.parse(fs.readFileSync(file, "utf8")) as Record<string, unknown>;
|
|
74
|
-
} catch {
|
|
75
|
-
current = {};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const next: Record<string, unknown> = { ...current, ...patch };
|
|
79
|
-
for (const key of Object.keys(patch)) {
|
|
80
|
-
const value = patch[key];
|
|
81
|
-
const existing = current[key];
|
|
82
|
-
if (value && typeof value === "object" && !Array.isArray(value) && existing && typeof existing === "object" && !Array.isArray(existing)) {
|
|
83
|
-
next[key] = { ...(existing as Record<string, unknown>), ...(value as Record<string, unknown>) };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const json = `${JSON.stringify(next, null, "\t")}\n`;
|
|
87
|
-
if (fs.existsSync(file) && fs.readFileSync(file, "utf8") === json) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
91
|
-
fs.writeFileSync(file, json, "utf8");
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
18
|
function copyDir(from: string, to: string): void {
|
|
96
19
|
fs.mkdirSync(to, { recursive: true });
|
|
97
20
|
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
@@ -105,153 +28,94 @@ function copyDir(from: string, to: string): void {
|
|
|
105
28
|
}
|
|
106
29
|
}
|
|
107
30
|
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (process.platform === "darwin") {
|
|
122
|
-
return `macos-clang-${arch}`;
|
|
31
|
+
function uniqueStrings(...lists: unknown[]): string[] {
|
|
32
|
+
const seen = new Set<string>();
|
|
33
|
+
const out: string[] = [];
|
|
34
|
+
for (const list of lists) {
|
|
35
|
+
if (!Array.isArray(list)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
for (const item of list) {
|
|
39
|
+
if (typeof item === "string" && item && !seen.has(item)) {
|
|
40
|
+
seen.add(item);
|
|
41
|
+
out.push(item);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
123
44
|
}
|
|
124
|
-
return
|
|
45
|
+
return out;
|
|
125
46
|
}
|
|
126
47
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
48
|
+
function readJsonObject(file: string): Record<string, unknown> {
|
|
49
|
+
if (!fs.existsSync(file)) {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
54
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
55
|
+
return parsed as Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// ignore
|
|
137
59
|
}
|
|
138
|
-
|
|
139
|
-
return true;
|
|
60
|
+
return {};
|
|
140
61
|
}
|
|
141
62
|
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
CompilationDatabase: .
|
|
148
|
-
Add:
|
|
149
|
-
${flags}
|
|
150
|
-
|
|
151
|
-
Diagnostics:
|
|
152
|
-
Suppress: '*'
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
If:
|
|
156
|
-
PathMatch: .*\\.(cpp|cc|cxx|c|h|hpp|hh)$
|
|
157
|
-
CompileFlags:
|
|
158
|
-
Add:
|
|
159
|
-
- -xc++
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
If:
|
|
163
|
-
PathMatch: (out|libs)/.*
|
|
164
|
-
Index:
|
|
165
|
-
Background: Skip
|
|
166
|
-
`;
|
|
167
|
-
const dest = path.join(root, ".clangd");
|
|
168
|
-
if (fs.existsSync(dest) && fs.readFileSync(dest, "utf8") === contents) {
|
|
169
|
-
return false;
|
|
63
|
+
function writeJson(file: string, value: Record<string, unknown>): void {
|
|
64
|
+
const json = `${JSON.stringify(value, null, "\t")}\n`;
|
|
65
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
66
|
+
if (fs.existsSync(file) && fs.readFileSync(file, "utf8") === json) {
|
|
67
|
+
return;
|
|
170
68
|
}
|
|
171
|
-
fs.writeFileSync(
|
|
172
|
-
return true;
|
|
69
|
+
fs.writeFileSync(file, json, "utf8");
|
|
173
70
|
}
|
|
174
71
|
|
|
175
72
|
function writeVscode(root: string): void {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
"*.legacy.cpp": "cpp",
|
|
193
|
-
"*.legacy.server.cpp": "cpp",
|
|
194
|
-
"*.legacy.client.cpp": "cpp",
|
|
195
|
-
},
|
|
73
|
+
const settingsPath = path.join(root, ".vscode", "settings.json");
|
|
74
|
+
const current = readJsonObject(settingsPath);
|
|
75
|
+
const associations = {
|
|
76
|
+
...((current["files.associations"] && typeof current["files.associations"] === "object" && !Array.isArray(current["files.associations"]))
|
|
77
|
+
? (current["files.associations"] as Record<string, unknown>)
|
|
78
|
+
: {}),
|
|
79
|
+
"*.clpp": "clpp",
|
|
80
|
+
"*.clp": "clpp",
|
|
81
|
+
"*.clh": "clpp",
|
|
82
|
+
"*.server.clpp": "clpp",
|
|
83
|
+
"*.client.clpp": "clpp",
|
|
84
|
+
"*.plugin.clpp": "clpp",
|
|
85
|
+
};
|
|
86
|
+
writeJson(settingsPath, {
|
|
87
|
+
...current,
|
|
88
|
+
"files.associations": associations,
|
|
196
89
|
});
|
|
197
|
-
|
|
198
|
-
|
|
90
|
+
|
|
91
|
+
const extensionsPath = path.join(root, ".vscode", "extensions.json");
|
|
92
|
+
const extensions = readJsonObject(extensionsPath);
|
|
93
|
+
writeJson(extensionsPath, {
|
|
94
|
+
...extensions,
|
|
95
|
+
recommendations: uniqueStrings(extensions.recommendations),
|
|
96
|
+
unwantedRecommendations: uniqueStrings(extensions.unwantedRecommendations, ["ms-vscode.cpptools"]),
|
|
199
97
|
});
|
|
200
|
-
const props = path.join(root, ".vscode", "c_cpp_properties.json");
|
|
201
|
-
const nextProps = {
|
|
202
|
-
configurations: [
|
|
203
|
-
{
|
|
204
|
-
name: "Cluaupp",
|
|
205
|
-
compilerPath: clangPath(),
|
|
206
|
-
cStandard: "c17",
|
|
207
|
-
cppStandard: "c++20",
|
|
208
|
-
intelliSenseMode: intelliSenseMode(),
|
|
209
|
-
includePath: ["${workspaceFolder}/include", "${workspaceFolder}/src"],
|
|
210
|
-
forcedInclude: ["${workspaceFolder}/include/cluaupp/roblox.hpp"],
|
|
211
|
-
compileCommands: "${workspaceFolder}/compile_commands.json",
|
|
212
|
-
browse: {
|
|
213
|
-
path: ["${workspaceFolder}/include", "${workspaceFolder}/src"],
|
|
214
|
-
limitSymbolsToIncludedHeaders: true,
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
],
|
|
218
|
-
version: 4,
|
|
219
|
-
};
|
|
220
|
-
const json = `${JSON.stringify(nextProps, null, "\t")}\n`;
|
|
221
|
-
fs.mkdirSync(path.dirname(props), { recursive: true });
|
|
222
|
-
if (!fs.existsSync(props) || fs.readFileSync(props, "utf8") !== json) {
|
|
223
|
-
fs.writeFileSync(props, json, "utf8");
|
|
224
|
-
}
|
|
225
98
|
}
|
|
226
99
|
|
|
227
|
-
export function diagnosticsFor(source: string, fileName?: string
|
|
100
|
+
export function diagnosticsFor(source: string, fileName?: string): CluauppDiagnostic[] {
|
|
101
|
+
if (!hasClpp()) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
228
104
|
try {
|
|
229
|
-
|
|
230
|
-
parse(prepared, fileName || "input.cpp");
|
|
105
|
+
compileViaClpp({ source, fileName: fileName || "input.clpp" });
|
|
231
106
|
return [];
|
|
232
107
|
} catch (err) {
|
|
233
|
-
const
|
|
234
|
-
const message = error.message ? String(error.message) : "parse error";
|
|
108
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
235
109
|
const match = message.match(/:(\d+):(\d+):\s*(.*)$/);
|
|
236
110
|
if (match) {
|
|
237
111
|
return [{ line: Number(match[1]), col: Number(match[2]), message: match[3], severity: "error" }];
|
|
238
112
|
}
|
|
239
|
-
return [{ line:
|
|
113
|
+
return [{ line: 1, col: 1, message, severity: "error" }];
|
|
240
114
|
}
|
|
241
115
|
}
|
|
242
116
|
|
|
243
|
-
export function syncEditorSupport(root: string,
|
|
244
|
-
const srcDir = path.join(root, config.rootDir || "src");
|
|
245
|
-
if (fs.existsSync(srcDir)) {
|
|
246
|
-
writeCompileCommands(root, srcDir);
|
|
247
|
-
}
|
|
248
|
-
writeClangd(root);
|
|
117
|
+
export function syncEditorSupport(root: string, _config: Partial<ProjectConfig> = {}): void {
|
|
249
118
|
writeVscode(root);
|
|
250
|
-
const flags = ["-xc++", "-std=c++20", "-ferror-limit=0", "-Iinclude", "-Isrc", "-include", "include/cluaupp/roblox.hpp"].join("\n") + "\n";
|
|
251
|
-
const flagsFile = path.join(root, "compile_flags.txt");
|
|
252
|
-
if (!fs.existsSync(flagsFile) || fs.readFileSync(flagsFile, "utf8") !== flags) {
|
|
253
|
-
fs.writeFileSync(flagsFile, flags, "utf8");
|
|
254
|
-
}
|
|
255
119
|
}
|
|
256
120
|
|
|
257
121
|
export function installEditorExtension(cluauppRoot = PACKAGE_ROOT): string[] {
|
|
@@ -275,12 +139,19 @@ export function installEditorExtension(cluauppRoot = PACKAGE_ROOT): string[] {
|
|
|
275
139
|
return installed;
|
|
276
140
|
}
|
|
277
141
|
|
|
278
|
-
export async function installEditorSupport(
|
|
279
|
-
const local = installEditorExtension(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
142
|
+
export async function installEditorSupport() {
|
|
143
|
+
const local = installEditorExtension();
|
|
144
|
+
if (hasClpp()) {
|
|
145
|
+
const result = spawnSync("clpp", ["install"], { encoding: "utf8", windowsHide: true });
|
|
146
|
+
if (result.status === 0) {
|
|
147
|
+
console.log(String(result.stderr || result.stdout || "clpp install ok").trim());
|
|
148
|
+
} else {
|
|
149
|
+
console.log("cluaupp:", CLPP_INSTALL_HINT);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
console.log("cluaupp:", CLPP_INSTALL_HINT);
|
|
153
|
+
}
|
|
154
|
+
return { local };
|
|
286
155
|
}
|
|
156
|
+
|
|
157
|
+
export const isClppSource = isSourceFile;
|
package/src/libs.ts
CHANGED
|
@@ -248,7 +248,7 @@ function walk(node, visit) {
|
|
|
248
248
|
function modulesFromIncludes(source) {
|
|
249
249
|
const found = new Set();
|
|
250
250
|
for (const line of String(source).split(/\r?\n/)) {
|
|
251
|
-
const match = line.match(/^\s*#\s*include\s+<cluaupp\/(?:libs\/)?([A-Za-z0-9]+)\.hpp>/);
|
|
251
|
+
const match = line.match(/^\s*#\s*include\s+<(?:clpp|cluaupp)\/(?:libs\/)?([A-Za-z0-9]+)\.(?:clh|hpp)>/);
|
|
252
252
|
if (!match) {
|
|
253
253
|
continue;
|
|
254
254
|
}
|
package/src/lsp.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import {
|
|
3
2
|
createConnection,
|
|
4
3
|
ProposedFeatures,
|
|
@@ -31,10 +30,7 @@ export function start(options: { projectRoot?: string } = {}): void {
|
|
|
31
30
|
|
|
32
31
|
const publish = (document: TextDocument) => {
|
|
33
32
|
const file = uriPath(document.uri);
|
|
34
|
-
const items = diagnosticsFor(document.getText(), file
|
|
35
|
-
filePath: file,
|
|
36
|
-
includeDirs: [path.dirname(file), path.join(projectRoot, "src"), path.join(projectRoot, "include")],
|
|
37
|
-
});
|
|
33
|
+
const items = diagnosticsFor(document.getText(), file);
|
|
38
34
|
connection.sendDiagnostics({
|
|
39
35
|
uri: document.uri,
|
|
40
36
|
diagnostics: items.map((item) => ({
|
package/src/preprocess.ts
CHANGED
|
@@ -1,180 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function isHeaderFile(fileName: string): boolean {
|
|
12
|
-
return [".h", ".hpp", ".hh"].includes(path.extname(fileName).toLowerCase());
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function toLuauPath(filePath: string): string {
|
|
16
|
-
return filePath.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isEngineStub(filePath: string): boolean {
|
|
20
|
-
const normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
21
|
-
return (
|
|
22
|
-
normalized.includes("/include/cluaupp/") ||
|
|
23
|
-
normalized.includes("/include/cluau/") ||
|
|
24
|
-
normalized.endsWith("/roblox.hpp") ||
|
|
25
|
-
normalized.endsWith("/roblox.h")
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function includeVariants(name: string): string[] {
|
|
30
|
-
const variants = [name];
|
|
31
|
-
if (/\.h$/i.test(name)) {
|
|
32
|
-
variants.push(name.replace(/\.h$/i, ".hpp"), name.replace(/\.h$/i, ".hh"));
|
|
33
|
-
} else if (/\.hpp$/i.test(name)) {
|
|
34
|
-
variants.push(name.replace(/\.hpp$/i, ".h"), name.replace(/\.hpp$/i, ".hh"));
|
|
35
|
-
} else if (/\.hh$/i.test(name)) {
|
|
36
|
-
variants.push(name.replace(/\.hh$/i, ".h"), name.replace(/\.hh$/i, ".hpp"));
|
|
37
|
-
}
|
|
38
|
-
return [...new Set(variants)];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function siblingImplementation(headerPath: string | null | undefined): string | null {
|
|
42
|
-
if (!headerPath || !isHeaderFile(headerPath)) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
const base = headerPath.replace(/\.(h|hpp|hh)$/i, "");
|
|
46
|
-
for (const ext of [".cpp", ".cc", ".cxx", ".c"]) {
|
|
47
|
-
if (fs.existsSync(base + ext)) {
|
|
48
|
-
return base + ext;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function siblingHeader(implPath: string | null | undefined): string | null {
|
|
55
|
-
if (!implPath || isHeaderFile(implPath)) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const base = String(implPath).replace(/\.(cpp|cc|cxx|c)$/i, "");
|
|
59
|
-
if (base === String(implPath)) {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
for (const ext of [".h", ".hpp", ".hh"]) {
|
|
63
|
-
if (fs.existsSync(base + ext)) {
|
|
64
|
-
return base + ext;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function implOutName(rel: string): string {
|
|
71
|
-
return String(rel)
|
|
72
|
-
.replace(/\\/g, "/")
|
|
73
|
-
.replace(/\.(cpp|cc|cxx|c)$/i, "Impl.luau");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function resolveInclude(name: string, fromFile: string, includeDirs?: string[]): string | null {
|
|
77
|
-
const bases = [path.dirname(fromFile), ...(includeDirs || [])];
|
|
78
|
-
for (const base of bases) {
|
|
79
|
-
for (const variant of includeVariants(name)) {
|
|
80
|
-
const candidate = path.resolve(base, variant);
|
|
81
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
82
|
-
return candidate;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function scanHeaderExports(source: string): { consts: string[]; structs: string[]; hasProtos: boolean } {
|
|
90
|
-
const consts: string[] = [];
|
|
91
|
-
const structs: string[] = [];
|
|
92
|
-
for (const match of String(source).matchAll(/\bconst\s+(?:int|bool|float|double|auto|string)\s+(\w+)/g)) {
|
|
93
|
-
if (!consts.includes(match[1])) {
|
|
94
|
-
consts.push(match[1]);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
for (const match of String(source).matchAll(/\bstruct\s+(\w+)/g)) {
|
|
98
|
-
if (!structs.includes(match[1])) {
|
|
99
|
-
structs.push(match[1]);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const hasProtos = /\b(?:void|int|bool|float|double|auto|string|[\w:]+)\s+\w+\s*\([^;]*\)\s*;/.test(String(source));
|
|
103
|
-
return { consts, structs, hasProtos };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function pushModuleInclude(options: PreprocessOptions, resolved: string, impl: string | null): void {
|
|
107
|
-
options.moduleIncludes = options.moduleIncludes || [];
|
|
108
|
-
const srcDir = options.srcDir || null;
|
|
109
|
-
const moduleFile = impl || resolved;
|
|
110
|
-
const rel = srcDir ? path.relative(srcDir, moduleFile).replace(/\\/g, "/") : path.basename(moduleFile);
|
|
111
|
-
const headerText = fs.readFileSync(resolved, "utf8");
|
|
112
|
-
options.moduleIncludes.push({
|
|
113
|
-
name: path.basename(resolved).replace(/\.(h|hpp|hh)$/i, ""),
|
|
114
|
-
outRel: rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau"),
|
|
115
|
-
header: resolved,
|
|
116
|
-
impl: impl || null,
|
|
117
|
-
exports: scanHeaderExports(headerText),
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function applyPragmas(source: string, options: PreprocessOptions): void {
|
|
122
|
-
let nstrict = false;
|
|
123
|
-
let strict = false;
|
|
124
|
-
for (const line of String(source).split(/\r?\n/)) {
|
|
125
|
-
if (/^\s*#\s*pragma\s+nstrict\b/i.test(line)) {
|
|
126
|
-
nstrict = true;
|
|
127
|
-
} else if (/^\s*#\s*pragma\s+strict\b/i.test(line)) {
|
|
128
|
-
strict = true;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
if (nstrict) {
|
|
132
|
-
options.strict = false;
|
|
133
|
-
} else if (strict) {
|
|
134
|
-
options.strict = true;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function preprocess(source: string, filePath: string | null, options: PreprocessOptions = {}): string {
|
|
139
|
-
if (!options.pragmaResolved) {
|
|
140
|
-
options.pragmaResolved = true;
|
|
141
|
-
applyPragmas(source, options);
|
|
142
|
-
}
|
|
143
|
-
const seen = options.seen || new Set<string>();
|
|
144
|
-
options.seen = seen;
|
|
145
|
-
const includeDirs = options.includeDirs || [];
|
|
146
|
-
const resolvedSelf = filePath ? path.resolve(filePath) : null;
|
|
147
|
-
if (resolvedSelf) {
|
|
148
|
-
seen.add(resolvedSelf);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const lines = String(source).split(/\r?\n/);
|
|
152
|
-
const out: string[] = [];
|
|
153
|
-
|
|
154
|
-
for (const line of lines) {
|
|
155
|
-
const quoted = line.match(/^\s*#\s*include\s+"([^"]+)"/);
|
|
156
|
-
if (quoted) {
|
|
157
|
-
const resolved = resolveInclude(quoted[1], filePath || process.cwd(), includeDirs);
|
|
158
|
-
if (!resolved || isEngineStub(resolved) || seen.has(resolved)) {
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
const impl = siblingImplementation(resolved);
|
|
162
|
-
const including = filePath ? path.resolve(filePath) : null;
|
|
163
|
-
const ownHeader = Boolean(impl && including && path.resolve(impl) === including);
|
|
164
|
-
if (!ownHeader) {
|
|
165
|
-
pushModuleInclude(options, resolved, impl);
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
seen.add(resolved);
|
|
169
|
-
const inner = fs.readFileSync(resolved, "utf8");
|
|
170
|
-
out.push(preprocess(inner, resolved, { ...options, seen }));
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (/^\s*#\s*include\s+</.test(line)) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
out.push(line);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return out.join("\n");
|
|
180
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
SOURCE_EXTS,
|
|
3
|
+
isSourceFile,
|
|
4
|
+
isHeaderFile,
|
|
5
|
+
isEngineStub,
|
|
6
|
+
toLuauPath,
|
|
7
|
+
siblingHeader,
|
|
8
|
+
} from "./clpp/paths.js";
|
package/src/transpile.ts
CHANGED
|
@@ -1,64 +1,21 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import type { CompileOptions, CompileServiceResult
|
|
3
|
-
import { ASTCollector } from "./parser/collector.js";
|
|
4
|
-
import { parseCpp } from "./parser/index.js";
|
|
5
|
-
import { LuauCodeEmitter } from "./emitter/luau-codegen.js";
|
|
2
|
+
import type { CompileOptions, CompileServiceResult } from "./types.js";
|
|
6
3
|
import { compileService } from "./compile.js";
|
|
7
|
-
import { determineArchitecture } from "./system-understander.js";
|
|
8
4
|
import type { RojoMapper } from "./utils/rojo-mapper.js";
|
|
9
5
|
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function collectState(source: string, mapper: RojoMapper, options: CompileOptions): TranspilerState {
|
|
20
|
-
const tree = parseCpp(source);
|
|
21
|
-
if (!tree) {
|
|
22
|
-
const state = LuauCodeEmitter.emptyState(options.strict === true);
|
|
23
|
-
return state;
|
|
24
|
-
}
|
|
25
|
-
const collector = new ASTCollector(mapper, {
|
|
26
|
-
fileName: options.relativeName || options.filePath || "input.cpp",
|
|
27
|
-
sourcePath: options.filePath,
|
|
28
|
-
srcDir: options.srcDir,
|
|
29
|
-
outDir: options.outDir,
|
|
30
|
-
strict: options.strict,
|
|
31
|
-
});
|
|
32
|
-
const state = collector.collect(tree.rootNode);
|
|
33
|
-
const report = determineArchitecture(options.relativeName || options.filePath || "input.cpp", null, tree.rootNode);
|
|
34
|
-
for (const service of report.injectedServices) {
|
|
35
|
-
state.robloxServices.add(service);
|
|
36
|
-
}
|
|
37
|
-
for (const line of report.reasoning) {
|
|
38
|
-
state.headerLines.push(`-- ${line}`);
|
|
39
|
-
}
|
|
40
|
-
return state;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function transpileSource(source: string, fileName: string, options: CompileOptions, mapper: RojoMapper): CompileServiceResult {
|
|
44
|
-
const state = collectState(source, mapper, options);
|
|
45
|
-
if (state.strict) {
|
|
46
|
-
options.strict = true;
|
|
47
|
-
}
|
|
48
|
-
const compiled = compileService(source, fileName, options);
|
|
49
|
-
return {
|
|
50
|
-
...compiled,
|
|
51
|
-
files: compiled.files.map((file) => ({
|
|
52
|
-
...file,
|
|
53
|
-
contents: shouldMerge(file.name, file.contents) ? LuauCodeEmitter.merge(state, file.contents) : file.contents,
|
|
54
|
-
})),
|
|
55
|
-
};
|
|
6
|
+
export function transpileSource(
|
|
7
|
+
source: string,
|
|
8
|
+
fileName: string,
|
|
9
|
+
options: CompileOptions,
|
|
10
|
+
_mapper?: RojoMapper,
|
|
11
|
+
): CompileServiceResult {
|
|
12
|
+
return compileService(source, fileName, options);
|
|
56
13
|
}
|
|
57
14
|
|
|
58
15
|
export function outputNameFor(inputFile: string, outputPath: string): string {
|
|
59
16
|
if (/\.luau$/i.test(outputPath)) {
|
|
60
17
|
return outputPath;
|
|
61
18
|
}
|
|
62
|
-
const base = path.basename(inputFile).replace(/\.(
|
|
19
|
+
const base = path.basename(inputFile).replace(/\.(clpp|clp|clh)$/i, ".luau");
|
|
63
20
|
return path.join(outputPath, base);
|
|
64
21
|
}
|