Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1718 stepper built in function errors not caught and crashing the frontend #1719

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading