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

source-academy / py-slang / 23740908439

30 Mar 2026 10:48AM UTC coverage: 60.529% (+1.1%) from 59.421%
23740908439

Pull #130

github

web-flow
Merge f90e51715 into e908db8d6
Pull Request #130: Add spread expression

726 of 1423 branches covered (51.02%)

Branch coverage included in aggregate %.

90 of 96 new or added lines in 13 files covered. (93.75%)

4 existing lines in 2 files now uncovered.

2456 of 3834 relevant lines covered (64.06%)

3201.14 hits per line

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

90.05
/src/parser/python-grammar.ts
1
// Generated automatically by nearley, version 2.20.1
2
// http://github.com/Hardmath123/nearley
3
function id(x: unknown[]) {
4
  return x[0];
1,055,239✔
5
}
6

7
import { ExprNS, FunctionParam, StmtNS } from "../ast-types";
7✔
8
import { Token } from "../tokenizer";
9
import pythonLexer from "./lexer";
7✔
10
import { toAstToken, toFunctionParam } from "./token-bridge";
7✔
11

12
const list = <T>([x]: [T]) => [x];
7✔
13
const drop = () => [];
65✔
14

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

57
// ── Leaf AST constructors (token → node) ────────────────────────────────
58
const astVariable = ([t]: [moo.Token]) => {
7✔
59
  const k = toAstToken(t);
58,009✔
60
  return new ExprNS.Variable(k, k, k);
58,009✔
61
};
62
const astBigInt = ([t]: [moo.Token]) => {
7✔
63
  const k = toAstToken(t);
3,259✔
64
  return new ExprNS.BigIntLiteral(k, k, t.value);
3,259✔
65
};
66
const astComplex = ([t]: [moo.Token]) => {
7✔
67
  const k = toAstToken(t);
258✔
68
  return new ExprNS.Complex(k, k, t.value);
258✔
69
};
70
const astNone = ([t]: [moo.Token]) => {
7✔
71
  const k = toAstToken(t);
1,950✔
72
  return new ExprNS.None(k, k);
1,950✔
73
};
74
const astString = ([t]: [moo.Token]) => {
7✔
75
  const k = toAstToken(t);
5,049✔
76
  return new ExprNS.Literal(k, k, stripQuotes(t.value));
5,049✔
77
};
78
const astTrue = ([t]: [moo.Token]) => {
7✔
79
  const k = toAstToken(t);
539✔
80
  return new ExprNS.Literal(k, k, true);
539✔
81
};
82
const astFalse = ([t]: [moo.Token]) => {
7✔
83
  const k = toAstToken(t);
485✔
84
  return new ExprNS.Literal(k, k, false);
485✔
85
};
86

87
// ── Operator AST constructors (children → node) ────────────────────────
88
const astBinary = ([l, op, r]: [ExprNS.Expr, Token, ExprNS.Expr]) =>
7✔
89
  new ExprNS.Binary(l.startToken, r.endToken, l, op, r);
2,530✔
90
const astBinaryTok = ([l, op, r]: [ExprNS.Expr, moo.Token, ExprNS.Expr]) =>
7✔
91
  new ExprNS.Binary(l.startToken, r.endToken, l, toAstToken(op), r);
54✔
92
const astBoolOp = ([l, op, r]: [ExprNS.Expr, moo.Token, ExprNS.Expr]) =>
7✔
93
  new ExprNS.BoolOp(l.startToken, r.endToken, l, toAstToken(op), r);
4,751✔
94
const astUnary = ([op, arg]: [moo.Token, ExprNS.Expr]) =>
7✔
95
  new ExprNS.Unary(toAstToken(op), arg.endToken, toAstToken(op), arg);
685✔
96
const astCompare = ([l, op, r]: [ExprNS.Expr, Token, ExprNS.Expr]) =>
7✔
97
  new ExprNS.Compare(l.startToken, r.endToken, l, op, r);
3,065✔
98

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

© 2026 Coveralls, Inc