Skip to content

Commit

Permalink
1718 stepper built in function errors not caught and crashing the fro…
Browse files Browse the repository at this point in the history
…ntend (#1719)

* Refactor: BuiltInFunctionError to catch Built-In Function Error

* Refactor: Change Error to BuiltInFunctionError

* Fix: Tests
  • Loading branch information
hanscau authored Sep 10, 2024
1 parent 0a284d2 commit f443ccc
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/errors/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { RuntimeSourceError } from './runtimeSourceError'

//Wrap build-in function error in SourceError
export class BuiltInFunctionError extends RuntimeSourceError {
constructor(node: Node, private explanation: String) {
super(node)
constructor(private explanation: String) {
super(undefined)
this.explanation = explanation
}

Expand Down
4 changes: 2 additions & 2 deletions src/stepper/__tests__/__snapshots__/stepper.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5507,12 +5507,12 @@ math_abs(-1);

exports[`Test catching errors from built in function Incorrect number of arguments 1`] = `
"Start of evaluation
"
Expected 2 arguments, but got 1"
`;

exports[`Test catching errors from built in function Incorrect type of argument for math function 1`] = `
"Start of evaluation
"
Math functions must be called with number arguments"
`;

exports[`Test catching errors from built in function Incorrect type of arguments for module function 1`] = `
Expand Down
20 changes: 12 additions & 8 deletions src/stepper/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as es from 'estree'
import * as misc from '../stdlib/misc'
import { Context, substituterNodes } from '../types'
import * as ast from '../utils/ast/astCreator'
import { BuiltInFunctionError } from '../errors/errors'
import { nodeToValue, nodeToValueWithContext, valueToExpression } from './converter'
import { codify } from './stepper'
import { isBuiltinFunction, isNumber } from './util'
Expand Down Expand Up @@ -30,13 +31,13 @@ export function stringify(val: substituterNodes): es.Literal {
// defineBuiltin(context, 'error(str)', misc.error_message)
export function error(val: substituterNodes, str?: substituterNodes) {
const output = (str === undefined ? '' : str + ' ') + stringify(val)
throw new Error(output)
throw new BuiltInFunctionError(output)
}

// defineBuiltin(context, 'prompt(str)', prompt)
export function prompt(str: substituterNodes): es.Literal {
if (str.type !== 'Literal' || typeof str.value !== 'string') {
throw new Error('Argument to error must be a string.')
throw new BuiltInFunctionError('Argument to error must be a string.')
}
const result = window.prompt(str.value as string)
return ast.literal((result ? result : null) as string)
Expand Down Expand Up @@ -99,9 +100,9 @@ export function parse_int(str: substituterNodes, radix: substituterNodes): es.Ex
export function evaluateMath(mathFn: string, ...args: substituterNodes[]): es.Expression {
const fn = Math[mathFn.split('_')[1]]
if (!fn) {
throw new Error(`Math function ${mathFn} not found.`)
throw new BuiltInFunctionError(`Math function ${mathFn} not found.`)
} else if (args.some(arg => !isNumber(arg))) {
throw new Error(`Math functions must be called with number arguments`)
throw new BuiltInFunctionError(`Math functions must be called with number arguments`)
}
const jsArgs = args.map(nodeToValue)
return valueToExpression(fn(...jsArgs))
Expand All @@ -115,7 +116,7 @@ export function evaluateModuleFunction(
): es.Expression {
const fn = context.runtime.environments[0].head[moduleFn]
if (!fn) {
throw new Error(`Module function ${moduleFn} not found.`)
throw new BuiltInFunctionError(`Module function ${moduleFn} not found.`)
}
const jsArgs = args.map(arg => nodeToValueWithContext(arg, context))
return valueToExpression(fn(...jsArgs), context)
Expand All @@ -126,7 +127,7 @@ export function evaluateModuleFunction(
// defineBuiltin(context, 'pair(left, right)', list.pair)
export function pair(left: substituterNodes, right: substituterNodes): es.ArrayExpression {
if (left == null || right == null) {
throw new Error(
throw new BuiltInFunctionError(
//Count the number of arguments that are not undefined
`Expected 2 arguments, but got ${[left, right].filter(x => x != undefined).length}`
)
Expand All @@ -136,21 +137,24 @@ export function pair(left: substituterNodes, right: substituterNodes): es.ArrayE

// defineBuiltin(context, 'is_pair(val)', list.is_pair)
export function is_pair(val: substituterNodes): es.Literal {
if (val == null) {
throw new BuiltInFunctionError('Expected 1 argument, but got 0')
}
return ast.literal(val.type === 'ArrayExpression' && val.elements.length === 2)
}

// defineBuiltin(context, 'head(xs)', list.head)
export function head(xs: substituterNodes): es.Expression {
if (is_pair(xs).value === false) {
throw new Error(`${codify(xs)} is not a pair`)
throw new BuiltInFunctionError(`${codify(xs)} is not a pair`)
}
return (xs as es.ArrayExpression).elements[0] as es.Expression
}

// defineBuiltin(context, 'tail(xs)', list.tail)
export function tail(xs: substituterNodes): es.Expression {
if (is_pair(xs).value === false) {
throw new Error(`${codify(xs)} is not a pair`)
throw new BuiltInFunctionError(`${codify(xs)} is not a pair`)
}
return (xs as es.ArrayExpression).elements[1] as es.Expression
}
Expand Down
5 changes: 4 additions & 1 deletion src/stepper/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import { Context } from '..'
import * as errors from '../errors/errors'
import { RuntimeSourceError } from '../errors/runtimeSourceError'
import { BlockExpression, Environment, Node, substituterNodes, Value } from '../types'
import { UNKNOWN_LOCATION } from '../constants'
import * as builtin from './lib'

export function prettyPrintError(error: RuntimeSourceError): string {
return `Line ${error.location.start.line}: ${error.explain()}`
return error.location == UNKNOWN_LOCATION
? error.explain()
: `Line ${error.location.start.line}: ${error.explain()}`
}

export function isBuiltinFunction(node: substituterNodes): boolean {
Expand Down

0 comments on commit f443ccc

Please sign in to comment.