cluaupp 1.5.2 → 2.0.0-beta.1

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.
Files changed (195) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +27 -40
  3. package/api/generated/bench-latest.json +43 -21
  4. package/api/generated/lsp-index.json +1 -1
  5. package/api/manifests/roblox-target.manifest.json +5 -5
  6. package/api/normalized/roblox-target.profile.json +8729 -267
  7. package/api/roblox/luau-builtins.d.luau +185 -0
  8. package/api/roblox/surface.json +1 -0
  9. package/api/roblox-api.lock.json +11 -2
  10. package/bin/cluaupp.js +38 -0
  11. package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
  12. package/examples/README.md +9 -9
  13. package/examples/game/cluaupp.config.json +2 -2
  14. package/examples/game/default.project.json +4 -15
  15. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
  16. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
  17. package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
  18. package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
  19. package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
  20. package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
  21. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
  22. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
  23. package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  24. package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
  25. package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
  26. package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  27. package/generated/api/build-profile.js +1 -1
  28. package/generated/api/loader.js +2 -2
  29. package/generated/api/surface/build.js +328 -0
  30. package/generated/api/surface/luau-defs.js +595 -0
  31. package/generated/api/surface/model.js +8 -0
  32. package/generated/clpp/contract.js +6 -3
  33. package/generated/clpp/handshake.js +36 -0
  34. package/generated/clpp/modules.js +20 -9
  35. package/generated/clpp/runner.js +62 -31
  36. package/generated/compile.js +18 -1
  37. package/generated/compiler/ast.js +3 -0
  38. package/generated/compiler/checker/calls.js +999 -0
  39. package/generated/compiler/checker/checker.js +1215 -0
  40. package/generated/compiler/checker/exprs.js +791 -0
  41. package/generated/compiler/checker/flow.js +168 -0
  42. package/generated/compiler/checker/members.js +763 -0
  43. package/generated/compiler/checker/statements.js +792 -0
  44. package/generated/compiler/checker/symbols.js +90 -0
  45. package/generated/compiler/checker/typeops.js +348 -0
  46. package/generated/compiler/checker/yields.js +115 -0
  47. package/generated/compiler/diagnostics.js +138 -0
  48. package/generated/compiler/emit/emitter.js +2356 -0
  49. package/generated/compiler/emit/luau-types.js +137 -0
  50. package/generated/compiler/emit/names.js +130 -0
  51. package/generated/compiler/emit/runtime.js +432 -0
  52. package/generated/compiler/lexer.js +403 -0
  53. package/generated/compiler/parser.js +1881 -0
  54. package/generated/compiler/project.js +332 -0
  55. package/generated/compiler/roblox/api.js +388 -0
  56. package/generated/compiler/roblox/context.js +100 -0
  57. package/generated/compiler/source.js +59 -0
  58. package/generated/compiler/stdlib.js +232 -0
  59. package/generated/compiler/types.js +250 -0
  60. package/generated/target/build-cache.js +4 -4
  61. package/generated/target/gen-rojo-tree.js +15 -0
  62. package/generated/target/source-map.js +61 -8
  63. package/generated/target/studio-bridge.js +1 -0
  64. package/generated/utils/project.js +83 -25
  65. package/native/bin/.gitkeep +1 -0
  66. package/package.json +17 -6
  67. package/plugins/CluauppNav/Plugin.luau +28 -4
  68. package/rust/cluaupp/Cargo.lock +795 -0
  69. package/rust/cluaupp/Cargo.toml +17 -0
  70. package/rust/cluaupp/src/main.rs +717 -0
  71. package/scripts/postinstall-native.js +56 -0
  72. package/skills/README.md +3 -3
  73. package/skills/clpp-hive-flare/SKILL.md +30 -26
  74. package/skills/clpp-language/SKILL.md +67 -74
  75. package/skills/clpp-language/compiler.md +24 -36
  76. package/skills/clpp-style/SKILL.md +31 -44
  77. package/src/api/build-profile.ts +1 -1
  78. package/src/api/loader.ts +2 -2
  79. package/src/api/surface/build.ts +374 -0
  80. package/src/api/surface/luau-defs.ts +635 -0
  81. package/src/api/surface/model.ts +96 -0
  82. package/src/clpp/contract.ts +20 -3
  83. package/src/clpp/handshake.ts +25 -0
  84. package/src/clpp/modules.ts +17 -10
  85. package/src/clpp/runner.ts +52 -34
  86. package/src/compile.ts +20 -2
  87. package/src/compiler/ast.ts +336 -0
  88. package/src/compiler/checker/calls.ts +1025 -0
  89. package/src/compiler/checker/checker.ts +1352 -0
  90. package/src/compiler/checker/exprs.ts +805 -0
  91. package/src/compiler/checker/flow.ts +174 -0
  92. package/src/compiler/checker/members.ts +793 -0
  93. package/src/compiler/checker/statements.ts +802 -0
  94. package/src/compiler/checker/symbols.ts +219 -0
  95. package/src/compiler/checker/typeops.ts +355 -0
  96. package/src/compiler/checker/yields.ts +120 -0
  97. package/src/compiler/diagnostics.ts +163 -0
  98. package/src/compiler/emit/emitter.ts +2486 -0
  99. package/src/compiler/emit/luau-types.ts +143 -0
  100. package/src/compiler/emit/names.ts +130 -0
  101. package/src/compiler/emit/runtime.ts +438 -0
  102. package/src/compiler/lexer.ts +444 -0
  103. package/src/compiler/parser.ts +1943 -0
  104. package/src/compiler/project.ts +354 -0
  105. package/src/compiler/roblox/api.ts +408 -0
  106. package/src/compiler/roblox/context.ts +108 -0
  107. package/src/compiler/source.ts +67 -0
  108. package/src/compiler/stdlib.ts +256 -0
  109. package/src/compiler/types.ts +313 -0
  110. package/src/target/build-cache.ts +5 -8
  111. package/src/target/gen-rojo-tree.ts +15 -0
  112. package/src/target/source-map.ts +65 -27
  113. package/src/target/studio-bridge.ts +3 -0
  114. package/src/types.ts +3 -0
  115. package/src/utils/project.ts +79 -31
  116. package/templates/README.md +7 -0
  117. package/templates/{game → game-modern}/cluaupp.config.json +2 -2
  118. package/templates/{game → game-modern}/package.json +1 -2
  119. package/templates/game-modern/rokit.toml +2 -0
  120. package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  121. package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
  122. package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
  123. package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  124. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
  125. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
  126. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
  127. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
  128. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
  129. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
  130. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
  131. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
  132. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
  133. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
  134. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
  135. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
  136. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
  137. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
  138. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
  139. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
  140. package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
  141. package/examples/game/src/Client/Main.client.clpp +0 -4
  142. package/examples/game/src/Declarations/Net.flare +0 -5
  143. package/examples/game/src/Include/GameTypes.clh +0 -40
  144. package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
  145. package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
  146. package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  147. package/examples/game/src/Server/Main.server.clpp +0 -4
  148. package/templates/game/.clangd +0 -25
  149. package/templates/game/.vscode/extensions.json +0 -8
  150. package/templates/game/.vscode/settings.json +0 -17
  151. package/templates/game/Packages/.gitkeep +0 -0
  152. package/templates/game/compile_flags.txt +0 -6
  153. package/templates/game/default.project.json +0 -41
  154. package/templates/game/include/clpp/datatypes.clh +0 -575
  155. package/templates/game/include/clpp/generated/enums.clh +0 -4920
  156. package/templates/game/include/clpp/generated/instances.clh +0 -87182
  157. package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
  158. package/templates/game/include/clpp/libs/axiom.clh +0 -154
  159. package/templates/game/include/clpp/libs/bloom.clh +0 -58
  160. package/templates/game/include/clpp/libs/coil.clh +0 -37
  161. package/templates/game/include/clpp/libs/crest.clh +0 -22
  162. package/templates/game/include/clpp/libs/echo.clh +0 -52
  163. package/templates/game/include/clpp/libs/ember.clh +0 -17
  164. package/templates/game/include/clpp/libs/flare.clh +0 -65
  165. package/templates/game/include/clpp/libs/gleam.clh +0 -111
  166. package/templates/game/include/clpp/libs/guide.clh +0 -42
  167. package/templates/game/include/clpp/libs/helm.clh +0 -31
  168. package/templates/game/include/clpp/libs/hive.clh +0 -20
  169. package/templates/game/include/clpp/libs/keep.clh +0 -158
  170. package/templates/game/include/clpp/libs/lens.clh +0 -24
  171. package/templates/game/include/clpp/libs/mint.clh +0 -28
  172. package/templates/game/include/clpp/libs/net.clh +0 -31
  173. package/templates/game/include/clpp/libs/occlude.clh +0 -15
  174. package/templates/game/include/clpp/libs/pin.clh +0 -28
  175. package/templates/game/include/clpp/libs/promise.clh +0 -24
  176. package/templates/game/include/clpp/libs/roster.clh +0 -123
  177. package/templates/game/include/clpp/libs/shift.clh +0 -22
  178. package/templates/game/include/clpp/libs/spark.clh +0 -28
  179. package/templates/game/include/clpp/libs/stage.clh +0 -23
  180. package/templates/game/include/clpp/libs/sweep.clh +0 -23
  181. package/templates/game/include/clpp/libs/trace.clh +0 -18
  182. package/templates/game/include/clpp/libs/ward.clh +0 -19
  183. package/templates/game/include/clpp/libs.clh +0 -31
  184. package/templates/game/include/clpp/roblox.clh +0 -41
  185. package/templates/game/rokit.toml +0 -5
  186. package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
  187. package/templates/game/src/Client/Main.client.clpp +0 -4
  188. package/templates/game/src/Declarations/Net.flare +0 -5
  189. package/templates/game/src/Include/GameTypes.clh +0 -40
  190. package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
  191. package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
  192. package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  193. package/templates/game/src/Server/Main.server.clpp +0 -4
  194. package/templates/game/stylua.toml +0 -9
  195. package/templates/game/wally.toml +0 -12
@@ -0,0 +1,635 @@
1
+ /**
2
+ * Parser for Luau definition files (`*.d.luau`, the luau-lsp format):
3
+ * `declare extern type X [extends Y] with … end`, `declare name: Type`, `declare function f(…): R`,
4
+ * `type Alias = Type`, `export type Alias<T> = Type`, `@deprecated` / `@[deprecated {use = "…"}]`.
5
+ */
6
+
7
+ import type { LFn, LParam, LType } from "./model.js";
8
+
9
+ interface Tok {
10
+ t: "name" | "string" | "number" | "op" | "eof";
11
+ v: string;
12
+ line: number;
13
+ }
14
+
15
+ const OPS = ["...", "->", "::", "@[", "(", ")", "{", "}", "[", "]", "<", ">", ",", ":", ";", "?", "|", "&", "=", ".", "@"];
16
+
17
+ function lex(text: string): Tok[] {
18
+ const out: Tok[] = [];
19
+ let i = 0;
20
+ let line = 1;
21
+ while (i < text.length) {
22
+ const c = text[i];
23
+ if (c === "\n") {
24
+ line++;
25
+ i++;
26
+ continue;
27
+ }
28
+ if (c === " " || c === "\t" || c === "\r") {
29
+ i++;
30
+ continue;
31
+ }
32
+ if (c === "-" && text[i + 1] === "-") {
33
+ if (text.startsWith("--[[", i) || text.startsWith("--[=[", i)) {
34
+ const close = text.indexOf("]]", i);
35
+ const chunk = text.slice(i, close < 0 ? text.length : close + 2);
36
+ line += chunk.split("\n").length - 1;
37
+ i = close < 0 ? text.length : close + 2;
38
+ continue;
39
+ }
40
+ while (i < text.length && text[i] !== "\n") {
41
+ i++;
42
+ }
43
+ continue;
44
+ }
45
+ if (/[A-Za-z_]/.test(c)) {
46
+ let j = i + 1;
47
+ while (j < text.length && /[A-Za-z0-9_]/.test(text[j])) {
48
+ j++;
49
+ }
50
+ out.push({ t: "name", v: text.slice(i, j), line });
51
+ i = j;
52
+ continue;
53
+ }
54
+ if (/[0-9]/.test(c)) {
55
+ let j = i + 1;
56
+ while (j < text.length && /[0-9.]/.test(text[j])) {
57
+ j++;
58
+ }
59
+ out.push({ t: "number", v: text.slice(i, j), line });
60
+ i = j;
61
+ continue;
62
+ }
63
+ if (c === '"' || c === "'") {
64
+ let j = i + 1;
65
+ while (j < text.length && text[j] !== c) {
66
+ if (text[j] === "\\") {
67
+ j++;
68
+ }
69
+ j++;
70
+ }
71
+ out.push({ t: "string", v: text.slice(i + 1, j), line });
72
+ i = j + 1;
73
+ continue;
74
+ }
75
+ const op = OPS.find((o) => text.startsWith(o, i));
76
+ if (op) {
77
+ out.push({ t: "op", v: op, line });
78
+ i += op.length;
79
+ continue;
80
+ }
81
+ i++;
82
+ }
83
+ out.push({ t: "eof", v: "", line });
84
+ return out;
85
+ }
86
+
87
+ export interface ExternMember {
88
+ name: string;
89
+ type?: LType;
90
+ fn?: LFn;
91
+ deprecated?: string | true;
92
+ }
93
+
94
+ export interface ExternType {
95
+ name: string;
96
+ extends: string | null;
97
+ members: ExternMember[];
98
+ line: number;
99
+ }
100
+
101
+ export interface DefinitionFile {
102
+ metadata: Record<string, unknown>;
103
+ externs: ExternType[];
104
+ /** `declare name: Type` and `declare function name(…)` */
105
+ declares: Array<{ name: string; type: LType; deprecated?: string | true }>;
106
+ aliases: Record<string, LType>;
107
+ aliasParams: Record<string, string[]>;
108
+ }
109
+
110
+ class DefParser {
111
+ private i = 0;
112
+ /** Defaults of the generic parameters read last (`<T = BasePart>`). */
113
+ lastDefaults: Record<string, LType> = {};
114
+
115
+ constructor(private readonly toks: Tok[]) {}
116
+
117
+ private peek(n = 0): Tok {
118
+ return this.toks[Math.min(this.i + n, this.toks.length - 1)];
119
+ }
120
+
121
+ private next(): Tok {
122
+ return this.toks[this.i++] ?? this.toks[this.toks.length - 1];
123
+ }
124
+
125
+ private is(v: string, n = 0): boolean {
126
+ const t = this.peek(n);
127
+ return (t.t === "op" || t.t === "name") && t.v === v;
128
+ }
129
+
130
+ private eat(v: string): boolean {
131
+ if (this.is(v)) {
132
+ this.i++;
133
+ return true;
134
+ }
135
+ return false;
136
+ }
137
+
138
+ private expect(v: string): void {
139
+ if (!this.eat(v)) {
140
+ const t = this.peek();
141
+ throw new Error(`definitions: expected '${v}' at line ${t.line}, found '${t.v}'`);
142
+ }
143
+ }
144
+
145
+ private name(): string {
146
+ const t = this.next();
147
+ if (t.t !== "name") {
148
+ throw new Error(`definitions: expected name at line ${t.line}, found '${t.v}'`);
149
+ }
150
+ return t.v;
151
+ }
152
+
153
+ file(): DefinitionFile {
154
+ const out: DefinitionFile = { metadata: {}, externs: [], declares: [], aliases: {}, aliasParams: {} };
155
+ while (this.peek().t !== "eof") {
156
+ const deprecated = this.attributes();
157
+ if (this.is("declare")) {
158
+ this.next();
159
+ if (this.is("extern")) {
160
+ this.next();
161
+ this.expect("type");
162
+ out.externs.push(this.externType());
163
+ continue;
164
+ }
165
+ if (this.is("class")) {
166
+ this.next();
167
+ out.externs.push(this.externType(true));
168
+ continue;
169
+ }
170
+ if (this.is("function")) {
171
+ this.next();
172
+ const name = this.name();
173
+ const fn = this.funcSignature(false);
174
+ out.declares.push({ name, type: fn, ...(deprecated ? { deprecated } : {}) });
175
+ continue;
176
+ }
177
+ const name = this.name();
178
+ this.expect(":");
179
+ const type = this.type();
180
+ out.declares.push({ name, type, ...(deprecated ? { deprecated } : {}) });
181
+ continue;
182
+ }
183
+ if (this.is("export")) {
184
+ this.next();
185
+ }
186
+ if (this.is("type")) {
187
+ this.next();
188
+ const name = this.name();
189
+ this.lastDefaults = {};
190
+ const params = this.genericParamNames();
191
+ const defaults = this.lastDefaults;
192
+ this.expect("=");
193
+ out.aliases[name] = substituteNames(this.type(), defaults);
194
+ if (params.length) {
195
+ out.aliasParams[name] = params;
196
+ }
197
+ continue;
198
+ }
199
+ this.next();
200
+ }
201
+ return out;
202
+ }
203
+
204
+ /** `@deprecated`, `@[deprecated {use = "X"}]`, `@checked` … — returns the deprecation, if any. */
205
+ private attributes(): string | true | undefined {
206
+ let deprecated: string | true | undefined;
207
+ for (;;) {
208
+ if (this.is("@[")) {
209
+ this.next();
210
+ let depth = 1;
211
+ let sawDeprecated = false;
212
+ while (depth > 0 && this.peek().t !== "eof") {
213
+ const t = this.next();
214
+ if (t.t === "op" && (t.v === "[" || t.v === "@[")) {
215
+ depth++;
216
+ } else if (t.t === "op" && t.v === "]") {
217
+ depth--;
218
+ } else if (t.t === "name" && t.v === "deprecated") {
219
+ sawDeprecated = true;
220
+ deprecated = true;
221
+ } else if (sawDeprecated && t.t === "string") {
222
+ deprecated = t.v;
223
+ }
224
+ }
225
+ continue;
226
+ }
227
+ if (this.is("@")) {
228
+ this.next();
229
+ const word = this.name();
230
+ if (word === "deprecated") {
231
+ deprecated = true;
232
+ }
233
+ continue;
234
+ }
235
+ return deprecated;
236
+ }
237
+ }
238
+
239
+ private genericParamNames(): string[] {
240
+ const names: string[] = [];
241
+ if (this.eat("<")) {
242
+ while (!this.is(">") && this.peek().t !== "eof") {
243
+ const t = this.next();
244
+ if (t.t === "name") {
245
+ names.push(t.v);
246
+ }
247
+ if (this.eat("=")) {
248
+ const def = this.type();
249
+ if (t.t === "name") {
250
+ this.lastDefaults[t.v] = def;
251
+ }
252
+ }
253
+ this.eat("...");
254
+ this.eat(",");
255
+ }
256
+ this.expect(">");
257
+ }
258
+ return names;
259
+ }
260
+
261
+ private externType(isClass = false): ExternType {
262
+ const line = this.peek().line;
263
+ const name = this.name();
264
+ let ext: string | null = null;
265
+ if (this.eat("extends")) {
266
+ ext = this.name();
267
+ }
268
+ if (!isClass) {
269
+ this.expect("with");
270
+ }
271
+ const members: ExternMember[] = [];
272
+ while (!this.is("end") && this.peek().t !== "eof") {
273
+ const deprecated = this.attributes();
274
+ if (this.is("function")) {
275
+ this.next();
276
+ const mname = this.name();
277
+ const fn = this.funcSignature(true);
278
+ members.push({ name: mname, fn, ...(deprecated ? { deprecated } : {}) });
279
+ continue;
280
+ }
281
+ if (this.is("[")) {
282
+ // indexer `[string]: any`
283
+ this.next();
284
+ this.type();
285
+ this.expect("]");
286
+ this.expect(":");
287
+ this.type();
288
+ continue;
289
+ }
290
+ if (this.peek().t === "name" && (this.is(":", 1))) {
291
+ const mname = this.name();
292
+ this.expect(":");
293
+ const type = this.type();
294
+ members.push({ name: mname, type, ...(deprecated ? { deprecated } : {}) });
295
+ continue;
296
+ }
297
+ if (this.is("read") || this.is("write")) {
298
+ this.next();
299
+ continue;
300
+ }
301
+ this.next();
302
+ }
303
+ this.expect("end");
304
+ return { name, extends: ext, members, line };
305
+ }
306
+
307
+ /** `(self, a: T, b: U?, ...: V): R` after the function name. */
308
+ private funcSignature(method: boolean): LFn {
309
+ const generics = this.genericParamNames();
310
+ this.expect("(");
311
+ const params: LParam[] = [];
312
+ let variadic: LType | undefined;
313
+ let first = true;
314
+ while (!this.is(")") && this.peek().t !== "eof") {
315
+ if (this.eat("...")) {
316
+ variadic = this.eat(":") ? this.type() : { k: "name", name: "any" };
317
+ this.eat(",");
318
+ continue;
319
+ }
320
+ const pname = this.name();
321
+ if (first && method && pname === "self") {
322
+ if (this.eat(":")) {
323
+ this.type();
324
+ }
325
+ first = false;
326
+ this.eat(",");
327
+ continue;
328
+ }
329
+ first = false;
330
+ let t: LType = { k: "name", name: "any" };
331
+ if (this.eat(":")) {
332
+ t = this.type();
333
+ }
334
+ params.push({ name: pname, t });
335
+ this.eat(",");
336
+ }
337
+ this.expect(")");
338
+ const fn: LFn = { k: "fn", params, returns: [] };
339
+ if (variadic) {
340
+ fn.variadic = variadic;
341
+ }
342
+ if (generics.length) {
343
+ fn.generics = generics;
344
+ }
345
+ if (this.eat(":")) {
346
+ this.returnTypes(fn);
347
+ }
348
+ return fn;
349
+ }
350
+
351
+ private returnTypes(fn: LFn): void {
352
+ if (this.is("(")) {
353
+ // tuple `(A, B)` or a parenthesized function type `((a) -> b)`
354
+ const mark = this.i;
355
+ this.next();
356
+ if (this.eat(")")) {
357
+ if (this.is("->")) {
358
+ this.i = mark;
359
+ fn.returns = [this.type()];
360
+ }
361
+ return;
362
+ }
363
+ const list: LType[] = [];
364
+ let tail: LType | undefined;
365
+ while (!this.is(")") && this.peek().t !== "eof") {
366
+ if (this.eat("...")) {
367
+ tail = this.type();
368
+ } else {
369
+ list.push(this.type());
370
+ }
371
+ this.eat(",");
372
+ }
373
+ this.expect(")");
374
+ if (this.is("->") || this.is("|") || this.is("?") || this.is("&")) {
375
+ this.i = mark;
376
+ fn.returns = [this.type()];
377
+ return;
378
+ }
379
+ fn.returns = list;
380
+ if (tail) {
381
+ fn.returnsVariadic = tail;
382
+ }
383
+ return;
384
+ }
385
+ if (this.eat("...")) {
386
+ fn.returnsVariadic = this.type();
387
+ return;
388
+ }
389
+ const t = this.type();
390
+ if (t.k === "name" && (t.name === "nil" || t.name === "()")) {
391
+ return;
392
+ }
393
+ fn.returns = [t];
394
+ }
395
+
396
+ type(): LType {
397
+ this.eat("|");
398
+ const first = this.intersection();
399
+ if (!this.is("|")) {
400
+ return first;
401
+ }
402
+ const ts = [first];
403
+ while (this.eat("|")) {
404
+ ts.push(this.intersection());
405
+ }
406
+ return { k: "union", ts };
407
+ }
408
+
409
+ private intersection(): LType {
410
+ const first = this.postfix();
411
+ if (!this.is("&")) {
412
+ return first;
413
+ }
414
+ const ts = [first];
415
+ while (this.eat("&")) {
416
+ ts.push(this.postfix());
417
+ }
418
+ return { k: "inter", ts };
419
+ }
420
+
421
+ private postfix(): LType {
422
+ let t = this.primary();
423
+ while (this.eat("?")) {
424
+ t = { k: "opt", t };
425
+ }
426
+ return t;
427
+ }
428
+
429
+ private primary(): LType {
430
+ const tok = this.peek();
431
+ if (tok.t === "string") {
432
+ this.next();
433
+ return { k: "lit", value: tok.v };
434
+ }
435
+ if (this.is("typeof")) {
436
+ this.next();
437
+ this.expect("(");
438
+ let depth = 1;
439
+ while (depth > 0 && this.peek().t !== "eof") {
440
+ const t = this.next();
441
+ if (t.v === "(") {
442
+ depth++;
443
+ } else if (t.v === ")") {
444
+ depth--;
445
+ }
446
+ }
447
+ return { k: "name", name: "any" };
448
+ }
449
+ if (this.is("{")) {
450
+ return this.table();
451
+ }
452
+ if (this.is("<")) {
453
+ // generic function type `<T>(a: T) -> T`
454
+ const generics = this.genericParamNames();
455
+ const fn = this.parenType() as LFn;
456
+ if (fn.k === "fn" && generics.length) {
457
+ fn.generics = generics;
458
+ }
459
+ return fn;
460
+ }
461
+ if (this.is("(")) {
462
+ return this.parenType();
463
+ }
464
+ if (this.is("...")) {
465
+ this.next();
466
+ const inner = this.type();
467
+ return inner.k === "name" ? { k: "pack", name: inner.name } : inner;
468
+ }
469
+ if (tok.t === "name") {
470
+ this.next();
471
+ let name = tok.v;
472
+ while (this.is(".") && this.peek(1).t === "name") {
473
+ this.next();
474
+ name += `.${this.next().v}`;
475
+ }
476
+ if (name === "true" || name === "false") {
477
+ return { k: "lit", value: name === "true" };
478
+ }
479
+ if (this.eat("...")) {
480
+ return { k: "pack", name };
481
+ }
482
+ if (this.is("<")) {
483
+ this.next();
484
+ const args: LType[] = [];
485
+ while (!this.is(">") && this.peek().t !== "eof") {
486
+ if (this.is("(") && this.is(")", 1)) {
487
+ this.next();
488
+ this.next();
489
+ args.push({ k: "name", name: "()" });
490
+ } else {
491
+ const arg = this.type();
492
+ if (arg.k === "name" && arg.name === "__pack") {
493
+ args.push(...(arg.args || []));
494
+ } else {
495
+ args.push(arg);
496
+ }
497
+ }
498
+ this.eat(",");
499
+ }
500
+ this.expect(">");
501
+ return { k: "name", name, args };
502
+ }
503
+ return { k: "name", name };
504
+ }
505
+ throw new Error(`definitions: unexpected '${tok.v}' at line ${tok.line}`);
506
+ }
507
+
508
+ /** `( … )` — a function type when followed by `->`, else grouping. */
509
+ private parenType(): LType {
510
+ this.expect("(");
511
+ const params: LParam[] = [];
512
+ let variadic: LType | undefined;
513
+ while (!this.is(")") && this.peek().t !== "eof") {
514
+ if (this.eat("...")) {
515
+ variadic = this.is(",") || this.is(")") ? { k: "name", name: "any" } : this.type();
516
+ this.eat(",");
517
+ continue;
518
+ }
519
+ let pname: string | undefined;
520
+ if (this.peek().t === "name" && this.is(":", 1)) {
521
+ pname = this.next().v;
522
+ this.next();
523
+ }
524
+ const t = this.type();
525
+ if (t.k === "pack") {
526
+ variadic = { k: "name", name: "any" };
527
+ this.eat(",");
528
+ continue;
529
+ }
530
+ params.push(pname ? { name: pname, t } : { t });
531
+ this.eat(",");
532
+ }
533
+ this.expect(")");
534
+ if (this.eat("->")) {
535
+ const fn: LFn = { k: "fn", params, returns: [] };
536
+ if (variadic) {
537
+ fn.variadic = variadic;
538
+ }
539
+ this.returnTypes(fn);
540
+ return fn;
541
+ }
542
+ if (params.length === 1 && !variadic) {
543
+ return params[0].t;
544
+ }
545
+ if (params.length === 0) {
546
+ return { k: "name", name: "()" };
547
+ }
548
+ // a type pack `(A, B)`, as in RBXScriptSignal<(InputObject, boolean)>
549
+ return { k: "name", name: "__pack", args: params.map((p) => p.t) };
550
+ }
551
+
552
+ private table(): LType {
553
+ this.expect("{");
554
+ if (this.eat("}")) {
555
+ return { k: "table", fields: {} };
556
+ }
557
+ // `{ T }` array
558
+ const mark = this.i;
559
+ const isField = (this.peek().t === "name" && this.is(":", 1)) || this.is("[") || ((this.is("read") || this.is("write")) && this.peek(1).t === "name");
560
+ if (!isField) {
561
+ const elem = this.type();
562
+ if (this.eat("}")) {
563
+ return { k: "array", t: elem };
564
+ }
565
+ this.i = mark;
566
+ }
567
+ const fields: Record<string, LType> = {};
568
+ let indexer: [LType, LType] | undefined;
569
+ while (!this.is("}") && this.peek().t !== "eof") {
570
+ if ((this.is("read") || this.is("write")) && this.peek(1).t === "name" && this.is(":", 2)) {
571
+ this.next();
572
+ }
573
+ if (this.eat("[")) {
574
+ const key = this.type();
575
+ this.expect("]");
576
+ this.expect(":");
577
+ indexer = [key, this.type()];
578
+ } else {
579
+ const fname = this.name();
580
+ this.expect(":");
581
+ fields[fname] = this.type();
582
+ }
583
+ if (!this.eat(",")) {
584
+ this.eat(";");
585
+ }
586
+ }
587
+ this.expect("}");
588
+ if (indexer && Object.keys(fields).length === 0) {
589
+ return { k: "map", key: indexer[0], value: indexer[1] };
590
+ }
591
+ return { k: "table", fields };
592
+ }
593
+ }
594
+
595
+ /** Replace generic parameter names by their defaults (`RaycastResult<T = BasePart>` → BasePart). */
596
+ function substituteNames(t: LType, map: Record<string, LType>): LType {
597
+ if (Object.keys(map).length === 0) {
598
+ return t;
599
+ }
600
+ const sub = (x: LType): LType => substituteNames(x, map);
601
+ switch (t.k) {
602
+ case "name":
603
+ return map[t.name] && !t.args ? map[t.name] : t.args ? { ...t, args: t.args.map(sub) } : t;
604
+ case "opt":
605
+ return { k: "opt", t: sub(t.t) };
606
+ case "union":
607
+ case "inter":
608
+ return { k: t.k, ts: t.ts.map(sub) };
609
+ case "array":
610
+ return { k: "array", t: sub(t.t) };
611
+ case "map":
612
+ return { k: "map", key: sub(t.key), value: sub(t.value) };
613
+ case "table":
614
+ return { k: "table", fields: Object.fromEntries(Object.entries(t.fields).map(([k, v]) => [k, sub(v)])) };
615
+ case "fn":
616
+ return { ...t, params: t.params.map((p) => ({ ...p, t: sub(p.t) })), returns: t.returns.map(sub) };
617
+ default:
618
+ return t;
619
+ }
620
+ }
621
+
622
+ export function parseDefinitions(text: string): DefinitionFile {
623
+ let metadata: Record<string, unknown> = {};
624
+ const first = text.split("\n", 1)[0];
625
+ if (first.startsWith("--#METADATA#")) {
626
+ try {
627
+ metadata = JSON.parse(first.slice("--#METADATA#".length));
628
+ } catch {
629
+ metadata = {};
630
+ }
631
+ }
632
+ const file = new DefParser(lex(text)).file();
633
+ file.metadata = metadata;
634
+ return file;
635
+ }