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

source-academy / js-slang / 15235726114

25 May 2025 07:55AM UTC coverage: 77.048%. First build
15235726114

Pull #1742

github

web-flow
Merge d7783cf1e into 0be74e78c
Pull Request #1742: Rewrite: Stepper

3433 of 4826 branches covered (71.14%)

Branch coverage included in aggregate %.

1032 of 1260 new or added lines in 27 files covered. (81.9%)

10099 of 12737 relevant lines covered (79.29%)

140954.8 hits per line

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

84.42
/src/tracer/generator.ts
1
/*
2
TODO: Write docs  
3
convert estree into corresponding stepper type
4
Every class should have the following properties
5
- basic StepperBaseNodeInterface
6
- constructor: create new AST from class type StepperBaseNode
7
- static create: factory method to parse estree to StepperAST
8
*/
9

10
import * as es from 'estree'
11
import { generate } from 'astring'
61✔
12
import { StepperBinaryExpression } from './nodes/Expression/BinaryExpression'
61✔
13
import { StepperUnaryExpression } from './nodes/Expression/UnaryExpression'
61✔
14
import { StepperLiteral } from './nodes/Expression/Literal'
61✔
15
import { StepperBaseNode } from './interface'
16
import { StepperExpressionStatement } from './nodes/Statement/ExpressionStatement'
61✔
17
import { StepperProgram } from './nodes/Program'
61✔
18
import {
61✔
19
  StepperVariableDeclaration,
20
  StepperVariableDeclarator
21
} from './nodes/Statement/VariableDeclaration'
22
import { StepperIdentifier } from './nodes/Expression/Identifier'
61✔
23
import { StepperBlockStatement } from './nodes/Statement/BlockStatement'
61✔
24
import { StepperIfStatement } from './nodes/Statement/IfStatement'
61✔
25
import { StepperConditionalExpression } from './nodes/Expression/ConditionalExpression'
61✔
26
import { StepperArrowFunctionExpression } from './nodes/Expression/ArrowFunctionExpression'
61✔
27
import { StepperFunctionApplication } from './nodes/Expression/FunctionApplication'
61✔
28
import { StepperReturnStatement } from './nodes/Statement/ReturnStatement'
61✔
29
import { StepperFunctionDeclaration } from './nodes/Statement/FunctionDeclaration'
61✔
30
import { StepperArrayExpression } from './nodes/Expression/ArrayExpression'
61✔
31
import { StepperLogicalExpression } from './nodes/Expression/LogicalExpression'
61✔
32
import { StepperBlockExpression } from './nodes/Expression/BlockExpression'
33
import { isBuiltinFunction } from './builtins'
61✔
34
const undefinedNode = new StepperLiteral('undefined')
61✔
35

36
const nodeConverters: { [Key: string]: (node: any) => StepperBaseNode } = {
61✔
37
  Literal: (node: es.SimpleLiteral) => StepperLiteral.create(node),
1,275✔
38
  UnaryExpression: (node: es.UnaryExpression) => StepperUnaryExpression.create(node),
103✔
39
  BinaryExpression: (node: es.BinaryExpression) => StepperBinaryExpression.create(node),
583✔
40
  LogicalExpression: (node: es.LogicalExpression) => StepperLogicalExpression.create(node),
402✔
41
  FunctionDeclaration: (node: es.FunctionDeclaration) => StepperFunctionDeclaration.create(node),
210✔
42
  ExpressionStatement: (node: es.ExpressionStatement) => StepperExpressionStatement.create(node),
522✔
43
  ConditionalExpression: (node: es.ConditionalExpression) =>
44
    StepperConditionalExpression.create(node),
380✔
45
  ArrowFunctionExpression: (node: es.ArrowFunctionExpression) =>
46
    StepperArrowFunctionExpression.create(node),
207✔
NEW
47
  ArrayExpression: (node: es.ArrayExpression) => StepperArrayExpression.create(node),
×
48
  CallExpression: (node: es.CallExpression) =>
49
    StepperFunctionApplication.create(node as es.SimpleCallExpression),
1,711✔
50
  ReturnStatement: (node: es.ReturnStatement) => StepperReturnStatement.create(node),
195✔
51
  Program: (node: es.Program) => StepperProgram.create(node),
383✔
52
  VariableDeclaration: (node: es.VariableDeclaration) => StepperVariableDeclaration.create(node),
348✔
53
  VariableDeclarator: (node: es.VariableDeclarator) => StepperVariableDeclarator.create(node),
348✔
54
  Identifier: (node: es.Identifier) => {
55
    if (node.name === 'NaN') {
4,807✔
56
      return new StepperLiteral(NaN, 'NaN')
1✔
57
    } else if (node.name === 'Infinity') {
4,806!
NEW
58
      return new StepperLiteral(Infinity, 'Infinity')
×
59
    } else {
60
      return StepperIdentifier.create(node)
4,806✔
61
    }
62
  },
63
  BlockStatement: (node: es.BlockStatement) => StepperBlockStatement.create(node),
432✔
64
  IfStatement: (node: es.IfStatement) => StepperIfStatement.create(node)
76✔
65
}
66

67
export function convert(node: es.BaseNode): StepperBaseNode {
61✔
68
  const converter = nodeConverters[node.type as keyof typeof nodeConverters]
11,982✔
69
  return converter ? converter(node as any) : undefinedNode
11,982!
70
}
71

72
// Explanation generator
73
export function explain(redex: StepperBaseNode): string {
61✔
74
  const explainers = {
3,037✔
75
    UnaryExpression: (node: StepperUnaryExpression) => {
76
      if (node.operator === '-') {
4✔
77
        return (
1✔
78
          'Unary expression evaluated, value ' +
79
          JSON.stringify((node.argument as StepperLiteral).value) +
80
          ' negated.'
81
        )
82
      } else if (node.operator === '!') {
3!
83
        return (
3✔
84
          'Unary expression evaluated, boolean ' +
85
          JSON.stringify((node.argument as StepperLiteral).value) +
86
          ' negated.'
87
        )
88
      } else {
NEW
89
        throw new Error('Unsupported unary operator ' + node.operator)
×
90
      }
91
    },
92
    BinaryExpression: (node: StepperBinaryExpression) => {
93
      return 'Binary expression ' + generate(node) + ' evaluated'
493✔
94
    },
95
    LogicalExpression: (node: StepperLogicalExpression) => {
96
      if (node.operator == '&&') {
59✔
97
        return (node.left as StepperLiteral).value === false
55✔
98
          ? 'AND operation evaluated, left of operator is true, continue evaluating right of operator'
99
          : 'AND operation evaluated, left of operator is false, stop evaluation'
100
      } else if (node.operator == '||') {
4!
101
        return (node.left as StepperLiteral).value === true
4✔
102
          ? 'OR operation evaluated, left of operator is true, stop evaluation'
103
          : 'OR operation evaluated, left of operator is false, continue evaluating right of operator'
104
      } else {
NEW
105
        throw new Error('Invalid operator')
×
106
      }
107
    },
108
    VariableDeclaration: (node: StepperVariableDeclaration) => {
109
      if (node.kind === 'const') {
115!
110
        return (
115✔
111
          'Constant ' +
112
          node.declarations.map(ast => ast.id.name).join(', ') +
115✔
113
          ' declared and substituted into the rest of block'
114
        )
115
      } else {
NEW
116
        return '...'
×
117
      }
118
    },
119
    ReturnStatement: (node: StepperReturnStatement) => {
120
      if (!node.argument) {
38!
NEW
121
        throw new Error('return argument should not be empty')
×
122
      }
123
      return generate(node.argument!) + ' returned'
38✔
124
    },
125
    FunctionDeclaration: (node: StepperFunctionDeclaration) => {
126
      return `Function ${node.id.name} declared, parameter(s) ${node.params.map(x =>
75✔
127
        generate(x)
61✔
128
      )} required`
129
    },
130
    ExpressionStatement: (node: StepperExpressionStatement) => {
131
      return generate(node.expression) + ' finished evaluating'
50✔
132
    },
133
    BlockStatement: (node: StepperBlockStatement) => {
134
      if (node.body.length === 0) {
62✔
135
        return 'Empty block expression evaluated'
15✔
136
      }
137
      return generate(node.body[0]) + ' finished evaluating'
47✔
138
    },
139
    BlockExpression: (_node: StepperBlockExpression) => {
NEW
140
      throw new Error('Not implemented')
×
141
    },
142
    ConditionalExpression: (node: StepperConditionalExpression) => {
143
      const test = node.test // test should have typeof literal
254✔
144
      if (test.type !== 'Literal') {
254!
NEW
145
        throw new Error('Invalid conditional contraction. `test` should be literal.')
×
146
      }
147
      const testStatus = (test as StepperLiteral).value
254✔
148
      if (typeof testStatus !== 'boolean') {
254!
NEW
149
        throw new Error(
×
150
          'Invalid conditional contraction. `test` should be boolean, got ' +
151
            typeof testStatus +
152
            ' instead.'
153
        )
154
      }
155
      if (testStatus === true) {
254✔
156
        return 'Conditional expression evaluated, condition is true, consequent evaluated'
69✔
157
      } else {
158
        return 'Conditional expression evaluated, condition is false, alternate evaluated'
185✔
159
      }
160
    },
161
    CallExpression: (node: StepperFunctionApplication) => {
162
      if (node.callee.type !== 'ArrowFunctionExpression' && node.callee.type !== 'Identifier') {
1,855!
NEW
163
        throw new Error('`callee` should be function expression.')
×
164
      }
165

166
      // Determine whether the called function is built-in or not and create explanation accordingly
167
      const func: StepperArrowFunctionExpression = node.callee as StepperArrowFunctionExpression
1,855✔
168
      if (func.name && isBuiltinFunction(func.name)) {
1,855✔
169
        return `${func.name} runs`
483✔
170
        // @ts-ignore func.body.type can be StepperBlockExpression
171
      } else if (func.body.type === 'BlockStatement') {
1,372✔
172
        if (func.params.length === 0) {
1,184✔
173
          return '() => {...}' + ' runs'
1,019✔
174
        }
175
        const paramDisplay = func.params.map(x => x.name).join(', ')
188✔
176
        const argDisplay: string = node.arguments
165✔
177
          .map(x =>
178
            (x.type === 'ArrowFunctionExpression' || x.type === 'Identifier') &&
188✔
179
            x.name !== undefined
180
              ? x.name
181
              : generate(x)
182
          )
183
          .join(', ')
184
        return 'Function ' + func.name + ' takes in ' + argDisplay + ' as input ' + paramDisplay
165✔
185
      } else {
186
        if (func.params.length === 0) {
188✔
187
          return generate(func) + ' runs'
12✔
188
        }
189
        return (
176✔
190
          node.arguments.map(x => generate(x)).join(', ') +
322✔
191
          ' substituted into ' +
192
          func.params.map(x => x.name).join(', ') +
322✔
193
          ' of ' +
194
          generate(func)
195
        )
196
      }
197
    },
198
    ArrowFunctionExpression: (_node: StepperArrowFunctionExpression) => {
NEW
199
      throw new Error('Not implemented.')
×
200
    },
201
    IfStatement: (node: StepperIfStatement) => {
202
      if (node.test instanceof StepperLiteral) {
32!
203
        if (node.test.value) {
32✔
204
          return 'If statement evaluated, condition true, proceed to if block'
13✔
205
        } else {
206
          return 'If statement evaluated, condition false, proceed to else block'
19✔
207
        }
208
      } else {
NEW
209
        throw new Error('Not implemented')
×
210
      }
211
    },
212
    Default: (_: StepperBaseNode) => {
NEW
213
      return '...'
×
214
    }
215
  }
216
  //@ts-ignore gracefully handle default ast node
217
  const explainer = explainers[redex.type] ?? explainers.Default
3,037!
218
  return explainer(redex)
3,037✔
219
}
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

© 2025 Coveralls, Inc