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,444 @@
1
+ /**
2
+ * CL++ 0.9 lexer (mirrors KartzRbx/CLPP `src/core/lexer/lexer.cpp`).
3
+ * `<<` line comments, `<<[ … ]>>` blocks, `<<[[]] … >>` doc comments, `<<!strict` directives,
4
+ * `.:` concat, `~>` connect, `::` scope, templates with `${}`, numbers with `_`, hex and binary.
5
+ */
6
+
7
+ import type { DiagnosticBag } from "./diagnostics.js";
8
+ import type { Span } from "./source.js";
9
+
10
+ export type TokenKind =
11
+ | "ident"
12
+ | "keyword"
13
+ | "int"
14
+ | "float"
15
+ | "string"
16
+ | "template"
17
+ | "op"
18
+ | "directive"
19
+ | "doc"
20
+ | "eof";
21
+
22
+ export interface TemplatePart {
23
+ /** Decoded literal text, or the source range of an `${ … }` hole. */
24
+ text?: string;
25
+ hole?: Span;
26
+ }
27
+
28
+ export interface Token {
29
+ kind: TokenKind;
30
+ /** Lexeme as written (decoded value lives in `value`). */
31
+ text: string;
32
+ start: number;
33
+ end: number;
34
+ /** Decoded string value, numeric value, or directive word. */
35
+ value?: string | number;
36
+ parts?: TemplatePart[];
37
+ /** First token on its line (used for statement-start recovery heuristics). */
38
+ newlineBefore: boolean;
39
+ }
40
+
41
+ export const KEYWORDS = new Set([
42
+ "Vector2", "Vector3", "Vector4", "abstract", "and", "as", "async", "atomic", "auto", "await", "bool", "break",
43
+ "buffer", "case", "catch", "const", "constexpr", "continue", "cout", "decltype", "default", "double", "else",
44
+ "endl", "enum", "extern", "false", "final", "float", "for", "from", "func", "guard", "if", "import", "in", "int",
45
+ "join", "let", "link", "match", "move", "mut", "namespace", "new", "not", "null", "observable", "operator", "or",
46
+ "override", "parallel", "pcall", "post", "private", "public", "report", "return", "shl", "shr", "signal", "spawn",
47
+ "static", "string", "struct", "switch", "task", "thread", "true", "try", "type", "using", "variant", "void",
48
+ "warn", "where", "while",
49
+ ]);
50
+
51
+ /** Keywords that also name a value in Roblox code (`task.wait`, `string.format`, `buffer.create`, `type(x)`). */
52
+ export const VALUE_KEYWORDS = new Set(["task", "string", "buffer", "type", "Vector2", "Vector3", "warn", "thread"]);
53
+
54
+ const OPERATORS = [
55
+ "...", "<=", ">=", "==", "!=", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "->", "=>", "~>", "::", ".:",
56
+ "..", "+", "-", "*", "/", "%", "=", "<", ">", "!", "&", "|", "^", "~", "?", ":", ".", ",", ";", "(", ")", "{",
57
+ "}", "[", "]", "@", "#",
58
+ ];
59
+
60
+ function isIdentStart(c: number): boolean {
61
+ return (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95 || c >= 0x80;
62
+ }
63
+
64
+ function isIdentPart(c: number): boolean {
65
+ return isIdentStart(c) || (c >= 48 && c <= 57);
66
+ }
67
+
68
+ function isDigit(c: number): boolean {
69
+ return c >= 48 && c <= 57;
70
+ }
71
+
72
+ const ESCAPES: Record<string, string> = {
73
+ n: "\n",
74
+ t: "\t",
75
+ r: "\r",
76
+ "0": "\0",
77
+ "\\": "\\",
78
+ '"': '"',
79
+ "'": "'",
80
+ "`": "`",
81
+ $: "$",
82
+ "{": "{",
83
+ "}": "}",
84
+ };
85
+
86
+ export class Lexer {
87
+ private pos = 0;
88
+ private readonly tokens: Token[] = [];
89
+ private newline = true;
90
+ private readonly text: string;
91
+
92
+ constructor(
93
+ text: string,
94
+ private readonly diagnostics: DiagnosticBag,
95
+ private readonly base = 0,
96
+ private readonly end = text.length,
97
+ ) {
98
+ this.text = text;
99
+ this.pos = base;
100
+ if (base === 0 && text.charCodeAt(0) === 0xfeff) {
101
+ this.pos = 1;
102
+ }
103
+ }
104
+
105
+ tokenize(): Token[] {
106
+ while (true) {
107
+ this.skipTrivia();
108
+ if (this.pos >= this.end) {
109
+ this.push("eof", this.pos, this.pos);
110
+ return this.tokens;
111
+ }
112
+ this.next();
113
+ }
114
+ }
115
+
116
+ private push(kind: TokenKind, start: number, end: number, value?: string | number, parts?: TemplatePart[]): Token {
117
+ const token: Token = { kind, text: this.text.slice(start, end), start, end, newlineBefore: this.newline };
118
+ if (value !== undefined) {
119
+ token.value = value;
120
+ }
121
+ if (parts) {
122
+ token.parts = parts;
123
+ }
124
+ this.newline = false;
125
+ this.tokens.push(token);
126
+ return token;
127
+ }
128
+
129
+ private span(start: number, end: number): Span {
130
+ return { start, end };
131
+ }
132
+
133
+ private skipTrivia(): void {
134
+ const t = this.text;
135
+ while (this.pos < this.end) {
136
+ const c = t.charCodeAt(this.pos);
137
+ if (c === 10 || c === 13) {
138
+ this.newline = true;
139
+ this.pos++;
140
+ continue;
141
+ }
142
+ if (c === 32 || c === 9 || c === 11 || c === 12) {
143
+ this.pos++;
144
+ continue;
145
+ }
146
+ if (c === 60 && t.charCodeAt(this.pos + 1) === 60) {
147
+ // `<<` comment family
148
+ const start = this.pos;
149
+ const third = t[this.pos + 2];
150
+ if (third === "!") {
151
+ let p = this.pos + 3;
152
+ while (p < this.end && /[A-Za-z0-9_-]/.test(t[p])) {
153
+ p++;
154
+ }
155
+ const word = t.slice(this.pos + 3, p);
156
+ this.push("directive", start, p, word);
157
+ this.pos = p;
158
+ this.skipLine();
159
+ continue;
160
+ }
161
+ if (third === "[" && t[this.pos + 3] === "[" && t[this.pos + 4] === "]" && t[this.pos + 5] === "]") {
162
+ const close = t.indexOf(">>", this.pos + 6);
163
+ if (close < 0 || close >= this.end) {
164
+ this.diagnostics.error("CL1003", this.span(start, Math.min(this.end, start + 6)), "unterminated doc comment", ["close it with `>>`"]);
165
+ this.pos = this.end;
166
+ return;
167
+ }
168
+ this.push("doc", start, close + 2, t.slice(this.pos + 6, close).trim());
169
+ this.pos = close + 2;
170
+ continue;
171
+ }
172
+ if (third === "[") {
173
+ const close = t.indexOf("]>>", this.pos + 3);
174
+ if (close < 0 || close >= this.end) {
175
+ this.diagnostics.error("CL1002", this.span(start, Math.min(this.end, start + 3)), "unterminated block comment", ["close it with `]>>`"]);
176
+ this.pos = this.end;
177
+ return;
178
+ }
179
+ for (let i = this.pos; i < close; i++) {
180
+ const cc = t.charCodeAt(i);
181
+ if (cc === 10 || cc === 13) {
182
+ this.newline = true;
183
+ }
184
+ }
185
+ this.pos = close + 3;
186
+ continue;
187
+ }
188
+ this.skipLine();
189
+ continue;
190
+ }
191
+ break;
192
+ }
193
+ }
194
+
195
+ private skipLine(): void {
196
+ while (this.pos < this.end) {
197
+ const c = this.text.charCodeAt(this.pos);
198
+ if (c === 10 || c === 13) {
199
+ return;
200
+ }
201
+ this.pos++;
202
+ }
203
+ }
204
+
205
+ private next(): void {
206
+ const t = this.text;
207
+ const start = this.pos;
208
+ const c = t.charCodeAt(start);
209
+
210
+ if (isIdentStart(c)) {
211
+ let p = start + 1;
212
+ while (p < this.end && isIdentPart(t.charCodeAt(p))) {
213
+ p++;
214
+ }
215
+ this.pos = p;
216
+ const word = t.slice(start, p);
217
+ this.push(KEYWORDS.has(word) ? "keyword" : "ident", start, p);
218
+ return;
219
+ }
220
+ if (isDigit(c)) {
221
+ this.number(start);
222
+ return;
223
+ }
224
+ if (c === 34 || c === 39) {
225
+ this.string(start, c);
226
+ return;
227
+ }
228
+ if (c === 96) {
229
+ this.template(start);
230
+ return;
231
+ }
232
+ for (const op of OPERATORS) {
233
+ if (t.startsWith(op, start)) {
234
+ this.pos = start + op.length;
235
+ this.push("op", start, this.pos);
236
+ return;
237
+ }
238
+ }
239
+ this.pos = start + 1;
240
+ this.diagnostics.error("CL1001", this.span(start, this.pos), `unexpected character '${t[start]}'`);
241
+ }
242
+
243
+ private number(start: number): void {
244
+ const t = this.text;
245
+ let p = start;
246
+ let isFloat = false;
247
+ let bad = false;
248
+ const digits = (accept: (ch: string) => boolean) => {
249
+ let last = "";
250
+ let count = 0;
251
+ while (p < this.end) {
252
+ const ch = t[p];
253
+ if (ch === "_") {
254
+ if (!accept(last) || !accept(t[p + 1] ?? "")) {
255
+ bad = true;
256
+ }
257
+ last = ch;
258
+ p++;
259
+ continue;
260
+ }
261
+ if (!accept(ch)) {
262
+ break;
263
+ }
264
+ last = ch;
265
+ count++;
266
+ p++;
267
+ }
268
+ return count;
269
+ };
270
+ const dec = (ch: string) => ch >= "0" && ch <= "9";
271
+ if (t[p] === "0" && (t[p + 1] === "x" || t[p + 1] === "X")) {
272
+ p += 2;
273
+ const n = digits((ch) => /[0-9a-fA-F]/.test(ch));
274
+ if (n === 0) {
275
+ bad = true;
276
+ }
277
+ } else if (t[p] === "0" && (t[p + 1] === "b" || t[p + 1] === "B")) {
278
+ p += 2;
279
+ const n = digits((ch) => ch === "0" || ch === "1");
280
+ if (n === 0) {
281
+ bad = true;
282
+ }
283
+ } else {
284
+ digits(dec);
285
+ // `0..4` is a range, `3.5` a float, `3.` followed by a name is member access.
286
+ if (t[p] === "." && dec(t[p + 1] ?? "")) {
287
+ isFloat = true;
288
+ p++;
289
+ digits(dec);
290
+ }
291
+ if (t[p] === "e" || t[p] === "E") {
292
+ let q = p + 1;
293
+ if (t[q] === "+" || t[q] === "-") {
294
+ q++;
295
+ }
296
+ if (dec(t[q] ?? "")) {
297
+ isFloat = true;
298
+ p = q;
299
+ digits(dec);
300
+ }
301
+ }
302
+ }
303
+ this.pos = p;
304
+ const raw = t.slice(start, p);
305
+ const clean = raw.replace(/_/g, "");
306
+ let value: number;
307
+ if (/^0[xX]/.test(clean)) {
308
+ value = parseInt(clean.slice(2), 16);
309
+ } else if (/^0[bB]/.test(clean)) {
310
+ value = parseInt(clean.slice(2), 2);
311
+ } else {
312
+ value = Number(clean);
313
+ }
314
+ if (bad || !Number.isFinite(value)) {
315
+ this.diagnostics.error("CL1004", this.span(start, p), raw.includes("_") ? "invalid numeric separator" : "invalid numeric literal");
316
+ value = 0;
317
+ }
318
+ this.push(isFloat ? "float" : "int", start, p, value);
319
+ }
320
+
321
+ private escape(p: number, out: string[]): number {
322
+ const t = this.text;
323
+ const ch = t[p + 1];
324
+ if (ch === "u" && t[p + 2] === "{") {
325
+ const close = t.indexOf("}", p + 3);
326
+ const hex = close > 0 ? t.slice(p + 3, close) : "";
327
+ if (/^[0-9a-fA-F]{1,6}$/.test(hex)) {
328
+ out.push(String.fromCodePoint(parseInt(hex, 16)));
329
+ return close + 1;
330
+ }
331
+ }
332
+ const mapped = ch !== undefined ? ESCAPES[ch] : undefined;
333
+ if (mapped === undefined) {
334
+ this.diagnostics.error("CL1005", this.span(p, Math.min(this.end, p + 2)), `invalid escape '\\${ch ?? ""}'`, ["valid escapes: \\n \\t \\r \\0 \\\\ \\\" \\' \\` \\$ \\u{hex}"]);
335
+ out.push(ch ?? "");
336
+ return p + 2;
337
+ }
338
+ out.push(mapped);
339
+ return p + 2;
340
+ }
341
+
342
+ private string(start: number, quote: number): void {
343
+ const t = this.text;
344
+ let p = start + 1;
345
+ const out: string[] = [];
346
+ while (p < this.end) {
347
+ const c = t.charCodeAt(p);
348
+ if (c === quote) {
349
+ this.pos = p + 1;
350
+ this.push("string", start, this.pos, out.join(""));
351
+ return;
352
+ }
353
+ if (c === 10 || c === 13) {
354
+ break;
355
+ }
356
+ if (c === 92) {
357
+ p = this.escape(p, out);
358
+ continue;
359
+ }
360
+ out.push(t[p]);
361
+ p++;
362
+ }
363
+ this.pos = p;
364
+ this.diagnostics.error("CL1006", this.span(start, p), "unterminated string");
365
+ this.push("string", start, p, out.join(""));
366
+ }
367
+
368
+ private template(start: number): void {
369
+ const t = this.text;
370
+ let p = start + 1;
371
+ const parts: TemplatePart[] = [];
372
+ let literal: string[] = [];
373
+ const flush = () => {
374
+ if (literal.length) {
375
+ parts.push({ text: literal.join("") });
376
+ literal = [];
377
+ }
378
+ };
379
+ while (p < this.end) {
380
+ const c = t.charCodeAt(p);
381
+ if (c === 96) {
382
+ flush();
383
+ this.pos = p + 1;
384
+ this.push("template", start, this.pos, undefined, parts);
385
+ return;
386
+ }
387
+ if (c === 10 || c === 13) {
388
+ break;
389
+ }
390
+ if (c === 92) {
391
+ p = this.escape(p, literal);
392
+ continue;
393
+ }
394
+ if (c === 36 && t[p + 1] === "{") {
395
+ flush();
396
+ let depth = 1;
397
+ let q = p + 2;
398
+ let quote = 0;
399
+ while (q < this.end && depth > 0) {
400
+ const d = t.charCodeAt(q);
401
+ if (quote) {
402
+ if (d === 92) {
403
+ q += 2;
404
+ continue;
405
+ }
406
+ if (d === quote) {
407
+ quote = 0;
408
+ }
409
+ } else if (d === 34 || d === 39) {
410
+ quote = d;
411
+ } else if (d === 123) {
412
+ depth++;
413
+ } else if (d === 125) {
414
+ depth--;
415
+ if (depth === 0) {
416
+ break;
417
+ }
418
+ } else if (d === 10 || d === 13 || d === 96) {
419
+ break;
420
+ }
421
+ q++;
422
+ }
423
+ if (depth !== 0 || q >= this.end || t.charCodeAt(q) !== 125) {
424
+ this.diagnostics.error("CL1007", this.span(p, Math.min(this.end, q)), "unterminated interpolation", ["close `${` with `}`"]);
425
+ p = q;
426
+ break;
427
+ }
428
+ parts.push({ hole: { start: p + 2, end: q } });
429
+ p = q + 1;
430
+ continue;
431
+ }
432
+ literal.push(t[p]);
433
+ p++;
434
+ }
435
+ flush();
436
+ this.pos = p;
437
+ this.diagnostics.error("CL1008", this.span(start, p), "unterminated template string");
438
+ this.push("template", start, p, undefined, parts);
439
+ }
440
+ }
441
+
442
+ export function tokenize(text: string, diagnostics: DiagnosticBag, base = 0, end = text.length): Token[] {
443
+ return new Lexer(text, diagnostics, base, end).tokenize();
444
+ }