gofront 0.0.8 → 1.0.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.
- package/README.md +88 -12
- package/package.json +8 -4
- package/src/codegen/expressions.js +520 -1717
- package/src/{codegen.js → codegen/index.js} +155 -184
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +305 -296
- package/src/codegen/stdlib/builder.js +36 -0
- package/src/codegen/stdlib/bytes.js +62 -0
- package/src/codegen/stdlib/errors.js +17 -0
- package/src/codegen/stdlib/fmt.js +101 -0
- package/src/codegen/stdlib/gom.js +152 -0
- package/src/codegen/stdlib/html.js +17 -0
- package/src/codegen/stdlib/index.js +95 -0
- package/src/codegen/stdlib/io.js +33 -0
- package/src/codegen/stdlib/maps.js +42 -0
- package/src/codegen/stdlib/math.js +53 -0
- package/src/codegen/stdlib/os.js +10 -0
- package/src/codegen/stdlib/path.js +31 -0
- package/src/codegen/stdlib/rand.js +24 -0
- package/src/codegen/stdlib/regexp.js +51 -0
- package/src/codegen/stdlib/slices.js +48 -0
- package/src/codegen/stdlib/sort.js +28 -0
- package/src/codegen/stdlib/strconv.js +30 -0
- package/src/codegen/stdlib/strings.js +83 -0
- package/src/codegen/stdlib/time.js +54 -0
- package/src/codegen/stdlib/unicode.js +22 -0
- package/src/codegen/stdlib/utf8.js +23 -0
- package/src/codegen/templ.js +281 -0
- package/src/compiler.js +102 -62
- package/src/dts-parser.js +355 -371
- package/src/index.js +22 -54
- package/src/lexer.js +200 -286
- package/src/minifier.js +305 -318
- package/src/parser/expressions.js +54 -36
- package/src/{parser.js → parser/index.js} +12 -5
- package/src/parser/statements.js +29 -35
- package/src/parser/types.js +31 -29
- package/src/templ-lexer.js +737 -0
- package/src/templ-parser.js +235 -0
- package/src/tokens.js +109 -0
- package/src/typechecker/assignability.js +258 -0
- package/src/typechecker/expressions.js +559 -549
- package/src/typechecker/index.js +555 -0
- package/src/typechecker/resolve.js +421 -0
- package/src/typechecker/statements.js +267 -280
- package/src/typechecker/stdlib/core.js +592 -0
- package/src/typechecker/stdlib/extended.js +332 -0
- package/src/typechecker/stdlib.js +9 -1060
- package/src/typechecker/termination.js +83 -0
- package/src/typechecker/types.js +6 -3
- package/src/typechecker.js +0 -1271
package/src/index.js
CHANGED
|
@@ -27,15 +27,13 @@ const _require = createRequire(import.meta.url);
|
|
|
27
27
|
const { version } = _require("../package.json");
|
|
28
28
|
|
|
29
29
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
30
|
-
import { CodeGen } from "./codegen.js";
|
|
31
|
-
import { compileDir } from "./compiler.js";
|
|
30
|
+
import { CodeGen } from "./codegen/index.js";
|
|
31
|
+
import { compileDir, resolveImports } from "./compiler.js";
|
|
32
32
|
import { createDevServer, liveReloadClient } from "./dev-server.js";
|
|
33
|
-
import { parseDts } from "./dts-parser.js";
|
|
34
33
|
import { Lexer } from "./lexer.js";
|
|
35
34
|
import { minify } from "./minifier.js";
|
|
36
|
-
import { Parser } from "./parser.js";
|
|
37
|
-
import {
|
|
38
|
-
import { TypeChecker } from "./typechecker.js";
|
|
35
|
+
import { Parser } from "./parser/index.js";
|
|
36
|
+
import { TypeChecker } from "./typechecker/index.js";
|
|
39
37
|
|
|
40
38
|
// ── Parse CLI args ───────────────────────────────────────────
|
|
41
39
|
|
|
@@ -173,55 +171,21 @@ function runCompile() {
|
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
const checker = new TypeChecker();
|
|
176
|
-
const fromDir = dirname(inputPath);
|
|
177
174
|
const jsImports = new Map();
|
|
178
|
-
|
|
179
|
-
for (const imp of ast.imports) {
|
|
180
|
-
for (const { path } of imp.imports) {
|
|
181
|
-
if (!path.startsWith("js:")) continue;
|
|
182
|
-
const dtsPath = join(fromDir, path.slice(3));
|
|
183
|
-
try {
|
|
184
|
-
const { types, values } = parseDts(readFileSync(dtsPath, "utf8"));
|
|
185
|
-
checker.addDefinitions(types, values);
|
|
186
|
-
} catch (e) {
|
|
187
|
-
throw new Error(`cannot read '${dtsPath}': ${e.message}`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const resolved = resolveAll(ast.imports, inputPath, parseDts);
|
|
193
|
-
for (const [path, info] of resolved) {
|
|
194
|
-
if (!info) continue;
|
|
195
|
-
checker.addDefinitions(info.types, info.values);
|
|
196
|
-
jsImports.set(path, [...info.values.keys()]);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
175
|
const bundledPackages = new Set();
|
|
200
176
|
const preambles = [];
|
|
201
177
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
const dep = compileDir(depDir);
|
|
213
|
-
preambles.push(dep.js);
|
|
214
|
-
const nameUsed = alias ?? dep.pkgName;
|
|
215
|
-
bundledPackages.add(nameUsed);
|
|
216
|
-
checker.addPackageNamespace(
|
|
217
|
-
nameUsed,
|
|
218
|
-
dep.exportedSymbols,
|
|
219
|
-
dep.exportedTypes,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
178
|
+
resolveImports(
|
|
179
|
+
[ast],
|
|
180
|
+
inputPath,
|
|
181
|
+
checker,
|
|
182
|
+
jsImports,
|
|
183
|
+
bundledPackages,
|
|
184
|
+
preambles,
|
|
185
|
+
);
|
|
223
186
|
|
|
224
187
|
const errors = checker.check(ast);
|
|
188
|
+
checker.reportUnusedImports();
|
|
225
189
|
if (errors.length > 0) {
|
|
226
190
|
throw new Error(errors.map((e) => e.message).join("\n"));
|
|
227
191
|
}
|
|
@@ -311,7 +275,7 @@ if (serveMode) {
|
|
|
311
275
|
devServer = createDevServer(serveDir, servePort);
|
|
312
276
|
}
|
|
313
277
|
|
|
314
|
-
function buildOnce() {
|
|
278
|
+
function buildOnce(changedFile = null) {
|
|
315
279
|
try {
|
|
316
280
|
const startMs = performance.now();
|
|
317
281
|
const result = runCompile();
|
|
@@ -326,16 +290,19 @@ function buildOnce() {
|
|
|
326
290
|
}
|
|
327
291
|
}
|
|
328
292
|
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
293
|
+
const changeNote = changedFile ? ` — ${changedFile} changed` : "";
|
|
329
294
|
if (outputFile) {
|
|
330
295
|
writeFileSync(outputFile, `${js}\n`);
|
|
331
296
|
console.error(
|
|
332
|
-
`[${timestamp()}] gofront: OK — wrote ${outputFile} (${elapsedMs}ms)`,
|
|
297
|
+
`[${timestamp()}] gofront: OK — wrote ${outputFile} (${elapsedMs}ms${changeNote})`,
|
|
333
298
|
);
|
|
334
299
|
} else {
|
|
335
300
|
// Clear screen then print
|
|
336
301
|
process.stdout.write("\x1Bc");
|
|
337
302
|
console.log(js);
|
|
338
|
-
console.error(
|
|
303
|
+
console.error(
|
|
304
|
+
`[${timestamp()}] gofront: OK (${elapsedMs}ms${changeNote})`,
|
|
305
|
+
);
|
|
339
306
|
}
|
|
340
307
|
devServer?.notify();
|
|
341
308
|
} catch (e) {
|
|
@@ -352,9 +319,10 @@ const watchTarget = isDir ? inputPath : dirname(inputPath);
|
|
|
352
319
|
|
|
353
320
|
let debounce = null;
|
|
354
321
|
watch(watchTarget, { recursive: true }, (_event, filename) => {
|
|
355
|
-
if (filename && !filename.endsWith(".go"))
|
|
322
|
+
if (filename && !filename.endsWith(".go") && !filename.endsWith(".templ"))
|
|
323
|
+
return;
|
|
356
324
|
clearTimeout(debounce);
|
|
357
|
-
debounce = setTimeout(() => buildOnce(), 80);
|
|
325
|
+
debounce = setTimeout(() => buildOnce(filename), 80);
|
|
358
326
|
});
|
|
359
327
|
|
|
360
328
|
console.error(`[${timestamp()}] gofront: watching ${inputArg} ...`);
|
package/src/lexer.js
CHANGED
|
@@ -1,99 +1,12 @@
|
|
|
1
1
|
// Lexer: tokenizes GoFront source into a flat token stream.
|
|
2
2
|
// Implements Go-style automatic semicolon insertion.
|
|
3
|
+
//
|
|
4
|
+
// `T` and `Token` live in ./tokens.js (a pure-data leaf module shared by
|
|
5
|
+
// lexer, parser, and codegen). They are re-exported here for backward compat.
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
FLOAT: "FLOAT",
|
|
8
|
-
STRING: "STRING",
|
|
9
|
-
IDENT: "IDENT",
|
|
10
|
-
|
|
11
|
-
IMAG: "IMAG",
|
|
12
|
-
|
|
13
|
-
// Keywords
|
|
14
|
-
FUNC: "func",
|
|
15
|
-
VAR: "var",
|
|
16
|
-
CONST: "const",
|
|
17
|
-
TYPE: "type",
|
|
18
|
-
STRUCT: "struct",
|
|
19
|
-
INTERFACE: "interface",
|
|
20
|
-
IF: "if",
|
|
21
|
-
ELSE: "else",
|
|
22
|
-
FOR: "for",
|
|
23
|
-
RANGE: "range",
|
|
24
|
-
RETURN: "return",
|
|
25
|
-
PACKAGE: "package",
|
|
26
|
-
IMPORT: "import",
|
|
27
|
-
TRUE: "true",
|
|
28
|
-
FALSE: "false",
|
|
29
|
-
NIL: "nil",
|
|
30
|
-
BREAK: "break",
|
|
31
|
-
CONTINUE: "continue",
|
|
32
|
-
SWITCH: "switch",
|
|
33
|
-
CASE: "case",
|
|
34
|
-
DEFAULT: "default",
|
|
35
|
-
MAP: "map",
|
|
36
|
-
NEW: "new",
|
|
37
|
-
DEFER: "defer",
|
|
38
|
-
ASYNC: "async",
|
|
39
|
-
AWAIT: "await",
|
|
40
|
-
GO: "go",
|
|
41
|
-
CHAN: "chan",
|
|
42
|
-
SELECT: "select",
|
|
43
|
-
|
|
44
|
-
// Operators
|
|
45
|
-
PLUS: "+",
|
|
46
|
-
MINUS: "-",
|
|
47
|
-
STAR: "*",
|
|
48
|
-
SLASH: "/",
|
|
49
|
-
PERCENT: "%",
|
|
50
|
-
EQ: "==",
|
|
51
|
-
NEQ: "!=",
|
|
52
|
-
LT: "<",
|
|
53
|
-
GT: ">",
|
|
54
|
-
LTE: "<=",
|
|
55
|
-
GTE: ">=",
|
|
56
|
-
AND: "&&",
|
|
57
|
-
OR: "||",
|
|
58
|
-
NOT: "!",
|
|
59
|
-
ASSIGN: "=",
|
|
60
|
-
DEFINE: ":=",
|
|
61
|
-
PLUS_ASSIGN: "+=",
|
|
62
|
-
MINUS_ASSIGN: "-=",
|
|
63
|
-
STAR_ASSIGN: "*=",
|
|
64
|
-
SLASH_ASSIGN: "/=",
|
|
65
|
-
PERCENT_ASSIGN: "%=",
|
|
66
|
-
AMP_ASSIGN: "&=",
|
|
67
|
-
PIPE_ASSIGN: "|=",
|
|
68
|
-
CARET_ASSIGN: "^=",
|
|
69
|
-
LSHIFT_ASSIGN: "<<=",
|
|
70
|
-
RSHIFT_ASSIGN: ">>=",
|
|
71
|
-
INC: "++",
|
|
72
|
-
DEC: "--",
|
|
73
|
-
AMP: "&",
|
|
74
|
-
PIPE: "|",
|
|
75
|
-
CARET: "^",
|
|
76
|
-
AND_NOT: "&^",
|
|
77
|
-
TILDE: "~",
|
|
78
|
-
LSHIFT: "<<",
|
|
79
|
-
RSHIFT: ">>",
|
|
80
|
-
ELLIPSIS: "...",
|
|
81
|
-
FALLTHROUGH: "fallthrough",
|
|
82
|
-
|
|
83
|
-
// Delimiters
|
|
84
|
-
LPAREN: "(",
|
|
85
|
-
RPAREN: ")",
|
|
86
|
-
LBRACE: "{",
|
|
87
|
-
RBRACE: "}",
|
|
88
|
-
LBRACKET: "[",
|
|
89
|
-
RBRACKET: "]",
|
|
90
|
-
COMMA: ",",
|
|
91
|
-
DOT: ".",
|
|
92
|
-
COLON: ":",
|
|
93
|
-
SEMICOLON: ";",
|
|
94
|
-
|
|
95
|
-
EOF: "EOF",
|
|
96
|
-
};
|
|
7
|
+
import { T, Token } from "./tokens.js";
|
|
8
|
+
|
|
9
|
+
export { T, Token };
|
|
97
10
|
|
|
98
11
|
// Tokens where a trailing newline inserts a semicolon (Go spec §Semicolons)
|
|
99
12
|
const SEMI_TRIGGERS = new Set([
|
|
@@ -149,17 +62,29 @@ const KEYWORDS = new Set([
|
|
|
149
62
|
"select",
|
|
150
63
|
]);
|
|
151
64
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
65
|
+
// Single-character punctuation: push a fixed token with no lookahead.
|
|
66
|
+
const PUNCT_TOKEN = {
|
|
67
|
+
"~": [T.TILDE, "~"],
|
|
68
|
+
"(": [T.LPAREN, "("],
|
|
69
|
+
")": [T.RPAREN, ")"],
|
|
70
|
+
"{": [T.LBRACE, "{"],
|
|
71
|
+
"}": [T.RBRACE, "}"],
|
|
72
|
+
"[": [T.LBRACKET, "["],
|
|
73
|
+
"]": [T.RBRACKET, "]"],
|
|
74
|
+
",": [T.COMMA, ","],
|
|
75
|
+
";": [T.SEMICOLON, ";"],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Operator + optional "=" compound form: [compoundType, compoundVal, baseType, baseVal]
|
|
79
|
+
const ASSIGN_OP = {
|
|
80
|
+
"*": [T.STAR_ASSIGN, "*=", T.STAR, "*"],
|
|
81
|
+
"/": [T.SLASH_ASSIGN, "/=", T.SLASH, "/"],
|
|
82
|
+
"%": [T.PERCENT_ASSIGN, "%=", T.PERCENT, "%"],
|
|
83
|
+
"=": [T.EQ, "==", T.ASSIGN, "="],
|
|
84
|
+
"!": [T.NEQ, "!=", T.NOT, "!"],
|
|
85
|
+
"^": [T.CARET_ASSIGN, "^=", T.CARET, "^"],
|
|
86
|
+
":": [T.DEFINE, ":=", T.COLON, ":"],
|
|
87
|
+
};
|
|
163
88
|
|
|
164
89
|
export class LexError extends Error {
|
|
165
90
|
constructor(msg, line, col, filename, sourceCode) {
|
|
@@ -262,7 +187,7 @@ export class Lexer {
|
|
|
262
187
|
const l = this.line,
|
|
263
188
|
c = this.col;
|
|
264
189
|
this.advance(); // opening "
|
|
265
|
-
|
|
190
|
+
const parts = [];
|
|
266
191
|
while (this.pos < this.src.length && this.peek() !== '"') {
|
|
267
192
|
if (this.peek() === "\n") this.err("Unterminated string literal");
|
|
268
193
|
if (this.peek() === "\\") {
|
|
@@ -270,43 +195,42 @@ export class Lexer {
|
|
|
270
195
|
const esc = this.advance();
|
|
271
196
|
switch (esc) {
|
|
272
197
|
case "n":
|
|
273
|
-
|
|
198
|
+
parts.push("\n");
|
|
274
199
|
break;
|
|
275
200
|
case "t":
|
|
276
|
-
|
|
201
|
+
parts.push("\t");
|
|
277
202
|
break;
|
|
278
203
|
case "r":
|
|
279
|
-
|
|
204
|
+
parts.push("\r");
|
|
280
205
|
break;
|
|
281
206
|
case '"':
|
|
282
|
-
|
|
207
|
+
parts.push('"');
|
|
283
208
|
break;
|
|
284
209
|
case "\\":
|
|
285
|
-
|
|
210
|
+
parts.push("\\");
|
|
286
211
|
break;
|
|
287
212
|
case "0":
|
|
288
|
-
|
|
213
|
+
parts.push("\0");
|
|
289
214
|
break;
|
|
290
215
|
default:
|
|
291
|
-
|
|
216
|
+
parts.push(`\\${esc}`);
|
|
292
217
|
}
|
|
293
218
|
} else {
|
|
294
|
-
|
|
219
|
+
parts.push(this.advance());
|
|
295
220
|
}
|
|
296
221
|
}
|
|
297
222
|
if (this.pos >= this.src.length)
|
|
298
223
|
throw new LexError("Unterminated string", l, c, this.filename, this.src);
|
|
299
224
|
this.advance(); // closing "
|
|
300
|
-
return
|
|
225
|
+
return parts.join("");
|
|
301
226
|
}
|
|
302
227
|
|
|
303
228
|
readRawString() {
|
|
304
229
|
const l = this.line,
|
|
305
230
|
c = this.col;
|
|
306
231
|
this.advance(); // opening `
|
|
307
|
-
|
|
308
|
-
while (this.pos < this.src.length && this.peek() !== "`")
|
|
309
|
-
s += this.advance();
|
|
232
|
+
const start = this.pos;
|
|
233
|
+
while (this.pos < this.src.length && this.peek() !== "`") this.advance();
|
|
310
234
|
if (this.pos >= this.src.length)
|
|
311
235
|
throw new LexError(
|
|
312
236
|
"Unterminated raw string",
|
|
@@ -315,8 +239,9 @@ export class Lexer {
|
|
|
315
239
|
this.filename,
|
|
316
240
|
this.src,
|
|
317
241
|
);
|
|
242
|
+
const raw = this.src.slice(start, this.pos);
|
|
318
243
|
this.advance(); // closing `
|
|
319
|
-
return
|
|
244
|
+
return raw;
|
|
320
245
|
}
|
|
321
246
|
|
|
322
247
|
readRuneLiteral() {
|
|
@@ -374,70 +299,83 @@ export class Lexer {
|
|
|
374
299
|
}
|
|
375
300
|
|
|
376
301
|
readNumber() {
|
|
377
|
-
let n = "";
|
|
378
|
-
let isFloat = false;
|
|
379
|
-
|
|
380
|
-
// Check for prefix: 0b, 0o, 0x
|
|
381
302
|
const first = this.advance();
|
|
382
|
-
n
|
|
303
|
+
const n = first;
|
|
383
304
|
if (first === "0") {
|
|
384
305
|
const p = this.peek();
|
|
385
|
-
if (p === "b" || p === "B")
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
// Hex float: 0x1.Fp10 or 0xAp-2
|
|
408
|
-
if (this.peek() === "." || this.peek() === "p" || this.peek() === "P") {
|
|
409
|
-
let frac = "";
|
|
410
|
-
if (this.peek() === ".") {
|
|
411
|
-
this.advance(); // skip the dot (don't add to n)
|
|
412
|
-
while (
|
|
413
|
-
this.pos < this.src.length &&
|
|
414
|
-
/[0-9a-fA-F_]/.test(this.peek())
|
|
415
|
-
) {
|
|
416
|
-
const ch = this.advance();
|
|
417
|
-
if (ch !== "_") frac += ch;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
if (this.peek() === "p" || this.peek() === "P") {
|
|
421
|
-
this.advance(); // skip p/P
|
|
422
|
-
let exp = "";
|
|
423
|
-
if (this.peek() === "+" || this.peek() === "-")
|
|
424
|
-
exp += this.advance();
|
|
425
|
-
while (this.pos < this.src.length && /[0-9]/.test(this.peek()))
|
|
426
|
-
exp += this.advance();
|
|
427
|
-
// Evaluate: JS can't parse hex floats, so convert manually
|
|
428
|
-
// n is "0x1" (integer hex part), frac is hex fractional digits
|
|
429
|
-
let mantissa = Number(n);
|
|
430
|
-
if (frac) {
|
|
431
|
-
mantissa += Number.parseInt(frac, 16) / 16 ** frac.length;
|
|
432
|
-
}
|
|
433
|
-
const value = mantissa * 2 ** Number(exp);
|
|
434
|
-
return { n: String(value), isFloat: true };
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return { n, isFloat: false };
|
|
438
|
-
}
|
|
306
|
+
if (p === "b" || p === "B") return this._readBinaryNumber(n);
|
|
307
|
+
if (p === "o" || p === "O") return this._readOctalNumber(n);
|
|
308
|
+
if (p === "x" || p === "X") return this._readHexNumber(n);
|
|
309
|
+
}
|
|
310
|
+
return this._readDecimalNumber(n);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_readBinaryNumber(n) {
|
|
314
|
+
n += this.advance(); // consume b/B
|
|
315
|
+
while (this.pos < this.src.length && /[01_]/.test(this.peek())) {
|
|
316
|
+
const ch = this.advance();
|
|
317
|
+
if (ch !== "_") n += ch;
|
|
318
|
+
}
|
|
319
|
+
return { n, isFloat: false };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
_readOctalNumber(n) {
|
|
323
|
+
n += this.advance(); // consume o/O
|
|
324
|
+
while (this.pos < this.src.length && /[0-7_]/.test(this.peek())) {
|
|
325
|
+
const ch = this.advance();
|
|
326
|
+
if (ch !== "_") n += ch;
|
|
439
327
|
}
|
|
328
|
+
return { n, isFloat: false };
|
|
329
|
+
}
|
|
440
330
|
|
|
331
|
+
_readHexFrac() {
|
|
332
|
+
let frac = "";
|
|
333
|
+
this.advance(); // skip the dot
|
|
334
|
+
while (this.pos < this.src.length && /[0-9a-fA-F_]/.test(this.peek())) {
|
|
335
|
+
const ch = this.advance();
|
|
336
|
+
if (ch !== "_") frac += ch;
|
|
337
|
+
}
|
|
338
|
+
return frac;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
_readHexFloatExp(n, frac) {
|
|
342
|
+
this.advance(); // skip p/P
|
|
343
|
+
let exp = "";
|
|
344
|
+
if (this.peek() === "+" || this.peek() === "-") exp += this.advance();
|
|
345
|
+
while (this.pos < this.src.length && /[0-9]/.test(this.peek()))
|
|
346
|
+
exp += this.advance();
|
|
347
|
+
let mantissa = Number(n);
|
|
348
|
+
if (frac) mantissa += Number.parseInt(frac, 16) / 16 ** frac.length;
|
|
349
|
+
return { n: String(mantissa * 2 ** Number(exp)), isFloat: true };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_readHexNumber(n) {
|
|
353
|
+
n += this.advance(); // consume x/X
|
|
354
|
+
while (this.pos < this.src.length && /[0-9a-fA-F_]/.test(this.peek())) {
|
|
355
|
+
const ch = this.advance();
|
|
356
|
+
if (ch !== "_") n += ch;
|
|
357
|
+
}
|
|
358
|
+
if (this.peek() === "." || this.peek() === "p" || this.peek() === "P") {
|
|
359
|
+
let frac = "";
|
|
360
|
+
if (this.peek() === ".") frac = this._readHexFrac();
|
|
361
|
+
if (this.peek() === "p" || this.peek() === "P")
|
|
362
|
+
return this._readHexFloatExp(n, frac);
|
|
363
|
+
}
|
|
364
|
+
return { n, isFloat: false };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
_readDecimalExponent(n) {
|
|
368
|
+
n += this.advance(); // consume e/E
|
|
369
|
+
if (this.peek() === "+" || this.peek() === "-") n += this.advance();
|
|
370
|
+
while (this.pos < this.src.length && /[0-9_]/.test(this.peek())) {
|
|
371
|
+
const ch = this.advance();
|
|
372
|
+
if (ch !== "_") n += ch;
|
|
373
|
+
}
|
|
374
|
+
return n;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
_readDecimalNumber(n) {
|
|
378
|
+
let isFloat = false;
|
|
441
379
|
while (this.pos < this.src.length && /[0-9_]/.test(this.peek())) {
|
|
442
380
|
const ch = this.advance();
|
|
443
381
|
if (ch !== "_") n += ch;
|
|
@@ -452,12 +390,7 @@ export class Lexer {
|
|
|
452
390
|
}
|
|
453
391
|
if (this.peek() === "e" || this.peek() === "E") {
|
|
454
392
|
isFloat = true;
|
|
455
|
-
n
|
|
456
|
-
if (this.peek() === "+" || this.peek() === "-") n += this.advance();
|
|
457
|
-
while (this.pos < this.src.length && /[0-9_]/.test(this.peek())) {
|
|
458
|
-
const ch = this.advance();
|
|
459
|
-
if (ch !== "_") n += ch;
|
|
460
|
-
}
|
|
393
|
+
n = this._readDecimalExponent(n);
|
|
461
394
|
}
|
|
462
395
|
return { n, isFloat };
|
|
463
396
|
}
|
|
@@ -534,113 +467,94 @@ export class Lexer {
|
|
|
534
467
|
|
|
535
468
|
// Operators & punctuation
|
|
536
469
|
this.advance();
|
|
537
|
-
|
|
538
|
-
case "+":
|
|
539
|
-
if (this.match("+")) this.push(T.INC, "++", l, c);
|
|
540
|
-
else if (this.match("=")) this.push(T.PLUS_ASSIGN, "+=", l, c);
|
|
541
|
-
else this.push(T.PLUS, "+", l, c);
|
|
542
|
-
break;
|
|
543
|
-
case "-":
|
|
544
|
-
if (this.match("-")) this.push(T.DEC, "--", l, c);
|
|
545
|
-
else if (this.match("=")) this.push(T.MINUS_ASSIGN, "-=", l, c);
|
|
546
|
-
else this.push(T.MINUS, "-", l, c);
|
|
547
|
-
break;
|
|
548
|
-
case "*":
|
|
549
|
-
if (this.match("=")) this.push(T.STAR_ASSIGN, "*=", l, c);
|
|
550
|
-
else this.push(T.STAR, "*", l, c);
|
|
551
|
-
break;
|
|
552
|
-
case "/":
|
|
553
|
-
if (this.match("=")) this.push(T.SLASH_ASSIGN, "/=", l, c);
|
|
554
|
-
else this.push(T.SLASH, "/", l, c);
|
|
555
|
-
break;
|
|
556
|
-
case "%":
|
|
557
|
-
if (this.match("=")) this.push(T.PERCENT_ASSIGN, "%=", l, c);
|
|
558
|
-
else this.push(T.PERCENT, "%", l, c);
|
|
559
|
-
break;
|
|
560
|
-
case "=":
|
|
561
|
-
if (this.match("=")) this.push(T.EQ, "==", l, c);
|
|
562
|
-
else this.push(T.ASSIGN, "=", l, c);
|
|
563
|
-
break;
|
|
564
|
-
case "!":
|
|
565
|
-
if (this.match("=")) this.push(T.NEQ, "!=", l, c);
|
|
566
|
-
else this.push(T.NOT, "!", l, c);
|
|
567
|
-
break;
|
|
568
|
-
case "<":
|
|
569
|
-
if (this.match("<")) {
|
|
570
|
-
if (this.match("=")) this.push(T.LSHIFT_ASSIGN, "<<=", l, c);
|
|
571
|
-
else this.push(T.LSHIFT, "<<", l, c);
|
|
572
|
-
} else if (this.match("=")) this.push(T.LTE, "<=", l, c);
|
|
573
|
-
else this.push(T.LT, "<", l, c);
|
|
574
|
-
break;
|
|
575
|
-
case ">":
|
|
576
|
-
if (this.match(">")) {
|
|
577
|
-
if (this.match("=")) this.push(T.RSHIFT_ASSIGN, ">>=", l, c);
|
|
578
|
-
else this.push(T.RSHIFT, ">>", l, c);
|
|
579
|
-
} else if (this.match("=")) this.push(T.GTE, ">=", l, c);
|
|
580
|
-
else this.push(T.GT, ">", l, c);
|
|
581
|
-
break;
|
|
582
|
-
case "&":
|
|
583
|
-
if (this.match("&")) this.push(T.AND, "&&", l, c);
|
|
584
|
-
else if (this.match("^")) this.push(T.AND_NOT, "&^", l, c);
|
|
585
|
-
else if (this.match("=")) this.push(T.AMP_ASSIGN, "&=", l, c);
|
|
586
|
-
else this.push(T.AMP, "&", l, c);
|
|
587
|
-
break;
|
|
588
|
-
case "|":
|
|
589
|
-
if (this.match("|")) this.push(T.OR, "||", l, c);
|
|
590
|
-
else if (this.match("=")) this.push(T.PIPE_ASSIGN, "|=", l, c);
|
|
591
|
-
else this.push(T.PIPE, "|", l, c);
|
|
592
|
-
break;
|
|
593
|
-
case "^":
|
|
594
|
-
if (this.match("=")) this.push(T.CARET_ASSIGN, "^=", l, c);
|
|
595
|
-
else this.push(T.CARET, "^", l, c);
|
|
596
|
-
break;
|
|
597
|
-
case "~":
|
|
598
|
-
this.push(T.TILDE, "~", l, c);
|
|
599
|
-
break;
|
|
600
|
-
case ":":
|
|
601
|
-
if (this.match("=")) this.push(T.DEFINE, ":=", l, c);
|
|
602
|
-
else this.push(T.COLON, ":", l, c);
|
|
603
|
-
break;
|
|
604
|
-
case ".":
|
|
605
|
-
if (this.peek() === "." && this.peek(1) === ".") {
|
|
606
|
-
this.advance();
|
|
607
|
-
this.advance();
|
|
608
|
-
this.push(T.ELLIPSIS, "...", l, c);
|
|
609
|
-
} else {
|
|
610
|
-
this.push(T.DOT, ".", l, c);
|
|
611
|
-
}
|
|
612
|
-
break;
|
|
613
|
-
case "(":
|
|
614
|
-
this.push(T.LPAREN, "(", l, c);
|
|
615
|
-
break;
|
|
616
|
-
case ")":
|
|
617
|
-
this.push(T.RPAREN, ")", l, c);
|
|
618
|
-
break;
|
|
619
|
-
case "{":
|
|
620
|
-
this.push(T.LBRACE, "{", l, c);
|
|
621
|
-
break;
|
|
622
|
-
case "}":
|
|
623
|
-
this.push(T.RBRACE, "}", l, c);
|
|
624
|
-
break;
|
|
625
|
-
case "[":
|
|
626
|
-
this.push(T.LBRACKET, "[", l, c);
|
|
627
|
-
break;
|
|
628
|
-
case "]":
|
|
629
|
-
this.push(T.RBRACKET, "]", l, c);
|
|
630
|
-
break;
|
|
631
|
-
case ",":
|
|
632
|
-
this.push(T.COMMA, ",", l, c);
|
|
633
|
-
break;
|
|
634
|
-
case ";":
|
|
635
|
-
this.push(T.SEMICOLON, ";", l, c);
|
|
636
|
-
break;
|
|
637
|
-
default:
|
|
638
|
-
this.err(`Unexpected character: '${ch}'`);
|
|
639
|
-
}
|
|
470
|
+
this._readOperator(ch, l, c);
|
|
640
471
|
}
|
|
641
472
|
|
|
642
473
|
if (this.shouldSemi()) this.push(T.SEMICOLON, ";", this.line, this.col);
|
|
643
474
|
this.push(T.EOF, "", this.line, this.col);
|
|
644
475
|
return this.tokens;
|
|
645
476
|
}
|
|
477
|
+
_readPlusOp(l, c) {
|
|
478
|
+
if (this.match("+")) this.push(T.INC, "++", l, c);
|
|
479
|
+
else if (this.match("=")) this.push(T.PLUS_ASSIGN, "+=", l, c);
|
|
480
|
+
else this.push(T.PLUS, "+", l, c);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
_readMinusOp(l, c) {
|
|
484
|
+
if (this.match("-")) this.push(T.DEC, "--", l, c);
|
|
485
|
+
else if (this.match("=")) this.push(T.MINUS_ASSIGN, "-=", l, c);
|
|
486
|
+
else this.push(T.MINUS, "-", l, c);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
_readLessOp(l, c) {
|
|
490
|
+
if (this.match("<")) {
|
|
491
|
+
if (this.match("=")) this.push(T.LSHIFT_ASSIGN, "<<=", l, c);
|
|
492
|
+
else this.push(T.LSHIFT, "<<", l, c);
|
|
493
|
+
} else if (this.match("=")) this.push(T.LTE, "<=", l, c);
|
|
494
|
+
else this.push(T.LT, "<", l, c);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
_readGreaterOp(l, c) {
|
|
498
|
+
if (this.match(">")) {
|
|
499
|
+
if (this.match("=")) this.push(T.RSHIFT_ASSIGN, ">>=", l, c);
|
|
500
|
+
else this.push(T.RSHIFT, ">>", l, c);
|
|
501
|
+
} else if (this.match("=")) this.push(T.GTE, ">=", l, c);
|
|
502
|
+
else this.push(T.GT, ">", l, c);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
_readAmpOp(l, c) {
|
|
506
|
+
if (this.match("&")) this.push(T.AND, "&&", l, c);
|
|
507
|
+
else if (this.match("^")) this.push(T.AND_NOT, "&^", l, c);
|
|
508
|
+
else if (this.match("=")) this.push(T.AMP_ASSIGN, "&=", l, c);
|
|
509
|
+
else this.push(T.AMP, "&", l, c);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
_readOperator(ch, l, c) {
|
|
513
|
+
const punct = PUNCT_TOKEN[ch];
|
|
514
|
+
if (punct) {
|
|
515
|
+
this.push(punct[0], punct[1], l, c);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const assign = ASSIGN_OP[ch];
|
|
520
|
+
if (assign) {
|
|
521
|
+
if (this.match("=")) this.push(assign[0], assign[1], l, c);
|
|
522
|
+
else this.push(assign[2], assign[3], l, c);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
switch (ch) {
|
|
527
|
+
case "+":
|
|
528
|
+
this._readPlusOp(l, c);
|
|
529
|
+
break;
|
|
530
|
+
case "-":
|
|
531
|
+
this._readMinusOp(l, c);
|
|
532
|
+
break;
|
|
533
|
+
case "<":
|
|
534
|
+
this._readLessOp(l, c);
|
|
535
|
+
break;
|
|
536
|
+
case ">":
|
|
537
|
+
this._readGreaterOp(l, c);
|
|
538
|
+
break;
|
|
539
|
+
case "&":
|
|
540
|
+
this._readAmpOp(l, c);
|
|
541
|
+
break;
|
|
542
|
+
case "|":
|
|
543
|
+
if (this.match("|")) this.push(T.OR, "||", l, c);
|
|
544
|
+
else if (this.match("=")) this.push(T.PIPE_ASSIGN, "|=", l, c);
|
|
545
|
+
else this.push(T.PIPE, "|", l, c);
|
|
546
|
+
break;
|
|
547
|
+
case ".":
|
|
548
|
+
if (this.peek() === "." && this.peek(1) === ".") {
|
|
549
|
+
this.advance();
|
|
550
|
+
this.advance();
|
|
551
|
+
this.push(T.ELLIPSIS, "...", l, c);
|
|
552
|
+
} else {
|
|
553
|
+
this.push(T.DOT, ".", l, c);
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
default:
|
|
557
|
+
this.err(`Unexpected character: '${ch}'`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
646
560
|
}
|