cluaupp 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +12 -7
  3. package/bin/cluau.js +1 -1
  4. package/bin/cluaupp.js +1 -1
  5. package/docs/README.md +1 -1
  6. package/docs/architecture.md +52 -77
  7. package/docs/cli.md +29 -9
  8. package/docs/comparison.md +3 -3
  9. package/docs/config.md +6 -6
  10. package/docs/cpp-organization.md +2 -2
  11. package/docs/examples/index.md +2 -0
  12. package/docs/getting-started.md +7 -5
  13. package/docs/intellisense.md +49 -31
  14. package/docs/intro.md +2 -2
  15. package/docs/oop/file-tags.md +16 -17
  16. package/docs/oop/index.md +2 -2
  17. package/docs/oop/modules.md +3 -1
  18. package/docs/oop/services.md +7 -14
  19. package/docs/syntax.md +6 -5
  20. package/editors/vscode/extension.js +97 -146
  21. package/editors/vscode/package.json +6 -6
  22. package/examples/README.md +18 -0
  23. package/examples/game/.clangd +25 -0
  24. package/examples/game/.vscode/c_cpp_properties.json +27 -0
  25. package/examples/game/.vscode/extensions.json +5 -0
  26. package/examples/game/.vscode/settings.json +27 -0
  27. package/examples/game/Packages/.gitkeep +0 -0
  28. package/examples/game/cluaupp.config.json +6 -0
  29. package/examples/game/compile_flags.txt +6 -0
  30. package/examples/game/default.project.json +39 -0
  31. package/examples/game/package.json +9 -0
  32. package/examples/game/rokit.toml +5 -0
  33. package/examples/game/src/client/hud.client.cpp +12 -0
  34. package/{src → examples/game/src}/client/init.client.cpp +1 -0
  35. package/examples/game/src/server/combat.server.cpp +23 -0
  36. package/{src → examples/game/src}/server/leaderstats.server.cpp +1 -0
  37. package/examples/game/wally.toml +11 -0
  38. package/generated/api.js +51 -0
  39. package/generated/architecture.js +473 -0
  40. package/generated/ast.js +2 -0
  41. package/generated/cli.js +191 -0
  42. package/generated/compile.js +115 -0
  43. package/generated/editor-install.js +170 -0
  44. package/generated/emit.js +503 -0
  45. package/generated/emitter/luau-codegen.js +167 -0
  46. package/generated/emitter/translators.js +164 -0
  47. package/generated/headers.js +220 -0
  48. package/generated/index.html +49 -0
  49. package/generated/intellisense.js +277 -0
  50. package/generated/layout.js +112 -0
  51. package/generated/lex.js +146 -0
  52. package/generated/libs.js +484 -0
  53. package/generated/lsp.js +55 -0
  54. package/generated/package-info.js +11 -0
  55. package/generated/parse.js +607 -0
  56. package/generated/parser/collector.js +100 -0
  57. package/generated/parser/index.js +30 -0
  58. package/generated/preprocess.js +181 -0
  59. package/generated/system-understander.js +443 -0
  60. package/generated/transpile.js +66 -0
  61. package/generated/types.js +2 -0
  62. package/generated/understand.js +298 -0
  63. package/generated/utils/process-orchestrator.js +63 -0
  64. package/generated/utils/project.js +491 -0
  65. package/generated/utils/rojo-mapper.js +181 -0
  66. package/generated/utils/safe-paths.js +104 -0
  67. package/package.json +25 -8
  68. package/src/api.ts +53 -0
  69. package/src/{architecture.js → architecture.ts} +80 -33
  70. package/src/ast.ts +37 -0
  71. package/src/cli.ts +214 -0
  72. package/src/compile.ts +118 -0
  73. package/src/editor-install.ts +182 -0
  74. package/src/{emit.js → emit.ts} +5 -6
  75. package/src/emitter/luau-codegen.ts +187 -0
  76. package/src/emitter/translators.ts +168 -0
  77. package/src/headers.ts +233 -0
  78. package/src/intellisense.ts +286 -0
  79. package/src/{layout.js → layout.ts} +114 -129
  80. package/src/{lex.js → lex.ts} +4 -6
  81. package/src/{libs.js → libs.ts} +119 -46
  82. package/src/lsp.ts +60 -0
  83. package/src/package-info.ts +7 -0
  84. package/src/{parse.js → parse.ts} +3 -4
  85. package/src/parser/collector.ts +115 -0
  86. package/src/parser/index.ts +27 -0
  87. package/src/{preprocess.js → preprocess.ts} +64 -32
  88. package/src/system-understander.ts +501 -0
  89. package/src/transpile.ts +64 -0
  90. package/src/tree-sitter-cpp.d.ts +4 -0
  91. package/src/types.ts +81 -0
  92. package/src/{understand.js → understand.ts} +24 -213
  93. package/src/utils/process-orchestrator.ts +72 -0
  94. package/src/utils/project.ts +506 -0
  95. package/src/utils/rojo-mapper.ts +190 -0
  96. package/src/utils/safe-paths.ts +98 -0
  97. package/templates/game/.clangd +9 -0
  98. package/templates/game/.vscode/c_cpp_properties.json +1 -1
  99. package/templates/game/.vscode/extensions.json +5 -6
  100. package/templates/game/.vscode/settings.json +7 -6
  101. package/templates/game/cluaupp.config.json +2 -2
  102. package/templates/game/compile_flags.txt +1 -0
  103. package/templates/game/rokit.toml +5 -0
  104. package/templates/game/src/client/init.client.cpp +1 -0
  105. package/templates/game/src/server/leaderstats.server.cpp +1 -0
  106. package/src/api.js +0 -57
  107. package/src/cli.js +0 -481
  108. package/src/compile.js +0 -56
  109. package/src/editor-install.js +0 -218
  110. package/src/intellisense.js +0 -1368
  111. package/src/lsp.js +0 -227
  112. /package/{src → examples/game/src}/shared/config.cpp +0 -0
  113. /package/{src → examples/game/src}/shared/config.h +0 -0
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.intelliSenseMode = intelliSenseMode;
7
+ exports.clangPath = clangPath;
8
+ exports.writeCompileCommands = writeCompileCommands;
9
+ exports.writeClangd = writeClangd;
10
+ exports.diagnosticsFor = diagnosticsFor;
11
+ exports.syncEditorSupport = syncEditorSupport;
12
+ exports.installEditorExtension = installEditorExtension;
13
+ exports.installEditorSupport = installEditorSupport;
14
+ const node_fs_1 = __importDefault(require("node:fs"));
15
+ const node_os_1 = __importDefault(require("node:os"));
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const parse_js_1 = require("./parse.js");
18
+ const preprocess_js_1 = require("./preprocess.js");
19
+ const editor_install_js_1 = require("./editor-install.js");
20
+ const package_info_js_1 = require("./package-info.js");
21
+ function posix(file) {
22
+ return String(file).replace(/\\/g, "/");
23
+ }
24
+ function collectFiles(dir, files = []) {
25
+ if (!node_fs_1.default.existsSync(dir)) {
26
+ return files;
27
+ }
28
+ for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
29
+ const full = node_path_1.default.join(dir, entry.name);
30
+ if (entry.isDirectory()) {
31
+ collectFiles(full, files);
32
+ }
33
+ else {
34
+ files.push(full);
35
+ }
36
+ }
37
+ return files;
38
+ }
39
+ function isCppFile(file) {
40
+ return /\.(cpp|cc|cxx|c|h|hpp|hh)$/i.test(file);
41
+ }
42
+ function clangCompileArgs(root) {
43
+ const absRoot = posix(node_path_1.default.resolve(root));
44
+ const absInclude = posix(node_path_1.default.join(absRoot, "include"));
45
+ const absSrc = posix(node_path_1.default.join(absRoot, "src"));
46
+ const roblox = posix(node_path_1.default.join(absInclude, "cluaupp", "roblox.hpp"));
47
+ return [
48
+ "-xc++",
49
+ "-std=c++20",
50
+ "-ferror-limit=0",
51
+ "-I" + absInclude,
52
+ "-I" + absSrc,
53
+ "-Iinclude",
54
+ "-Isrc",
55
+ "-include",
56
+ roblox,
57
+ ];
58
+ }
59
+ function compileCommandEntry(root, file) {
60
+ const absRoot = posix(node_path_1.default.resolve(root));
61
+ const absFile = posix(node_path_1.default.resolve(file));
62
+ return {
63
+ directory: absRoot,
64
+ file: absFile,
65
+ arguments: [clangPath(), ...clangCompileArgs(root), "-c", absFile],
66
+ };
67
+ }
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
+ function copyDir(from, to) {
95
+ node_fs_1.default.mkdirSync(to, { recursive: true });
96
+ for (const entry of node_fs_1.default.readdirSync(from, { withFileTypes: true })) {
97
+ const src = node_path_1.default.join(from, entry.name);
98
+ const dest = node_path_1.default.join(to, entry.name);
99
+ if (entry.isDirectory()) {
100
+ copyDir(src, dest);
101
+ }
102
+ else {
103
+ node_fs_1.default.copyFileSync(src, dest);
104
+ }
105
+ }
106
+ }
107
+ function clangdEditorArgs() {
108
+ return [
109
+ "--compile-commands-dir=${workspaceFolder}",
110
+ "--header-insertion=never",
111
+ "--query-driver=**/clang++*,**/clang++.exe,**/g++*,**/g++.exe",
112
+ ];
113
+ }
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
+ function clangPath() {
125
+ return (0, editor_install_js_1.findClangPlusPlus)() || "clang++";
126
+ }
127
+ function writeCompileCommands(root, srcDir) {
128
+ const files = collectFiles(srcDir).filter((file) => isCppFile(file)).sort();
129
+ const json = `${JSON.stringify(files.map((file) => compileCommandEntry(root, file)), null, "\t")}\n`;
130
+ const dest = node_path_1.default.join(root, "compile_commands.json");
131
+ if (node_fs_1.default.existsSync(dest) && node_fs_1.default.readFileSync(dest, "utf8") === json) {
132
+ return false;
133
+ }
134
+ node_fs_1.default.writeFileSync(dest, json, "utf8");
135
+ return true;
136
+ }
137
+ function writeClangd(root) {
138
+ const flags = clangCompileArgs(root)
139
+ .map((flag) => ` - ${flag}`)
140
+ .join("\n");
141
+ const contents = `CompileFlags:
142
+ CompilationDatabase: .
143
+ Add:
144
+ ${flags}
145
+
146
+ Diagnostics:
147
+ Suppress: '*'
148
+
149
+ ---
150
+ If:
151
+ PathMatch: .*\\.(cpp|cc|cxx|c|h|hpp|hh)$
152
+ CompileFlags:
153
+ Add:
154
+ - -xc++
155
+
156
+ ---
157
+ If:
158
+ PathMatch: (out|libs)/.*
159
+ Index:
160
+ Background: Skip
161
+ `;
162
+ const dest = node_path_1.default.join(root, ".clangd");
163
+ if (node_fs_1.default.existsSync(dest) && node_fs_1.default.readFileSync(dest, "utf8") === contents) {
164
+ return false;
165
+ }
166
+ node_fs_1.default.writeFileSync(dest, contents, "utf8");
167
+ return true;
168
+ }
169
+ function writeVscode(root) {
170
+ const settings = node_path_1.default.join(root, ".vscode", "settings.json");
171
+ mergeJson(settings, {
172
+ "C_Cpp.intelliSenseEngine": "Disabled",
173
+ "C_Cpp.default.cppStandard": "c++20",
174
+ "C_Cpp.default.cStandard": "c17",
175
+ "C_Cpp.default.compilerPath": clangPath(),
176
+ "C_Cpp.default.intelliSenseMode": intelliSenseMode(),
177
+ "C_Cpp.default.includePath": ["${workspaceFolder}/include", "${workspaceFolder}/src"],
178
+ "clangd.enable": true,
179
+ "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
+ },
190
+ });
191
+ mergeJson(node_path_1.default.join(root, ".vscode", "extensions.json"), {
192
+ recommendations: ["llvm-vs-code-extensions.vscode-clangd"],
193
+ });
194
+ const props = node_path_1.default.join(root, ".vscode", "c_cpp_properties.json");
195
+ const nextProps = {
196
+ configurations: [
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");
218
+ }
219
+ }
220
+ function diagnosticsFor(source, fileName, options = {}) {
221
+ try {
222
+ const prepared = options.filePath ? (0, preprocess_js_1.preprocess)(source, options.filePath, options) : source;
223
+ (0, parse_js_1.parse)(prepared, fileName || "input.cpp");
224
+ return [];
225
+ }
226
+ catch (err) {
227
+ const error = err;
228
+ const message = error.message ? String(error.message) : "parse error";
229
+ const match = message.match(/:(\d+):(\d+):\s*(.*)$/);
230
+ if (match) {
231
+ return [{ line: Number(match[1]), col: Number(match[2]), message: match[3], severity: "error" }];
232
+ }
233
+ return [{ line: error.line || 1, col: error.col || 1, message, severity: "error" }];
234
+ }
235
+ }
236
+ function syncEditorSupport(root, config = {}) {
237
+ const srcDir = node_path_1.default.join(root, config.rootDir || "src");
238
+ if (node_fs_1.default.existsSync(srcDir)) {
239
+ writeCompileCommands(root, srcDir);
240
+ }
241
+ writeClangd(root);
242
+ writeVscode(root);
243
+ const flags = ["-xc++", "-std=c++20", "-ferror-limit=0", "-Iinclude", "-Isrc", "-include", "include/cluaupp/roblox.hpp"].join("\n") + "\n";
244
+ const flagsFile = node_path_1.default.join(root, "compile_flags.txt");
245
+ if (!node_fs_1.default.existsSync(flagsFile) || node_fs_1.default.readFileSync(flagsFile, "utf8") !== flags) {
246
+ node_fs_1.default.writeFileSync(flagsFile, flags, "utf8");
247
+ }
248
+ }
249
+ function installEditorExtension(cluauppRoot = package_info_js_1.projectRoot) {
250
+ const from = node_path_1.default.join(cluauppRoot, "editors", "vscode");
251
+ if (!node_fs_1.default.existsSync(from)) {
252
+ return [];
253
+ }
254
+ const id = `kartzdev.cluaupp-diagnostics-${package_info_js_1.pkg.version}`;
255
+ const homes = [node_path_1.default.join(node_os_1.default.homedir(), ".cursor", "extensions"), node_path_1.default.join(node_os_1.default.homedir(), ".vscode", "extensions")];
256
+ const installed = [];
257
+ for (const home of homes) {
258
+ if (!node_fs_1.default.existsSync(node_path_1.default.dirname(home))) {
259
+ continue;
260
+ }
261
+ node_fs_1.default.mkdirSync(home, { recursive: true });
262
+ const dest = node_path_1.default.join(home, id);
263
+ copyDir(from, dest);
264
+ node_fs_1.default.writeFileSync(node_path_1.default.join(dest, "cluaupp.root"), node_path_1.default.resolve(cluauppRoot), "utf8");
265
+ installed.push(dest);
266
+ }
267
+ return installed;
268
+ }
269
+ async function installEditorSupport(cluauppRoot = package_info_js_1.projectRoot) {
270
+ const local = installEditorExtension(cluauppRoot);
271
+ const llvm = (0, editor_install_js_1.ensureLlvm)();
272
+ (0, editor_install_js_1.reportInstall)(llvm, "LLVM clang++");
273
+ const clangd = await (0, editor_install_js_1.installMarketplaceExtension)(editor_install_js_1.CLANGD_ID, "clangd (LLVM)");
274
+ (0, editor_install_js_1.reportInstall)(clangd, "clangd");
275
+ console.log("cluaupp: C++ completion is clangd — reload the editor (Ctrl+Shift+P → Developer: Reload Window)");
276
+ return { local, llvm, clangd };
277
+ }
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENTRY_FN = exports.PRINCIPAL_FN = exports.CLEANUP_FN = exports.SECTION = void 0;
4
+ exports.isJanitorDecl = isJanitorDecl;
5
+ exports.isApiDecl = isApiDecl;
6
+ exports.classifyDecl = classifyDecl;
7
+ exports.organizeDecls = organizeDecls;
8
+ exports.janitorNames = janitorNames;
9
+ exports.emitSection = emitSection;
10
+ exports.joinBlocks = joinBlocks;
11
+ exports.commentLine = commentLine;
12
+ exports.SECTION = {
13
+ api: "API",
14
+ constants: "CONSTANTS",
15
+ variables: "VARIABLES",
16
+ types: "TYPES",
17
+ support: "HELPERS FUNCTIONS",
18
+ principal: "MAIN FUNCTIONS",
19
+ cleanup: "JANITOR",
20
+ returns: "RETURN",
21
+ };
22
+ exports.CLEANUP_FN = /^(OnClose|OnPlayerRemoving|Cleanup|Shutdown|OnDestroy)$/i;
23
+ exports.PRINCIPAL_FN = /^(init|start|main|setup)/i;
24
+ exports.ENTRY_FN = /^(init|main)$/i;
25
+ function isGetService(node) {
26
+ return Boolean(node && node.type === "getService");
27
+ }
28
+ function isDataServiceAlias(node) {
29
+ let current = node || undefined;
30
+ while (current && current.type === "member") {
31
+ if (current.object && current.object.type === "ident" && current.object.name === "DataService") {
32
+ return true;
33
+ }
34
+ current = current.object;
35
+ }
36
+ return false;
37
+ }
38
+ function isJanitorDecl(decl) {
39
+ if (!decl || decl.type !== "decl") {
40
+ return false;
41
+ }
42
+ const name = String(decl.name || "").toLowerCase();
43
+ const typeName = String(decl.valueType || "").toLowerCase();
44
+ const created = decl.value && decl.value.type === "new" && decl.value.className === "Janitor";
45
+ return name.includes("janitor") || typeName.includes("janitor") || created;
46
+ }
47
+ function isApiDecl(decl) {
48
+ if (!decl || decl.type !== "decl") {
49
+ return false;
50
+ }
51
+ return isGetService(decl.value) || isDataServiceAlias(decl.value);
52
+ }
53
+ function classifyDecl(decl) {
54
+ if (!decl || decl.type === "proto") {
55
+ return null;
56
+ }
57
+ if (decl.type === "decl") {
58
+ if (isApiDecl(decl)) {
59
+ return "api";
60
+ }
61
+ if (isJanitorDecl(decl)) {
62
+ return "variables";
63
+ }
64
+ if (decl.isConst) {
65
+ return "constants";
66
+ }
67
+ return "variables";
68
+ }
69
+ if (decl.type === "function") {
70
+ if (exports.CLEANUP_FN.test(decl.name || "")) {
71
+ return "cleanup";
72
+ }
73
+ if (exports.PRINCIPAL_FN.test(decl.name || "")) {
74
+ return "principal";
75
+ }
76
+ return "support";
77
+ }
78
+ return null;
79
+ }
80
+ function organizeDecls(keep) {
81
+ const groups = {
82
+ api: [],
83
+ constants: [],
84
+ variables: [],
85
+ support: [],
86
+ principal: [],
87
+ cleanup: [],
88
+ };
89
+ for (const decl of keep) {
90
+ const bucket = classifyDecl(decl);
91
+ if (bucket && groups[bucket]) {
92
+ groups[bucket].push(decl);
93
+ }
94
+ }
95
+ return groups;
96
+ }
97
+ function janitorNames(keep) {
98
+ return (keep || []).filter(isJanitorDecl).map((decl) => decl.name);
99
+ }
100
+ function emitSection(_title, body) {
101
+ const text = String(body || "").trimEnd();
102
+ if (!text) {
103
+ return "";
104
+ }
105
+ return `${text}\n\n`;
106
+ }
107
+ function joinBlocks(parts) {
108
+ return parts.filter((part) => Boolean(part && String(part).trim())).join("\n\n");
109
+ }
110
+ function commentLine(title) {
111
+ return `-- ${title}`;
112
+ }
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tokenize = tokenize;
4
+ const KEYWORDS = new Set([
5
+ "if",
6
+ "else",
7
+ "for",
8
+ "while",
9
+ "return",
10
+ "new",
11
+ "auto",
12
+ "const",
13
+ "void",
14
+ "int",
15
+ "bool",
16
+ "float",
17
+ "double",
18
+ "true",
19
+ "false",
20
+ "nullptr",
21
+ "class",
22
+ "struct",
23
+ "public",
24
+ "private",
25
+ "protected",
26
+ "namespace",
27
+ "using",
28
+ "template",
29
+ "typedef",
30
+ "extern",
31
+ "enum",
32
+ "switch",
33
+ "case",
34
+ "default",
35
+ "break",
36
+ ]);
37
+ function tokenize(source) {
38
+ const tokens = [];
39
+ let i = 0;
40
+ let line = 1;
41
+ let col = 1;
42
+ const push = (type, value, startLine, startCol, start, end) => {
43
+ tokens.push({ type, value, line: startLine, col: startCol, start, end });
44
+ };
45
+ while (i < source.length) {
46
+ const c = source[i];
47
+ if (c === "\n") {
48
+ i += 1;
49
+ line += 1;
50
+ col = 1;
51
+ continue;
52
+ }
53
+ if (c === " " || c === "\t" || c === "\r") {
54
+ i += 1;
55
+ col += 1;
56
+ continue;
57
+ }
58
+ if (c === "#") {
59
+ while (i < source.length && source[i] !== "\n") {
60
+ i += 1;
61
+ }
62
+ continue;
63
+ }
64
+ if (c === "/" && source[i + 1] === "/") {
65
+ while (i < source.length && source[i] !== "\n") {
66
+ i += 1;
67
+ }
68
+ continue;
69
+ }
70
+ if (c === "/" && source[i + 1] === "*") {
71
+ i += 2;
72
+ col += 2;
73
+ while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
74
+ if (source[i] === "\n") {
75
+ line += 1;
76
+ col = 1;
77
+ }
78
+ else {
79
+ col += 1;
80
+ }
81
+ i += 1;
82
+ }
83
+ i += 2;
84
+ col += 2;
85
+ continue;
86
+ }
87
+ const startLine = line;
88
+ const startCol = col;
89
+ if (c === '"') {
90
+ const start = i;
91
+ i += 1;
92
+ col += 1;
93
+ let value = "";
94
+ while (i < source.length && source[i] !== '"') {
95
+ if (source[i] === "\\" && i + 1 < source.length) {
96
+ value += source[i] + source[i + 1];
97
+ i += 2;
98
+ col += 2;
99
+ continue;
100
+ }
101
+ value += source[i];
102
+ i += 1;
103
+ col += 1;
104
+ }
105
+ i += 1;
106
+ col += 1;
107
+ push("string", value, startLine, startCol, start, i);
108
+ continue;
109
+ }
110
+ if (/[0-9]/.test(c)) {
111
+ const start = i;
112
+ let value = "";
113
+ while (i < source.length && /[0-9.]/.test(source[i])) {
114
+ value += source[i];
115
+ i += 1;
116
+ col += 1;
117
+ }
118
+ push("number", value, startLine, startCol, start, i);
119
+ continue;
120
+ }
121
+ if (/[A-Za-z_]/.test(c)) {
122
+ const start = i;
123
+ let value = "";
124
+ while (i < source.length && /[A-Za-z0-9_]/.test(source[i])) {
125
+ value += source[i];
126
+ i += 1;
127
+ col += 1;
128
+ }
129
+ push(KEYWORDS.has(value) ? "kw" : "ident", value, startLine, startCol, start, i);
130
+ continue;
131
+ }
132
+ const two = source.slice(i, i + 2);
133
+ const start = i;
134
+ if (["->", "==", "!=", "<=", ">=", "&&", "||", "::", "<<", ">>"].includes(two)) {
135
+ push("op", two, startLine, startCol, start, i + 2);
136
+ i += 2;
137
+ col += 2;
138
+ continue;
139
+ }
140
+ push("op", c, startLine, startCol, start, i + 1);
141
+ i += 1;
142
+ col += 1;
143
+ }
144
+ push("eof", "", line, col, source.length, source.length);
145
+ return tokens;
146
+ }