cluaupp 0.2.2 → 0.2.3
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 +8 -0
- package/README.md +1 -1
- package/docs/README.md +32 -20
- package/docs/cli.md +1 -1
- package/docs/cpp-advanced.md +6 -3
- package/docs/cpp-types.md +2 -0
- package/docs/examples/hud.md +1 -1
- package/docs/examples/index.md +1 -1
- package/docs/examples/leaderstats.md +44 -115
- package/docs/getting-started.md +4 -2
- package/docs/intellisense.md +4 -5
- 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/oop/index.md +20 -7
- package/docs/roblox-api.md +4 -56
- package/docs/syntax.md +112 -55
- package/examples/game/.vscode/extensions.json +3 -0
- package/examples/game/.vscode/settings.json +0 -9
- package/generated/api.js +15 -1
- package/generated/emit.js +79 -3
- package/generated/intellisense.js +75 -80
- package/generated/lex.js +3 -0
- package/generated/parse.js +119 -24
- package/include/cluaupp/datatypes.hpp +4 -0
- package/package.json +15 -4
- package/src/api.ts +15 -1
- package/src/emit.ts +81 -3
- package/src/intellisense.ts +80 -80
- package/src/lex.ts +3 -0
- package/src/parse.ts +124 -25
- package/templates/game/.vscode/extensions.json +3 -0
- package/templates/game/.vscode/settings.json +0 -9
- package/examples/game/.vscode/c_cpp_properties.json +0 -27
- package/templates/game/.vscode/c_cpp_properties.json +0 -27
package/docs/syntax.md
CHANGED
|
@@ -2,16 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
Cluaupp is not a full C++ compiler. It is a **subset** aimed at Roblox scripts, in the same spirit as roblox-ts (restricted TypeScript → Luau).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Live grid of every construct: **[Language reference](https://kartzrbx.github.io/Cluaupp/docs/reference.html)**.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Generated Luau follows the current language: [`local`](https://luau.org/getting-started), `const`, and `const function`. Injected `require` / `GetService` lines are `const`. `--!strict` is emitted only when the file has `#pragma strict` (or `"strict": true` in config). `#pragma nstrict` never emits it.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- `#
|
|
14
|
-
-
|
|
9
|
+
## Files and includes
|
|
10
|
+
|
|
11
|
+
- Extensions: `.cpp`, `.cc`, `.cxx`, `.c`, `.h`, `.hpp`, `.hh`
|
|
12
|
+
- Tags: `*.server.cpp` → Script, `*.client.cpp` → LocalScript, `*.plugin.cpp` → Plugin, `*.legacy*` → Legacy, untagged → ModuleScript
|
|
13
|
+
- Quoted `#include "Own.h"` (same stem as the `.cpp`) is **inlined**. Other quoted includes become `require` (Rojo path)
|
|
14
|
+
- `#include <cluaupp/roblox.hpp>` is IntelliSense only. `#include <cluaupp/libs/janitor.hpp>` also injects `require(CluauppLibs.Janitor)`
|
|
15
|
+
- `#pragma once` is ignored. Other macros are not expanded. `//` and `/* */` comments are stripped
|
|
16
|
+
- `using …;` is skipped. `namespace { }` is flattened. `enum` / `template` / `typedef` / `extern` declarations are skipped
|
|
17
|
+
|
|
18
|
+
## Types
|
|
19
|
+
|
|
20
|
+
| C++ | Luau |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `int` / `float` / `double` | `number` |
|
|
23
|
+
| `bool` | `boolean` |
|
|
24
|
+
| `string` | `string` (not `std::string`) |
|
|
25
|
+
| `void` | no return annotation |
|
|
26
|
+
| `auto` / `auto*` | inferred from `new` / `GetService` / datatype ctor |
|
|
27
|
+
| `Player*` / `Folder*` | `Player` / `Folder` (Instance handle, not a heap address) |
|
|
28
|
+
| `LuaArray<T>` / `vector<T>` / `array<T>` / `span<T>` | `{T}` |
|
|
29
|
+
| `optional<T>` | `T?` |
|
|
30
|
+
| `const` / `static constexpr` | Luau `const` |
|
|
31
|
+
| `nullptr` | `nil` |
|
|
32
|
+
| `Vector3` `CFrame` `UDim2` `Color3` | same names (copied values) |
|
|
33
|
+
| `Enum::Material::Plastic` | `Enum.Material.Plastic` |
|
|
34
|
+
|
|
35
|
+
Always initialize: `int coins = 0;` not `int coins;` (uninitialized emits `nil`). There is no `delete`, no `*part`, no `&part`, no `int&`. Numbers copy; Instances mutate through `->`.
|
|
36
|
+
|
|
37
|
+
`auto` infers when the value is `new Class(...)`, `GetService<T>()`, or a datatype constructor. Prefer explicit types on parameters and struct fields.
|
|
15
38
|
|
|
16
39
|
## Functions
|
|
17
40
|
|
|
@@ -35,28 +58,18 @@ const function doubleCoins(coins: number): number
|
|
|
35
58
|
end
|
|
36
59
|
```
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
If `void init()` exists, emit appends `init()` at the end of
|
|
41
|
-
|
|
42
|
-
## Locals and const
|
|
61
|
+
- Only functions with a body are emitted from a `.cpp`. Prototypes in a `.h` become `export type` fields
|
|
62
|
+
- No overloading — one name, one emit. No default arguments
|
|
63
|
+
- If `void init()` exists, emit appends `init()` at the end of Scripts / LocalScripts. There is no `int main()`
|
|
64
|
+
- Shared ModuleScripts should not define `init()` unless you want them to run on `require`
|
|
43
65
|
|
|
44
|
-
|
|
66
|
+
## Scopes
|
|
45
67
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```luau
|
|
53
|
-
local coins: number = 10
|
|
54
|
-
const STARTING_COINS: number = 0
|
|
55
|
-
local folder: Folder = Instance.new("Folder")
|
|
56
|
-
folder.Parent = player
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
`auto` infers the type when the value is `new Class(...)` or `GetService<Service>()`.
|
|
68
|
+
- File-level decls become file `local` / `const`
|
|
69
|
+
- Function decls become `local` from that line to the end of the block. Nested `{ }` can shadow
|
|
70
|
+
- In `Class::Method`, `this` is `self`. Bare field names become `self.field`. Bare calls to other methods become `self:Method(...)`. Parameters and locals shadow fields
|
|
71
|
+
- Globals stay bare: `print`, `game`, `workspace`, library types, Instance types, datatypes
|
|
72
|
+
- Instances are not RAII. Leaving a block does not Destroy a Part — use Janitor
|
|
60
73
|
|
|
61
74
|
## Control flow
|
|
62
75
|
|
|
@@ -121,9 +134,9 @@ repeat
|
|
|
121
134
|
until true
|
|
122
135
|
```
|
|
123
136
|
|
|
124
|
-
`switch` evaluates the discriminant **once**, then becomes `if` / `elseif` / `else` inside `repeat … until true` so `break` still exits the switch (even from inside an `if`). Stacked `case` labels share a body
|
|
137
|
+
`switch` evaluates the discriminant **once**, then becomes `if` / `elseif` / `else` inside `repeat … until true` so `break` still exits the switch (even from inside an `if`). Stacked `case` labels share a body. There is no C-style fall-through. C-style `for (int i = 0; i < n; i++)` is not supported. No `continue`, no `do/while`, no ternary. `else if (x)` emits a nested `if` inside `else` (not Luau `elseif`).
|
|
125
138
|
|
|
126
|
-
## Expressions
|
|
139
|
+
## Expressions and operators
|
|
127
140
|
|
|
128
141
|
| C++ | Luau |
|
|
129
142
|
| --- | --- |
|
|
@@ -132,15 +145,20 @@ until true
|
|
|
132
145
|
| `!=` | `~=` |
|
|
133
146
|
| `&&` | `and` |
|
|
134
147
|
| `\|\|` | `or` |
|
|
135
|
-
|
|
|
148
|
+
| `!` | `not` |
|
|
149
|
+
| `==` `+` `-` `*` `/` `<` `>` `<=` `>=` | same (`*` is multiply) |
|
|
136
150
|
| `"text"` | `"text"` |
|
|
151
|
+
| `=` | assignment |
|
|
137
152
|
| `obj->Prop` | `obj.Prop` |
|
|
138
|
-
| `obj->Method(a)` | `obj:Method(a)` if the method is a Roblox API |
|
|
153
|
+
| `obj->Method(a)` | `obj:Method(a)` if the method is a Roblox / library API |
|
|
139
154
|
| `fn(a)` | `fn(a)` |
|
|
140
155
|
| `signal.Connect(fn)` | `signal:Connect(fn)` |
|
|
141
156
|
| `Type { .Field = value }` | `{ Field = value }` |
|
|
157
|
+
| unary `-` | `-` |
|
|
158
|
+
|
|
159
|
+
No `++` `--` `+=`. Write `n = n + 1`. Binary operators in this subset are left-associative — use parentheses when mixing.
|
|
142
160
|
|
|
143
|
-
Designated initializers become Luau tables
|
|
161
|
+
Designated initializers become Luau tables:
|
|
144
162
|
|
|
145
163
|
```cpp
|
|
146
164
|
DataService::Server.Init(DataServiceOptions {
|
|
@@ -158,48 +176,87 @@ DataService.Server:Init({
|
|
|
158
176
|
})
|
|
159
177
|
```
|
|
160
178
|
|
|
179
|
+
## Strings and string_concat
|
|
180
|
+
|
|
181
|
+
Luau concatenates with `..`. Do not write `..` in a `.cpp`.
|
|
182
|
+
|
|
183
|
+
- If either side of `+` is a string literal, another concat, or `.Name` / `.Text` / `.DisplayName`, `+` becomes `..`
|
|
184
|
+
- `string_concat(a, b, c)` is variadic: zero args → `""`, one arg → that value, several → `(a .. b .. c)`
|
|
185
|
+
- Declared in `datatypes.hpp` (via `roblox.hpp`)
|
|
186
|
+
|
|
187
|
+
```cpp
|
|
188
|
+
return player->Name + "_LeaderstatsJanitor";
|
|
189
|
+
return string_concat(player->Name, "_", "LeaderstatsJanitor");
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```luau
|
|
193
|
+
return player.Name .. "_LeaderstatsJanitor"
|
|
194
|
+
return (player.Name .. "_" .. "LeaderstatsJanitor")
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## print / cout
|
|
198
|
+
|
|
199
|
+
`print`, `warn`, and `error` work as in Luau. `cout << … << endl` is the C++ spelling — each `<<` is another argument, `endl` ends the line. `cout::warn` / `cout::error` / `cout::ping` pick the Roblox function. `cerr` is `warn`. Full table: [print and cout](print-cout.md).
|
|
200
|
+
|
|
161
201
|
## `new` and services
|
|
162
202
|
|
|
163
203
|
```cpp
|
|
164
204
|
auto* coins = new IntValue(leaderstats);
|
|
165
205
|
auto* players = GetService<Players>();
|
|
206
|
+
auto* janitor = new Janitor();
|
|
207
|
+
part->Size = Vector3(8, 1, 8);
|
|
208
|
+
part->CFrame = CFrame::lookAt(from, look);
|
|
209
|
+
part->Material = Enum::Material::Plastic;
|
|
166
210
|
```
|
|
167
211
|
|
|
168
212
|
```luau
|
|
169
213
|
local coins: IntValue = Instance.new("IntValue")
|
|
170
214
|
coins.Parent = leaderstats
|
|
171
215
|
local players: Players = game:GetService("Players")
|
|
216
|
+
local janitor = Janitor.new()
|
|
217
|
+
part.Size = Vector3.new(8, 1, 8)
|
|
218
|
+
part.CFrame = CFrame.lookAt(from, look)
|
|
219
|
+
part.Material = Enum.Material.Plastic
|
|
172
220
|
```
|
|
173
221
|
|
|
174
|
-
The first argument of `new Class(parent)` becomes `.Parent
|
|
222
|
+
The first argument of `new Class(parent)` becomes `.Parent` for Instance classes. Libraries use `Janitor.new()`. Datatypes use `Vector3(...)`, not `new`.
|
|
175
223
|
|
|
176
|
-
##
|
|
224
|
+
## Casts
|
|
177
225
|
|
|
178
|
-
`
|
|
226
|
+
`static_cast<T>(x)`, `const_cast`, `reinterpret_cast`, `dynamic_cast` emit the argument. `(void)x;` is omitted (silences unused for clangd). Luau has no casts — they do not check ClassName.
|
|
227
|
+
|
|
228
|
+
## Callbacks (events and lambdas)
|
|
229
|
+
|
|
230
|
+
Signals use `.Connect`. Callbacks may be **named functions** or **lambdas** (`[](Player* player) { ... }`, including `[&]` / `[=]` / `[coinsValue]`). Capture lists are accepted; Luau closures do not copy C++ captures — keep Instances alive with Janitor.
|
|
231
|
+
|
|
232
|
+
Passing a method by name from inside `Class::` binds `self`: `function(...) self:OnPlayer(...) end`.
|
|
179
233
|
|
|
180
234
|
```cpp
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
cout::ping("here");
|
|
235
|
+
players->PlayerAdded.Connect([&](Player* playerEntered) {
|
|
236
|
+
leaderstatsServer.PlayerEntered(playerEntered);
|
|
237
|
+
});
|
|
238
|
+
game->BindToClose([&]() {
|
|
239
|
+
leaderstatsServer.janitor->Cleanup();
|
|
240
|
+
});
|
|
188
241
|
```
|
|
189
242
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
243
|
+
## OOP: structs, methods, singletons
|
|
244
|
+
|
|
245
|
+
How you write a type: a `struct` (or `class`) in the sibling `.h`, `Class::Method` in the `.cpp`, `void init()` to boot.
|
|
246
|
+
|
|
247
|
+
- `static constexpr` fields are `const`
|
|
248
|
+
- Fields are `self.field`. `public:` / `private:` are ignored
|
|
249
|
+
- Nested structs with defaults become nested tables (DataService Templates). `LuaArray<T>` fields stay on the table
|
|
250
|
+
- `Class::` methods emit `function Class:Method(...)`
|
|
251
|
+
- Construct **one** service table in `init()` (`LeaderstatsServer leaderstatsServer;`) and capture it in lambdas. That is the game singleton
|
|
252
|
+
- Library singletons already exist: `DataService::Server` / `Client` — `Init` once from a boot script
|
|
253
|
+
- Header-only field structs emit `const function Name()` returning defaults
|
|
254
|
+
- Untagged files of only `Class::` methods `return` the table (ModuleScript)
|
|
198
255
|
|
|
199
|
-
|
|
256
|
+
Stem must match: `LeaderstatsServer.h` next to `LeaderstatsServer.server.cpp`. A differently named include is a `require`, not the class body.
|
|
200
257
|
|
|
201
|
-
## Not supported
|
|
258
|
+
## Not supported
|
|
202
259
|
|
|
203
|
-
Custom C++ classes, generic templates besides `GetService<T
|
|
260
|
+
Custom C++ classes as metatables, generic templates besides `GetService<T>` / `static_cast<T>` / `LuaArray<T>` / `string_concat`, pointer arithmetic, `std::` (except mapped aliases), overloading, macros (except `#pragma strict` / `#pragma nstrict` / `#pragma once`), JSX / XML UI tags, C-style `for`, `++` `--` `+=`, `continue`, ternary, `do/while`, `try/catch`, `goto`, `int&` references.
|
|
204
261
|
|
|
205
|
-
|
|
262
|
+
CLI forms: [CLI](cli.md). How to write a type: [OOP](oop/index.md). Full handbook: **[Docs](https://kartzrbx.github.io/Cluaupp/docs/)**.
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"C_Cpp.intelliSenseEngine": "Disabled",
|
|
3
|
-
"C_Cpp.default.cppStandard": "c++20",
|
|
4
|
-
"C_Cpp.default.cStandard": "c17",
|
|
5
|
-
"C_Cpp.default.compilerPath": "clang++",
|
|
6
|
-
"C_Cpp.default.intelliSenseMode": "windows-clang-x64",
|
|
7
|
-
"C_Cpp.default.includePath": [
|
|
8
|
-
"${workspaceFolder}/include",
|
|
9
|
-
"${workspaceFolder}/src"
|
|
10
|
-
],
|
|
11
2
|
"clangd.enable": true,
|
|
12
3
|
"clangd.arguments": [
|
|
13
4
|
"--compile-commands-dir=${workspaceFolder}",
|
package/generated/api.js
CHANGED
|
@@ -43,9 +43,23 @@ function luauType(name) {
|
|
|
43
43
|
if (!name) {
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
46
|
-
const cleaned = String(name)
|
|
46
|
+
const cleaned = String(name)
|
|
47
|
+
.replace(/\*+$/, "")
|
|
48
|
+
.replace(/^const\s+/, "")
|
|
49
|
+
.replace(/^Enum::/, "Enum.")
|
|
50
|
+
.replace(/\s+/g, " ")
|
|
51
|
+
.trim();
|
|
47
52
|
if (cleaned in LUAU_TYPES) {
|
|
48
53
|
return LUAU_TYPES[cleaned];
|
|
49
54
|
}
|
|
55
|
+
const generic = cleaned.match(/^(?:std::)?(?:vector|array|span|LuaArray)\s*<\s*([^>]+)\s*>$/);
|
|
56
|
+
if (generic) {
|
|
57
|
+
const inner = luauType(generic[1]) || generic[1];
|
|
58
|
+
return `{${inner}}`;
|
|
59
|
+
}
|
|
60
|
+
const optional = cleaned.match(/^(?:std::)?optional\s*<\s*([^>]+)\s*>$/);
|
|
61
|
+
if (optional) {
|
|
62
|
+
return `${luauType(optional[1]) || optional[1]}?`;
|
|
63
|
+
}
|
|
50
64
|
return cleaned;
|
|
51
65
|
}
|
package/generated/emit.js
CHANGED
|
@@ -31,6 +31,7 @@ function emit(ast, options = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
const ownedFns = (ast.body || []).filter((decl) => decl.type === "function");
|
|
34
|
+
const hasOwnedMethods = ownedFns.some((decl) => Boolean(decl.owner));
|
|
34
35
|
const classModule = ownedFns.length > 0 && ownedFns.every((decl) => Boolean(decl.owner));
|
|
35
36
|
const nestedTypes = new Set();
|
|
36
37
|
for (const decl of ast.body || []) {
|
|
@@ -131,6 +132,35 @@ function emit(ast, options = {}) {
|
|
|
131
132
|
}
|
|
132
133
|
return linesOut;
|
|
133
134
|
};
|
|
135
|
+
const isStringConcatCall = (node) => node && node.type === "call" && !node.object && node.name === "string_concat";
|
|
136
|
+
const emitStringConcat = (args) => {
|
|
137
|
+
const parts = (args || []).map(emitExpr);
|
|
138
|
+
if (parts.length === 0) {
|
|
139
|
+
return `""`;
|
|
140
|
+
}
|
|
141
|
+
if (parts.length === 1) {
|
|
142
|
+
return parts[0];
|
|
143
|
+
}
|
|
144
|
+
return `(${parts.join(" .. ")})`;
|
|
145
|
+
};
|
|
146
|
+
const isStringExpr = (node) => {
|
|
147
|
+
if (!node) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (node.type === "string") {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (isStringConcatCall(node)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (node.type === "binary" && node.op === "+" && (isStringExpr(node.left) || isStringExpr(node.right))) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
if (node.type === "member" && (node.name === "Name" || node.name === "Text" || node.name === "DisplayName")) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
};
|
|
134
164
|
const emitExpr = (node) => {
|
|
135
165
|
if (!node) {
|
|
136
166
|
return "nil";
|
|
@@ -153,6 +183,28 @@ function emit(ast, options = {}) {
|
|
|
153
183
|
}
|
|
154
184
|
return `{ ${entries.join(", ")} }`;
|
|
155
185
|
}
|
|
186
|
+
case "cast":
|
|
187
|
+
if (node.valueType === "void") {
|
|
188
|
+
return emitExpr(node.argument);
|
|
189
|
+
}
|
|
190
|
+
return emitExpr(node.argument);
|
|
191
|
+
case "lambda": {
|
|
192
|
+
const params = (node.params || [])
|
|
193
|
+
.map((param) => {
|
|
194
|
+
if (typeof param === "string") {
|
|
195
|
+
return param;
|
|
196
|
+
}
|
|
197
|
+
const typeAnn = (0, api_js_1.luauType)(param.valueType);
|
|
198
|
+
return typeAnn ? `${param.name}: ${typeAnn}` : param.name;
|
|
199
|
+
})
|
|
200
|
+
.join(", ");
|
|
201
|
+
const inner = [];
|
|
202
|
+
for (const stmt of node.body || []) {
|
|
203
|
+
inner.push(...emitStmt(stmt, 1));
|
|
204
|
+
}
|
|
205
|
+
const body = inner.length > 0 ? `\n${inner.join("\n")}\n` : "\n";
|
|
206
|
+
return `function(${params})${body}end`;
|
|
207
|
+
}
|
|
156
208
|
case "unary": {
|
|
157
209
|
const inner = emitExpr(node.argument);
|
|
158
210
|
if (node.op === "!") {
|
|
@@ -172,6 +224,9 @@ function emit(ast, options = {}) {
|
|
|
172
224
|
case "member":
|
|
173
225
|
return `${emitExpr(node.object)}.${node.name}`;
|
|
174
226
|
case "call": {
|
|
227
|
+
if (isStringConcatCall(node)) {
|
|
228
|
+
return emitStringConcat(node.args);
|
|
229
|
+
}
|
|
175
230
|
const args = node.args.map(emitMethodArg).join(", ");
|
|
176
231
|
if (!node.object) {
|
|
177
232
|
if ((0, api_js_1.isDatatype)(node.name)) {
|
|
@@ -183,6 +238,9 @@ function emit(ast, options = {}) {
|
|
|
183
238
|
return `${node.name}(${args})`;
|
|
184
239
|
}
|
|
185
240
|
const obj = emitExpr(node.object);
|
|
241
|
+
if (classMethods.has(node.name) && !localNames.has(node.name)) {
|
|
242
|
+
return `${obj}:${node.name}(${args})`;
|
|
243
|
+
}
|
|
186
244
|
if (node.access === "::" && node.object.type === "ident" && node.object.name === "cout") {
|
|
187
245
|
if (node.name === "endl") {
|
|
188
246
|
return "print()";
|
|
@@ -220,6 +278,9 @@ function emit(ast, options = {}) {
|
|
|
220
278
|
return printed.join("; ");
|
|
221
279
|
}
|
|
222
280
|
}
|
|
281
|
+
if (node.op === "+" && (isStringExpr(node.left) || isStringExpr(node.right))) {
|
|
282
|
+
return `${emitExpr(node.left)} .. ${emitExpr(node.right)}`;
|
|
283
|
+
}
|
|
223
284
|
const ops = { "!=": "~=", "&&": "and", "||": "or" };
|
|
224
285
|
const op = ops[node.op] || node.op;
|
|
225
286
|
return `${emitExpr(node.left)} ${op} ${emitExpr(node.right)}`;
|
|
@@ -278,6 +339,9 @@ function emit(ast, options = {}) {
|
|
|
278
339
|
return [`${prefix}${kind} ${node.name}${typed} = ${created.className}.new(${args})`];
|
|
279
340
|
}
|
|
280
341
|
const value = node.value ? emitExpr(node.value) : "nil";
|
|
342
|
+
if (!node.value && node.valueType && classOwners.has(node.valueType)) {
|
|
343
|
+
return [`${prefix}${kind} ${node.name}${typed} = ${node.valueType}`];
|
|
344
|
+
}
|
|
281
345
|
return [`${prefix}${kind} ${node.name}${typed} = ${value}`];
|
|
282
346
|
};
|
|
283
347
|
const emitStmt = (node, indent) => {
|
|
@@ -290,6 +354,9 @@ function emit(ast, options = {}) {
|
|
|
290
354
|
return emitNewDecl(node, indent);
|
|
291
355
|
case "expr": {
|
|
292
356
|
const expr = node.expr;
|
|
357
|
+
if (expr && expr.type === "cast" && expr.valueType === "void") {
|
|
358
|
+
return [];
|
|
359
|
+
}
|
|
293
360
|
if (expr && expr.type === "binary" && expr.op === "<<") {
|
|
294
361
|
const printed = emitCout(expr);
|
|
295
362
|
if (printed) {
|
|
@@ -433,10 +500,8 @@ function emit(ast, options = {}) {
|
|
|
433
500
|
lines.push(" }");
|
|
434
501
|
lines.push("end");
|
|
435
502
|
lines.push("");
|
|
436
|
-
lines.push(`return ${root}`);
|
|
437
|
-
lines.push("");
|
|
438
503
|
};
|
|
439
|
-
if (
|
|
504
|
+
if (hasOwnedMethods) {
|
|
440
505
|
for (const owner of classOwners) {
|
|
441
506
|
lines.push(`local ${owner} = {}`);
|
|
442
507
|
lines.push("");
|
|
@@ -446,6 +511,17 @@ function emit(ast, options = {}) {
|
|
|
446
511
|
for (const root of structRoots) {
|
|
447
512
|
emitStructConstructor(root);
|
|
448
513
|
}
|
|
514
|
+
if (structRoots.length === 1) {
|
|
515
|
+
lines.push(`return ${structRoots[0]}`);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
lines.push("return {");
|
|
519
|
+
for (const root of structRoots) {
|
|
520
|
+
lines.push(` ${root} = ${root},`);
|
|
521
|
+
}
|
|
522
|
+
lines.push("}");
|
|
523
|
+
}
|
|
524
|
+
lines.push("");
|
|
449
525
|
return lines.join("\n");
|
|
450
526
|
}
|
|
451
527
|
for (const decl of ast.body) {
|
|
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.intelliSenseMode = intelliSenseMode;
|
|
7
6
|
exports.clangPath = clangPath;
|
|
8
7
|
exports.writeCompileCommands = writeCompileCommands;
|
|
9
8
|
exports.writeClangd = writeClangd;
|
|
@@ -65,32 +64,6 @@ function compileCommandEntry(root, file) {
|
|
|
65
64
|
arguments: [clangPath(), ...clangCompileArgs(root), "-c", absFile],
|
|
66
65
|
};
|
|
67
66
|
}
|
|
68
|
-
function mergeJson(file, patch) {
|
|
69
|
-
let current = {};
|
|
70
|
-
if (node_fs_1.default.existsSync(file)) {
|
|
71
|
-
try {
|
|
72
|
-
current = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
current = {};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const next = { ...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, ...value };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const json = `${JSON.stringify(next, null, "\t")}\n`;
|
|
87
|
-
if (node_fs_1.default.existsSync(file) && node_fs_1.default.readFileSync(file, "utf8") === json) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
|
|
91
|
-
node_fs_1.default.writeFileSync(file, json, "utf8");
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
67
|
function copyDir(from, to) {
|
|
95
68
|
node_fs_1.default.mkdirSync(to, { recursive: true });
|
|
96
69
|
for (const entry of node_fs_1.default.readdirSync(from, { withFileTypes: true })) {
|
|
@@ -104,6 +77,55 @@ function copyDir(from, to) {
|
|
|
104
77
|
}
|
|
105
78
|
}
|
|
106
79
|
}
|
|
80
|
+
function uniqueStrings(...lists) {
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const list of lists) {
|
|
84
|
+
if (!Array.isArray(list)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
for (const item of list) {
|
|
88
|
+
if (typeof item === "string" && item && !seen.has(item)) {
|
|
89
|
+
seen.add(item);
|
|
90
|
+
out.push(item);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
function stripCppToolsSettings(current) {
|
|
97
|
+
const next = {};
|
|
98
|
+
for (const [key, value] of Object.entries(current)) {
|
|
99
|
+
if (key === "C_Cpp" || key.startsWith("C_Cpp.")) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
next[key] = value;
|
|
103
|
+
}
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
function readJsonObject(file) {
|
|
107
|
+
if (!node_fs_1.default.existsSync(file)) {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
|
|
112
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// ignore corrupt editor json
|
|
118
|
+
}
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
function writeJson(file, value) {
|
|
122
|
+
const json = `${JSON.stringify(value, null, "\t")}\n`;
|
|
123
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
|
|
124
|
+
if (node_fs_1.default.existsSync(file) && node_fs_1.default.readFileSync(file, "utf8") === json) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
node_fs_1.default.writeFileSync(file, json, "utf8");
|
|
128
|
+
}
|
|
107
129
|
function clangdEditorArgs() {
|
|
108
130
|
return [
|
|
109
131
|
"--compile-commands-dir=${workspaceFolder}",
|
|
@@ -111,16 +133,6 @@ function clangdEditorArgs() {
|
|
|
111
133
|
"--query-driver=**/clang++*,**/clang++.exe,**/g++*,**/g++.exe",
|
|
112
134
|
];
|
|
113
135
|
}
|
|
114
|
-
function intelliSenseMode() {
|
|
115
|
-
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
116
|
-
if (process.platform === "win32") {
|
|
117
|
-
return `windows-clang-${arch}`;
|
|
118
|
-
}
|
|
119
|
-
if (process.platform === "darwin") {
|
|
120
|
-
return `macos-clang-${arch}`;
|
|
121
|
-
}
|
|
122
|
-
return `linux-clang-${arch}`;
|
|
123
|
-
}
|
|
124
136
|
function clangPath() {
|
|
125
137
|
return (0, editor_install_js_1.findClangPlusPlus)() || "clang++";
|
|
126
138
|
}
|
|
@@ -167,54 +179,37 @@ Index:
|
|
|
167
179
|
return true;
|
|
168
180
|
}
|
|
169
181
|
function writeVscode(root) {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
"
|
|
177
|
-
"
|
|
182
|
+
const settingsPath = node_path_1.default.join(root, ".vscode", "settings.json");
|
|
183
|
+
const current = readJsonObject(settingsPath);
|
|
184
|
+
const associations = {
|
|
185
|
+
...((current["files.associations"] && typeof current["files.associations"] === "object" && !Array.isArray(current["files.associations"]))
|
|
186
|
+
? current["files.associations"]
|
|
187
|
+
: {}),
|
|
188
|
+
"*.hpp": "cpp",
|
|
189
|
+
"*.h": "cpp",
|
|
190
|
+
"*.server.cpp": "cpp",
|
|
191
|
+
"*.client.cpp": "cpp",
|
|
192
|
+
"*.plugin.cpp": "cpp",
|
|
193
|
+
"*.legacy.cpp": "cpp",
|
|
194
|
+
"*.legacy.server.cpp": "cpp",
|
|
195
|
+
"*.legacy.client.cpp": "cpp",
|
|
196
|
+
};
|
|
197
|
+
writeJson(settingsPath, {
|
|
198
|
+
...stripCppToolsSettings(current),
|
|
178
199
|
"clangd.enable": true,
|
|
179
200
|
"clangd.arguments": clangdEditorArgs(),
|
|
180
|
-
"files.associations":
|
|
181
|
-
"*.hpp": "cpp",
|
|
182
|
-
"*.h": "cpp",
|
|
183
|
-
"*.server.cpp": "cpp",
|
|
184
|
-
"*.client.cpp": "cpp",
|
|
185
|
-
"*.plugin.cpp": "cpp",
|
|
186
|
-
"*.legacy.cpp": "cpp",
|
|
187
|
-
"*.legacy.server.cpp": "cpp",
|
|
188
|
-
"*.legacy.client.cpp": "cpp",
|
|
189
|
-
},
|
|
201
|
+
"files.associations": associations,
|
|
190
202
|
});
|
|
191
|
-
|
|
192
|
-
|
|
203
|
+
const extensionsPath = node_path_1.default.join(root, ".vscode", "extensions.json");
|
|
204
|
+
const extensions = readJsonObject(extensionsPath);
|
|
205
|
+
writeJson(extensionsPath, {
|
|
206
|
+
...extensions,
|
|
207
|
+
recommendations: uniqueStrings(extensions.recommendations, ["llvm-vs-code-extensions.vscode-clangd"]),
|
|
208
|
+
unwantedRecommendations: uniqueStrings(extensions.unwantedRecommendations, ["ms-vscode.cpptools"]),
|
|
193
209
|
});
|
|
194
210
|
const props = node_path_1.default.join(root, ".vscode", "c_cpp_properties.json");
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
{
|
|
198
|
-
name: "Cluaupp",
|
|
199
|
-
compilerPath: clangPath(),
|
|
200
|
-
cStandard: "c17",
|
|
201
|
-
cppStandard: "c++20",
|
|
202
|
-
intelliSenseMode: intelliSenseMode(),
|
|
203
|
-
includePath: ["${workspaceFolder}/include", "${workspaceFolder}/src"],
|
|
204
|
-
forcedInclude: ["${workspaceFolder}/include/cluaupp/roblox.hpp"],
|
|
205
|
-
compileCommands: "${workspaceFolder}/compile_commands.json",
|
|
206
|
-
browse: {
|
|
207
|
-
path: ["${workspaceFolder}/include", "${workspaceFolder}/src"],
|
|
208
|
-
limitSymbolsToIncludedHeaders: true,
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
version: 4,
|
|
213
|
-
};
|
|
214
|
-
const json = `${JSON.stringify(nextProps, null, "\t")}\n`;
|
|
215
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(props), { recursive: true });
|
|
216
|
-
if (!node_fs_1.default.existsSync(props) || node_fs_1.default.readFileSync(props, "utf8") !== json) {
|
|
217
|
-
node_fs_1.default.writeFileSync(props, json, "utf8");
|
|
211
|
+
if (node_fs_1.default.existsSync(props)) {
|
|
212
|
+
node_fs_1.default.unlinkSync(props);
|
|
218
213
|
}
|
|
219
214
|
}
|
|
220
215
|
function diagnosticsFor(source, fileName, options = {}) {
|