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.
@@ -46,21 +46,124 @@ function parse(source, fileName) {
46
46
  eat("op", ">");
47
47
  return name;
48
48
  };
49
+ const CAST_NAMES = new Set(["static_cast", "const_cast", "reinterpret_cast", "dynamic_cast"]);
50
+ function skipSpecifiers() {
51
+ let isConst = false;
52
+ while (at("kw", "static") || at("kw", "inline") || at("kw", "constexpr") || at("kw", "const")) {
53
+ if (at("kw", "constexpr") || at("kw", "const")) {
54
+ isConst = true;
55
+ }
56
+ i += 1;
57
+ }
58
+ return isConst;
59
+ }
60
+ function looksLikePrimaryStart() {
61
+ if (at("ident") || at("number") || at("string")) {
62
+ return true;
63
+ }
64
+ if (at("kw", "true") || at("kw", "false") || at("kw", "nullptr") || at("kw", "new")) {
65
+ return true;
66
+ }
67
+ return at("op", "(") || at("op", "{") || at("op", "[") || at("op", "-") || at("op", "!");
68
+ }
69
+ function parseLambda() {
70
+ eat("op", "[");
71
+ while (!at("eof") && !at("op", "]")) {
72
+ i += 1;
73
+ }
74
+ eat("op", "]");
75
+ const params = [];
76
+ if (at("op", "(")) {
77
+ i += 1;
78
+ if (!at("op", ")")) {
79
+ params.push(parseParam());
80
+ while (at("op", ",")) {
81
+ i += 1;
82
+ params.push(parseParam());
83
+ }
84
+ }
85
+ eat("op", ")");
86
+ }
87
+ const body = parseBlock();
88
+ return { type: "lambda", params, body };
89
+ }
90
+ function parseNamedCast() {
91
+ i += 1;
92
+ eat("op", "<");
93
+ const valueType = parseType() || "any";
94
+ eat("op", ">");
95
+ const args = parseArgs();
96
+ return { type: "cast", valueType, argument: args[0] || { type: "null" } };
97
+ }
98
+ function parseParenPrimary() {
99
+ eat("op", "(");
100
+ const saved = i;
101
+ skipSpecifiers();
102
+ const valueType = parseType();
103
+ if (valueType && at("op", ")")) {
104
+ i += 1;
105
+ if (looksLikePrimaryStart()) {
106
+ return { type: "cast", valueType, argument: parseUnary() };
107
+ }
108
+ }
109
+ i = saved;
110
+ const expr = parseExpr();
111
+ eat("op", ")");
112
+ return expr;
113
+ }
114
+ function eatGeneric() {
115
+ let text = "";
116
+ let depth = 0;
117
+ while (!at("eof")) {
118
+ const token = peek();
119
+ if (at("op", "<")) {
120
+ depth += 1;
121
+ text += "<";
122
+ i += 1;
123
+ continue;
124
+ }
125
+ if (at("op", ">")) {
126
+ depth -= 1;
127
+ text += ">";
128
+ i += 1;
129
+ if (depth === 0) {
130
+ return text;
131
+ }
132
+ continue;
133
+ }
134
+ if (token.type === "string") {
135
+ text += `"${token.value}"`;
136
+ }
137
+ else {
138
+ text += token.value;
139
+ }
140
+ i += 1;
141
+ }
142
+ return text;
143
+ }
49
144
  function parseType() {
50
145
  if (at("kw", "const")) {
51
146
  i += 1;
52
147
  }
148
+ let name = null;
53
149
  if (at("kw", "auto") || at("kw", "void") || at("kw", "int") || at("kw", "bool") || at("kw", "float") || at("kw", "double")) {
54
- const name = eat("kw").value;
55
- while (at("op", "*")) {
150
+ name = eat("kw").value;
151
+ }
152
+ else if (at("ident")) {
153
+ name = eat("ident").value;
154
+ while (at("op", "::")) {
56
155
  i += 1;
156
+ if (at("ident")) {
157
+ name += `::${eat("ident").value}`;
158
+ }
57
159
  }
58
- return name;
59
160
  }
60
- if (!at("ident")) {
161
+ else {
61
162
  return null;
62
163
  }
63
- const name = eat("ident").value;
164
+ if (at("op", "<")) {
165
+ name += eatGeneric();
166
+ }
64
167
  while (at("op", "*")) {
65
168
  i += 1;
66
169
  }
@@ -129,14 +232,17 @@ function parse(source, fileName) {
129
232
  eat("op", ")");
130
233
  return { type: "getService", service };
131
234
  }
235
+ if (at("ident") && CAST_NAMES.has(peek().value) && peek(1).value === "<") {
236
+ return parseNamedCast();
237
+ }
132
238
  if (at("ident")) {
133
239
  return { type: "ident", name: eat("ident").value };
134
240
  }
241
+ if (at("op", "[")) {
242
+ return parseLambda();
243
+ }
135
244
  if (at("op", "(")) {
136
- i += 1;
137
- const expr = parseExpr();
138
- eat("op", ")");
139
- return expr;
245
+ return parseParenPrimary();
140
246
  }
141
247
  if (at("op", "{")) {
142
248
  return parseInitList();
@@ -309,11 +415,8 @@ function parse(source, fileName) {
309
415
  throw error("only range-for is supported: for (auto* x : list)");
310
416
  }
311
417
  function parseDeclOrExpr() {
312
- const isConst = at("kw", "const");
313
- if (isConst) {
314
- i += 1;
315
- }
316
418
  const saved = i;
419
+ const isConst = skipSpecifiers();
317
420
  const maybeType = parseType();
318
421
  if (maybeType && at("ident") && (peek(1).value === "=" || peek(1).value === ";")) {
319
422
  const name = eat("ident").value;
@@ -325,9 +428,6 @@ function parse(source, fileName) {
325
428
  return { type: "decl", name, valueType: maybeType, value, isConst: isConst || maybeType === "const" };
326
429
  }
327
430
  i = saved;
328
- if (isConst) {
329
- i = saved - 1;
330
- }
331
431
  return { type: "expr", expr: parseExpr() };
332
432
  }
333
433
  function parseStmt() {
@@ -380,6 +480,7 @@ function parse(source, fileName) {
380
480
  };
381
481
  }
382
482
  function parseFunction() {
483
+ skipSpecifiers();
383
484
  const returnType = parseType();
384
485
  if (!returnType || !at("ident")) {
385
486
  throw error("invalid function declaration");
@@ -449,10 +550,7 @@ function parse(source, fileName) {
449
550
  continue;
450
551
  }
451
552
  const saved = i;
452
- const isConst = at("kw", "const");
453
- if (isConst) {
454
- i += 1;
455
- }
553
+ const isConst = skipSpecifiers();
456
554
  const valueType = parseType();
457
555
  if (valueType && at("ident")) {
458
556
  if (peek(1).value === "(") {
@@ -573,10 +671,7 @@ function parse(source, fileName) {
573
671
  continue;
574
672
  }
575
673
  const saved = i;
576
- const isConst = at("kw", "const");
577
- if (isConst) {
578
- i += 1;
579
- }
674
+ const isConst = skipSpecifiers();
580
675
  const maybeType = parseType();
581
676
  if (maybeType && at("ident")) {
582
677
  const next = peek(1);
@@ -17,6 +17,10 @@ using Enum::Axis;
17
17
 
18
18
  using string = const char*;
19
19
 
20
+ // Variadic join. Cluaupp emits Luau a .. b .. c.
21
+ template <typename... Args>
22
+ string string_concat(Args... args);
23
+
20
24
  struct Instance;
21
25
  struct Player;
22
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cluaupp",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Cluaupp — the definitive merge of C++ and modern Luau. Source-to-source transpiler with first-class Roblox APIs.",
5
5
  "author": "KartzDev",
6
6
  "license": "MIT",
@@ -36,11 +36,12 @@
36
36
  "prepare": "tsc",
37
37
  "test": "node test/out.test.js && node test/types.test.js && node test/modules.test.js && node test/security.test.js && node test/understander.test.js",
38
38
  "generate-api": "node scripts/generate-api.js",
39
- "check-highlight": "node scripts/check-highlight.js",
39
+ "check-highlight": "tsx web/src/check-content.ts",
40
40
  "vendor-libs": "node scripts/vendor-libs.js",
41
41
  "docs": "moonwave dev",
42
42
  "docs:build": "moonwave build",
43
- "site": "node scripts/generate-api.js && node scripts/check-highlight.js",
43
+ "site": "vite build --config web/vite.config.mts && npm run check-highlight",
44
+ "site:dev": "vite --config web/vite.config.mts",
44
45
  "dev": "node scripts/dev.js",
45
46
  "stop": "node scripts/stop.js",
46
47
  "prepublishOnly": "tsc && npm test"
@@ -68,6 +69,7 @@
68
69
  },
69
70
  "homepage": "https://github.com/KartzRbx/Cluaupp#readme",
70
71
  "dependencies": {
72
+ "cluaupp": "^0.2.2",
71
73
  "commander": "^15.0.0",
72
74
  "comment-json": "^5.0.0",
73
75
  "tree-sitter": "0.21.1",
@@ -81,7 +83,16 @@
81
83
  },
82
84
  "devDependencies": {
83
85
  "@types/node": "^22.20.3",
86
+ "@types/react": "^19.3.0",
87
+ "@types/react-dom": "^19.3.0",
88
+ "@vitejs/plugin-react": "^6.0.1",
89
+ "@vitejs/plugin-vue": "^6.0.1",
90
+ "react": "^19.1.1",
91
+ "react-dom": "^19.1.1",
92
+ "react-router-dom": "^7.8.2",
84
93
  "tsx": "^4.23.13",
85
- "typescript": "^7.0.2"
94
+ "typescript": "^7.0.2",
95
+ "vite": "^8.0.0",
96
+ "vue": "^3.5.21"
86
97
  }
87
98
  }
package/src/api.ts CHANGED
@@ -45,9 +45,23 @@ export function luauType(name: string | null | undefined): string | null {
45
45
  if (!name) {
46
46
  return null;
47
47
  }
48
- const cleaned = String(name).replace(/\*+$/, "").replace(/^const\s+/, "").replace(/^Enum::/, "Enum.");
48
+ const cleaned = String(name)
49
+ .replace(/\*+$/, "")
50
+ .replace(/^const\s+/, "")
51
+ .replace(/^Enum::/, "Enum.")
52
+ .replace(/\s+/g, " ")
53
+ .trim();
49
54
  if (cleaned in LUAU_TYPES) {
50
55
  return LUAU_TYPES[cleaned];
51
56
  }
57
+ const generic = cleaned.match(/^(?:std::)?(?:vector|array|span|LuaArray)\s*<\s*([^>]+)\s*>$/);
58
+ if (generic) {
59
+ const inner = luauType(generic[1]) || generic[1];
60
+ return `{${inner}}`;
61
+ }
62
+ const optional = cleaned.match(/^(?:std::)?optional\s*<\s*([^>]+)\s*>$/);
63
+ if (optional) {
64
+ return `${luauType(optional[1]) || optional[1]}?`;
65
+ }
52
66
  return cleaned;
53
67
  }
package/src/emit.ts 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 || []) {
@@ -142,6 +143,38 @@ function emit(ast, options = {}) {
142
143
  return linesOut;
143
144
  };
144
145
 
146
+ const isStringConcatCall = (node) => node && node.type === "call" && !node.object && node.name === "string_concat";
147
+
148
+ const emitStringConcat = (args) => {
149
+ const parts = (args || []).map(emitExpr);
150
+ if (parts.length === 0) {
151
+ return `""`;
152
+ }
153
+ if (parts.length === 1) {
154
+ return parts[0];
155
+ }
156
+ return `(${parts.join(" .. ")})`;
157
+ };
158
+
159
+ const isStringExpr = (node) => {
160
+ if (!node) {
161
+ return false;
162
+ }
163
+ if (node.type === "string") {
164
+ return true;
165
+ }
166
+ if (isStringConcatCall(node)) {
167
+ return true;
168
+ }
169
+ if (node.type === "binary" && node.op === "+" && (isStringExpr(node.left) || isStringExpr(node.right))) {
170
+ return true;
171
+ }
172
+ if (node.type === "member" && (node.name === "Name" || node.name === "Text" || node.name === "DisplayName")) {
173
+ return true;
174
+ }
175
+ return false;
176
+ };
177
+
145
178
  const emitExpr = (node) => {
146
179
  if (!node) {
147
180
  return "nil";
@@ -164,6 +197,28 @@ function emit(ast, options = {}) {
164
197
  }
165
198
  return `{ ${entries.join(", ")} }`;
166
199
  }
200
+ case "cast":
201
+ if (node.valueType === "void") {
202
+ return emitExpr(node.argument);
203
+ }
204
+ return emitExpr(node.argument);
205
+ case "lambda": {
206
+ const params = (node.params || [])
207
+ .map((param) => {
208
+ if (typeof param === "string") {
209
+ return param;
210
+ }
211
+ const typeAnn = luauType(param.valueType);
212
+ return typeAnn ? `${param.name}: ${typeAnn}` : param.name;
213
+ })
214
+ .join(", ");
215
+ const inner = [];
216
+ for (const stmt of node.body || []) {
217
+ inner.push(...emitStmt(stmt, 1));
218
+ }
219
+ const body = inner.length > 0 ? `\n${inner.join("\n")}\n` : "\n";
220
+ return `function(${params})${body}end`;
221
+ }
167
222
  case "unary": {
168
223
  const inner = emitExpr(node.argument);
169
224
  if (node.op === "!") {
@@ -183,6 +238,9 @@ function emit(ast, options = {}) {
183
238
  case "member":
184
239
  return `${emitExpr(node.object)}.${node.name}`;
185
240
  case "call": {
241
+ if (isStringConcatCall(node)) {
242
+ return emitStringConcat(node.args);
243
+ }
186
244
  const args = node.args.map(emitMethodArg).join(", ");
187
245
  if (!node.object) {
188
246
  if (isDatatype(node.name)) {
@@ -194,6 +252,9 @@ function emit(ast, options = {}) {
194
252
  return `${node.name}(${args})`;
195
253
  }
196
254
  const obj = emitExpr(node.object);
255
+ if (classMethods.has(node.name) && !localNames.has(node.name)) {
256
+ return `${obj}:${node.name}(${args})`;
257
+ }
197
258
  if (node.access === "::" && node.object.type === "ident" && node.object.name === "cout") {
198
259
  if (node.name === "endl") {
199
260
  return "print()";
@@ -232,6 +293,9 @@ function emit(ast, options = {}) {
232
293
  return printed.join("; ");
233
294
  }
234
295
  }
296
+ if (node.op === "+" && (isStringExpr(node.left) || isStringExpr(node.right))) {
297
+ return `${emitExpr(node.left)} .. ${emitExpr(node.right)}`;
298
+ }
235
299
  const ops = { "!=": "~=", "&&": "and", "||": "or" };
236
300
  const op = ops[node.op] || node.op;
237
301
  return `${emitExpr(node.left)} ${op} ${emitExpr(node.right)}`;
@@ -292,6 +356,9 @@ function emit(ast, options = {}) {
292
356
  return [`${prefix}${kind} ${node.name}${typed} = ${created.className}.new(${args})`];
293
357
  }
294
358
  const value = node.value ? emitExpr(node.value) : "nil";
359
+ if (!node.value && node.valueType && classOwners.has(node.valueType)) {
360
+ return [`${prefix}${kind} ${node.name}${typed} = ${node.valueType}`];
361
+ }
295
362
  return [`${prefix}${kind} ${node.name}${typed} = ${value}`];
296
363
  };
297
364
 
@@ -305,6 +372,9 @@ function emit(ast, options = {}) {
305
372
  return emitNewDecl(node, indent);
306
373
  case "expr": {
307
374
  const expr = node.expr;
375
+ if (expr && expr.type === "cast" && expr.valueType === "void") {
376
+ return [];
377
+ }
308
378
  if (expr && expr.type === "binary" && expr.op === "<<") {
309
379
  const printed = emitCout(expr);
310
380
  if (printed) {
@@ -456,11 +526,9 @@ function emit(ast, options = {}) {
456
526
  lines.push(" }");
457
527
  lines.push("end");
458
528
  lines.push("");
459
- lines.push(`return ${root}`);
460
- lines.push("");
461
529
  };
462
530
 
463
- if (classModule) {
531
+ if (hasOwnedMethods) {
464
532
  for (const owner of classOwners) {
465
533
  lines.push(`local ${owner} = {}`);
466
534
  lines.push("");
@@ -471,6 +539,16 @@ function emit(ast, options = {}) {
471
539
  for (const root of structRoots) {
472
540
  emitStructConstructor(root);
473
541
  }
542
+ if (structRoots.length === 1) {
543
+ lines.push(`return ${structRoots[0]}`);
544
+ } else {
545
+ lines.push("return {");
546
+ for (const root of structRoots) {
547
+ lines.push(` ${root} = ${root},`);
548
+ }
549
+ lines.push("}");
550
+ }
551
+ lines.push("");
474
552
  return lines.join("\n");
475
553
  }
476
554
 
@@ -66,32 +66,6 @@ function compileCommandEntry(root: string, file: string) {
66
66
  };
67
67
  }
68
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
69
  function copyDir(from: string, to: string): void {
96
70
  fs.mkdirSync(to, { recursive: true });
97
71
  for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
@@ -105,6 +79,58 @@ function copyDir(from: string, to: string): void {
105
79
  }
106
80
  }
107
81
 
82
+ function uniqueStrings(...lists: unknown[]): string[] {
83
+ const seen = new Set<string>();
84
+ const out: string[] = [];
85
+ for (const list of lists) {
86
+ if (!Array.isArray(list)) {
87
+ continue;
88
+ }
89
+ for (const item of list) {
90
+ if (typeof item === "string" && item && !seen.has(item)) {
91
+ seen.add(item);
92
+ out.push(item);
93
+ }
94
+ }
95
+ }
96
+ return out;
97
+ }
98
+
99
+ function stripCppToolsSettings(current: Record<string, unknown>): Record<string, unknown> {
100
+ const next: Record<string, unknown> = {};
101
+ for (const [key, value] of Object.entries(current)) {
102
+ if (key === "C_Cpp" || key.startsWith("C_Cpp.")) {
103
+ continue;
104
+ }
105
+ next[key] = value;
106
+ }
107
+ return next;
108
+ }
109
+
110
+ function readJsonObject(file: string): Record<string, unknown> {
111
+ if (!fs.existsSync(file)) {
112
+ return {};
113
+ }
114
+ try {
115
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
116
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
117
+ return parsed as Record<string, unknown>;
118
+ }
119
+ } catch {
120
+ // ignore corrupt editor json
121
+ }
122
+ return {};
123
+ }
124
+
125
+ function writeJson(file: string, value: Record<string, unknown>): void {
126
+ const json = `${JSON.stringify(value, null, "\t")}\n`;
127
+ fs.mkdirSync(path.dirname(file), { recursive: true });
128
+ if (fs.existsSync(file) && fs.readFileSync(file, "utf8") === json) {
129
+ return;
130
+ }
131
+ fs.writeFileSync(file, json, "utf8");
132
+ }
133
+
108
134
  function clangdEditorArgs(): string[] {
109
135
  return [
110
136
  "--compile-commands-dir=${workspaceFolder}",
@@ -113,17 +139,6 @@ function clangdEditorArgs(): string[] {
113
139
  ];
114
140
  }
115
141
 
116
- export function intelliSenseMode(): string {
117
- const arch = process.arch === "arm64" ? "arm64" : "x64";
118
- if (process.platform === "win32") {
119
- return `windows-clang-${arch}`;
120
- }
121
- if (process.platform === "darwin") {
122
- return `macos-clang-${arch}`;
123
- }
124
- return `linux-clang-${arch}`;
125
- }
126
-
127
142
  export function clangPath(): string {
128
143
  return findClangPlusPlus() || "clang++";
129
144
  }
@@ -173,54 +188,39 @@ Index:
173
188
  }
174
189
 
175
190
  function writeVscode(root: string): void {
176
- const settings = path.join(root, ".vscode", "settings.json");
177
- mergeJson(settings, {
178
- "C_Cpp.intelliSenseEngine": "Disabled",
179
- "C_Cpp.default.cppStandard": "c++20",
180
- "C_Cpp.default.cStandard": "c17",
181
- "C_Cpp.default.compilerPath": clangPath(),
182
- "C_Cpp.default.intelliSenseMode": intelliSenseMode(),
183
- "C_Cpp.default.includePath": ["${workspaceFolder}/include", "${workspaceFolder}/src"],
191
+ const settingsPath = path.join(root, ".vscode", "settings.json");
192
+ const current = readJsonObject(settingsPath);
193
+ const associations = {
194
+ ...((current["files.associations"] && typeof current["files.associations"] === "object" && !Array.isArray(current["files.associations"]))
195
+ ? (current["files.associations"] as Record<string, unknown>)
196
+ : {}),
197
+ "*.hpp": "cpp",
198
+ "*.h": "cpp",
199
+ "*.server.cpp": "cpp",
200
+ "*.client.cpp": "cpp",
201
+ "*.plugin.cpp": "cpp",
202
+ "*.legacy.cpp": "cpp",
203
+ "*.legacy.server.cpp": "cpp",
204
+ "*.legacy.client.cpp": "cpp",
205
+ };
206
+ writeJson(settingsPath, {
207
+ ...stripCppToolsSettings(current),
184
208
  "clangd.enable": true,
185
209
  "clangd.arguments": clangdEditorArgs(),
186
- "files.associations": {
187
- "*.hpp": "cpp",
188
- "*.h": "cpp",
189
- "*.server.cpp": "cpp",
190
- "*.client.cpp": "cpp",
191
- "*.plugin.cpp": "cpp",
192
- "*.legacy.cpp": "cpp",
193
- "*.legacy.server.cpp": "cpp",
194
- "*.legacy.client.cpp": "cpp",
195
- },
210
+ "files.associations": associations,
196
211
  });
197
- mergeJson(path.join(root, ".vscode", "extensions.json"), {
198
- recommendations: ["llvm-vs-code-extensions.vscode-clangd"],
212
+
213
+ const extensionsPath = path.join(root, ".vscode", "extensions.json");
214
+ const extensions = readJsonObject(extensionsPath);
215
+ writeJson(extensionsPath, {
216
+ ...extensions,
217
+ recommendations: uniqueStrings(extensions.recommendations, ["llvm-vs-code-extensions.vscode-clangd"]),
218
+ unwantedRecommendations: uniqueStrings(extensions.unwantedRecommendations, ["ms-vscode.cpptools"]),
199
219
  });
220
+
200
221
  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");
222
+ if (fs.existsSync(props)) {
223
+ fs.unlinkSync(props);
224
224
  }
225
225
  }
226
226
 
package/src/lex.ts CHANGED
@@ -32,6 +32,9 @@ const KEYWORDS = new Set([
32
32
  "case",
33
33
  "default",
34
34
  "break",
35
+ "static",
36
+ "constexpr",
37
+ "inline",
35
38
  ]);
36
39
 
37
40
  export function tokenize(source: string): Token[] {