gofront 0.0.2 → 0.0.4

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/src/compiler.js CHANGED
@@ -113,35 +113,49 @@ export function compileFiles(files, options = {}) {
113
113
 
114
114
  // local GoFront packages (./subdir)
115
115
  const seenLocalPaths = new Set();
116
- for (const imp of allImports) {
117
- for (const { path, alias } of imp.imports) {
118
- if (!isLocalPath(path) || seenLocalPaths.has(path)) continue;
119
- seenLocalPaths.add(path);
120
-
121
- const depDir = resolveGwDir(path, dummyFromFile);
122
- if (!depDir) {
123
- console.error(
124
- `gofront: warning: cannot find local package '${path}' relative to ${fromDir}`,
116
+ for (const p of programs) {
117
+ for (const imp of p.imports) {
118
+ for (const { path, alias, _line } of imp.imports) {
119
+ if (!isLocalPath(path) || seenLocalPaths.has(path)) continue;
120
+ seenLocalPaths.add(path);
121
+
122
+ const depDir = resolveGwDir(path, dummyFromFile);
123
+ if (!depDir) {
124
+ console.error(
125
+ `gofront: warning: cannot find local package '${path}' relative to ${fromDir}`,
126
+ );
127
+ continue;
128
+ }
129
+
130
+ // Recursively compile dependency
131
+ const dep = compileDir(depDir);
132
+ preambles.push(dep.js);
133
+ if (alias === "_") continue; // side-effect import — bundle code but don't expose namespace
134
+
135
+ // Dot import: flatten all exported symbols into global scope
136
+ if (alias === ".") {
137
+ checker.addDefinitions(dep.exportedTypes, dep.exportedSymbols);
138
+ // All dep symbols must be treated as bundled so codegen
139
+ // can emit them without a qualifier. We DON'T add a
140
+ // namespace, so SelectorExpr never appears for these.
141
+ continue;
142
+ }
143
+
144
+ const nameUsed = alias ?? dep.pkgName;
145
+ bundledPackages.add(nameUsed);
146
+ checker.addPackageNamespace(
147
+ nameUsed,
148
+ dep.exportedSymbols,
149
+ dep.exportedTypes,
125
150
  );
126
- continue;
151
+ checker.trackImport(nameUsed, { _line }, p._filename, p._source);
127
152
  }
128
-
129
- // Recursively compile dependency
130
- const dep = compileDir(depDir);
131
- preambles.push(dep.js);
132
- if (alias === "_") continue; // side-effect import — bundle code but don't expose namespace
133
- const nameUsed = alias ?? dep.pkgName;
134
- bundledPackages.add(nameUsed);
135
- checker.addPackageNamespace(
136
- nameUsed,
137
- dep.exportedSymbols,
138
- dep.exportedTypes,
139
- );
140
153
  }
141
154
  }
142
155
 
143
156
  // ── 3. Type-check ─────────────────────────────────────────────
144
157
  const errors = checker.checkAll(programs);
158
+ checker.reportUnusedImports();
145
159
  if (errors.length > 0) {
146
160
  const msgs = errors.map((e) => e.message).join("\n");
147
161
  throw new Error(msgs);
package/src/index.js CHANGED
@@ -290,7 +290,7 @@ function timestamp() {
290
290
  return new Date().toLocaleTimeString();
291
291
  }
292
292
 
293
- async function buildOnce(_label) {
293
+ async function buildOnce() {
294
294
  try {
295
295
  const startMs = performance.now();
296
296
  const result = runCompile();
@@ -314,7 +314,7 @@ async function buildOnce(_label) {
314
314
  }
315
315
 
316
316
  // Initial build
317
- buildOnce("initial");
317
+ buildOnce();
318
318
 
319
319
  // Determine what to watch
320
320
  const watchTarget = isDir ? inputPath : dirname(inputPath);
@@ -323,7 +323,7 @@ let debounce = null;
323
323
  watch(watchTarget, { recursive: true }, (_event, filename) => {
324
324
  if (filename && !filename.endsWith(".go")) return;
325
325
  clearTimeout(debounce);
326
- debounce = setTimeout(() => buildOnce(filename ?? "change"), 80);
326
+ debounce = setTimeout(() => buildOnce(), 80);
327
327
  });
328
328
 
329
329
  console.error(`[${timestamp()}] gofront: watching ${inputArg} ...`);
package/src/lexer.js CHANGED
@@ -35,6 +35,9 @@ export const T = {
35
35
  DEFER: "defer",
36
36
  ASYNC: "async",
37
37
  AWAIT: "await",
38
+ GO: "go",
39
+ CHAN: "chan",
40
+ SELECT: "select",
38
41
 
39
42
  // Operators
40
43
  PLUS: "+",
@@ -68,6 +71,7 @@ export const T = {
68
71
  AMP: "&",
69
72
  PIPE: "|",
70
73
  CARET: "^",
74
+ AND_NOT: "&^",
71
75
  LSHIFT: "<<",
72
76
  RSHIFT: ">>",
73
77
  ELLIPSIS: "...",
@@ -136,6 +140,9 @@ const KEYWORDS = new Set([
136
140
  "defer",
137
141
  "async",
138
142
  "await",
143
+ "go",
144
+ "chan",
145
+ "select",
139
146
  ]);
140
147
 
141
148
  export class Token {
@@ -217,16 +224,28 @@ export class Lexer {
217
224
  while (this.pos < this.src.length && this.peek() !== "\n")
218
225
  this.advance();
219
226
  } else if (ch === "/" && this.peek(1) === "*") {
227
+ const startLine = this.line,
228
+ startCol = this.col;
220
229
  this.advance();
221
230
  this.advance();
231
+ let closed = false;
222
232
  while (this.pos < this.src.length) {
223
233
  if (this.peek() === "*" && this.peek(1) === "/") {
224
234
  this.advance();
225
235
  this.advance();
236
+ closed = true;
226
237
  break;
227
238
  }
228
239
  this.advance();
229
240
  }
241
+ if (!closed)
242
+ throw new LexError(
243
+ "Unterminated block comment",
244
+ startLine,
245
+ startCol,
246
+ this.filename,
247
+ this.src,
248
+ );
230
249
  } else {
231
250
  break;
232
251
  }
@@ -343,20 +362,88 @@ export class Lexer {
343
362
  readNumber() {
344
363
  let n = "";
345
364
  let isFloat = false;
346
- while (this.pos < this.src.length && /[0-9]/.test(this.peek()))
347
- n += this.advance();
365
+
366
+ // Check for prefix: 0b, 0o, 0x
367
+ const first = this.advance();
368
+ n += first;
369
+ if (first === "0") {
370
+ const p = this.peek();
371
+ if (p === "b" || p === "B") {
372
+ n += this.advance();
373
+ while (this.pos < this.src.length && /[01_]/.test(this.peek())) {
374
+ const ch = this.advance();
375
+ if (ch !== "_") n += ch;
376
+ }
377
+ return { n, isFloat: false };
378
+ }
379
+ if (p === "o" || p === "O") {
380
+ n += this.advance();
381
+ while (this.pos < this.src.length && /[0-7_]/.test(this.peek())) {
382
+ const ch = this.advance();
383
+ if (ch !== "_") n += ch;
384
+ }
385
+ return { n, isFloat: false };
386
+ }
387
+ if (p === "x" || p === "X") {
388
+ n += this.advance();
389
+ while (this.pos < this.src.length && /[0-9a-fA-F_]/.test(this.peek())) {
390
+ const ch = this.advance();
391
+ if (ch !== "_") n += ch;
392
+ }
393
+ // Hex float: 0x1.Fp10 or 0xAp-2
394
+ if (this.peek() === "." || this.peek() === "p" || this.peek() === "P") {
395
+ let frac = "";
396
+ if (this.peek() === ".") {
397
+ this.advance(); // skip the dot (don't add to n)
398
+ while (
399
+ this.pos < this.src.length &&
400
+ /[0-9a-fA-F_]/.test(this.peek())
401
+ ) {
402
+ const ch = this.advance();
403
+ if (ch !== "_") frac += ch;
404
+ }
405
+ }
406
+ if (this.peek() === "p" || this.peek() === "P") {
407
+ this.advance(); // skip p/P
408
+ let exp = "";
409
+ if (this.peek() === "+" || this.peek() === "-")
410
+ exp += this.advance();
411
+ while (this.pos < this.src.length && /[0-9]/.test(this.peek()))
412
+ exp += this.advance();
413
+ // Evaluate: JS can't parse hex floats, so convert manually
414
+ // n is "0x1" (integer hex part), frac is hex fractional digits
415
+ let mantissa = Number(n);
416
+ if (frac) {
417
+ mantissa += Number.parseInt(frac, 16) / 16 ** frac.length;
418
+ }
419
+ const value = mantissa * 2 ** Number(exp);
420
+ return { n: String(value), isFloat: true };
421
+ }
422
+ }
423
+ return { n, isFloat: false };
424
+ }
425
+ }
426
+
427
+ while (this.pos < this.src.length && /[0-9_]/.test(this.peek())) {
428
+ const ch = this.advance();
429
+ if (ch !== "_") n += ch;
430
+ }
348
431
  if (this.peek() === "." && /[0-9]/.test(this.peek(1))) {
349
432
  isFloat = true;
350
433
  n += this.advance();
351
- while (this.pos < this.src.length && /[0-9]/.test(this.peek()))
352
- n += this.advance();
434
+ while (this.pos < this.src.length && /[0-9_]/.test(this.peek())) {
435
+ const ch = this.advance();
436
+ if (ch !== "_") n += ch;
437
+ }
353
438
  }
354
439
  if (this.peek() === "e" || this.peek() === "E") {
355
440
  isFloat = true;
356
441
  n += this.advance();
357
442
  if (this.peek() === "+" || this.peek() === "-") n += this.advance();
358
- while (this.pos < this.src.length && /[0-9]/.test(this.peek()))
359
- n += this.advance();
443
+ while (this.pos < this.src.length && /[0-9_]/.test(this.peek())) {
444
+ const ch = this.advance();
445
+ if (ch !== "_") n += ch;
446
+ }
360
447
  }
361
448
  return { n, isFloat };
362
449
  }
@@ -474,6 +561,7 @@ export class Lexer {
474
561
  break;
475
562
  case "&":
476
563
  if (this.match("&")) this.push(T.AND, "&&", l, c);
564
+ else if (this.match("^")) this.push(T.AND_NOT, "&^", l, c);
477
565
  else if (this.match("=")) this.push(T.AMP_ASSIGN, "&=", l, c);
478
566
  else this.push(T.AMP, "&", l, c);
479
567
  break;
@@ -0,0 +1,389 @@
1
+ // Parser expression methods — installed as a mixin on Parser.prototype.
2
+
3
+ import { T } from "../lexer.js";
4
+
5
+ // Binary operator precedence (higher = binds tighter, matching Go spec)
6
+ const PREC = {
7
+ [T.OR]: 1,
8
+ [T.AND]: 2,
9
+ [T.EQ]: 3,
10
+ [T.NEQ]: 3,
11
+ [T.LT]: 3,
12
+ [T.GT]: 3,
13
+ [T.LTE]: 3,
14
+ [T.GTE]: 3,
15
+ [T.PLUS]: 4,
16
+ [T.MINUS]: 4,
17
+ [T.PIPE]: 4,
18
+ [T.CARET]: 4,
19
+ [T.STAR]: 5,
20
+ [T.SLASH]: 5,
21
+ [T.PERCENT]: 5,
22
+ [T.LSHIFT]: 5,
23
+ [T.RSHIFT]: 5,
24
+ [T.AMP]: 5,
25
+ [T.AND_NOT]: 5,
26
+ };
27
+
28
+ const TYPE_KEYWORDS = new Set([
29
+ "int",
30
+ "float64",
31
+ "string",
32
+ "bool",
33
+ "any",
34
+ "byte",
35
+ "rune",
36
+ "error",
37
+ ]);
38
+
39
+ const BUILTIN_KEYWORDS = new Set([
40
+ "new",
41
+ "make",
42
+ "len",
43
+ "cap",
44
+ "append",
45
+ "delete",
46
+ "copy",
47
+ "print",
48
+ "println",
49
+ "panic",
50
+ "error",
51
+ ]);
52
+
53
+ export const expressionParserMethods = {
54
+ parseExprList() {
55
+ const exprs = [this.parseExpr()];
56
+ while (this.match(T.COMMA)) exprs.push(this.parseExpr());
57
+ return exprs;
58
+ },
59
+
60
+ // noCompLit=true disables composite literal parsing in if/for/switch conditions
61
+ // to resolve the ambiguity between "if x {" and "if Type{ } {"
62
+ parseExpr(minPrec = 0, noCompLit = false) {
63
+ let left = this.parseUnary(noCompLit);
64
+ while (true) {
65
+ const prec = PREC[this.peek().type];
66
+ if (!prec || prec <= minPrec) break;
67
+ const op = this.advance().value;
68
+ const right = this.parseExpr(prec, noCompLit);
69
+ left = { kind: "BinaryExpr", left, op, right };
70
+ }
71
+ return left;
72
+ },
73
+
74
+ parseUnary(noCompLit = false) {
75
+ const t = this.peek();
76
+ if (
77
+ t.type === T.NOT ||
78
+ t.type === T.MINUS ||
79
+ t.type === T.PLUS ||
80
+ t.type === T.CARET ||
81
+ t.type === T.AMP ||
82
+ t.type === T.STAR
83
+ ) {
84
+ const op = this.advance().value;
85
+ return { kind: "UnaryExpr", op, operand: this.parseUnary(noCompLit) };
86
+ }
87
+ if (t.type === T.AWAIT) {
88
+ this.advance();
89
+ return { kind: "AwaitExpr", expr: this.parseUnary(noCompLit) };
90
+ }
91
+ return this.parsePostfix(noCompLit);
92
+ },
93
+
94
+ parsePostfix(noCompLit = false) {
95
+ let expr = this.parsePrimary();
96
+ while (true) {
97
+ const t = this.peek();
98
+ if (t.type === T.DOT) {
99
+ this.advance();
100
+ // type assertion: expr.(Type)
101
+ if (this.check(T.LPAREN)) {
102
+ this.advance();
103
+ // x.(type) — type switch guard expression
104
+ if (this.check(T.TYPE)) {
105
+ this.advance();
106
+ this.expect(T.RPAREN);
107
+ expr = { kind: "TypeSwitchExpr", expr };
108
+ } else {
109
+ const type = this.parseType();
110
+ this.expect(T.RPAREN);
111
+ expr = { kind: "TypeAssertExpr", expr, type };
112
+ }
113
+ } else {
114
+ const field = this.advance().value;
115
+ expr = { kind: "SelectorExpr", expr, field };
116
+ }
117
+ } else if (t.type === T.LBRACKET) {
118
+ this.advance();
119
+ const low = this.check(T.COLON) ? null : this.parseExpr();
120
+ if (this.match(T.COLON)) {
121
+ const high =
122
+ this.check(T.RBRACKET) || this.check(T.COLON)
123
+ ? null
124
+ : this.parseExpr();
125
+ let max = null;
126
+ if (this.match(T.COLON)) {
127
+ max = this.parseExpr();
128
+ }
129
+ this.expect(T.RBRACKET);
130
+ expr = { kind: "SliceExpr", expr, low, high, max };
131
+ } else {
132
+ this.expect(T.RBRACKET);
133
+ expr = { kind: "IndexExpr", expr, index: low };
134
+ }
135
+ } else if (t.type === T.LPAREN) {
136
+ expr = this.parseCall(expr);
137
+ } else if (
138
+ t.type === T.LBRACE &&
139
+ !noCompLit &&
140
+ this.isCompositeLitContext(expr)
141
+ ) {
142
+ expr = this.parseCompositeLit(expr);
143
+ } else {
144
+ break;
145
+ }
146
+ }
147
+ return expr;
148
+ },
149
+
150
+ // Composite literals are only allowed after a type name (not arbitrary exprs)
151
+ isCompositeLitContext(expr) {
152
+ return (
153
+ expr.kind === "Ident" ||
154
+ expr.kind === "SelectorExpr" ||
155
+ expr.kind === "TypeExpr" ||
156
+ expr.kind === "StructType"
157
+ );
158
+ },
159
+
160
+ parseCall(fn) {
161
+ this.expect(T.LPAREN);
162
+ const args = [];
163
+ if (!this.check(T.RPAREN)) {
164
+ do {
165
+ if (this.check(T.RPAREN)) break;
166
+ // map[K]V and []T as call args must be parsed as types, not expressions,
167
+ // when they are NOT followed by { (which would make them composite literals).
168
+ // Needed for make(map[K]V), make([]T, n), new(T), etc.
169
+ const t = this.peek();
170
+ const isSliceOrArrayType =
171
+ t.type === T.LBRACKET &&
172
+ (this.peek2()?.type === T.RBRACKET || // []T
173
+ this.peek2()?.type === T.INT); // [n]T
174
+ if (
175
+ (t.type === T.MAP || isSliceOrArrayType) &&
176
+ !this.looksLikeCompositeLitArg()
177
+ ) {
178
+ const typeNode = this.parseType();
179
+ // []byte(s) / []rune(s) used as a call argument — it's a type conversion
180
+ if (this.check(T.LPAREN)) {
181
+ this.advance();
182
+ const convExpr = this.parseExpr();
183
+ this.expect(T.RPAREN);
184
+ args.push({
185
+ kind: "TypeConversion",
186
+ targetType: typeNode,
187
+ expr: convExpr,
188
+ });
189
+ } else {
190
+ args.push({ kind: "TypeExpr", type: typeNode });
191
+ }
192
+ } else {
193
+ const arg = this.parseExpr();
194
+ if (this.match(T.ELLIPSIS)) arg._spread = true;
195
+ args.push(arg);
196
+ }
197
+ } while (this.match(T.COMMA));
198
+ }
199
+ this.expect(T.RPAREN);
200
+ return { kind: "CallExpr", func: fn, args };
201
+ },
202
+
203
+ // Returns true if the current position looks like a composite literal argument,
204
+ // e.g. []int{1,2,3} or map[string]int{"a":1}. We scan ahead past the type
205
+ // tokens to check whether a { follows, without advancing this.pos permanently.
206
+ looksLikeCompositeLitArg() {
207
+ let i = this.pos;
208
+ const src = this.tokens;
209
+ // Skip over the type tokens: [, ], map, [, K, ], V, ident, etc.
210
+ // Simple approach: skip until we hit something that can't be part of a type
211
+ let depth = 0;
212
+ while (i < src.length) {
213
+ const tt = src[i].type;
214
+ if (tt === T.LBRACKET) {
215
+ depth++;
216
+ i++;
217
+ continue;
218
+ }
219
+ if (tt === T.RBRACKET) {
220
+ depth--;
221
+ i++;
222
+ continue;
223
+ }
224
+ if (depth > 0) {
225
+ i++;
226
+ continue;
227
+ }
228
+ if (
229
+ tt === T.MAP ||
230
+ tt === T.IDENT ||
231
+ tt === T.STAR ||
232
+ tt === T.INT ||
233
+ tt === T.FLOAT ||
234
+ tt === T.STRING
235
+ ) {
236
+ i++;
237
+ continue;
238
+ }
239
+ break;
240
+ }
241
+ // Skip whitespace tokens (shouldn't exist post-lex, but be safe)
242
+ while (i < src.length && src[i].type === T.SEMICOLON) i++;
243
+ return i < src.length && src[i].type === T.LBRACE;
244
+ },
245
+
246
+ parseCompositeLit(typeExpr) {
247
+ this.expect(T.LBRACE);
248
+ const elems = [];
249
+ while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
250
+ // Implicit composite literal: {X: 1} inside []Point{{X:1}} or map[string]Point{"a": {X:1}}
251
+ if (this.check(T.LBRACE)) {
252
+ elems.push(this.parseCompositeLit(null)); // null typeExpr = inferred
253
+ } else {
254
+ const first = this.parseExpr();
255
+ if (this.match(T.COLON)) {
256
+ // Map key: value — value may also be an implicit composite lit
257
+ const val = this.check(T.LBRACE)
258
+ ? this.parseCompositeLit(null)
259
+ : this.parseExpr();
260
+ elems.push({ kind: "KeyValueExpr", key: first, value: val });
261
+ } else {
262
+ elems.push(first);
263
+ }
264
+ }
265
+ this.match(T.COMMA);
266
+ }
267
+ this.expect(T.RBRACE);
268
+ return { kind: "CompositeLit", typeExpr, elems };
269
+ },
270
+
271
+ parsePrimary() {
272
+ const line = this.peek().line;
273
+ const expr = this._parsePrimary();
274
+ if (!expr._line) expr._line = line;
275
+ return expr;
276
+ },
277
+
278
+ _parsePrimary() {
279
+ const t = this.peek();
280
+
281
+ // Literals
282
+ if (t.type === T.INT) {
283
+ this.advance();
284
+ return { kind: "BasicLit", litKind: "INT", value: t.value };
285
+ }
286
+ if (t.type === T.FLOAT) {
287
+ this.advance();
288
+ return { kind: "BasicLit", litKind: "FLOAT", value: t.value };
289
+ }
290
+ if (t.type === T.STRING) {
291
+ this.advance();
292
+ return { kind: "BasicLit", litKind: "STRING", value: t.value };
293
+ }
294
+ if (t.type === T.TRUE) {
295
+ this.advance();
296
+ return { kind: "BasicLit", litKind: "BOOL", value: "true" };
297
+ }
298
+ if (t.type === T.FALSE) {
299
+ this.advance();
300
+ return { kind: "BasicLit", litKind: "BOOL", value: "false" };
301
+ }
302
+ if (t.type === T.NIL) {
303
+ this.advance();
304
+ return { kind: "BasicLit", litKind: "NIL", value: "null" };
305
+ }
306
+
307
+ // Parenthesised expression
308
+ if (t.type === T.LPAREN) {
309
+ this.advance();
310
+ const expr = this.parseExpr();
311
+ this.expect(T.RPAREN);
312
+ return expr;
313
+ }
314
+
315
+ // Function literal (sync or async)
316
+ if (t.type === T.FUNC || t.type === T.ASYNC) {
317
+ const isAsync = t.type === T.ASYNC;
318
+ this.advance();
319
+ if (isAsync) this.expect(T.FUNC);
320
+ const params = this.parseParamList();
321
+ const returnType = this.parseReturnType();
322
+ const body = this.parseBlock();
323
+ return { kind: "FuncLit", params, returnType, body, async: isAsync };
324
+ }
325
+
326
+ // range expression (only valid inside a for statement)
327
+ if (t.type === T.RANGE) {
328
+ this.advance();
329
+ // Always use noCompLit here: "range slice {" — { is the for body, not a composite lit
330
+ return { kind: "RangeExpr", expr: this.parseExpr(0, true) };
331
+ }
332
+
333
+ // Builtin / identifier
334
+ if (
335
+ t.type === T.IDENT ||
336
+ this.isTypeKeyword(t) ||
337
+ this.isBuiltinKeyword(t)
338
+ ) {
339
+ this.advance();
340
+ // Type conversion or composite lit: Type(expr) or Type{...}
341
+ if (this.isTypeKeyword(t) && this.check(T.LPAREN)) {
342
+ // type conversion: int(x), string(x), etc.
343
+ this.advance();
344
+ const expr = this.parseExpr();
345
+ this.expect(T.RPAREN);
346
+ return {
347
+ kind: "TypeConversion",
348
+ targetType: { kind: "TypeName", name: t.value },
349
+ expr,
350
+ };
351
+ }
352
+ return { kind: "Ident", name: t.value };
353
+ }
354
+
355
+ // Slice/array/map composite literals or type conversions:
356
+ // []int{1,2} → CompositeLit
357
+ // []byte(s), []rune(s) → TypeConversion
358
+ if (t.type === T.LBRACKET || t.type === T.MAP) {
359
+ const typeExpr = this.parseType();
360
+ if (this.check(T.LPAREN)) {
361
+ this.advance();
362
+ const expr = this.parseExpr();
363
+ this.expect(T.RPAREN);
364
+ return { kind: "TypeConversion", targetType: typeExpr, expr };
365
+ }
366
+ return this.parseCompositeLit(typeExpr);
367
+ }
368
+
369
+ // Anonymous struct composite literal: struct{Name string}{Name: "Alice"}
370
+ if (t.type === T.STRUCT) {
371
+ const typeExpr = this.parseStructType();
372
+ return this.parseCompositeLit(typeExpr);
373
+ }
374
+
375
+ if (t.type === T.CHAN) {
376
+ this.err("channels are not supported in GoFront");
377
+ }
378
+
379
+ this.err(`Unexpected token in expression: '${t.value}'`);
380
+ },
381
+
382
+ isTypeKeyword(t) {
383
+ return TYPE_KEYWORDS.has(t.value);
384
+ },
385
+
386
+ isBuiltinKeyword(t) {
387
+ return BUILTIN_KEYWORDS.has(t.value);
388
+ },
389
+ };