Skip to content

Commit b267976

Browse files
committed
Add stack trace for Evaluation Errors
1 parent 2b88ffa commit b267976

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

src/lambda-calculus.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,15 @@ function evalLC(term) {
345345
// term :: Tuple
346346
// isRight :: bool (indicating whether the term is left or right side of an Application)
347347
// isEvaluated :: bool (indicating whether the current term should be stored in the Env)
348+
// callstack :: [String] (History of var lookups, for better error messages)
348349
function runEval({term,env},stack) { // stack: [[term, isRight, isEvaluated]], term: LC term, env = Env { name => term }
350+
const callstack = []; // Does not persist between requests for arguments
349351
while ( ! ( term instanceof L ) || stack.length > 0 ) {
350352
if ( term instanceof V )
351353
if ( term.name==="()" )
352-
{ console.error(`eval: evaluating undefined inside definition of "${term.defName}"`); throw new EvalError; } // depend on verbosity here
354+
{ printStackTrace("eval: evaluating undefined", term, callstack); throw new EvalError; }
353355
else {
356+
callstack.push(term.name);
354357
const res = env.getValue(term.name);
355358
if ( ! res.env )
356359
term = res;
@@ -407,6 +410,32 @@ function evalLC(term) {
407410
return runEval(term, []);
408411
}
409412

413+
// Print an error, with stack trace according to verbosity level
414+
function printStackTrace(error, term, stack) {
415+
if (config.verbosity == "Calm") return; // No error message for Calm
416+
else if (config.verbosity == "Concise")
417+
console.error(`${error} inside definition of <code>${term.defName}</code>`);
418+
else if (config.verbosity == "Loquacious") {
419+
// Loquacious will provide a stack trace localised to the definition
420+
if (stack.length == 0 || stack[stack.length-1] == term.defName)
421+
console.error(`${error} inside definition of <code>${term.defName}</code>`);
422+
else {
423+
const localStack = stack.slice(stack.indexOf(term.defName)+1).reverse();
424+
console.error(`${error} inside definition of <code>${term.defName}</code>
425+
${localStack.map(v=>'\twhile evaluating <code>' + v + '</code>').join('\n')}`)
426+
}
427+
} else if (config.verbosity == "Verbose") {
428+
// Verbose will provide a full stack trace
429+
if (stack.length == 0)
430+
console.error(`${error} inside definition of <code>${term.defName}</code>`);
431+
else {
432+
const localStack = stack.reverse();
433+
console.error(`${error} inside definition of <code>${term.defName}</code>
434+
${localStack.map(v=>'\twhile evaluating <code>' + v + '</code>').join('\n')}`)
435+
}
436+
}
437+
}
438+
410439
Object.defineProperty( Function.prototype, "valueOf", { value: function valueOf() { return toInt(this); } } );
411440

412441
exports.config = config;

0 commit comments

Comments
 (0)