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

source-academy / py-slang / 23331903244

20 Mar 2026 06:34AM UTC coverage: 40.054% (+5.1%) from 34.925%
23331903244

Pull #69

github

web-flow
Merge 470cd13b2 into 8d5d05e29
Pull Request #69: Nearley dsl

421 of 1377 branches covered (30.57%)

Branch coverage included in aggregate %.

485 of 614 new or added lines in 20 files covered. (78.99%)

37 existing lines in 2 files now uncovered.

1347 of 3037 relevant lines covered (44.35%)

65.24 hits per line

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

65.67
/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,446✔
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;
24✔
14
const list = ([x]) => [x];
91✔
15
const drop = () => [];
253✔
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);
26!
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) => {
26✔
24
    switch (ch[0]) {
6!
25
      case "n":
26
        return "\n";
1✔
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
const Lexer = pythonLexer;
3✔
59
const ParserRules = [
3✔
60
  {
61
    name: "file",
62
    symbols: ["stmts"],
63
    postprocess: ([stmts]) => {
64
      const filtered = (stmts || []).filter(Boolean);
543!
65
      const start = filtered[0]
543✔
66
        ? filtered[0].startToken
67
        : toAstToken({ type: "newline", value: "", line: 1, col: 1, offset: 0 });
68
      const end = filtered.length > 0 ? filtered[filtered.length - 1].endToken : start;
543✔
69
      return new StmtNS.FileInput(start, end, filtered, []);
543✔
70
    },
71
  },
72
  { name: "stmts", symbols: [], postprocess: drop },
73
  { name: "stmts", symbols: ["stmts", "stmt"], postprocess: ([xs, x]) => (x ? [...xs, x] : xs) },
271!
74
  {
75
    name: "stmts",
76
    symbols: ["stmts", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
77
    postprocess: id,
78
  },
79
  {
80
    name: "stmts",
81
    symbols: ["stmts", pythonLexer.has("ws") ? { type: "ws" } : ws],
3!
82
    postprocess: id,
83
  },
84
  { name: "stmt", symbols: ["simple_stmt"], postprocess: id },
85
  { name: "stmt", symbols: ["compound_stmt"], postprocess: id },
86
  {
87
    name: "simple_stmt",
88
    symbols: ["small_stmt", "_", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
89
    postprocess: id,
90
  },
91
  { name: "small_stmt", symbols: ["pass_stmt"], postprocess: id },
92
  { name: "small_stmt", symbols: ["break_stmt"], postprocess: id },
93
  { name: "small_stmt", symbols: ["continue_stmt"], postprocess: id },
94
  { name: "small_stmt", symbols: ["return_stmt"], postprocess: id },
95
  { name: "small_stmt", symbols: ["assign_stmt"], postprocess: id },
96
  { name: "small_stmt", symbols: ["import_stmt"], postprocess: id },
97
  { name: "small_stmt", symbols: ["global_stmt"], postprocess: id },
98
  { name: "small_stmt", symbols: ["nonlocal_stmt"], postprocess: id },
99
  { name: "small_stmt", symbols: ["assert_stmt"], postprocess: id },
100
  { name: "small_stmt", symbols: ["expr_stmt"], postprocess: id },
101
  {
102
    name: "pass_stmt",
103
    symbols: [pythonLexer.has("kw_pass") ? { type: "kw_pass" } : kw_pass],
3!
104
    postprocess: ([t]) => {
105
      const tok = toAstToken(t);
30✔
106
      return new StmtNS.Pass(tok, tok);
30✔
107
    },
108
  },
109
  {
110
    name: "break_stmt",
111
    symbols: [pythonLexer.has("kw_break") ? { type: "kw_break" } : kw_break],
3!
112
    postprocess: ([t]) => {
113
      const tok = toAstToken(t);
3✔
114
      return new StmtNS.Break(tok, tok);
3✔
115
    },
116
  },
117
  {
118
    name: "continue_stmt",
119
    symbols: [pythonLexer.has("kw_continue") ? { type: "kw_continue" } : kw_continue],
3!
120
    postprocess: ([t]) => {
121
      const tok = toAstToken(t);
3✔
122
      return new StmtNS.Continue(tok, tok);
3✔
123
    },
124
  },
125
  {
126
    name: "return_stmt",
127
    symbols: [pythonLexer.has("kw_return") ? { type: "kw_return" } : kw_return, "_", "test"],
3!
128
    postprocess: ([kw, , expr]) => new StmtNS.Return(toAstToken(kw), expr.endToken, expr),
44✔
129
  },
130
  {
131
    name: "return_stmt",
132
    symbols: [pythonLexer.has("kw_return") ? { type: "kw_return" } : kw_return],
3!
133
    postprocess: ([t]) => {
134
      const tok = toAstToken(t);
33✔
135
      return new StmtNS.Return(tok, tok, null);
33✔
136
    },
137
  },
138
  {
139
    name: "assign_stmt",
140
    symbols: [
141
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
142
      "_",
143
      { literal: ":" },
144
      "_",
145
      "test",
146
      "_",
147
      { literal: "=" },
148
      "_",
149
      "test",
150
    ],
151
    postprocess: ([n, , , , ann, , , , v]) => {
152
      const tok = toAstToken(n);
2✔
153
      return new StmtNS.AnnAssign(tok, v.endToken, new ExprNS.Variable(tok, tok, tok), v, ann);
2✔
154
    },
155
  },
156
  {
157
    name: "assign_stmt",
158
    symbols: [
159
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
160
      "_",
161
      { literal: ":" },
162
      "_",
163
      "test",
164
    ],
165
    postprocess: ([n, , , , ann]) => {
166
      const nameTok = toAstToken(n);
3✔
167
      const dummyVal = new ExprNS.None(ann.endToken, ann.endToken);
3✔
168
      return new StmtNS.AnnAssign(
3✔
169
        nameTok,
170
        ann.endToken,
171
        new ExprNS.Variable(nameTok, nameTok, nameTok),
172
        dummyVal,
173
        ann,
174
      );
175
    },
176
  },
177
  {
178
    name: "assign_stmt",
179
    symbols: [
180
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
181
      "_",
182
      { literal: "=" },
183
      "_",
184
      "test",
185
    ],
186
    postprocess: ([n, , , , v]) => {
187
      const tok = toAstToken(n);
87✔
188
      return new StmtNS.Assign(tok, v.endToken, new ExprNS.Variable(tok, tok, tok), v);
87✔
189
    },
190
  },
191
  {
192
    name: "import_stmt",
193
    symbols: [
194
      pythonLexer.has("kw_from") ? { type: "kw_from" } : kw_from,
3!
195
      "_",
196
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
197
      "_",
198
      pythonLexer.has("kw_import") ? { type: "kw_import" } : kw_import,
3!
199
      "_",
200
      "import_names",
201
    ],
202
    postprocess: ([kw, , mod, , , , names]) =>
203
      new StmtNS.FromImport(toAstToken(kw), names[names.length - 1], toAstToken(mod), names),
4✔
204
  },
205
  {
206
    name: "import_names",
207
    symbols: [pythonLexer.has("identifier") ? { type: "identifier" } : identifier],
3!
208
    postprocess: ([t]) => [toAstToken(t)],
2✔
209
  },
210
  {
211
    name: "import_names",
212
    symbols: [{ literal: "(" }, "_", "name_list", "_", { literal: ")" }],
213
    postprocess: ([, , ns]) => ns,
2✔
214
  },
215
  {
216
    name: "name_list",
217
    symbols: [pythonLexer.has("identifier") ? { type: "identifier" } : identifier],
3!
218
    postprocess: ([t]) => [toAstToken(t)],
2✔
219
  },
220
  {
221
    name: "name_list",
222
    symbols: [
223
      "name_list",
224
      "_",
225
      { literal: "," },
226
      "_",
227
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
228
    ],
229
    postprocess: ([ns, , , , t]) => [...ns, toAstToken(t)],
2✔
230
  },
231
  {
232
    name: "global_stmt",
233
    symbols: [
234
      pythonLexer.has("kw_global") ? { type: "kw_global" } : kw_global,
3!
235
      "_",
236
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
237
    ],
238
    postprocess: ([kw, , n]) => new StmtNS.Global(toAstToken(kw), toAstToken(n), toAstToken(n)),
1✔
239
  },
240
  {
241
    name: "nonlocal_stmt",
242
    symbols: [
243
      pythonLexer.has("kw_nonlocal") ? { type: "kw_nonlocal" } : kw_nonlocal,
3!
244
      "_",
245
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
246
    ],
247
    postprocess: ([kw, , n]) => new StmtNS.NonLocal(toAstToken(kw), toAstToken(n), toAstToken(n)),
6✔
248
  },
249
  {
250
    name: "assert_stmt",
251
    symbols: [pythonLexer.has("kw_assert") ? { type: "kw_assert" } : kw_assert, "_", "test"],
3!
252
    postprocess: ([kw, , e]) => new StmtNS.Assert(toAstToken(kw), e.endToken, e),
5✔
253
  },
254
  {
255
    name: "expr_stmt",
256
    symbols: ["test"],
257
    postprocess: ([e]) => new StmtNS.SimpleExpr(e.startToken, e.endToken, e),
383✔
258
  },
259
  { name: "compound_stmt", symbols: ["if_stmt"], postprocess: id },
260
  { name: "compound_stmt", symbols: ["while_stmt"], postprocess: id },
261
  { name: "compound_stmt", symbols: ["for_stmt"], postprocess: id },
262
  { name: "compound_stmt", symbols: ["funcdef"], postprocess: id },
263
  {
264
    name: "if_stmt",
265
    symbols: [
266
      pythonLexer.has("kw_if") ? { type: "kw_if" } : kw_if,
3!
267
      "_",
268
      "test",
269
      "_",
270
      { literal: ":" },
271
      "_",
272
      "suite",
273
      "elif_chain",
274
    ],
275
    postprocess: ([kw, , test, , , , body, else_]) =>
276
      new StmtNS.If(
39✔
277
        toAstToken(kw),
278
        else_ && else_.length > 0
98✔
279
          ? else_[else_.length - 1].endToken
280
          : body[body.length - 1].endToken,
281
        test,
282
        body,
283
        else_,
284
      ),
285
  },
286
  {
287
    name: "elif_chain",
288
    symbols: [
289
      "_",
290
      pythonLexer.has("kw_elif") ? { type: "kw_elif" } : kw_elif,
3!
291
      "_",
292
      "test",
293
      "_",
294
      { literal: ":" },
295
      "_",
296
      "suite",
297
      "elif_chain",
298
    ],
299
    postprocess: ([, kw, , test, , , , body, else_]) => [
11✔
300
      new StmtNS.If(
301
        toAstToken(kw),
302
        else_ && else_.length > 0
28✔
303
          ? else_[else_.length - 1].endToken
304
          : body[body.length - 1].endToken,
305
        test,
306
        body,
307
        else_,
308
      ),
309
    ],
310
  },
311
  {
312
    name: "elif_chain",
313
    symbols: [
314
      "_",
315
      pythonLexer.has("kw_else") ? { type: "kw_else" } : kw_else,
3!
316
      "_",
317
      { literal: ":" },
318
      "_",
319
      "suite",
320
    ],
321
    postprocess: ([, , , , , body]) => body,
15✔
322
  },
323
  { name: "elif_chain", symbols: [], postprocess: nil },
324
  {
325
    name: "while_stmt",
326
    symbols: [
327
      pythonLexer.has("kw_while") ? { type: "kw_while" } : kw_while,
3!
328
      "_",
329
      "test",
330
      "_",
331
      { literal: ":" },
332
      "_",
333
      "suite",
334
    ],
335
    postprocess: ([kw, , test, , , , body]) =>
336
      new StmtNS.While(toAstToken(kw), body[body.length - 1].endToken, test, body),
8✔
337
  },
338
  {
339
    name: "for_stmt",
340
    symbols: [
341
      pythonLexer.has("kw_for") ? { type: "kw_for" } : kw_for,
3!
342
      "_",
343
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
344
      "_",
345
      pythonLexer.has("kw_in") ? { type: "kw_in" } : kw_in,
3!
346
      "_",
347
      "test",
348
      "_",
349
      { literal: ":" },
350
      "_",
351
      "suite",
352
    ],
353
    postprocess: ([kw, , target, , , , iter, , , , body]) =>
354
      new StmtNS.For(
7✔
355
        toAstToken(kw),
356
        body[body.length - 1].endToken,
357
        toAstToken(target),
358
        iter,
359
        body,
360
      ),
361
  },
362
  {
363
    name: "funcdef",
364
    symbols: [
365
      pythonLexer.has("kw_def") ? { type: "kw_def" } : kw_def,
3!
366
      "_",
367
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
368
      "_",
369
      "params",
370
      "_",
371
      { literal: ":" },
372
      "_",
373
      "suite",
374
    ],
375
    postprocess: ([kw, , name, , params, , , , body]) =>
376
      new StmtNS.FunctionDef(
44✔
377
        toAstToken(kw),
378
        body[body.length - 1].endToken,
379
        toAstToken(name),
380
        params,
381
        body,
382
        [],
383
      ),
384
  },
385
  { name: "params", symbols: [{ literal: "(" }, "_nl", { literal: ")" }], postprocess: drop },
386
  {
387
    name: "params",
388
    symbols: [{ literal: "(" }, "_nl", "param_list", "_nl", { literal: ")" }],
389
    postprocess: ([, , ps]) => ps,
11✔
390
  },
391
  {
392
    name: "param_list",
393
    symbols: [pythonLexer.has("identifier") ? { type: "identifier" } : identifier],
3!
394
    postprocess: ([t]) => [toAstToken(t)],
22✔
395
  },
396
  {
397
    name: "param_list",
398
    symbols: [
399
      "param_list",
400
      "_nl",
401
      { literal: "," },
402
      "_nl",
403
      pythonLexer.has("identifier") ? { type: "identifier" } : identifier,
3!
404
    ],
405
    postprocess: ([ps, , , , t]) => [...ps, toAstToken(t)],
12✔
406
  },
407
  { name: "suite", symbols: ["simple_stmt"], postprocess: list },
408
  {
409
    name: "suite",
410
    symbols: [
411
      pythonLexer.has("newline") ? { type: "newline" } : newline,
3!
412
      pythonLexer.has("indent") ? { type: "indent" } : indent,
3!
413
      "suite_stmts",
414
      pythonLexer.has("dedent") ? { type: "dedent" } : dedent,
3!
415
    ],
416
    postprocess: ([, , stmts]) => stmts,
98✔
417
  },
418
  { name: "suite_stmts", symbols: ["_", "stmt"], postprocess: ([, s]) => [s] },
107✔
419
  {
420
    name: "suite_stmts",
421
    symbols: ["suite_stmts", "_", "stmt"],
422
    postprocess: ([xs, , s]) => [...xs, s],
36✔
423
  },
424
  {
425
    name: "suite_stmts",
426
    symbols: ["suite_stmts", "_", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
427
    postprocess: id,
428
  },
429
  {
430
    name: "test",
431
    symbols: [
432
      "or_test",
433
      "_",
434
      pythonLexer.has("kw_if") ? { type: "kw_if" } : kw_if,
3!
435
      "_",
436
      "or_test",
437
      "_",
438
      pythonLexer.has("kw_else") ? { type: "kw_else" } : kw_else,
3!
439
      "_",
440
      "test",
441
    ],
442
    postprocess: ([cons, , , , test, , , , alt]) =>
443
      new ExprNS.Ternary(cons.startToken, alt.endToken, test, cons, alt),
8✔
444
  },
445
  { name: "test", symbols: ["or_test"], postprocess: id },
446
  { name: "test", symbols: ["lambdef"], postprocess: id },
447
  {
448
    name: "lambdef",
449
    symbols: [
450
      pythonLexer.has("kw_lambda") ? { type: "kw_lambda" } : kw_lambda,
3!
451
      "_",
452
      "param_list",
453
      "_",
454
      { literal: ":" },
455
      "_",
456
      "test",
457
    ],
458
    postprocess: ([kw, , params, , , , body]) =>
459
      new ExprNS.Lambda(toAstToken(kw), body.endToken, params, body),
16✔
460
  },
461
  {
462
    name: "lambdef",
463
    symbols: [
464
      pythonLexer.has("kw_lambda") ? { type: "kw_lambda" } : kw_lambda,
3!
465
      "_",
466
      "param_list",
467
      "_",
468
      { literal: "::" },
469
      "_",
470
      "suite",
471
    ],
472
    postprocess: ([kw, , params, , , , body]) =>
NEW
473
      new ExprNS.MultiLambda(toAstToken(kw), body[body.length - 1].endToken, params, body, []),
×
474
  },
475
  {
476
    name: "lambdef",
477
    symbols: [
478
      pythonLexer.has("kw_lambda") ? { type: "kw_lambda" } : kw_lambda,
3!
479
      "_",
480
      { literal: ":" },
481
      "_",
482
      "test",
483
    ],
484
    postprocess: ([kw, , , , body]) => new ExprNS.Lambda(toAstToken(kw), body.endToken, [], body),
1✔
485
  },
486
  {
487
    name: "lambdef",
488
    symbols: [
489
      pythonLexer.has("kw_lambda") ? { type: "kw_lambda" } : kw_lambda,
3!
490
      "_",
491
      { literal: "::" },
492
      "_",
493
      "suite",
494
    ],
495
    postprocess: ([kw, , , , body]) =>
NEW
496
      new ExprNS.MultiLambda(toAstToken(kw), body[body.length - 1].endToken, [], body, []),
×
497
  },
498
  {
499
    name: "or_test",
500
    symbols: [
501
      "or_test",
502
      "_",
503
      pythonLexer.has("kw_or") ? { type: "kw_or" } : kw_or,
3!
504
      "_",
505
      "and_test",
506
    ],
507
    postprocess: ([left, , op, , right]) =>
508
      new ExprNS.BoolOp(left.startToken, right.endToken, left, toAstToken(op), right),
14✔
509
  },
510
  { name: "or_test", symbols: ["and_test"], postprocess: id },
511
  {
512
    name: "and_test",
513
    symbols: [
514
      "and_test",
515
      "_",
516
      pythonLexer.has("kw_and") ? { type: "kw_and" } : kw_and,
3!
517
      "_",
518
      "not_test",
519
    ],
520
    postprocess: ([left, , op, , right]) =>
521
      new ExprNS.BoolOp(left.startToken, right.endToken, left, toAstToken(op), right),
15✔
522
  },
523
  { name: "and_test", symbols: ["not_test"], postprocess: id },
524
  {
525
    name: "not_test",
526
    symbols: [pythonLexer.has("kw_not") ? { type: "kw_not" } : kw_not, "_", "not_test"],
3!
527
    postprocess: ([op, , arg]) =>
528
      new ExprNS.Unary(toAstToken(op), arg.endToken, toAstToken(op), arg),
6✔
529
  },
530
  { name: "not_test", symbols: ["comparison"], postprocess: id },
531
  {
532
    name: "comparison",
533
    symbols: ["comparison", "_", "comp_op", "_", "arith"],
534
    postprocess: ([left, , op, , right]) =>
535
      new ExprNS.Compare(left.startToken, right.endToken, left, op, right),
38✔
536
  },
537
  { name: "comparison", symbols: ["arith"], postprocess: id },
538
  {
539
    name: "comp_op",
540
    symbols: [pythonLexer.has("less") ? { type: "less" } : less],
3!
541
    postprocess: ([t]) => toAstToken(t),
13✔
542
  },
543
  {
544
    name: "comp_op",
545
    symbols: [pythonLexer.has("greater") ? { type: "greater" } : greater],
3!
546
    postprocess: ([t]) => toAstToken(t),
7✔
547
  },
548
  {
549
    name: "comp_op",
550
    symbols: [pythonLexer.has("doubleequal") ? { type: "doubleequal" } : doubleequal],
3!
551
    postprocess: ([t]) => toAstToken(t),
7✔
552
  },
553
  {
554
    name: "comp_op",
555
    symbols: [pythonLexer.has("greaterequal") ? { type: "greaterequal" } : greaterequal],
3!
556
    postprocess: ([t]) => toAstToken(t),
1✔
557
  },
558
  {
559
    name: "comp_op",
560
    symbols: [pythonLexer.has("lessequal") ? { type: "lessequal" } : lessequal],
3!
561
    postprocess: ([t]) => toAstToken(t),
1✔
562
  },
563
  {
564
    name: "comp_op",
565
    symbols: [pythonLexer.has("notequal") ? { type: "notequal" } : notequal],
3!
566
    postprocess: ([t]) => toAstToken(t),
1✔
567
  },
568
  {
569
    name: "comp_op",
570
    symbols: [pythonLexer.has("kw_in") ? { type: "kw_in" } : kw_in],
3!
571
    postprocess: ([t]) => toAstToken(t),
1✔
572
  },
573
  {
574
    name: "comp_op",
575
    symbols: [
576
      pythonLexer.has("kw_not") ? { type: "kw_not" } : kw_not,
3!
577
      "_",
578
      pythonLexer.has("kw_in") ? { type: "kw_in" } : kw_in,
3!
579
    ],
580
    postprocess: ([t, ,]) => {
581
      const tok = toAstToken(t);
3✔
582
      tok.lexeme = "not in";
3✔
583
      return tok;
3✔
584
    },
585
  },
586
  {
587
    name: "comp_op",
588
    symbols: [pythonLexer.has("kw_is") ? { type: "kw_is" } : kw_is],
3!
589
    postprocess: ([t]) => toAstToken(t),
4✔
590
  },
591
  {
592
    name: "comp_op",
593
    symbols: [
594
      pythonLexer.has("kw_is") ? { type: "kw_is" } : kw_is,
3!
595
      "_",
596
      pythonLexer.has("kw_not") ? { type: "kw_not" } : kw_not,
3!
597
    ],
598
    postprocess: ([t, ,]) => {
599
      const tok = toAstToken(t);
3✔
600
      tok.lexeme = "is not";
3✔
601
      return tok;
3✔
602
    },
603
  },
604
  {
605
    name: "arith",
606
    symbols: ["arith", "_", "arith_op", "_", "term"],
607
    postprocess: ([left, , op, , right]) =>
608
      new ExprNS.Binary(left.startToken, right.endToken, left, op, right),
53✔
609
  },
610
  { name: "arith", symbols: ["term"], postprocess: id },
611
  {
612
    name: "arith_op",
613
    symbols: [pythonLexer.has("plus") ? { type: "plus" } : plus],
3!
614
    postprocess: ([t]) => toAstToken(t),
43✔
615
  },
616
  {
617
    name: "arith_op",
618
    symbols: [pythonLexer.has("minus") ? { type: "minus" } : minus],
3!
619
    postprocess: ([t]) => toAstToken(t),
6✔
620
  },
621
  {
622
    name: "term",
623
    symbols: ["term", "_", "term_op", "_", "factor"],
624
    postprocess: ([left, , op, , right]) =>
625
      new ExprNS.Binary(left.startToken, right.endToken, left, op, right),
19✔
626
  },
627
  { name: "term", symbols: ["factor"], postprocess: id },
628
  {
629
    name: "term_op",
630
    symbols: [pythonLexer.has("star") ? { type: "star" } : star],
3!
631
    postprocess: ([t]) => toAstToken(t),
10✔
632
  },
633
  {
634
    name: "term_op",
635
    symbols: [pythonLexer.has("slash") ? { type: "slash" } : slash],
3!
636
    postprocess: ([t]) => toAstToken(t),
5✔
637
  },
638
  {
639
    name: "term_op",
640
    symbols: [pythonLexer.has("percent") ? { type: "percent" } : percent],
3!
641
    postprocess: ([t]) => toAstToken(t),
1✔
642
  },
643
  {
644
    name: "term_op",
645
    symbols: [pythonLexer.has("doubleslash") ? { type: "doubleslash" } : doubleslash],
3!
646
    postprocess: ([t]) => toAstToken(t),
3✔
647
  },
648
  {
649
    name: "factor",
650
    symbols: [pythonLexer.has("plus") ? { type: "plus" } : plus, "_", "factor"],
3!
651
    postprocess: ([op, , arg]) =>
NEW
652
      new ExprNS.Unary(toAstToken(op), arg.endToken, toAstToken(op), arg),
×
653
  },
654
  {
655
    name: "factor",
656
    symbols: [pythonLexer.has("minus") ? { type: "minus" } : minus, "_", "factor"],
3!
657
    postprocess: ([op, , arg]) =>
658
      new ExprNS.Unary(toAstToken(op), arg.endToken, toAstToken(op), arg),
7✔
659
  },
660
  { name: "factor", symbols: ["power"], postprocess: id },
661
  {
662
    name: "power",
663
    symbols: [
664
      "atom_expr",
665
      "_",
666
      pythonLexer.has("doublestar") ? { type: "doublestar" } : doublestar,
3!
667
      "_",
668
      "factor",
669
    ],
670
    postprocess: ([left, , op, , right]) =>
671
      new ExprNS.Binary(left.startToken, right.endToken, left, toAstToken(op), right),
6✔
672
  },
673
  { name: "power", symbols: ["atom_expr"], postprocess: id },
674
  {
675
    name: "atom_expr",
676
    symbols: [
677
      "atom_expr",
678
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
679
      "_",
680
      "test",
681
      "_",
682
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
683
    ],
684
    postprocess: ([obj, , , idx, , rsqb]) =>
685
      new ExprNS.Subscript(obj.startToken, toAstToken(rsqb), obj, idx),
2✔
686
  },
687
  {
688
    name: "atom_expr",
689
    symbols: ["atom_expr", { literal: "(" }, "_", "args", "_", { literal: ")" }],
690
    postprocess: ([callee, , , args, , rparen]) =>
691
      new ExprNS.Call(callee.startToken, toAstToken(rparen), callee, args),
49✔
692
  },
693
  {
694
    name: "atom_expr",
695
    symbols: ["atom_expr", { literal: "(" }, "_", { literal: ")" }],
696
    postprocess: ([callee, , , rparen]) =>
697
      new ExprNS.Call(callee.startToken, toAstToken(rparen), callee, []),
24✔
698
  },
699
  { name: "atom_expr", symbols: ["atom"], postprocess: id },
700
  { name: "args", symbols: ["test"], postprocess: list },
701
  {
702
    name: "args",
703
    symbols: ["args", "_", { literal: "," }, "_", "test"],
704
    postprocess: ([as, , , , a]) => [...as, a],
31✔
705
  },
706
  { name: "args", symbols: ["test", "_", { literal: "," }], postprocess: list },
707
  {
708
    name: "atom",
709
    symbols: [{ literal: "(" }, "_", "test", "_", { literal: ")" }],
710
    postprocess: ([, , e]) => new ExprNS.Grouping(e.startToken, e.endToken, e),
10✔
711
  },
712
  {
713
    name: "atom",
714
    symbols: [
715
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
716
      "_",
717
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
718
    ],
719
    postprocess: ([l, , r]) => new ExprNS.List(toAstToken(l), toAstToken(r), []),
4✔
720
  },
721
  {
722
    name: "atom",
723
    symbols: [
724
      pythonLexer.has("lsqb") ? { type: "lsqb" } : lsqb,
3!
725
      "_",
726
      "args",
727
      "_",
728
      pythonLexer.has("rsqb") ? { type: "rsqb" } : rsqb,
3!
729
    ],
730
    postprocess: ([l, , elems, , r]) => new ExprNS.List(toAstToken(l), toAstToken(r), elems),
7✔
731
  },
732
  {
733
    name: "atom",
734
    symbols: [pythonLexer.has("identifier") ? { type: "identifier" } : identifier],
3!
735
    postprocess: ([t]) => {
736
      const tok = toAstToken(t);
266✔
737
      return new ExprNS.Variable(tok, tok, tok);
266✔
738
    },
739
  },
740
  {
741
    name: "atom",
742
    symbols: [pythonLexer.has("float") ? { type: "float" } : float],
3!
743
    postprocess: ([t]) => {
744
      const tok = toAstToken(t);
11✔
745
      return new ExprNS.Literal(tok, tok, parseFloat(t.value));
11✔
746
    },
747
  },
748
  {
749
    name: "atom",
750
    symbols: [pythonLexer.has("bigint") ? { type: "bigint" } : bigint],
3!
751
    postprocess: ([t]) => {
752
      const tok = toAstToken(t);
260✔
753
      return new ExprNS.BigIntLiteral(tok, tok, t.value);
260✔
754
    },
755
  },
756
  {
757
    name: "atom",
758
    symbols: [pythonLexer.has("hex") ? { type: "hex" } : hex],
3!
759
    postprocess: ([t]) => {
760
      const tok = toAstToken(t);
1✔
761
      return new ExprNS.BigIntLiteral(tok, tok, t.value);
1✔
762
    },
763
  },
764
  {
765
    name: "atom",
766
    symbols: [pythonLexer.has("octal") ? { type: "octal" } : octal],
3!
767
    postprocess: ([t]) => {
768
      const tok = toAstToken(t);
1✔
769
      return new ExprNS.BigIntLiteral(tok, tok, t.value);
1✔
770
    },
771
  },
772
  {
773
    name: "atom",
774
    symbols: [pythonLexer.has("binary") ? { type: "binary" } : binary],
3!
775
    postprocess: ([t]) => {
776
      const tok = toAstToken(t);
1✔
777
      return new ExprNS.BigIntLiteral(tok, tok, t.value);
1✔
778
    },
779
  },
780
  {
781
    name: "atom",
782
    symbols: [pythonLexer.has("complex") ? { type: "complex" } : complex],
3!
783
    postprocess: ([t]) => {
784
      const tok = toAstToken(t);
12✔
785
      return new ExprNS.Complex(tok, tok, t.value);
12✔
786
    },
787
  },
788
  { name: "atom", symbols: ["string"], postprocess: id },
789
  {
790
    name: "atom",
791
    symbols: [pythonLexer.has("kw_None") ? { type: "kw_None" } : kw_None],
3!
792
    postprocess: ([t]) => {
793
      const tok = toAstToken(t);
7✔
794
      return new ExprNS.None(tok, tok);
7✔
795
    },
796
  },
797
  {
798
    name: "atom",
799
    symbols: [pythonLexer.has("kw_True") ? { type: "kw_True" } : kw_True],
3!
800
    postprocess: ([t]) => {
801
      const tok = toAstToken(t);
21✔
802
      return new ExprNS.Literal(tok, tok, true);
21✔
803
    },
804
  },
805
  {
806
    name: "atom",
807
    symbols: [pythonLexer.has("kw_False") ? { type: "kw_False" } : kw_False],
3!
808
    postprocess: ([t]) => {
809
      const tok = toAstToken(t);
1✔
810
      return new ExprNS.Literal(tok, tok, false);
1✔
811
    },
812
  },
813
  {
814
    name: "string",
815
    symbols: [
816
      pythonLexer.has("stringTripleDouble") ? { type: "stringTripleDouble" } : stringTripleDouble,
3!
817
    ],
818
    postprocess: ([t]) => {
NEW
819
      const tok = toAstToken(t);
×
NEW
820
      return new ExprNS.Literal(tok, tok, stripQuotes(t.value));
×
821
    },
822
  },
823
  {
824
    name: "string",
825
    symbols: [
826
      pythonLexer.has("stringTripleSingle") ? { type: "stringTripleSingle" } : stringTripleSingle,
3!
827
    ],
828
    postprocess: ([t]) => {
NEW
829
      const tok = toAstToken(t);
×
NEW
830
      return new ExprNS.Literal(tok, tok, stripQuotes(t.value));
×
831
    },
832
  },
833
  {
834
    name: "string",
835
    symbols: [pythonLexer.has("stringDouble") ? { type: "stringDouble" } : stringDouble],
3!
836
    postprocess: ([t]) => {
837
      const tok = toAstToken(t);
26✔
838
      return new ExprNS.Literal(tok, tok, stripQuotes(t.value));
26✔
839
    },
840
  },
841
  {
842
    name: "string",
843
    symbols: [pythonLexer.has("stringSingle") ? { type: "stringSingle" } : stringSingle],
3!
844
    postprocess: ([t]) => {
NEW
845
      const tok = toAstToken(t);
×
NEW
846
      return new ExprNS.Literal(tok, tok, stripQuotes(t.value));
×
847
    },
848
  },
849
  { name: "_", symbols: [] },
850
  { name: "_", symbols: [pythonLexer.has("ws") ? { type: "ws" } : ws] },
3!
851
  { name: "__", symbols: [pythonLexer.has("ws") ? { type: "ws" } : ws] },
3!
852
  { name: "_nl", symbols: [], postprocess: id },
853
  { name: "_nl", symbols: ["_nl", pythonLexer.has("ws") ? { type: "ws" } : ws], postprocess: id },
3!
854
  {
855
    name: "_nl",
856
    symbols: ["_nl", pythonLexer.has("newline") ? { type: "newline" } : newline],
3!
857
    postprocess: id,
858
  },
859
  {
860
    name: "_nl",
861
    symbols: ["_nl", pythonLexer.has("indent") ? { type: "indent" } : indent],
3!
862
    postprocess: id,
863
  },
864
  {
865
    name: "_nl",
866
    symbols: ["_nl", pythonLexer.has("dedent") ? { type: "dedent" } : dedent],
3!
867
    postprocess: id,
868
  },
869
];
870
const ParserStart = "file";
3✔
871
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