diff --git a/app/javascript/interpreter/error.ts b/app/javascript/interpreter/error.ts index 7c7d745161..1e8371cb27 100644 --- a/app/javascript/interpreter/error.ts +++ b/app/javascript/interpreter/error.ts @@ -130,6 +130,8 @@ export type RuntimeErrorType = | 'IndexOutOfBoundsInGet' | 'IndexOutOfBoundsInChange' | 'MaxTotalExecutionTimeReached' + | 'ListsCannotBeCompared' + | 'IncomparableTypes' export type StaticErrorType = | DisabledLanguageFeatureErrorType diff --git a/app/javascript/interpreter/executor.ts b/app/javascript/interpreter/executor.ts index b51983410c..aafe81d1d9 100644 --- a/app/javascript/interpreter/executor.ts +++ b/app/javascript/interpreter/executor.ts @@ -776,7 +776,10 @@ export class Executor { public visitBinaryExpression(expression: BinaryExpression): EvaluationResult { const leftResult = this.evaluate(expression.left) + this.verifyLiteral(leftResult.resultingValue, expression.left) + const rightResult = this.evaluate(expression.right) + this.verifyLiteral(rightResult.resultingValue, expression.right) const result: EvaluationResult = { type: 'BinaryExpression', @@ -1119,6 +1122,23 @@ export class Executor { } } + private verifyLiteral(value: any, expr: Expression): void { + if (isNumber(value)) return + if (isString(value)) return + if (isBoolean(value)) return + + this.guardUncalledFunction(value, expr) + + if (isArray(value)) { + this.error('ListsCannotBeCompared', expr.location, { + value: formatLiteral(value), + }) + } + this.error('IncomparableTypes', expr.location, { + value: formatLiteral(value), + }) + } + private verifyNumber(value: any, expr: Expression): void { if (isNumber(value)) return this.guardUncalledFunction(value, expr) diff --git a/app/javascript/interpreter/locales/en/translation.json b/app/javascript/interpreter/locales/en/translation.json index 6489af4b2d..24a1cbdfc4 100644 --- a/app/javascript/interpreter/locales/en/translation.json +++ b/app/javascript/interpreter/locales/en/translation.json @@ -59,7 +59,8 @@ "PointlessStatement": "This line of code doesn't do anything.", "PotentialMissingParenthesesForFunctionCall": "Jiki didn't know what to do with this line of code. Did you mean to call a function and forget the parentheses? (`()`)", "MissingEachAfterFor": "Did you forget to write `each` after the `for` keyword?", - "ForeachNotIterable": "Jiki couldn't work out how to iterate over `{{value}}`. He expected it to be a string or a list." + "ForeachNotIterable": "Jiki couldn't work out how to iterate over `{{value}}`. He expected it to be a string or a list.", + "ListsCannotBeCompared": "You can't compare lists directly to each other in JikiScript. Different languages handle this very differently, and Jiki's decided to opt out of the confusion. If you need to compare lists, we'll provide you with a specific function in the instructions." }, "semantic": { "CannotAssignToConstant": "Cannot re-assign value of constant.", diff --git a/app/javascript/interpreter/locales/system/translation.json b/app/javascript/interpreter/locales/system/translation.json index 7e8d75c6c8..bdff7c0f50 100644 --- a/app/javascript/interpreter/locales/system/translation.json +++ b/app/javascript/interpreter/locales/system/translation.json @@ -109,7 +109,8 @@ "IndexIsZero": "IndexIsZero", "IndexOutOfBoundsInGet": "IndexOutOfBoundsInGet: index: {{index}}, length: {{length}}, dataType: {{dataType}}", "IndexOutOfBoundsInChange": "IndexOutOfBoundsInChange: index: {{index}}, length: {{length}}, dataType: {{dataType}}", - "MaxTotalExecutionTimeReached": "MaxTotalExecutionTimeReached" + "MaxTotalExecutionTimeReached": "MaxTotalExecutionTimeReached", + "ListsCannotBeCompared": "ListsCannotBeCompared" }, "disabledLanguageFeature": { "ExcludeListViolation": "ExcludeListViolation: tokenType: {{tokenType}}", diff --git a/test/javascript/interpreter/runtimeErrors.test.ts b/test/javascript/interpreter/runtimeErrors.test.ts index ae877e2198..7360516b10 100644 --- a/test/javascript/interpreter/runtimeErrors.test.ts +++ b/test/javascript/interpreter/runtimeErrors.test.ts @@ -504,4 +504,11 @@ test('InvalidChangeElementTarget', () => { expect(frames[1].error!.message).toBe('InvalidChangeElementTarget') }) +test('ListsCannotBeCompared', () => { + const code = `log [] == []` + const { frames } = interpret(code) + expectFrameToBeError(frames[0], `log [] == []`, 'ListsCannotBeCompared') + expect(frames[0].error!.message).toBe('ListsCannotBeCompared') +}) + // TOOD: Strings are immutable diff --git a/test/javascript/validate_bootcamp_content.test.ts b/test/javascript/validate_bootcamp_content.test.ts index a9ce45864d..b61ecd3b91 100644 --- a/test/javascript/validate_bootcamp_content.test.ts +++ b/test/javascript/validate_bootcamp_content.test.ts @@ -62,7 +62,7 @@ function testIo(project, exerciseSlug, config, task, testData, exampleScript) { } if (value != testData.expected) { - console.log(error, value, frames) + // console.log(error, value, frames) } expect(value).toEqual(testData.expected) })