Skip to content

Commit 531f93c

Browse files
authored
Restrict If and Ternary conditions to booleans (#29)
* Restrict ternary and If conditions to booleans * Add tests
1 parent 73a1e2a commit 531f93c

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

src/slang/interpreter.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ export const evaluators: { [nodeType: string]: Evaluator<es.Node> } = {
351351
},
352352
*IfStatement(node: es.IfStatement, context: Context) {
353353
const test = yield* evaluate(node.test, context)
354+
const error = rttc.checkIfStatement(context, test)
355+
if (error) {
356+
handleError(context, error)
357+
return undefined
358+
}
359+
354360
if (test) {
355361
return yield* evaluate(node.consequent, context)
356362
} else if (node.alternate) {

src/slang/utils/__tests__/rttc.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BinaryOperator, UnaryOperator } from 'estree'
22
import { mockClosure, mockRuntimeContext } from '../../../mocks/context'
3-
import { checkBinaryExpression, checkLogicalExpression, checkUnaryExpression } from '../rttc'
3+
import * as rttc from '../rttc'
44

55
const num = 0
66
const bool = true
@@ -11,7 +11,7 @@ test('Valid unary type combinations are OK', () => {
1111
const operatorValue = [['!', bool], ['+', num], ['-', num]]
1212
const context = mockRuntimeContext()
1313
const errors = operatorValue.map(opVals => {
14-
return checkUnaryExpression(context, opVals[0] as UnaryOperator, opVals[1])
14+
return rttc.checkUnaryExpression(context, opVals[0] as UnaryOperator, opVals[1])
1515
})
1616
errors.map(error => expect(error).toBe(undefined))
1717
})
@@ -30,7 +30,7 @@ test('Invalid unary type combinations return TypeError', () => {
3030
]
3131
const context = mockRuntimeContext()
3232
const errors = operatorValue.map(opVals => {
33-
return checkUnaryExpression(context, opVals[0] as UnaryOperator, opVals[1])
33+
return rttc.checkUnaryExpression(context, opVals[0] as UnaryOperator, opVals[1])
3434
})
3535
errors.map(error => expect(error).toBeDefined())
3636
})
@@ -47,7 +47,7 @@ test('Valid binary type combinations are OK for +', () => {
4747
]
4848
const context = mockRuntimeContext()
4949
const errors = operatorValues.map(opVals => {
50-
return checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
50+
return rttc.checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
5151
})
5252
errors.map(error => expect(error).toBe(undefined))
5353
})
@@ -101,7 +101,7 @@ test('Invalid binary type combinations for (-|*|/|%) return TypeError', () => {
101101
]
102102
const context = mockRuntimeContext()
103103
const errors = operatorValues.map(opVals => {
104-
return checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
104+
return rttc.checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
105105
})
106106
errors.map(error => expect(error).toBeDefined())
107107
})
@@ -119,7 +119,7 @@ test('Valid binary type combinations are OK for (===|!==)', () => {
119119
]
120120
const context = mockRuntimeContext()
121121
const errors = operatorValues.map(opVals => {
122-
return checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
122+
return rttc.checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
123123
})
124124
errors.map(error => expect(error).toBe(undefined))
125125
})
@@ -143,7 +143,7 @@ test('Invalid binary type combinations for (<|>|<==|>==) return TypeError', () =
143143
]
144144
const context = mockRuntimeContext()
145145
const errors = operatorValues.map(opVals => {
146-
return checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
146+
return rttc.checkBinaryExpression(context, opVals[0] as BinaryOperator, opVals[1], opVals[2])
147147
})
148148
errors.map(error => expect(error).toBeDefined())
149149
})
@@ -152,7 +152,7 @@ test('Valid logical type combinations are OK', () => {
152152
const operatorValues = [[bool, bool], [bool, bool]]
153153
const context = mockRuntimeContext()
154154
const errors = operatorValues.map(opVals => {
155-
return checkLogicalExpression(context, opVals[0], opVals[1])
155+
return rttc.checkLogicalExpression(context, opVals[0], opVals[1])
156156
})
157157
errors.map(error => expect(error).toBe(undefined))
158158
})
@@ -169,7 +169,21 @@ test('Invalid logical type combinations return TypeError', () => {
169169
]
170170
const context = mockRuntimeContext()
171171
const errors = operatorValues.map(opVals => {
172-
return checkLogicalExpression(context, opVals[0], opVals[1])
172+
return rttc.checkLogicalExpression(context, opVals[0], opVals[1])
173173
})
174174
errors.map(error => expect(error).toBeDefined())
175175
})
176+
177+
test('Valid ternary/if test expressions are OK', () => {
178+
const operatorValues = [bool]
179+
const context = mockRuntimeContext()
180+
const errors = operatorValues.map(opVal => rttc.checkIfStatement(context, opVal))
181+
errors.map(error => expect(error).toBe(undefined))
182+
})
183+
184+
test('Invalid ternary/if test expressions return TypeError', () => {
185+
const operatorValues = [num, str, func]
186+
const context = mockRuntimeContext()
187+
const errors = operatorValues.map(opVal => rttc.checkIfStatement(context, opVal))
188+
errors.map(error => expect(error).toBeDefined())
189+
})

src/slang/utils/rttc.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,8 @@ export const checkLogicalExpression = (context: Context, left: Value, right: Val
121121
return
122122
}
123123
}
124+
125+
export const checkIfStatement = (context: Context, test: Value) => {
126+
const node = context.runtime.nodes[0]
127+
return isBool(test) ? undefined : new TypeError(node, ' as condition', 'boolean', typeOf(test))
128+
}

0 commit comments

Comments
 (0)