public void ExecutionCannotBeHaltedWhenEvaluatingFromWithinANativeFunction() { var host = new LispHost(); host.AddFunction("NATIVE-FUNCTION", (host, executionState, args) => { var result = host.EvalAtStackFrame(executionState.StackFrame, LispList.FromEnumerable(new LispObject[] { LispSymbol.CreateFromString("*"), new LispInteger(2), new LispInteger(2) })); return(result); }); var hitBreakpoint = false; host.RootFrame.EvaluatingExpression += (s, e) => { if (!hitBreakpoint && e.Expression is LispList list && list.ToString() == "(* 2 2)") { hitBreakpoint = true; e.HaltExecution = true; // this should not be honored } }; var evalResult = host.Eval("(native-function)"); Assert.True(hitBreakpoint); Assert.True(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal(4, ((LispInteger)evalResult.LastResult).Value); }
public void ExternalFunction() { var host = new LispHost(); host.AddFunction("ADD", (host, executionState, args) => (LispInteger)args[0] + (LispInteger)args[1]); Assert.Equal(new LispInteger(3), host.Eval("(add 1 2)").LastResult); }
public void TailCallWithIf() { var host = new LispHost(useTailCalls: true); var lastInterpreterStackDepth = 0; var lastDotNetStackDepth = 0; var invocationCount = 0; var maxInvocationCount = 10; host.AddFunction("RECORD-STACK-DEPTH", (host, executionState, args) => { var currentInterpreterStackDepth = executionState.StackFrame.Depth; var currentDotNetStackDepth = new StackTrace().FrameCount; if (invocationCount++ > maxInvocationCount) { throw new Exception($@"Executed more than {maxInvocationCount} times; probably not going to tailcall. Last/current interpreter stack depth = {lastInterpreterStackDepth}/{currentInterpreterStackDepth}. Last/current .NET stack depth = {lastDotNetStackDepth}/{currentDotNetStackDepth}"); } if (currentInterpreterStackDepth == lastInterpreterStackDepth && currentDotNetStackDepth == lastDotNetStackDepth) { // done return(host.T); } else { // haven't reached a stable stack depth; keep going lastInterpreterStackDepth = currentInterpreterStackDepth; lastDotNetStackDepth = currentDotNetStackDepth; return(host.Nil); } }); var result = host.Eval(@" (defun do-lots-of-tail-calls-with-if () (if (record-stack-depth) t ; done (do-lots-of-tail-calls-with-if))) ; keep going (do-lots-of-tail-calls-with-if) ").LastResult; Assert.True(invocationCount >= 2, $"Must have been invoked at least twice, but was only invoked {invocationCount} time(s)."); Assert.Equal(host.T, result); }
public void HaltExecutionOnExpressionEvaluation() { var host = new LispHost(); bool hitBreakpoint = false; bool sentinelHit = false; host.AddFunction("SENTINEL", (host, executionState, args) => { sentinelHit = true; return(new LispInteger(54)); }); host.RootFrame.EvaluatingExpression += (s, e) => { if (!hitBreakpoint && e.Expression is LispList list && list.Value is LispSymbol symbol && symbol.LocalName == "+") { hitBreakpoint = true; e.HaltExecution = true; } }; var evalResult = host.Eval(@" (defun inner-function () (+ 40 2)) (defun outer-function () (inner-function) (sentinel)) (outer-function) "); Assert.True(hitBreakpoint); Assert.False(evalResult.ExecutionState.IsExecutionComplete); Assert.Null(evalResult.LastResult); Assert.False(sentinelHit); host.EvalContinue(evalResult.ExecutionState); Assert.True(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal(54, ((LispInteger)evalResult.LastResult).Value); Assert.True(sentinelHit); }
public void HaltExecutionOnFunctionReturn() { var host = new LispHost(); bool sentinelHit = false; host.AddFunction("SENTINEL", (host, executionState, args) => { sentinelHit = true; return(new LispInteger(54)); }); LispObject capturedReturnValue = null; host.RootFrame.FunctionReturned += (s, e) => { if (e.Frame.FunctionSymbol.Value == "COMMON-LISP-USER:INNER-FUNCTION") { e.HaltExecution = true; capturedReturnValue = e.ReturnValue; } }; var evalResult = host.Eval(@" (defun inner-function () 42) (defun outer-function () (inner-function) (sentinel)) (outer-function) "); Assert.False(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal(42, ((LispInteger)capturedReturnValue).Value); Assert.Equal(42, ((LispInteger)evalResult.LastResult).Value); Assert.False(sentinelHit); host.EvalContinue(evalResult.ExecutionState); Assert.True(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal(54, ((LispInteger)evalResult.LastResult).Value); Assert.True(sentinelHit); }
public void ExecutionCanStepIn(bool useTailCalls) { var host = new LispHost(useTailCalls: useTailCalls); host.AddFunction("NATIVE-FUNCTION", (host, executionState, args) => { return(host.T); }); var hasHalted = false; host.RootFrame.EvaluatingExpression += (s, e) => { if (!hasHalted && e.Expression.ToString() == "(TEST-METHOD)") { e.HaltExecution = true; hasHalted = true; } }; var evalResult = host.Eval(@" (defun test-method () (native-function) (+ 1 1)) (test-method) ; initial halt here "); Assert.False(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal("s: (TEST-METHOD)", evalResult.ExecutionState.PeekOperation().ToString()); host.StepIn(evalResult.ExecutionState); Assert.False(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal("s: (NATIVE-FUNCTION)", evalResult.ExecutionState.PeekOperation().ToString()); host.StepIn(evalResult.ExecutionState); // can't step in to a native function; this is really a step over Assert.False(evalResult.ExecutionState.IsExecutionComplete); Assert.Equal("s: (+ 1 1)", evalResult.ExecutionState.PeekOperation().ToString()); }