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

source-academy / py-slang / 23787023057

31 Mar 2026 08:04AM UTC coverage: 60.472% (+0.9%) from 59.608%
23787023057

push

github

web-flow
Add spread expression (#130)

* add spread expression to docs

* remove pipe symbol from if statement

* remove unused package

* fix python 3 bnf

* Fix types of Python grammar

* feat: add spread expression support and sync grammar with BNF specs

- Add Starred AST node for spread syntax (*expr)
- Add spread_expressions grammar rule for function call arguments
- Support rest parameters (*args) in lambda expressions
- Add NoSpreadValidator for chapters 1-2, extend NoRestParams to lambdas
- Implement spread flattening in CSE machine interpreter
- Add rest parameter collection in createEnvironment
- Add visitStarredExpr to resolver, traverse visitor, WASM stub

* fix formating

* follow gemini advice

---------

Co-authored-by: jonas.ong <jonasong2@gmail.com>
Co-authored-by: jonasongg <120372506+jonasongg@users.noreply.github.com>
Co-authored-by: Aarav Malani <aarav.malani@gmail.com>

722 of 1419 branches covered (50.88%)

Branch coverage included in aggregate %.

37 of 43 new or added lines in 12 files covered. (86.05%)

2 existing lines in 1 file now uncovered.

2454 of 3833 relevant lines covered (64.02%)

3198.0 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,235✔
5
}
6

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

12
const list = <T>([x]: [T]) => [x];
6✔
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]) => {
6✔
59
  const k = toAstToken(t);
58,009✔
60
  return new ExprNS.Variable(k, k, k);
58,009✔
61
};
62
const astBigInt = ([t]: [moo.Token]) => {
6✔
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]) => {
6✔
67
  const k = toAstToken(t);
258✔
68
  return new ExprNS.Complex(k, k, t.value);
258✔
69
};
70
const astNone = ([t]: [moo.Token]) => {
6✔
71
  const k = toAstToken(t);
1,950✔
72
  return new ExprNS.None(k, k);
1,950✔
73
};
74
const astString = ([t]: [moo.Token]) => {
6✔
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]) => {
6✔
79
  const k = toAstToken(t);
539✔
80
  return new ExprNS.Literal(k, k, true);
539✔
81
};
82
const astFalse = ([t]: [moo.Token]) => {
6✔
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]) =>
6✔
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]) =>
6✔
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]) =>
6✔
93
  new ExprNS.BoolOp(l.startToken, r.endToken, l, toAstToken(op), r);
4,751✔
94
const astUnary = ([op, arg]: [moo.Token, ExprNS.Expr]) =>
6✔
95
  new ExprNS.Unary(toAstToken(op), arg.endToken, toAstToken(op), arg);
685✔
96
const astCompare = ([l, op, r]: [ExprNS.Expr, Token, ExprNS.Expr]) =>
6✔
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][]]) => [
6✔
103
  toAstToken(first),
UNCOV
104
  ...rest.map(d => toAstToken(d[1])),
×
105
];
106
const Lexer = pythonLexer;
6✔
107
const ParserRules = [
6✔
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,450✔
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,451✔
135
      const stmtNodes = stmts.map(d => d[0]).filter(s => "startToken" in s);
106,097✔
136
      const filtered = [...importNodes, ...stmtNodes];
9,451✔
137
      const start = filtered[0]
9,451✔
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,451✔
141
      return new StmtNS.FileInput(start, end, filtered, []);
9,451✔
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);
47✔
302
      return new StmtNS.Pass(tok, tok);
47✔
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,529✔
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: function (d) {
420
      return null;
4,164✔
421
    },
422
  },
423
  {
424
    name: "if_statement",
425
    symbols: [
426
      { literal: "if" },
427
      "expression",
428
      { literal: ":" },
429
      "block",
430
      "if_statement$ebnf$1",
431
      "if_statement$ebnf$2",
432
    ],
433
    postprocess: ([kw, test, , body, elifs, elseBlock]: [
434
      moo.Token,
435
      ExprNS.Expr,
436
      moo.Token,
437
      StmtNS.Stmt[],
438
      [moo.Token, ExprNS.Expr, moo.Token, StmtNS.Stmt[]][],
439
      [moo.Token, moo.Token, StmtNS.Stmt[]],
440
    ]) => {
441
      let else_ = elseBlock ? elseBlock[2] : null;
6,271✔
442
      for (let i = elifs.length - 1; i >= 0; i--) {
6,271✔
443
        const [ekw, etest, , ebody] = elifs[i];
4,869✔
444
        const endTok =
445
          else_ && else_.length > 0
4,869✔
446
            ? else_[else_.length - 1].endToken
447
            : ebody[ebody.length - 1].endToken;
448
        else_ = [new StmtNS.If(toAstToken(ekw), endTok, etest, ebody, else_)];
4,869✔
449
      }
450
      const endTok =
451
        else_ && else_.length > 0
6,271✔
452
          ? else_[else_.length - 1].endToken
453
          : body[body.length - 1].endToken;
454
      return new StmtNS.If(toAstToken(kw), endTok, test, body, else_);
6,271✔
455
    },
456
  },
457
  { name: "names$ebnf$1", symbols: [] },
458
  { name: "names$ebnf$1$subexpression$1", symbols: [{ literal: "," }, { type: "name" }] },
459
  {
460
    name: "names$ebnf$1",
461
    symbols: ["names$ebnf$1", "names$ebnf$1$subexpression$1"],
462
    postprocess: function arrpush<T>(d: [T[], T]) {
UNCOV
463
      return d[0].concat([d[1]]);
×
464
    },
465
  },
466
  { name: "names", symbols: [{ type: "name" }, "names$ebnf$1"], postprocess: tokList },
467
  { name: "block", symbols: ["blockInline", { type: "newline" }], postprocess: list },
468
  { name: "block$ebnf$1$subexpression$1", symbols: ["statement"] },
469
  { name: "block$ebnf$1$subexpression$1", symbols: [{ type: "newline" }] },
470
  { name: "block$ebnf$1", symbols: ["block$ebnf$1$subexpression$1"] },
471
  { name: "block$ebnf$1$subexpression$2", symbols: ["statement"] },
472
  { name: "block$ebnf$1$subexpression$2", symbols: [{ type: "newline" }] },
473
  {
474
    name: "block$ebnf$1",
475
    symbols: ["block$ebnf$1", "block$ebnf$1$subexpression$2"],
476
    postprocess: function arrpush<T>(d: [T[], T]) {
477
      return d[0].concat([d[1]]);
6,368✔
478
    },
479
  },
480
  {
481
    name: "block",
482
    symbols: [{ type: "newline" }, { type: "indent" }, "block$ebnf$1", { type: "dedent" }],
483
    postprocess: ([, , stmts]: [moo.Token, moo.Token, ([StmtNS.Stmt] | [moo.Token])[]]) =>
484
      stmts.map(d => d[0]).filter(s => s && "startToken" in s),
16,568✔
485
  },
486
  { name: "blockInline", symbols: ["statementAssign"], postprocess: id },
487
  { name: "blockInline", symbols: ["statementAnnAssign"], postprocess: id },
488
  { name: "blockInline", symbols: ["statementSubscriptAssign"], postprocess: id },
489
  { name: "blockInline", symbols: ["statementReturn"], postprocess: id },
490
  { name: "blockInline", symbols: ["statementPass"], postprocess: id },
491
  { name: "blockInline", symbols: ["statementBreak"], postprocess: id },
492
  { name: "blockInline", symbols: ["statementContinue"], postprocess: id },
493
  { name: "blockInline", symbols: ["statementGlobal"], postprocess: id },
494
  { name: "blockInline", symbols: ["statementNonlocal"], postprocess: id },
495
  { name: "blockInline", symbols: ["statementAssert"], postprocess: id },
496
  { name: "blockInline", symbols: ["statementExpr"], postprocess: id },
497
  {
498
    name: "rest_names",
499
    symbols: [{ type: "name" }],
500
    postprocess: ([t]: [moo.Token]) => {
501
      const tok = toFunctionParam(t, false);
6,646✔
502
      return [tok];
6,646✔
503
    },
504
  },
505
  {
506
    name: "rest_names",
507
    symbols: [{ literal: "*" }, { type: "name" }],
508
    postprocess: ([, t]: [moo.Token, moo.Token]) => {
509
      const tok = toFunctionParam(t, true);
16✔
510
      return [tok];
16✔
511
    },
512
  },
513
  {
514
    name: "rest_names",
515
    symbols: ["rest_names", { literal: "," }, { type: "name" }],
516
    postprocess: ([params, , t]: [FunctionParam[], moo.Token, moo.Token]) => {
517
      const tok = toFunctionParam(t, false);
5,977✔
518
      return [...params, tok];
5,977✔
519
    },
520
  },
521
  {
522
    name: "rest_names",
523
    symbols: ["rest_names", { literal: "," }, { literal: "*" }, { type: "name" }],
524
    postprocess: ([params, , , t]: [FunctionParam[], moo.Token, moo.Token, moo.Token]) => {
525
      const tok = toFunctionParam(t, true);
3✔
526
      return [...params, tok];
3✔
527
    },
528
  },
529
  { name: "params", symbols: [{ literal: "(" }, { literal: ")" }], postprocess: drop },
530
  {
531
    name: "params",
532
    symbols: [{ literal: "(" }, "rest_names", { literal: ")" }],
533
    postprocess: ([, ps]: [moo.Token, FunctionParam[], moo.Token]) => ps,
5,466✔
534
  },
535
  {
536
    name: "expression",
537
    symbols: ["expressionOr", { literal: "if" }, "expressionOr", { literal: "else" }, "expression"],
538
    postprocess: ([cons, , test, , alt]: [
539
      ExprNS.Expr,
540
      moo.Token,
541
      ExprNS.Expr,
542
      moo.Token,
543
      ExprNS.Expr,
544
    ]) => new ExprNS.Ternary(cons.startToken, alt.endToken, test, cons, alt),
4,284✔
545
  },
546
  { name: "expression", symbols: ["expressionOr"], postprocess: id },
547
  { name: "expression", symbols: ["lambda_expr"], postprocess: id },
548
  {
549
    name: "expressionOr",
550
    symbols: ["expressionOr", { literal: "or" }, "expressionAnd"],
551
    postprocess: astBoolOp,
552
  },
553
  { name: "expressionOr", symbols: ["expressionAnd"], postprocess: id },
554
  {
555
    name: "expressionAnd",
556
    symbols: ["expressionAnd", { literal: "and" }, "expressionNot"],
557
    postprocess: astBoolOp,
558
  },
559
  { name: "expressionAnd", symbols: ["expressionNot"], postprocess: id },
560
  { name: "expressionNot", symbols: [{ literal: "not" }, "expressionNot"], postprocess: astUnary },
561
  { name: "expressionNot", symbols: ["expressionCmp"], postprocess: id },
562
  {
563
    name: "expressionCmp",
564
    symbols: ["expressionCmp", "expressionCmpOp", "expressionAdd"],
565
    postprocess: astCompare,
566
  },
567
  { name: "expressionCmp", symbols: ["expressionAdd"], postprocess: id },
568
  { name: "expressionCmpOp", symbols: [{ type: "less" }], postprocess: tok },
569
  { name: "expressionCmpOp", symbols: [{ type: "greater" }], postprocess: tok },
570
  { name: "expressionCmpOp", symbols: [{ type: "doubleequal" }], postprocess: tok },
571
  { name: "expressionCmpOp", symbols: [{ type: "greaterequal" }], postprocess: tok },
572
  { name: "expressionCmpOp", symbols: [{ type: "lessequal" }], postprocess: tok },
573
  { name: "expressionCmpOp", symbols: [{ type: "notequal" }], postprocess: tok },
574
  { name: "expressionCmpOp", symbols: [{ literal: "in" }], postprocess: tok },
575
  {
576
    name: "expressionCmpOp",
577
    symbols: [{ literal: "not" }, { literal: "in" }],
578
    postprocess: ([t]: [moo.Token]) => {
579
      const tok = toAstToken(t);
3✔
580
      tok.lexeme = "not in";
3✔
581
      return tok;
3✔
582
    },
583
  },
584
  { name: "expressionCmpOp", symbols: [{ literal: "is" }], postprocess: tok },
585
  {
586
    name: "expressionCmpOp",
587
    symbols: [{ literal: "is" }, { literal: "not" }],
588
    postprocess: ([t]: [moo.Token]) => {
589
      const tok = toAstToken(t);
3✔
590
      tok.lexeme = "is not";
3✔
591
      return tok;
3✔
592
    },
593
  },
594
  {
595
    name: "expressionAdd",
596
    symbols: ["expressionAdd", "expressionAddOp", "expressionMul"],
597
    postprocess: astBinary,
598
  },
599
  { name: "expressionAdd", symbols: ["expressionMul"], postprocess: id },
600
  { name: "expressionAddOp", symbols: [{ type: "plus" }], postprocess: tok },
601
  { name: "expressionAddOp", symbols: [{ type: "minus" }], postprocess: tok },
602
  {
603
    name: "expressionMul",
604
    symbols: ["expressionMul", "expressionMulOp", "expressionUnary"],
605
    postprocess: astBinary,
606
  },
607
  { name: "expressionMul", symbols: ["expressionUnary"], postprocess: id },
608
  { name: "expressionMulOp", symbols: [{ type: "star" }], postprocess: tok },
609
  { name: "expressionMulOp", symbols: [{ type: "slash" }], postprocess: tok },
610
  { name: "expressionMulOp", symbols: [{ type: "percent" }], postprocess: tok },
611
  { name: "expressionMulOp", symbols: [{ type: "doubleslash" }], postprocess: tok },
612
  {
613
    name: "expressionUnary",
614
    symbols: [{ type: "plus" }, "expressionUnary"],
615
    postprocess: astUnary,
616
  },
617
  {
618
    name: "expressionUnary",
619
    symbols: [{ type: "minus" }, "expressionUnary"],
620
    postprocess: astUnary,
621
  },
622
  { name: "expressionUnary", symbols: ["expressionPow"], postprocess: id },
623
  {
624
    name: "expressionPow",
625
    symbols: ["expressionPost", { type: "doublestar" }, "expressionUnary"],
626
    postprocess: astBinaryTok,
627
  },
628
  { name: "expressionPow", symbols: ["expressionPost"], postprocess: id },
629
  {
630
    name: "expressionPost",
631
    symbols: ["expressionPost", { type: "lsqb" }, "expression", { type: "rsqb" }],
632
    postprocess: ([obj, , idx, rsqb]: [ExprNS.Expr, moo.Token, ExprNS.Expr, moo.Token]) =>
633
      new ExprNS.Subscript(obj.startToken, toAstToken(rsqb), obj, idx),
168✔
634
  },
635
  {
636
    name: "expressionPost",
637
    symbols: ["expressionPost", { literal: "(" }, "spread_expressions", { literal: ")" }],
638
    postprocess: ([callee, , args, rparen]: [ExprNS.Expr, moo.Token, ExprNS.Expr[], moo.Token]) =>
639
      new ExprNS.Call(callee.startToken, toAstToken(rparen), callee, args),
26,860✔
640
  },
641
  {
642
    name: "expressionPost",
643
    symbols: ["expressionPost", { literal: "(" }, { literal: ")" }],
644
    postprocess: ([callee, , rparen]: [ExprNS.Expr, moo.Token, moo.Token]) =>
645
      new ExprNS.Call(callee.startToken, toAstToken(rparen), callee, []),
95✔
646
  },
647
  { name: "expressionPost", symbols: ["atom"], postprocess: id },
648
  {
649
    name: "atom",
650
    symbols: [{ literal: "(" }, "expression", { literal: ")" }],
651
    postprocess: ([, e]: [moo.Token, ExprNS.Expr, moo.Token]) =>
652
      new ExprNS.Grouping(e.startToken, e.endToken, e),
2,659✔
653
  },
654
  {
655
    name: "atom",
656
    symbols: [{ type: "lsqb" }, { type: "rsqb" }],
657
    postprocess: ([l, r]: [moo.Token, moo.Token]) =>
658
      new ExprNS.List(toAstToken(l), toAstToken(r), []),
6✔
659
  },
660
  {
661
    name: "atom",
662
    symbols: [{ type: "lsqb" }, "expressions", { type: "rsqb" }],
663
    postprocess: ([l, elems, r]: [moo.Token, ExprNS.Expr[], moo.Token]) =>
664
      new ExprNS.List(toAstToken(l), toAstToken(r), elems),
31✔
665
  },
666
  { name: "atom", symbols: [{ type: "name" }], postprocess: astVariable },
667
  {
668
    name: "atom",
669
    symbols: [{ type: "number_float" }],
670
    postprocess: ([t]: [moo.Token]) => {
671
      const tok = toAstToken(t);
310✔
672
      return new ExprNS.Literal(tok, tok, parseFloat(t.value));
310✔
673
    },
674
  },
675
  { name: "atom", symbols: [{ type: "number_int" }], postprocess: astBigInt },
676
  { name: "atom", symbols: [{ type: "number_hex" }], postprocess: astBigInt },
677
  { name: "atom", symbols: [{ type: "number_oct" }], postprocess: astBigInt },
678
  { name: "atom", symbols: [{ type: "number_bin" }], postprocess: astBigInt },
679
  { name: "atom", symbols: [{ type: "number_complex" }], postprocess: astComplex },
680
  { name: "atom", symbols: ["stringLit"], postprocess: id },
681
  { name: "atom", symbols: [{ literal: "None" }], postprocess: astNone },
682
  { name: "atom", symbols: [{ literal: "True" }], postprocess: astTrue },
683
  { name: "atom", symbols: [{ literal: "False" }], postprocess: astFalse },
684
  {
685
    name: "lambda_expr",
686
    symbols: [{ literal: "lambda" }, "rest_names", { literal: ":" }, "expression"],
687
    postprocess: ([kw, params, , body]: [moo.Token, FunctionParam[], moo.Token, ExprNS.Expr]) =>
688
      new ExprNS.Lambda(toAstToken(kw), body.endToken, params, body),
1,837✔
689
  },
690
  {
691
    name: "lambda_expr",
692
    symbols: [{ literal: "lambda" }, "rest_names", { type: "doublecolon" }, "block"],
693
    postprocess: ([kw, params, , body]: [moo.Token, FunctionParam[], moo.Token, StmtNS.Stmt[]]) =>
694
      new ExprNS.MultiLambda(toAstToken(kw), body[body.length - 1].endToken, params, body, []),
×
695
  },
696
  {
697
    name: "lambda_expr",
698
    symbols: [{ literal: "lambda" }, { literal: ":" }, "expression"],
699
    postprocess: ([kw, , body]: [moo.Token, moo.Token, ExprNS.Expr]) =>
700
      new ExprNS.Lambda(toAstToken(kw), body.endToken, [], body),
989✔
701
  },
702
  {
703
    name: "lambda_expr",
704
    symbols: [{ literal: "lambda" }, { type: "doublecolon" }, "block"],
705
    postprocess: ([kw, , body]: [moo.Token, moo.Token, StmtNS.Stmt[]]) =>
706
      new ExprNS.MultiLambda(toAstToken(kw), body[body.length - 1].endToken, [], body, []),
×
707
  },
708
  { name: "expressions$ebnf$1", symbols: [] },
709
  { name: "expressions$ebnf$1$subexpression$1", symbols: [{ literal: "," }, "expression"] },
710
  {
711
    name: "expressions$ebnf$1",
712
    symbols: ["expressions$ebnf$1", "expressions$ebnf$1$subexpression$1"],
713
    postprocess: function arrpush<T>(d: [T[], T]) {
714
      return d[0].concat([d[1]]);
53✔
715
    },
716
  },
717
  { name: "expressions$ebnf$2$subexpression$1", symbols: [{ type: "comma" }] },
718
  { name: "expressions$ebnf$2", symbols: ["expressions$ebnf$2$subexpression$1"], postprocess: id },
719
  {
720
    name: "expressions$ebnf$2",
721
    symbols: [],
722
    postprocess: function (d) {
723
      return null;
85✔
724
    },
725
  },
726
  {
727
    name: "expressions",
728
    symbols: ["expression", "expressions$ebnf$1", "expressions$ebnf$2"],
729
    postprocess: flatList,
730
  },
731
  { name: "spread_expressions$ebnf$1", symbols: [] },
732
  {
733
    name: "spread_expressions$ebnf$1$subexpression$1",
734
    symbols: [{ literal: "," }, "spread_expression"],
735
  },
736
  {
737
    name: "spread_expressions$ebnf$1",
738
    symbols: ["spread_expressions$ebnf$1", "spread_expressions$ebnf$1$subexpression$1"],
739
    postprocess: function arrpush<T>(d: [T[], T]) {
740
      return d[0].concat([d[1]]);
17,179✔
741
    },
742
  },
743
  { name: "spread_expressions$ebnf$2$subexpression$1", symbols: [{ type: "comma" }] },
744
  {
745
    name: "spread_expressions$ebnf$2",
746
    symbols: ["spread_expressions$ebnf$2$subexpression$1"],
747
    postprocess: id,
748
  },
749
  {
750
    name: "spread_expressions$ebnf$2",
751
    symbols: [],
752
    postprocess: function (d) {
753
      return null;
50,955✔
754
    },
755
  },
756
  {
757
    name: "spread_expressions",
758
    symbols: ["spread_expression", "spread_expressions$ebnf$1", "spread_expressions$ebnf$2"],
759
    postprocess: flatList,
760
  },
761
  { name: "spread_expression", symbols: ["expression"], postprocess: id },
762
  {
763
    name: "spread_expression",
764
    symbols: [{ type: "star" }, "expression"],
765
    postprocess: ([star, expr]: [moo.Token, ExprNS.Expr]) =>
766
      new ExprNS.Starred(toAstToken(star), expr.endToken, expr),
35✔
767
  },
768
  { name: "stringLit", symbols: [{ type: "string_triple_double" }], postprocess: astString },
769
  { name: "stringLit", symbols: [{ type: "string_triple_single" }], postprocess: astString },
770
  { name: "stringLit", symbols: [{ type: "string_double" }], postprocess: astString },
771
  { name: "stringLit", symbols: [{ type: "string_single" }], postprocess: astString },
772
];
773
const ParserStart = "program";
6✔
774
export default { Lexer, ParserRules, ParserStart };
6✔
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