diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 0647cff8a..3ea1c41d7 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -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 } diff --git a/src/stepper/__tests__/__snapshots__/stepper.ts.snap b/src/stepper/__tests__/__snapshots__/stepper.ts.snap index 7db6a5dd5..cf4da72fd 100644 --- a/src/stepper/__tests__/__snapshots__/stepper.ts.snap +++ b/src/stepper/__tests__/__snapshots__/stepper.ts.snap @@ -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`] = ` diff --git a/src/stepper/lib.ts b/src/stepper/lib.ts index c73947667..c2e2d73f9 100644 --- a/src/stepper/lib.ts +++ b/src/stepper/lib.ts @@ -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' @@ -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) @@ -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)) @@ -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) @@ -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}` ) @@ -136,13 +137,16 @@ 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 } @@ -150,7 +154,7 @@ export function head(xs: substituterNodes): 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 } diff --git a/src/stepper/util.ts b/src/stepper/util.ts index 034b563b5..d6afebfeb 100644 --- a/src/stepper/util.ts +++ b/src/stepper/util.ts @@ -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 {