gofront 0.0.9 → 1.0.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 (53) hide show
  1. package/README.md +5 -5
  2. package/package.json +7 -4
  3. package/src/cli-core.js +65 -0
  4. package/src/codegen/expressions.js +523 -1717
  5. package/src/codegen/index.js +576 -0
  6. package/src/codegen/runtime.js +151 -0
  7. package/src/codegen/statements.js +308 -296
  8. package/src/codegen/stdlib/builder.js +39 -0
  9. package/src/codegen/stdlib/bytes.js +65 -0
  10. package/src/codegen/stdlib/errors.js +20 -0
  11. package/src/codegen/stdlib/fmt.js +104 -0
  12. package/src/codegen/stdlib/gom.js +155 -0
  13. package/src/codegen/stdlib/html.js +20 -0
  14. package/src/codegen/stdlib/index.js +98 -0
  15. package/src/codegen/stdlib/io.js +36 -0
  16. package/src/codegen/stdlib/maps.js +45 -0
  17. package/src/codegen/stdlib/math.js +56 -0
  18. package/src/codegen/stdlib/os.js +13 -0
  19. package/src/codegen/stdlib/path.js +34 -0
  20. package/src/codegen/stdlib/rand.js +27 -0
  21. package/src/codegen/stdlib/regexp.js +54 -0
  22. package/src/codegen/stdlib/slices.js +51 -0
  23. package/src/codegen/stdlib/sort.js +31 -0
  24. package/src/codegen/stdlib/strconv.js +33 -0
  25. package/src/codegen/stdlib/strings.js +86 -0
  26. package/src/codegen/stdlib/time.js +57 -0
  27. package/src/codegen/stdlib/unicode.js +25 -0
  28. package/src/codegen/stdlib/utf8.js +26 -0
  29. package/src/codegen/templ.js +284 -0
  30. package/src/compiler.js +142 -77
  31. package/src/dts-parser.js +355 -371
  32. package/src/index.js +32 -162
  33. package/src/lexer.js +186 -272
  34. package/src/minifier.js +305 -318
  35. package/src/{parser.js → parser/declarations.js} +18 -175
  36. package/src/parser/expressions.js +51 -34
  37. package/src/parser/index.js +161 -0
  38. package/src/parser/statements.js +24 -34
  39. package/src/parser/types.js +31 -29
  40. package/src/templ-parser.js +2 -2
  41. package/src/tokens.js +109 -0
  42. package/src/typechecker/assignability.js +261 -0
  43. package/src/typechecker/expressions.js +568 -549
  44. package/src/typechecker/index.js +556 -0
  45. package/src/typechecker/resolve.js +428 -0
  46. package/src/typechecker/statements.js +270 -280
  47. package/src/typechecker/stdlib/core.js +592 -0
  48. package/src/typechecker/stdlib/extended.js +332 -0
  49. package/src/typechecker/stdlib.js +9 -1060
  50. package/src/typechecker/termination.js +86 -0
  51. package/src/typechecker/types.js +1 -0
  52. package/src/codegen.js +0 -882
  53. package/src/typechecker.js +0 -1299
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
- export const T = {
5
- // Literals
6
- INT: "INT",
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
- export class Token {
153
- constructor(type, value, line, col) {
154
- this.type = type;
155
- this.value = value;
156
- this.line = line;
157
- this.col = col;
158
- }
159
- toString() {
160
- return `Token(${this.type}, ${JSON.stringify(this.value)}, ${this.line}:${this.col})`;
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) {
@@ -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 += first;
303
+ const n = first;
383
304
  if (first === "0") {
384
305
  const p = this.peek();
385
- if (p === "b" || p === "B") {
386
- n += this.advance();
387
- while (this.pos < this.src.length && /[01_]/.test(this.peek())) {
388
- const ch = this.advance();
389
- if (ch !== "_") n += ch;
390
- }
391
- return { n, isFloat: false };
392
- }
393
- if (p === "o" || p === "O") {
394
- n += this.advance();
395
- while (this.pos < this.src.length && /[0-7_]/.test(this.peek())) {
396
- const ch = this.advance();
397
- if (ch !== "_") n += ch;
398
- }
399
- return { n, isFloat: false };
400
- }
401
- if (p === "x" || p === "X") {
402
- n += this.advance();
403
- while (this.pos < this.src.length && /[0-9a-fA-F_]/.test(this.peek())) {
404
- const ch = this.advance();
405
- if (ch !== "_") n += ch;
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;
327
+ }
328
+ return { n, isFloat: false };
329
+ }
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;
439
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
+ }
440
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 += this.advance();
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
- switch (ch) {
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
  }