• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / py-slang / 23358181246

20 Mar 2026 06:58PM UTC coverage: 42.048% (+7.1%) from 34.925%
23358181246

Pull #104

github

web-flow
Merge 77ac9acf7 into 8feccc8f9
Pull Request #104: feat: replace hand-written parser with Nearley grammar

402 of 1286 branches covered (31.26%)

Branch coverage included in aggregate %.

543 of 618 new or added lines in 22 files covered. (87.86%)

36 existing lines in 2 files now uncovered.

1409 of 3021 relevant lines covered (46.64%)

52.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.97
/src/parser/python-grammar.ts
1
/* eslint-disable */
2
// @ts-nocheck
3
// Generated automatically by nearley, version 2.20.1
4
// http://github.com/Hardmath123/nearley
5
function id(x) {
6
  return x[0];
8,721✔
7
}
8

9
import { StmtNS, ExprNS } from "../ast-types";
3✔
10
import pythonLexer from "./lexer";
3✔
11
import { toAstToken } from "./token-bridge";
3✔
12

13
const nil = () => null;
3✔
14
const list = ([x]) => [x];
3✔
15
const drop = () => [];
45✔
16

17
/** Strip surrounding quotes and process escape sequences. */
18
function stripQuotes(s) {
19
  let inner;
20
  if (s.startsWith('"""') || s.startsWith("'''")) inner = s.slice(3, -3);
32✔
21
  else if (s.startsWith('"') || s.startsWith("'")) inner = s.slice(1, -1);
26!
NEW
22
  else return s;
×
23
  return inner.replace(/\\(["'\\\/bfnrtav0]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|.)/g, (_, ch) => {
32✔
24
    switch (ch[0]) {
7!
25
      case "n":
26
        return "\n";
2✔
27
      case "t":
28
        return "\t";
1✔
29
      case "r":
NEW
30
        return "\r";
×
31
      case "\\":
32
        return "\\";
1✔
33
      case "'":
NEW
34
        return "'";
×
35
      case '"':
NEW
36
        return '"';
×
37
      case "/":
NEW
38
        return "/";
×
39
      case "b":
NEW
40
        return "\b";
×
41
      case "f":
NEW
42
        return "\f";
×
43
      case "a":
NEW
44
        return "\x07";
×
45
      case "v":
NEW
46
        return "\x0B";
×
47
      case "0":
48
        return "\0";
1✔
49
      case "x":
50
        return String.fromCharCode(parseInt(ch.slice(1), 16));
1✔
51
      case "u":
NEW
52
        return String.fromCharCode(parseInt(ch.slice(1), 16));
×
53
      default:
54
        return "\\" + ch; // unrecognized escapes kept literally
1✔
55
    }
56
  });
57
}
58

59
// ── Leaf AST constructors (token → node) ────────────────────────────────
60
const astVariable = ([t]) => {
3✔
61
  const k = toAstToken(t);
318✔
62
  return new ExprNS.Variable(k, k, k);
318✔
63
};
64
const astBigInt = ([t]) => {
3✔
65
  const k = toAstToken(t);
313✔
66
  return new ExprNS.BigIntLiteral(k, k, t.value);
313✔
67
};
68
const astComplex = ([t]) => {
3✔
69
  const k = toAstToken(t);
12✔
70
  return new ExprNS.Complex(k, k, t.value);
12✔
71
};
72
const astNone = ([t]) => {
3✔
73
  const k = toAstToken(t);
7✔
74
  return new ExprNS.None(k, k);
7✔
75
};
76
const astString = ([t]) => {
3✔
77
  const k = toAstToken(t);
32✔
78
  return new ExprNS.Literal(k, k, stripQuotes(t.value));
32✔
79
};
80
const astTrue = ([t]) => {
3✔
81
  const k = toAstToken(t);
31✔
82
  return new ExprNS.Literal(k, k, true);
31✔
83
};
84
const astFalse = ([t]) => {
3✔
85
  const k = toAstToken(t);
1✔
86
  return new ExprNS.Literal(k, k, false);
1✔
87
};
88

89
// ── Operator AST constructors (children → node) ────────────────────────
90
const astBinary = ([l, op, r]) => new ExprNS.Binary(l.startToken, r.endToken, l, op, r);
72✔
91
const astBinaryTok = ([l, op, r]) =>
3✔
92
  new ExprNS.Binary(l.startToken, r.endToken, l, toAstToken(op), r);
6✔
93
const astBoolOp = ([l, op, r]) => new ExprNS.BoolOp(l.startToken, r.endToken, l, toAstToken(op), r);
29✔
94
const astUnary = ([op, arg]) => new ExprNS.Unary(toAstToken(op), arg.endToken, toAstToken(op), arg);
13✔
95
const astCompare = ([l, op, r]) => new ExprNS.Compare(l.startToken, r.endToken, l, op, r);
38✔
96

97
// ── Token / list helpers ────────────────────────────────────────────────
98
const tok = ([t]) => toAstToken(t);
103✔
99
const flatList = ([first, rest]) => [first, ...rest.map(d => d[1])];
162✔
100
const tokList = ([first, rest]) => [toAstToken(first), ...rest.map(d => toAstToken(d[1]))];
11✔
101
let Lexer = pythonLexer;
3✔
102
let ParserRules = [
3✔
103
  { name: "program$ebnf$1", symbols: [] },
104
  {
105
    name: "program$ebnf$1$subexpression$1",
106
    symbols: ["import_stmt", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
107
  },
108
  {
109
    name: "program$ebnf$1",
110
    symbols: ["program$ebnf$1", "program$ebnf$1$subexpression$1"],
111
    postprocess: function arrpush(d) {
112
      return d[0].concat([d[1]]);
7✔
113
    },
114
  },
115
  { name: "program$ebnf$2", symbols: [] },
116
  { name: "program$ebnf$2$subexpression$1", symbols: ["statement"] },
117
  {
118
    name: "program$ebnf$2$subexpression$1",
119
    symbols: [pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
120
  },
121
  {
122
    name: "program$ebnf$2",
123
    symbols: ["program$ebnf$2", "program$ebnf$2$subexpression$1"],
124
    postprocess: function arrpush(d) {
125
      return d[0].concat([d[1]]);
346✔
126
    },
127
  },
128
  {
129
    name: "program",
130
    symbols: ["program$ebnf$1", "program$ebnf$2"],
131
    postprocess: ([imports, stmts]) => {
132
      const importNodes = imports.map(d => d[0]);
622✔
133
      const stmtNodes = stmts.map(d => d[0]).filter(s => s && s.startToken !== undefined);
622✔
134
      const filtered = [...importNodes, ...stmtNodes];
622✔
135
      const start = filtered[0]
622✔
136
        ? filtered[0].startToken
137
        : toAstToken({ type: "newline", value: "", line: 1, col: 1, offset: 0 });
138
      const end = filtered.length > 0 ? filtered[filtered.length - 1].endToken : start;
622✔
139
      return new StmtNS.FileInput(start, end, filtered, []);
622✔
140
    },
141
  },
142
  {
143
    name: "import_stmt",
144
    symbols: [{ literal: "from" }, "dotted_name", { literal: "import" }, "import_clause"],
145
    postprocess: ([kw, mod, , names]) => {
146
      const last = names[names.length - 1];
8✔
147
      const endTok = last.alias || last.name;
8✔
148
      return new StmtNS.FromImport(toAstToken(kw), endTok, mod, names);
8✔
149
    },
150
  },
151
  { name: "dotted_name$ebnf$1", symbols: [] },
152
  {
153
    name: "dotted_name$ebnf$1$subexpression$1",
154
    symbols: [{ literal: "." }, pythonLexer.has("name") ? { type: "name" } : name],
3!
155
  },
156
  {
157
    name: "dotted_name$ebnf$1",
158
    symbols: ["dotted_name$ebnf$1", "dotted_name$ebnf$1$subexpression$1"],
159
    postprocess: function arrpush(d) {
160
      return d[0].concat([d[1]]);
2✔
161
    },
162
  },
163
  {
164
    name: "dotted_name",
165
    symbols: [pythonLexer.has("name") ? { type: "name" } : name, "dotted_name$ebnf$1"],
3!
166
    postprocess: ([first, rest]) => {
167
      let tok = toAstToken(first);
9✔
168
      for (const [, n] of rest) {
9✔
169
        const right = toAstToken(n);
2✔
170
        tok.lexeme = tok.lexeme + "." + right.lexeme;
2✔
171
      }
172
      return tok;
9✔
173
    },
174
  },
175
  { name: "import_clause", symbols: ["import_as_names"], postprocess: id },
176
  {
177
    name: "import_clause",
178
    symbols: [{ literal: "(" }, "import_as_names", { literal: ")" }],
179
    postprocess: ([, ns]) => ns,
3✔
180
  },
181
  { name: "import_as_names$ebnf$1", symbols: [] },
182
  { name: "import_as_names$ebnf$1$subexpression$1", symbols: [{ literal: "," }, "import_as_name"] },
183
  {
184
    name: "import_as_names$ebnf$1",
185
    symbols: ["import_as_names$ebnf$1", "import_as_names$ebnf$1$subexpression$1"],
186
    postprocess: function arrpush(d) {
187
      return d[0].concat([d[1]]);
3✔
188
    },
189
  },
190
  {
191
    name: "import_as_names",
192
    symbols: ["import_as_name", "import_as_names$ebnf$1"],
193
    postprocess: flatList,
194
  },
195
  {
196
    name: "import_as_name",
197
    symbols: [pythonLexer.has("name") ? { type: "name" } : name],
3!
198
    postprocess: ([t]) => ({ name: toAstToken(t), alias: null }),
10✔
199
  },
200
  {
201
    name: "import_as_name",
202
    symbols: [
203
      pythonLexer.has("name") ? { type: "name" } : name,
3!
204
      { literal: "as" },
205
      pythonLexer.has("name") ? { type: "name" } : name,
3!
206
    ],
207
    postprocess: ([t, , a]) => ({ name: toAstToken(t), alias: toAstToken(a) }),
1✔
208
  },
209
  {
210
    name: "statement",
211
    symbols: ["statementAssign", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
212
    postprocess: id,
213
  },
214
  {
215
    name: "statement",
216
    symbols: ["statementAnnAssign", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
217
    postprocess: id,
218
  },
219
  {
220
    name: "statement",
221
    symbols: [
222
      "statementSubscriptAssign",
223
      pythonLexer.has("newline") ? { type: "newline" } : newline,
3!
224
    ],
225
    postprocess: id,
226
  },
227
  {
228
    name: "statement",
229
    symbols: ["statementReturn", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
230
    postprocess: id,
231
  },
232
  {
233
    name: "statement",
234
    symbols: ["statementPass", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
235
    postprocess: id,
236
  },
237
  {
238
    name: "statement",
239
    symbols: ["statementBreak", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
240
    postprocess: id,
241
  },
242
  {
243
    name: "statement",
244
    symbols: ["statementContinue", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
245
    postprocess: id,
246
  },
247
  {
248
    name: "statement",
249
    symbols: ["statementGlobal", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
250
    postprocess: id,
251
  },
252
  {
253
    name: "statement",
254
    symbols: ["statementNonlocal", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
255
    postprocess: id,
256
  },
257
  {
258
    name: "statement",
259
    symbols: ["statementAssert", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
260
    postprocess: id,
261
  },
262
  {
263
    name: "statement",
264
    symbols: ["statementExpr", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
265
    postprocess: id,
266
  },
267
  { name: "statement", symbols: ["if_statement"], postprocess: id },
268
  { name: "statement", symbols: ["statementWhile"], postprocess: id },
269
  { name: "statement", symbols: ["statementFor"], postprocess: id },
270
  { name: "statement", symbols: ["statementDef"], postprocess: id },
271
  {
272
    name: "statementAssign",
273
    symbols: [pythonLexer.has("name") ? { type: "name" } : name, { literal: "=" }, "expression"],
3!
274
    postprocess: ([n, , v]) => {
275
      const tok = toAstToken(n);
114✔
276
      return new StmtNS.Assign(tok, v.endToken, new ExprNS.Variable(tok, tok, tok), v);
114✔
277
    },
278
  },
279
  {
280
    name: "statementAnnAssign",
281
    symbols: [
282
      pythonLexer.has("name") ? { type: "name" } : name,
3!
283
      { literal: ":" },
284
      "expression",
285
      { literal: "=" },
286
      "expression",
287
    ],
288
    postprocess: ([n, , ann, , v]) => {
289
      const tok = toAstToken(n);
7✔
290
      return new StmtNS.AnnAssign(tok, v.endToken, new ExprNS.Variable(tok, tok, tok), v, ann);
7✔
291
    },
292
  },
293
  {
294
    name: "statementAnnAssign",
295
    symbols: [pythonLexer.has("name") ? { type: "name" } : name, { literal: ":" }, "expression"],
3!
296
    postprocess: ([n, , ann]) => {
297
      const nameTok = toAstToken(n);
8✔
298
      const dummyVal = new ExprNS.None(ann.endToken, ann.endToken);
8✔
299
      return new StmtNS.AnnAssign(
8✔
300
        nameTok,
301
        ann.endToken,
302
        new ExprNS.Variable(nameTok, nameTok, nameTok),
303
        dummyVal,
304
        ann,
305
      );
306
    },
307
  },
308
  {
309
    name: "statementSubscriptAssign",
310
    symbols: [
311
      "expressionPost",
312
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
313
      "expression",
314
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
315
      { literal: "=" },
316
      "expression",
317
    ],
318
    postprocess: function (d) {
319
      var obj = d[0],
2✔
320
        idx = d[2],
2✔
321
        rsqb = d[3],
2✔
322
        val = d[5];
2✔
323
      var sub = new ExprNS.Subscript(obj.startToken, toAstToken(rsqb), obj, idx);
2✔
324
      return new StmtNS.Assign(obj.startToken, val.endToken, sub, val);
2✔
325
    },
326
  },
327
  {
328
    name: "statementReturn",
329
    symbols: [{ literal: "return" }, "expression"],
330
    postprocess: ([kw, expr]) => new StmtNS.Return(toAstToken(kw), expr.endToken, expr),
48✔
331
  },
332
  {
333
    name: "statementReturn",
334
    symbols: [{ literal: "return" }],
335
    postprocess: ([t]) => {
336
      const tok = toAstToken(t);
37✔
337
      return new StmtNS.Return(tok, tok, null);
37✔
338
    },
339
  },
340
  {
341
    name: "statementPass",
342
    symbols: [{ literal: "pass" }],
343
    postprocess: ([t]) => {
344
      const tok = toAstToken(t);
43✔
345
      return new StmtNS.Pass(tok, tok);
43✔
346
    },
347
  },
348
  {
349
    name: "statementBreak",
350
    symbols: [{ literal: "break" }],
351
    postprocess: ([t]) => {
352
      const tok = toAstToken(t);
3✔
353
      return new StmtNS.Break(tok, tok);
3✔
354
    },
355
  },
356
  {
357
    name: "statementContinue",
358
    symbols: [{ literal: "continue" }],
359
    postprocess: ([t]) => {
360
      const tok = toAstToken(t);
3✔
361
      return new StmtNS.Continue(tok, tok);
3✔
362
    },
363
  },
364
  {
365
    name: "statementGlobal",
366
    symbols: [{ literal: "global" }, pythonLexer.has("name") ? { type: "name" } : name],
3!
367
    postprocess: ([kw, n]) => new StmtNS.Global(toAstToken(kw), toAstToken(n), toAstToken(n)),
1✔
368
  },
369
  {
370
    name: "statementNonlocal",
371
    symbols: [{ literal: "nonlocal" }, pythonLexer.has("name") ? { type: "name" } : name],
3!
372
    postprocess: ([kw, n]) => new StmtNS.NonLocal(toAstToken(kw), toAstToken(n), toAstToken(n)),
9✔
373
  },
374
  {
375
    name: "statementAssert",
376
    symbols: [{ literal: "assert" }, "expression"],
377
    postprocess: ([kw, e]) => new StmtNS.Assert(toAstToken(kw), e.endToken, e),
5✔
378
  },
379
  {
380
    name: "statementExpr",
381
    symbols: ["expression"],
382
    postprocess: ([e]) => new StmtNS.SimpleExpr(e.startToken, e.endToken, e),
433✔
383
  },
384
  {
385
    name: "statementWhile",
386
    symbols: [{ literal: "while" }, "expression", { literal: ":" }, "block"],
387
    postprocess: ([kw, test, , body]) =>
388
      new StmtNS.While(toAstToken(kw), body[body.length - 1].endToken, test, body),
8✔
389
  },
390
  {
391
    name: "statementFor",
392
    symbols: [
393
      { literal: "for" },
394
      pythonLexer.has("name") ? { type: "name" } : name,
3!
395
      { literal: "in" },
396
      "expression",
397
      { literal: ":" },
398
      "block",
399
    ],
400
    postprocess: ([kw, target, , iter, , body]) =>
401
      new StmtNS.For(
11✔
402
        toAstToken(kw),
403
        body[body.length - 1].endToken,
404
        toAstToken(target),
405
        iter,
406
        body,
407
      ),
408
  },
409
  {
410
    name: "statementDef",
411
    symbols: [
412
      { literal: "def" },
413
      pythonLexer.has("name") ? { type: "name" } : name,
3!
414
      "params",
415
      { literal: ":" },
416
      "block",
417
    ],
418
    postprocess: ([kw, name, params, , body]) =>
419
      new StmtNS.FunctionDef(
60✔
420
        toAstToken(kw),
421
        body[body.length - 1].endToken,
422
        toAstToken(name),
423
        params,
424
        body,
425
        [],
426
      ),
427
  },
428
  { name: "if_statement$ebnf$1", symbols: [] },
429
  {
430
    name: "if_statement$ebnf$1$subexpression$1",
431
    symbols: [{ literal: "elif" }, "expression", { literal: ":" }, "block"],
432
  },
433
  {
434
    name: "if_statement$ebnf$1",
435
    symbols: ["if_statement$ebnf$1", "if_statement$ebnf$1$subexpression$1"],
436
    postprocess: function arrpush(d) {
437
      return d[0].concat([d[1]]);
5✔
438
    },
439
  },
440
  {
441
    name: "if_statement$ebnf$2$subexpression$1",
442
    symbols: [{ literal: "else" }, { literal: ":" }, "block"],
443
  },
444
  {
445
    name: "if_statement$ebnf$2",
446
    symbols: ["if_statement$ebnf$2$subexpression$1"],
447
    postprocess: id,
448
  },
449
  {
450
    name: "if_statement$ebnf$2",
451
    symbols: [],
452
    postprocess: function (d) {
453
      return null;
34✔
454
    },
455
  },
456
  {
457
    name: "if_statement",
458
    symbols: [
459
      { literal: "if" },
460
      "expression",
461
      { literal: ":" },
462
      "block",
463
      "if_statement$ebnf$1",
464
      "if_statement$ebnf$2",
465
    ],
466
    postprocess: ([kw, test, , body, elifs, elseBlock]) => {
467
      let else_ = elseBlock ? elseBlock[0][2] : null;
49✔
468
      for (let i = elifs.length - 1; i >= 0; i--) {
49✔
469
        const [ekw, etest, ecolon, ebody] = elifs[i];
11✔
470
        const endTok =
471
          else_ && else_.length > 0
11✔
472
            ? else_[else_.length - 1].endToken
473
            : ebody[ebody.length - 1].endToken;
474
        else_ = [new StmtNS.If(toAstToken(ekw), endTok, etest, ebody, else_)];
11✔
475
      }
476
      const endTok =
477
        else_ && else_.length > 0
49✔
478
          ? else_[else_.length - 1].endToken
479
          : body[body.length - 1].endToken;
480
      return new StmtNS.If(toAstToken(kw), endTok, test, body, else_);
49✔
481
    },
482
  },
483
  { name: "names$ebnf$1", symbols: [] },
484
  {
485
    name: "names$ebnf$1$subexpression$1",
486
    symbols: [{ literal: "," }, pythonLexer.has("name") ? { type: "name" } : name],
3!
487
  },
488
  {
489
    name: "names$ebnf$1",
490
    symbols: ["names$ebnf$1", "names$ebnf$1$subexpression$1"],
491
    postprocess: function arrpush(d) {
NEW
492
      return d[0].concat([d[1]]);
×
493
    },
494
  },
495
  {
496
    name: "names",
497
    symbols: [pythonLexer.has("name") ? { type: "name" } : name, "names$ebnf$1"],
3!
498
    postprocess: tokList,
499
  },
500
  {
501
    name: "block",
502
    symbols: ["blockInline", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
503
    postprocess: list,
504
  },
505
  { name: "block$ebnf$1$subexpression$1", symbols: ["statement"] },
506
  {
507
    name: "block$ebnf$1$subexpression$1",
508
    symbols: [pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
509
  },
510
  { name: "block$ebnf$1", symbols: ["block$ebnf$1$subexpression$1"] },
511
  { name: "block$ebnf$1$subexpression$2", symbols: ["statement"] },
512
  {
513
    name: "block$ebnf$1$subexpression$2",
514
    symbols: [pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
515
  },
516
  {
517
    name: "block$ebnf$1",
518
    symbols: ["block$ebnf$1", "block$ebnf$1$subexpression$2"],
519
    postprocess: function arrpush(d) {
520
      return d[0].concat([d[1]]);
46✔
521
    },
522
  },
523
  {
524
    name: "block",
525
    symbols: [
526
      pythonLexer.has("newline") ? { type: "newline" } : newline,
3!
527
      pythonLexer.has("indent") ? { type: "indent" } : indent,
3!
528
      "block$ebnf$1",
529
      pythonLexer.has("dedent") ? { type: "dedent" } : dedent,
3!
530
    ],
531
    postprocess: ([, , stmts]) => stmts.map(d => d[0]).filter(s => s && s.startToken !== undefined),
174✔
532
  },
533
  { name: "blockInline", symbols: ["statementAssign"], postprocess: id },
534
  { name: "blockInline", symbols: ["statementAnnAssign"], postprocess: id },
535
  { name: "blockInline", symbols: ["statementSubscriptAssign"], postprocess: id },
536
  { name: "blockInline", symbols: ["statementReturn"], postprocess: id },
537
  { name: "blockInline", symbols: ["statementPass"], postprocess: id },
538
  { name: "blockInline", symbols: ["statementBreak"], postprocess: id },
539
  { name: "blockInline", symbols: ["statementContinue"], postprocess: id },
540
  { name: "blockInline", symbols: ["statementGlobal"], postprocess: id },
541
  { name: "blockInline", symbols: ["statementNonlocal"], postprocess: id },
542
  { name: "blockInline", symbols: ["statementAssert"], postprocess: id },
543
  { name: "blockInline", symbols: ["statementExpr"], postprocess: id },
544
  {
545
    name: "rest_names",
546
    symbols: [pythonLexer.has("name") ? { type: "name" } : name],
3!
547
    postprocess: ([t]) => {
548
      const tok = toAstToken(t);
13✔
549
      tok.isStarred = false;
13✔
550
      return [tok];
13✔
551
    },
552
  },
553
  {
554
    name: "rest_names",
555
    symbols: [{ literal: "*" }, pythonLexer.has("name") ? { type: "name" } : name],
3!
556
    postprocess: ([, t]) => {
557
      const tok = toAstToken(t);
4✔
558
      tok.isStarred = true;
4✔
559
      return [tok];
4✔
560
    },
561
  },
562
  {
563
    name: "rest_names",
564
    symbols: ["rest_names", { literal: "," }, pythonLexer.has("name") ? { type: "name" } : name],
3!
565
    postprocess: ([params, , t]) => {
566
      const tok = toAstToken(t);
14✔
567
      tok.isStarred = false;
14✔
568
      return [...params, tok];
14✔
569
    },
570
  },
571
  {
572
    name: "rest_names",
573
    symbols: [
574
      "rest_names",
575
      { literal: "," },
576
      { literal: "*" },
577
      pythonLexer.has("name") ? { type: "name" } : name,
3!
578
    ],
579
    postprocess: ([params, , , t]) => {
580
      const tok = toAstToken(t);
1✔
581
      tok.isStarred = true;
1✔
582
      return [...params, tok];
1✔
583
    },
584
  },
585
  { name: "params", symbols: [{ literal: "(" }, { literal: ")" }], postprocess: drop },
586
  {
587
    name: "params",
588
    symbols: [{ literal: "(" }, "rest_names", { literal: ")" }],
589
    postprocess: ([, ps]) => ps,
17✔
590
  },
591
  {
592
    name: "expression",
593
    symbols: ["expressionOr", { literal: "if" }, "expressionOr", { literal: "else" }, "expression"],
594
    postprocess: ([cons, , test, , alt]) =>
595
      new ExprNS.Ternary(cons.startToken, alt.endToken, test, cons, alt),
8✔
596
  },
597
  { name: "expression", symbols: ["expressionOr"], postprocess: id },
598
  { name: "expression", symbols: ["lambda_expr"], postprocess: id },
599
  {
600
    name: "expressionOr",
601
    symbols: ["expressionOr", { literal: "or" }, "expressionAnd"],
602
    postprocess: astBoolOp,
603
  },
604
  { name: "expressionOr", symbols: ["expressionAnd"], postprocess: id },
605
  {
606
    name: "expressionAnd",
607
    symbols: ["expressionAnd", { literal: "and" }, "expressionNot"],
608
    postprocess: astBoolOp,
609
  },
610
  { name: "expressionAnd", symbols: ["expressionNot"], postprocess: id },
611
  { name: "expressionNot", symbols: [{ literal: "not" }, "expressionNot"], postprocess: astUnary },
612
  { name: "expressionNot", symbols: ["expressionCmp"], postprocess: id },
613
  {
614
    name: "expressionCmp",
615
    symbols: ["expressionCmp", "expressionCmpOp", "expressionAdd"],
616
    postprocess: astCompare,
617
  },
618
  { name: "expressionCmp", symbols: ["expressionAdd"], postprocess: id },
619
  {
620
    name: "expressionCmpOp",
621
    symbols: [pythonLexer.has("less") ? { type: "less" } : less],
3!
622
    postprocess: tok,
623
  },
624
  {
625
    name: "expressionCmpOp",
626
    symbols: [pythonLexer.has("greater") ? { type: "greater" } : greater],
3!
627
    postprocess: tok,
628
  },
629
  {
630
    name: "expressionCmpOp",
631
    symbols: [pythonLexer.has("doubleequal") ? { type: "doubleequal" } : doubleequal],
3!
632
    postprocess: tok,
633
  },
634
  {
635
    name: "expressionCmpOp",
636
    symbols: [pythonLexer.has("greaterequal") ? { type: "greaterequal" } : greaterequal],
3!
637
    postprocess: tok,
638
  },
639
  {
640
    name: "expressionCmpOp",
641
    symbols: [pythonLexer.has("lessequal") ? { type: "lessequal" } : lessequal],
3!
642
    postprocess: tok,
643
  },
644
  {
645
    name: "expressionCmpOp",
646
    symbols: [pythonLexer.has("notequal") ? { type: "notequal" } : notequal],
3!
647
    postprocess: tok,
648
  },
649
  { name: "expressionCmpOp", symbols: [{ literal: "in" }], postprocess: tok },
650
  {
651
    name: "expressionCmpOp",
652
    symbols: [{ literal: "not" }, { literal: "in" }],
653
    postprocess: ([t]) => {
654
      const tok = toAstToken(t);
3✔
655
      tok.lexeme = "not in";
3✔
656
      return tok;
3✔
657
    },
658
  },
659
  { name: "expressionCmpOp", symbols: [{ literal: "is" }], postprocess: tok },
660
  {
661
    name: "expressionCmpOp",
662
    symbols: [{ literal: "is" }, { literal: "not" }],
663
    postprocess: ([t]) => {
664
      const tok = toAstToken(t);
3✔
665
      tok.lexeme = "is not";
3✔
666
      return tok;
3✔
667
    },
668
  },
669
  {
670
    name: "expressionAdd",
671
    symbols: ["expressionAdd", "expressionAddOp", "expressionMul"],
672
    postprocess: astBinary,
673
  },
674
  { name: "expressionAdd", symbols: ["expressionMul"], postprocess: id },
675
  {
676
    name: "expressionAddOp",
677
    symbols: [pythonLexer.has("plus") ? { type: "plus" } : plus],
3!
678
    postprocess: tok,
679
  },
680
  {
681
    name: "expressionAddOp",
682
    symbols: [pythonLexer.has("minus") ? { type: "minus" } : minus],
3!
683
    postprocess: tok,
684
  },
685
  {
686
    name: "expressionMul",
687
    symbols: ["expressionMul", "expressionMulOp", "expressionUnary"],
688
    postprocess: astBinary,
689
  },
690
  { name: "expressionMul", symbols: ["expressionUnary"], postprocess: id },
691
  {
692
    name: "expressionMulOp",
693
    symbols: [pythonLexer.has("star") ? { type: "star" } : star],
3!
694
    postprocess: tok,
695
  },
696
  {
697
    name: "expressionMulOp",
698
    symbols: [pythonLexer.has("slash") ? { type: "slash" } : slash],
3!
699
    postprocess: tok,
700
  },
701
  {
702
    name: "expressionMulOp",
703
    symbols: [pythonLexer.has("percent") ? { type: "percent" } : percent],
3!
704
    postprocess: tok,
705
  },
706
  {
707
    name: "expressionMulOp",
708
    symbols: [pythonLexer.has("doubleslash") ? { type: "doubleslash" } : doubleslash],
3!
709
    postprocess: tok,
710
  },
711
  {
712
    name: "expressionUnary",
713
    symbols: [pythonLexer.has("plus") ? { type: "plus" } : plus, "expressionUnary"],
3!
714
    postprocess: astUnary,
715
  },
716
  {
717
    name: "expressionUnary",
718
    symbols: [pythonLexer.has("minus") ? { type: "minus" } : minus, "expressionUnary"],
3!
719
    postprocess: astUnary,
720
  },
721
  { name: "expressionUnary", symbols: ["expressionPow"], postprocess: id },
722
  {
723
    name: "expressionPow",
724
    symbols: [
725
      "expressionPost",
726
      pythonLexer.has("doublestar") ? { type: "doublestar" } : doublestar,
3!
727
      "expressionUnary",
728
    ],
729
    postprocess: astBinaryTok,
730
  },
731
  { name: "expressionPow", symbols: ["expressionPost"], postprocess: id },
732
  {
733
    name: "expressionPost",
734
    symbols: [
735
      "expressionPost",
736
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
737
      "expression",
738
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
739
    ],
740
    postprocess: ([obj, , idx, rsqb]) =>
741
      new ExprNS.Subscript(obj.startToken, toAstToken(rsqb), obj, idx),
5✔
742
  },
743
  {
744
    name: "expressionPost",
745
    symbols: ["expressionPost", { literal: "(" }, "expressions", { literal: ")" }],
746
    postprocess: ([callee, , args, rparen]) =>
747
      new ExprNS.Call(callee.startToken, toAstToken(rparen), callee, args),
56✔
748
  },
749
  {
750
    name: "expressionPost",
751
    symbols: ["expressionPost", { literal: "(" }, { literal: ")" }],
752
    postprocess: ([callee, , rparen]) =>
753
      new ExprNS.Call(callee.startToken, toAstToken(rparen), callee, []),
25✔
754
  },
755
  { name: "expressionPost", symbols: ["atom"], postprocess: id },
756
  {
757
    name: "atom",
758
    symbols: [{ literal: "(" }, "expression", { literal: ")" }],
759
    postprocess: ([, e]) => new ExprNS.Grouping(e.startToken, e.endToken, e),
10✔
760
  },
761
  {
762
    name: "atom",
763
    symbols: [
764
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
765
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
766
    ],
767
    postprocess: ([l, r]) => new ExprNS.List(toAstToken(l), toAstToken(r), []),
4✔
768
  },
769
  {
770
    name: "atom",
771
    symbols: [
772
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
773
      "expressions",
774
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
775
    ],
776
    postprocess: ([l, elems, r]) => new ExprNS.List(toAstToken(l), toAstToken(r), elems),
9✔
777
  },
778
  {
779
    name: "atom",
780
    symbols: [pythonLexer.has("name") ? { type: "name" } : name],
3!
781
    postprocess: astVariable,
782
  },
783
  {
784
    name: "atom",
785
    symbols: [pythonLexer.has("number_float") ? { type: "number_float" } : number_float],
3!
786
    postprocess: ([t]) => {
787
      const tok = toAstToken(t);
11✔
788
      return new ExprNS.Literal(tok, tok, parseFloat(t.value));
11✔
789
    },
790
  },
791
  {
792
    name: "atom",
793
    symbols: [pythonLexer.has("number_int") ? { type: "number_int" } : number_int],
3!
794
    postprocess: astBigInt,
795
  },
796
  {
797
    name: "atom",
798
    symbols: [pythonLexer.has("number_hex") ? { type: "number_hex" } : number_hex],
3!
799
    postprocess: astBigInt,
800
  },
801
  {
802
    name: "atom",
803
    symbols: [pythonLexer.has("number_oct") ? { type: "number_oct" } : number_oct],
3!
804
    postprocess: astBigInt,
805
  },
806
  {
807
    name: "atom",
808
    symbols: [pythonLexer.has("number_bin") ? { type: "number_bin" } : number_bin],
3!
809
    postprocess: astBigInt,
810
  },
811
  {
812
    name: "atom",
813
    symbols: [pythonLexer.has("number_complex") ? { type: "number_complex" } : number_complex],
3!
814
    postprocess: astComplex,
815
  },
816
  { name: "atom", symbols: ["stringLit"], postprocess: id },
817
  { name: "atom", symbols: [{ literal: "None" }], postprocess: astNone },
818
  { name: "atom", symbols: [{ literal: "True" }], postprocess: astTrue },
819
  { name: "atom", symbols: [{ literal: "False" }], postprocess: astFalse },
820
  {
821
    name: "lambda_expr",
822
    symbols: [{ literal: "lambda" }, "names", { literal: ":" }, "expression"],
823
    postprocess: ([kw, params, , body]) =>
824
      new ExprNS.Lambda(toAstToken(kw), body.endToken, params, body),
16✔
825
  },
826
  {
827
    name: "lambda_expr",
828
    symbols: [
829
      { literal: "lambda" },
830
      "names",
831
      pythonLexer.has("doublecolon") ? { type: "doublecolon" } : doublecolon,
3!
832
      "block",
833
    ],
834
    postprocess: ([kw, params, , body]) =>
NEW
835
      new ExprNS.MultiLambda(toAstToken(kw), body[body.length - 1].endToken, params, body, []),
×
836
  },
837
  {
838
    name: "lambda_expr",
839
    symbols: [{ literal: "lambda" }, { literal: ":" }, "expression"],
840
    postprocess: ([kw, , body]) => new ExprNS.Lambda(toAstToken(kw), body.endToken, [], body),
1✔
841
  },
842
  {
843
    name: "lambda_expr",
844
    symbols: [
845
      { literal: "lambda" },
846
      pythonLexer.has("doublecolon") ? { type: "doublecolon" } : doublecolon,
3!
847
      "block",
848
    ],
849
    postprocess: ([kw, , body]) =>
NEW
850
      new ExprNS.MultiLambda(toAstToken(kw), body[body.length - 1].endToken, [], body, []),
×
851
  },
852
  { name: "expressions$ebnf$1", symbols: [] },
853
  { name: "expressions$ebnf$1$subexpression$1", symbols: [{ literal: "," }, "expression"] },
854
  {
855
    name: "expressions$ebnf$1",
856
    symbols: ["expressions$ebnf$1", "expressions$ebnf$1$subexpression$1"],
857
    postprocess: function arrpush(d) {
858
      return d[0].concat([d[1]]);
39✔
859
    },
860
  },
861
  { name: "expressions$ebnf$2$subexpression$1", symbols: [{ literal: "," }] },
862
  { name: "expressions$ebnf$2", symbols: ["expressions$ebnf$2$subexpression$1"], postprocess: id },
863
  {
864
    name: "expressions$ebnf$2",
865
    symbols: [],
866
    postprocess: function (d) {
867
      return null;
117✔
868
    },
869
  },
870
  {
871
    name: "expressions",
872
    symbols: ["expression", "expressions$ebnf$1", "expressions$ebnf$2"],
873
    postprocess: flatList,
874
  },
875
  {
876
    name: "stringLit",
877
    symbols: [
878
      pythonLexer.has("string_triple_double")
3!
879
        ? { type: "string_triple_double" }
880
        : string_triple_double,
881
    ],
882
    postprocess: astString,
883
  },
884
  {
885
    name: "stringLit",
886
    symbols: [
887
      pythonLexer.has("string_triple_single")
3!
888
        ? { type: "string_triple_single" }
889
        : string_triple_single,
890
    ],
891
    postprocess: astString,
892
  },
893
  {
894
    name: "stringLit",
895
    symbols: [pythonLexer.has("string_double") ? { type: "string_double" } : string_double],
3!
896
    postprocess: astString,
897
  },
898
  {
899
    name: "stringLit",
900
    symbols: [pythonLexer.has("string_single") ? { type: "string_single" } : string_single],
3!
901
    postprocess: astString,
902
  },
903
];
904
let ParserStart = "program";
3✔
905
export default { Lexer, ParserRules, ParserStart };
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc