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

source-academy / py-slang / 23332516234

20 Mar 2026 07:00AM UTC coverage: 40.05% (+5.1%) from 34.925%
23332516234

Pull #104

github

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

421 of 1378 branches covered (30.55%)

Branch coverage included in aggregate %.

484 of 611 new or added lines in 20 files covered. (79.21%)

37 existing lines in 2 files now uncovered.

1346 of 3034 relevant lines covered (44.36%)

65.31 hits per line

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

56.25
/src/resolver/resolver.ts
1
import { StmtNS, ExprNS } from "../ast-types";
1✔
2
type Expr = ExprNS.Expr;
3
type Stmt = StmtNS.Stmt;
4
import { Token } from "../tokenizer/tokenizer";
1✔
5
import { TokenType } from "../tokens";
1✔
6
import { ResolverErrors } from "./errors";
1✔
7
import { FeatureValidator } from "../validator/types";
8

9
import levenshtein from "fast-levenshtein";
1✔
10
// const levenshtein = require('fast-levenshtein');
11

12
const RedefineableTokenSentinel = new Token(TokenType.AT, "", 0, 0, 0);
1✔
13

14
export class Environment {
1✔
15
  source: string;
16
  // The parent of this environment
17
  enclosing: Environment | null;
18
  names: Map<string, Token>;
19
  // Function names in the environment.
20
  functions: Set<string>;
21
  // Names that are from import bindings, like 'y' in `from x import y`.
22
  // This only set at the top level environment. Child environments do not
23
  // copy this field.
24
  moduleBindings: Set<string>;
25
  definedNames: Set<string>;
26
  constructor(source: string, enclosing: Environment | null, names: Map<string, Token>) {
27
    this.source = source;
68✔
28
    this.enclosing = enclosing;
68✔
29
    this.names = names;
68✔
30
    this.functions = new Set();
68✔
31
    this.moduleBindings = new Set();
68✔
32
    this.definedNames = new Set();
68✔
33
  }
34

35
  /*
36
   * Does a full lookup up the environment chain for a name.
37
   * Returns the distance of the name from the current environment.
38
   * If name isn't found, return -1.
39
   * */
40
  lookupName(identifier: Token): number {
41
    const name = identifier.lexeme;
14✔
42
    let distance = 0;
14✔
43
    // eslint-disable-next-line @typescript-eslint/no-this-alias
44
    let curr: Environment | null = this;
14✔
45
    while (curr !== null) {
14✔
46
      if (curr.names.has(name)) {
20✔
47
        break;
11✔
48
      }
49
      distance += 1;
9✔
50
      curr = curr.enclosing;
9✔
51
    }
52
    return curr === null ? -1 : distance;
14✔
53
  }
54

55
  /* Looks up the name but only for the current environment. */
56
  lookupNameCurrentEnv(identifier: Token): Token | undefined {
57
    return this.names.get(identifier.lexeme);
8✔
58
  }
59
  lookupNameCurrentEnvWithError(identifier: Token) {
60
    if (this.lookupName(identifier) < 0) {
14✔
61
      throw new ResolverErrors.NameNotFoundError(
3✔
62
        identifier.line,
63
        identifier.col,
64
        this.source,
65
        identifier.indexInSource,
66
        identifier.indexInSource + identifier.lexeme.length,
67
        this.suggestName(identifier),
68
      );
69
    }
70
  }
71
  lookupNameParentEnvWithError(identifier: Token) {
72
    const name = identifier.lexeme;
×
73
    const parent = this.enclosing;
×
74

75
    if (parent === null || !parent.names.has(name)) {
×
76
      throw new ResolverErrors.NameNotFoundError(
×
77
        identifier.line,
78
        identifier.col,
79
        this.source,
80
        identifier.indexInSource,
81
        identifier.indexInSource + name.length,
82
        this.suggestName(identifier),
83
      );
84
    }
85
  }
86
  declareName(identifier: Token) {
87
    this.names.set(identifier.lexeme, identifier);
22✔
88
    this.definedNames.add(identifier.lexeme);
22✔
89
  }
90
  // Same as declareName but allowed to re-declare later.
91
  declarePlaceholderName(identifier: Token) {
92
    const lookup = this.lookupNameCurrentEnv(identifier);
8✔
93
    if (lookup !== undefined) {
8!
94
      throw new ResolverErrors.NameReassignmentError(
×
95
        identifier.line,
96
        identifier.col,
97
        this.source,
98
        identifier.indexInSource,
99
        identifier.indexInSource + identifier.lexeme.length,
100
        lookup,
101
      );
102
    }
103
    this.names.set(identifier.lexeme, RedefineableTokenSentinel);
8✔
104
  }
105
  suggestNameCurrentEnv(identifier: Token): string | null {
106
    const name = identifier.lexeme;
×
107
    let minDistance = Infinity;
×
108
    let minName = null;
×
109
    for (const declName of this.names.keys()) {
×
110
      const dist = levenshtein.get(name, declName);
×
111
      if (dist < minDistance) {
×
112
        minDistance = dist;
×
113
        minName = declName;
×
114
      }
115
    }
116
    return minName;
×
117
  }
118
  /*
119
   * Finds name closest to name in all environments up to builtin environment.
120
   * Calculated using min levenshtein distance.
121
   * */
122
  suggestName(identifier: Token): string | null {
123
    const name = identifier.lexeme;
3✔
124
    let minDistance = Infinity;
3✔
125
    let minName = null;
3✔
126
    // eslint-disable-next-line @typescript-eslint/no-this-alias
127
    let curr: Environment | null = this;
3✔
128
    while (curr !== null) {
3✔
129
      for (const declName of curr.names.keys()) {
6✔
130
        const dist = levenshtein.get(name, declName);
205✔
131
        if (dist < minDistance) {
205✔
132
          minDistance = dist;
6✔
133
          minName = declName;
6✔
134
        }
135
      }
136
      curr = curr.enclosing;
6✔
137
    }
138
    if (minDistance >= 4) {
3✔
139
      // This is pretty far, so just return null
140
      return null;
1✔
141
    }
142
    return minName;
2✔
143
  }
144
}
145
export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
1✔
146
  source: string;
147
  ast: Stmt;
148
  // change the environment to be suite scope as in python
149
  environment: Environment | null;
150
  functionScope: Environment | null;
151
  private validators: FeatureValidator[];
152

153
  constructor(source: string, ast: Stmt, validators: FeatureValidator[] = []) {
×
154
    this.source = source;
29✔
155
    this.ast = ast;
29✔
156
    this.validators = validators;
29✔
157
    // The global environment
158
    this.environment = new Environment(
29✔
159
      source,
160
      null,
161
      new Map([
162
        // misc library
163
        ["_int", new Token(TokenType.NAME, "_int", 0, 0, 0)],
164
        ["_int_from_string", new Token(TokenType.NAME, "_int_from_string", 0, 0, 0)],
165
        ["abs", new Token(TokenType.NAME, "abs", 0, 0, 0)],
166
        ["char_at", new Token(TokenType.NAME, "char_at", 0, 0, 0)],
167
        ["error", new Token(TokenType.NAME, "error", 0, 0, 0)],
168
        ["input", new Token(TokenType.NAME, "input", 0, 0, 0)],
169
        ["isinstance", new Token(TokenType.NAME, "isinstance", 0, 0, 0)],
170
        ["max", new Token(TokenType.NAME, "max", 0, 0, 0)],
171
        ["min", new Token(TokenType.NAME, "min", 0, 0, 0)],
172
        ["print", new Token(TokenType.NAME, "print", 0, 0, 0)],
173
        ["random_random", new Token(TokenType.NAME, "random_random", 0, 0, 0)],
174
        ["round", new Token(TokenType.NAME, "round", 0, 0, 0)],
175
        ["str", new Token(TokenType.NAME, "str", 0, 0, 0)],
176
        ["time_time", new Token(TokenType.NAME, "time_time", 0, 0, 0)],
177

178
        // math constants
179
        ["math_pi", new Token(TokenType.NAME, "math_pi", 0, 0, 0)],
180
        ["math_e", new Token(TokenType.NAME, "math_e", 0, 0, 0)],
181
        ["math_inf", new Token(TokenType.NAME, "math_inf", 0, 0, 0)],
182
        ["math_nan", new Token(TokenType.NAME, "math_nan", 0, 0, 0)],
183
        ["math_tau", new Token(TokenType.NAME, "math_tau", 0, 0, 0)],
184

185
        // math library
186
        ["math_acos", new Token(TokenType.NAME, "math_acos", 0, 0, 0)],
187
        ["math_acosh", new Token(TokenType.NAME, "math_acosh", 0, 0, 0)],
188
        ["math_asin", new Token(TokenType.NAME, "math_asin", 0, 0, 0)],
189
        ["math_asinh", new Token(TokenType.NAME, "math_asinh", 0, 0, 0)],
190
        ["math_atan", new Token(TokenType.NAME, "math_atan", 0, 0, 0)],
191
        ["math_atan2", new Token(TokenType.NAME, "math_atan2", 0, 0, 0)],
192
        ["math_atanh", new Token(TokenType.NAME, "math_atanh", 0, 0, 0)],
193
        ["math_cbrt", new Token(TokenType.NAME, "math_cbrt", 0, 0, 0)],
194
        ["math_ceil", new Token(TokenType.NAME, "math_ceil", 0, 0, 0)],
195
        ["math_comb", new Token(TokenType.NAME, "math_comb", 0, 0, 0)],
196
        ["math_copysign", new Token(TokenType.NAME, "math_copysign", 0, 0, 0)],
197
        ["math_cos", new Token(TokenType.NAME, "math_cos", 0, 0, 0)],
198
        ["math_cosh", new Token(TokenType.NAME, "math_cosh", 0, 0, 0)],
199
        ["math_degrees", new Token(TokenType.NAME, "math_degrees", 0, 0, 0)],
200
        ["math_erf", new Token(TokenType.NAME, "math_erf", 0, 0, 0)],
201
        ["math_erfc", new Token(TokenType.NAME, "math_erfc", 0, 0, 0)],
202
        ["math_exp", new Token(TokenType.NAME, "math_exp", 0, 0, 0)],
203
        ["math_exp2", new Token(TokenType.NAME, "math_exp2", 0, 0, 0)],
204
        ["math_expm1", new Token(TokenType.NAME, "math_expm1", 0, 0, 0)],
205
        ["math_fabs", new Token(TokenType.NAME, "math_fabs", 0, 0, 0)],
206
        ["math_factorial", new Token(TokenType.NAME, "math_factorial", 0, 0, 0)],
207
        ["math_floor", new Token(TokenType.NAME, "math_floor", 0, 0, 0)],
208
        ["math_fma", new Token(TokenType.NAME, "math_fma", 0, 0, 0)],
209
        ["math_fmod", new Token(TokenType.NAME, "math_fmod", 0, 0, 0)],
210
        ["math_gamma", new Token(TokenType.NAME, "math_gamma", 0, 0, 0)],
211
        ["math_gcd", new Token(TokenType.NAME, "math_gcd", 0, 0, 0)],
212
        ["math_isfinite", new Token(TokenType.NAME, "math_isfinite", 0, 0, 0)],
213
        ["math_isinf", new Token(TokenType.NAME, "math_isinf", 0, 0, 0)],
214
        ["math_isnan", new Token(TokenType.NAME, "math_isnan", 0, 0, 0)],
215
        ["math_isqrt", new Token(TokenType.NAME, "math_isqrt", 0, 0, 0)],
216
        ["math_lcm", new Token(TokenType.NAME, "math_lcm", 0, 0, 0)],
217
        ["math_ldexp", new Token(TokenType.NAME, "math_ldexp", 0, 0, 0)],
218
        ["math_lgamma", new Token(TokenType.NAME, "math_lgamma", 0, 0, 0)],
219
        ["math_log", new Token(TokenType.NAME, "math_log", 0, 0, 0)],
220
        ["math_log10", new Token(TokenType.NAME, "math_log10", 0, 0, 0)],
221
        ["math_log1p", new Token(TokenType.NAME, "math_log1p", 0, 0, 0)],
222
        ["math_log2", new Token(TokenType.NAME, "math_log2", 0, 0, 0)],
223
        ["math_nextafter", new Token(TokenType.NAME, "math_nextafter", 0, 0, 0)],
224
        ["math_perm", new Token(TokenType.NAME, "math_perm", 0, 0, 0)],
225
        ["math_pow", new Token(TokenType.NAME, "math_pow", 0, 0, 0)],
226
        ["math_radians", new Token(TokenType.NAME, "math_radians", 0, 0, 0)],
227
        ["math_remainder", new Token(TokenType.NAME, "math_remainder", 0, 0, 0)],
228
        ["math_sin", new Token(TokenType.NAME, "math_sin", 0, 0, 0)],
229
        ["math_sinh", new Token(TokenType.NAME, "math_sinh", 0, 0, 0)],
230
        ["math_sqrt", new Token(TokenType.NAME, "math_sqrt", 0, 0, 0)],
231
        ["math_tan", new Token(TokenType.NAME, "math_tan", 0, 0, 0)],
232
        ["math_tanh", new Token(TokenType.NAME, "math_tanh", 0, 0, 0)],
233
        ["math_trunc", new Token(TokenType.NAME, "math_trunc", 0, 0, 0)],
234
        ["math_ulp", new Token(TokenType.NAME, "math_ulp", 0, 0, 0)],
235
      ]),
236
    );
237
    this.functionScope = null;
29✔
238
  }
239

240
  private runValidators(node: StmtNS.Stmt | ExprNS.Expr): void {
241
    for (const v of this.validators) v.validate(node, this.environment ?? undefined);
169!
242
  }
243

244
  resolve(stmt: Stmt[] | Stmt | Expr[] | Expr | null) {
245
    if (stmt === null) {
110!
246
      return;
×
247
    }
248
    if (stmt instanceof Array) {
110✔
249
      // Resolve all top-level functions first. Python allows functions declared after
250
      // another function to be used in that function.
251
      for (const st of stmt) {
46✔
252
        if (st instanceof StmtNS.FunctionDef) {
56✔
253
          this.environment?.declarePlaceholderName(st.name);
8✔
254
        }
255
      }
256
      for (const st of stmt) {
46✔
257
        st.accept(this);
56✔
258
      }
259
    } else {
260
      stmt.accept(this);
64✔
261
    }
262
  }
263

264
  varDeclNames(names: Map<string, Token>): Token[] | null {
265
    const res = Array.from(names.values()).filter(
×
266
      name =>
267
        // Filter out functions and module bindings.
268
        // Those will be handled separately, so they don't
269
        // need to be hoisted.
270
        !this.environment?.functions.has(name.lexeme) &&
×
271
        !this.environment?.moduleBindings.has(name.lexeme),
272
    );
273
    return res.length === 0 ? null : res;
×
274
  }
275

276
  functionVarConstraint(identifier: Token): void {
277
    if (this.functionScope == null) {
12✔
278
      return;
10✔
279
    }
280
    let curr = this.environment;
2✔
281
    while (curr !== this.functionScope) {
2✔
282
      if (curr !== null && curr.names.has(identifier.lexeme)) {
×
283
        const token = curr.names.get(identifier.lexeme);
×
284
        if (token === undefined) {
×
285
          throw new Error("placeholder error");
×
286
        }
287
        throw new ResolverErrors.NameReassignmentError(
×
288
          identifier.line,
289
          identifier.col,
290
          this.source,
291
          identifier.indexInSource,
292
          identifier.indexInSource + identifier.lexeme.length,
293
          token,
294
        );
295
      }
296
      curr = curr?.enclosing ?? null;
×
297
    }
298
  }
299

300
  //// STATEMENTS
301
  visitFileInputStmt(stmt: StmtNS.FileInput): void {
302
    this.runValidators(stmt);
29✔
303
    // Create a new environment.
304
    const oldEnv = this.environment;
29✔
305
    this.environment = new Environment(this.source, this.environment, new Map());
29✔
306
    this.resolve(stmt.statements);
29✔
307
    // Grab identifiers from that new environment. That are NOT functions.
308
    // stmt.varDecls = this.varDeclNames(this.environment.names)
309
    this.environment = oldEnv;
14✔
310
  }
311

312
  visitIndentCreation(_stmt: StmtNS.Indent): void {
NEW
313
    this.runValidators(_stmt);
×
314
    // Create a new environment
315
    this.environment = new Environment(this.source, this.environment, new Map());
×
316
  }
317

318
  visitDedentCreation(_stmt: StmtNS.Dedent): void {
NEW
319
    this.runValidators(_stmt);
×
320
    // Switch to the previous environment.
321
    if (this.environment?.enclosing !== undefined) {
×
322
      this.environment = this.environment.enclosing;
×
323
    }
324
  }
325

326
  visitFunctionDefStmt(stmt: StmtNS.FunctionDef) {
327
    this.runValidators(stmt);
8✔
328
    this.environment?.declareName(stmt.name);
8✔
329
    this.environment?.functions.add(stmt.name.lexeme);
8✔
330

331
    // Create a new environment.
332
    const oldEnv = this.environment;
8✔
333
    // Assign the parameters to the new environment.
334
    const newEnv = new Map(stmt.parameters.map(param => [param.lexeme, param]));
8✔
335
    this.environment = new Environment(this.source, this.environment, newEnv);
8✔
336
    // const params = new Map(
337
    //     stmt.parameters.map(param => [param.lexeme, param])
338
    // );
339
    // if (this.environment !== null) {
340
    //     this.environment.names = params;
341
    // }
342
    this.functionScope = this.environment;
8✔
343
    this.resolve(stmt.body);
8✔
344
    // Grab identifiers from that new environment. That are NOT functions.
345
    // stmt.varDecls = this.varDeclNames(this.environment.names)
346
    // Restore old environment
347
    this.functionScope = null;
6✔
348
    this.environment = oldEnv;
6✔
349
  }
350

351
  visitAnnAssignStmt(stmt: StmtNS.AnnAssign): void {
NEW
352
    this.runValidators(stmt);
×
353
    this.resolve(stmt.ann);
×
354
    this.resolve(stmt.value);
×
355
    this.functionVarConstraint(stmt.target.name);
×
356
    this.environment?.declareName(stmt.target.name);
×
357
  }
358

359
  visitAssignStmt(stmt: StmtNS.Assign): void {
360
    this.runValidators(stmt);
17✔
361
    const target = stmt.target;
15✔
362
    if (target instanceof ExprNS.Subscript) {
15!
363
      throw new Error("Subscript assignment is not supported in assignment");
×
364
    }
365
    this.resolve(stmt.value);
15✔
366
    this.functionVarConstraint(target.name);
12✔
367
    this.environment?.declareName(target.name);
12✔
368
  }
369

370
  visitAssertStmt(stmt: StmtNS.Assert): void {
NEW
371
    this.runValidators(stmt);
×
UNCOV
372
    this.resolve(stmt.value);
×
373
  }
374
  visitForStmt(stmt: StmtNS.For): void {
375
    this.runValidators(stmt);
3✔
376
    this.environment?.declareName(stmt.target);
1✔
377
    this.resolve(stmt.iter);
1✔
378
    this.resolve(stmt.body);
1✔
379
  }
380

381
  visitIfStmt(stmt: StmtNS.If): void {
NEW
382
    this.runValidators(stmt);
×
383
    this.resolve(stmt.condition);
×
384
    this.resolve(stmt.body);
×
385
    this.resolve(stmt.elseBlock);
×
386
  }
387
  // @TODO we need to treat all global statements as variable declarations in the global
388
  // scope.
389
  visitGlobalStmt(stmt: StmtNS.Global): void {
NEW
390
    this.runValidators(stmt);
×
391
    // Do nothing because global can also be declared in our
392
    // own scope.
393
  }
394
  // @TODO nonlocals mean that any variable following that name in the current env
395
  // should not create a variable declaration, but instead point to an outer variable.
396
  visitNonLocalStmt(stmt: StmtNS.NonLocal): void {
NEW
397
    this.runValidators(stmt);
×
UNCOV
398
    this.environment?.lookupNameParentEnvWithError(stmt.name);
×
399
  }
400

401
  visitReturnStmt(stmt: StmtNS.Return): void {
NEW
402
    this.runValidators(stmt);
×
403
    if (stmt.value !== null) {
×
404
      this.resolve(stmt.value);
×
405
    }
406
  }
407

408
  visitWhileStmt(stmt: StmtNS.While): void {
409
    this.runValidators(stmt);
4✔
410
    this.resolve(stmt.condition);
2✔
411
    this.resolve(stmt.body);
2✔
412
  }
413
  visitSimpleExprStmt(stmt: StmtNS.SimpleExpr): void {
414
    this.runValidators(stmt);
11✔
415
    this.resolve(stmt.expression);
11✔
416
  }
417

418
  visitFromImportStmt(stmt: StmtNS.FromImport): void {
419
    this.runValidators(stmt);
1✔
420
    for (const name of stmt.names) {
1✔
421
      this.environment?.declareName(name);
1✔
422
      this.environment?.moduleBindings.add(name.lexeme);
1✔
423
    }
424
  }
425

426
  visitContinueStmt(stmt: StmtNS.Continue): void {
427
    this.runValidators(stmt);
1✔
428
  }
429
  visitBreakStmt(stmt: StmtNS.Break): void {
430
    this.runValidators(stmt);
1✔
431
  }
432
  visitPassStmt(stmt: StmtNS.Pass): void {
433
    this.runValidators(stmt);
4✔
434
  }
435

436
  //// EXPRESSIONS
437
  visitVariableExpr(expr: ExprNS.Variable): void {
438
    this.runValidators(expr);
14✔
439
    this.environment?.lookupNameCurrentEnvWithError(expr.name);
14✔
440
  }
441
  visitLambdaExpr(expr: ExprNS.Lambda): void {
442
    this.runValidators(expr);
3✔
443
    // Create a new environment.
444
    const oldEnv = this.environment;
2✔
445
    // Assign the parameters to the new environment.
446
    const newEnv = new Map(expr.parameters.map(param => [param.lexeme, param]));
2✔
447
    this.environment = new Environment(this.source, this.environment, newEnv);
2✔
448
    this.resolve(expr.body);
2✔
449
    // Restore old environment
450
    this.environment = oldEnv;
2✔
451
  }
452
  visitMultiLambdaExpr(expr: ExprNS.MultiLambda): void {
NEW
453
    this.runValidators(expr);
×
454
    // Create a new environment.
455
    const oldEnv = this.environment;
×
456
    // Assign the parameters to the new environment.
457
    const newEnv = new Map(expr.parameters.map(param => [param.lexeme, param]));
×
458
    this.environment = new Environment(this.source, this.environment, newEnv);
×
459
    this.resolve(expr.body);
×
460
    // Grab identifiers from that new environment.
461
    expr.varDecls = Array.from(this.environment.names.values());
×
462
    // Restore old environment
463
    this.environment = oldEnv;
×
464
  }
465
  visitUnaryExpr(expr: ExprNS.Unary): void {
NEW
466
    this.runValidators(expr);
×
UNCOV
467
    this.resolve(expr.right);
×
468
  }
469
  visitGroupingExpr(expr: ExprNS.Grouping): void {
NEW
470
    this.runValidators(expr);
×
UNCOV
471
    this.resolve(expr.expression);
×
472
  }
473
  visitBinaryExpr(expr: ExprNS.Binary): void {
NEW
474
    this.runValidators(expr);
×
475
    this.resolve(expr.left);
×
476
    this.resolve(expr.right);
×
477
  }
478
  visitBoolOpExpr(expr: ExprNS.BoolOp): void {
NEW
479
    this.runValidators(expr);
×
480
    this.resolve(expr.left);
×
481
    this.resolve(expr.right);
×
482
  }
483
  visitCompareExpr(expr: ExprNS.Compare): void {
NEW
484
    this.runValidators(expr);
×
485
    this.resolve(expr.left);
×
486
    this.resolve(expr.right);
×
487
  }
488

489
  visitCallExpr(expr: ExprNS.Call): void {
490
    this.runValidators(expr);
4✔
491
    this.resolve(expr.callee);
4✔
492
    this.resolve(expr.args);
4✔
493
  }
494
  visitTernaryExpr(expr: ExprNS.Ternary): void {
NEW
495
    this.runValidators(expr);
×
496
    this.resolve(expr.predicate);
×
497
    this.resolve(expr.consequent);
×
498
    this.resolve(expr.alternative);
×
499
  }
500
  visitNoneExpr(expr: ExprNS.None): void {
NEW
501
    this.runValidators(expr);
×
502
  }
503
  visitLiteralExpr(expr: ExprNS.Literal): void {
504
    this.runValidators(expr);
2✔
505
  }
506
  visitBigIntLiteralExpr(expr: ExprNS.BigIntLiteral): void {
507
    this.runValidators(expr);
13✔
508
  }
509
  visitComplexExpr(expr: ExprNS.Complex): void {
NEW
510
    this.runValidators(expr);
×
511
  }
512
  visitListExpr(expr: ExprNS.List): void {
513
    this.runValidators(expr);
5✔
514
    this.resolve(expr.elements);
2✔
515
  }
516
  visitSubscriptExpr(expr: ExprNS.Subscript): void {
NEW
517
    this.runValidators(expr);
×
518
    this.resolve(expr.value);
×
519
    this.resolve(expr.index);
×
520
  }
521
  visitStarredExpr(_expr: ExprNS.Starred): void {
522
    throw new Error("Starred expressions are not yet supported");
×
523
  }
524
}
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