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

source-academy / js-slang / 24834367427

23 Apr 2026 12:09PM UTC coverage: 78.541% (+0.2%) from 78.391%
24834367427

Pull #1893

github

web-flow
Merge ab101147d into 715603479
Pull Request #1893: Error Handling and Stringify Changes

3126 of 4197 branches covered (74.48%)

Branch coverage included in aggregate %.

801 of 975 new or added lines in 76 files covered. (82.15%)

20 existing lines in 11 files now uncovered.

7056 of 8767 relevant lines covered (80.48%)

173930.4 hits per line

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

83.95
/src/stepper/nodes/Expression/UnaryExpression.ts
1
import type { Comment, SourceLocation, UnaryExpression, UnaryOperator } from 'estree';
2
import type { StepperExpression, StepperPattern } from '..';
3
import { convert } from '../../generator';
4
import { StepperBaseNode } from '../../interface';
5
import { checkUnaryExpression } from '../../../utils/rttc';
6
import assert from '../../../utils/assert';
7
import { GeneralRuntimeError } from '../../../errors/base';
8
import type { RedexInfo } from '../..';
9
import { StepperLiteral } from './Literal';
10

11
export class StepperUnaryExpression
12
  extends StepperBaseNode<UnaryExpression>
13
  implements UnaryExpression
14
{
15
  public readonly prefix: true;
16

17
  constructor(
18
    public readonly operator: UnaryOperator,
312✔
19
    public readonly argument: StepperExpression,
312✔
20
    leadingComments?: Comment[],
21
    trailingComments?: Comment[],
22
    loc?: SourceLocation | null,
23
    range?: [number, number],
24
  ) {
25
    super('UnaryExpression', leadingComments, trailingComments, loc, range);
312✔
26
    this.prefix = true;
312✔
27
  }
28

29
  static createLiteral(node: StepperUnaryExpression | UnaryExpression): StepperLiteral | undefined {
30
    // if node argument is positive literal(x) and node operator is "-", we replace them with literal(-x) instead.
31
    if (
316✔
32
      node.operator === '-' &&
356✔
33
      node.argument.type === 'Literal' &&
34
      typeof node.argument.value === 'number' &&
35
      node.argument.value > 0
36
    ) {
37
      return new StepperLiteral(
5✔
38
        -node.argument.value,
39
        (-node.argument.value).toString(),
40
        node.leadingComments,
41
        node.trailingComments,
42
        node.loc,
43
        node.range,
44
      );
45
    }
46
    return undefined;
311✔
47
  }
48

49
  static create(node: UnaryExpression) {
50
    const literal = StepperUnaryExpression.createLiteral(node);
103✔
51
    if (literal) {
103✔
52
      return literal;
4✔
53
    }
54
    return new StepperUnaryExpression(
99✔
55
      node.operator,
56
      convert(node.argument),
57
      node.leadingComments,
58
      node.trailingComments,
59
      node.loc,
60
      node.range,
61
    );
62
  }
63

64
  public override isContractible(redex: RedexInfo): boolean {
65
    if (this.argument.type !== 'Literal') return false;
33✔
66

67
    const valueType = typeof this.argument.value;
15✔
68
    const markContractible = () => {
15✔
69
      redex.preRedex = [this];
15✔
70
      return true;
15✔
71
    };
72

73
    checkUnaryExpression(this, this.operator, this.argument.value);
15✔
74

75
    if (this.operator === '!' && valueType === 'boolean') {
15✔
76
      return markContractible();
12✔
77
    }
78

79
    if (this.operator === '-' && valueType === 'number') {
3!
80
      return markContractible();
3✔
81
    }
82

NEW
83
    return false;
×
84
  }
85

86
  public override isOneStepPossible(redex: RedexInfo): boolean {
87
    return this.isContractible(redex) || this.argument.isOneStepPossible(redex);
24✔
88
  }
89

90
  public override contract(redex: RedexInfo): StepperLiteral {
91
    redex.preRedex = [this];
4✔
92

93
    assert(
4✔
94
      this.argument.type === 'Literal',
95
      'UnaryExpressions cannot be contracted if the argument is not a Literal',
96
    );
97

98
    const operand = this.argument.value;
4✔
99
    switch (this.operator) {
4!
100
      case '!': {
101
        const ret = new StepperLiteral(
3✔
102
          !operand,
103
          (operand as boolean) ? 'false' : 'true',
3✔
104
          this.leadingComments,
105
          this.trailingComments,
106
          this.loc,
107
          this.range,
108
        );
109
        redex.postRedex = [ret];
3✔
110
        return ret;
3✔
111
      }
112
      case '-': {
113
        const ret = new StepperLiteral(
1✔
114
          -(operand as number),
115
          (-(operand as number)).toString(),
116
          this.leadingComments,
117
          this.trailingComments,
118
          this.loc,
119
          this.range,
120
        );
121
        redex.postRedex = [ret];
1✔
122
        return ret;
1✔
123
      }
124
      // provide support for the typeof operator to be used in Source Typed
125
      case 'typeof': {
NEW
126
        const typeValue = typeof operand;
×
NEW
127
        const ret = new StepperLiteral(
×
128
          typeValue,
129
          typeValue,
130
          this.leadingComments,
131
          this.trailingComments,
132
          this.loc,
133
          this.range,
134
        );
NEW
135
        redex.postRedex = [ret];
×
NEW
136
        return ret;
×
137
      }
138
      default:
NEW
139
        throw new GeneralRuntimeError(`Unsupported operator ${this.operator} in tracer`, this);
×
140
    }
141
  }
142

143
  public override oneStep(redex: RedexInfo): StepperExpression {
144
    if (this.isContractible(redex)) {
9✔
145
      return this.contract(redex);
4✔
146
    }
147
    const res = new StepperUnaryExpression(
5✔
148
      this.operator,
149
      this.argument.oneStep(redex),
150
      this.leadingComments,
151
      this.trailingComments,
152
      this.loc,
153
      this.range,
154
    );
155
    const literal = StepperUnaryExpression.createLiteral(res);
5✔
156
    return literal ? literal : res;
5✔
157
  }
158

159
  public override substitute(
160
    id: StepperPattern,
161
    value: StepperExpression,
162
    redex: RedexInfo,
163
  ): StepperExpression {
164
    const res = new StepperUnaryExpression(
208✔
165
      this.operator,
166
      this.argument.substitute(id, value, redex),
167
      this.leadingComments,
168
      this.trailingComments,
169
      this.loc,
170
      this.range,
171
    );
172
    const literal = StepperUnaryExpression.createLiteral(res);
208✔
173
    return literal ? literal : res;
208!
174
  }
175

176
  public override freeNames(): string[] {
177
    return this.argument.freeNames();
×
178
  }
179

180
  public override allNames(): string[] {
181
    return this.argument.allNames();
×
182
  }
183

184
  public override rename(before: string, after: string): StepperExpression {
185
    return new StepperUnaryExpression(
×
186
      this.operator,
187
      this.argument.rename(before, after),
188
      this.leadingComments,
189
      this.trailingComments,
190
      this.loc,
191
      this.range,
192
    );
193
  }
194
}
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