From a40c9b35fd2ddafc2bf5963e685997729c361b15 Mon Sep 17 00:00:00 2001 From: "Chris J. Breisch" Date: Sun, 22 May 2022 07:47:00 -0400 Subject: [PATCH] handle then branch better --- .../InterpreterTest.cs | 40 +++++++++++++ .../Exceptions/ReturnFromGosub.cs | 8 +++ .../Interpreter/Interpreter.cs | 56 ++++++++----------- .../Machine/IMachine.cs | 1 + .../Machine/Machine.cs | 19 +++++++ Trs80.Level1Basic.sln.DotSettings | 1 + .../Samples/ParserTests/recursiveFib.bas | 2 +- 7 files changed, 93 insertions(+), 34 deletions(-) create mode 100644 Trs80.Level1Basic.VirtualMachine/Exceptions/ReturnFromGosub.cs diff --git a/Trs80.Level1Basic.Interpreter.Test/InterpreterTest.cs b/Trs80.Level1Basic.Interpreter.Test/InterpreterTest.cs index 257ba6d..c990eeb 100644 --- a/Trs80.Level1Basic.Interpreter.Test/InterpreterTest.cs +++ b/Trs80.Level1Basic.Interpreter.Test/InterpreterTest.cs @@ -934,4 +934,44 @@ public void Interpreter_Evaluates_Logical_Expressions8() controller.ReadOutputLine().Should().Be("FALSE"); controller.IsEndOfRun().Should().BeTrue(); } + + [TestMethod] + public void Interpreter_Executes_Return_After_Then() + { + using var controller = new TestController(); + var program = new List { + "10 a=0", + "20 gosub 100", + "30 print \"SUCCESS!\"", + "40 end", + "100 if a = 0 then return", + "110 print \"FAIL!\"", + "120 end" + }; + + controller.RunProgram(program); + + controller.ReadOutputLine().Should().Be("SUCCESS!"); + controller.IsEndOfRun().Should().BeTrue(); + } + + [TestMethod] + public void Interpreter_Executes_Goto_After_Multiple_Thens() + { + using var controller = new TestController(); + var program = new List { + "10 a=0", + "20 if a = 0 then a = 1 : goto 100", + "30 print a", + "40 end", + "100 a = 2", + "110 print a", + "120 end" + }; + + controller.RunProgram(program); + + controller.ReadOutputLine().Should().Be(" 2 "); + controller.IsEndOfRun().Should().BeTrue(); + } } \ No newline at end of file diff --git a/Trs80.Level1Basic.VirtualMachine/Exceptions/ReturnFromGosub.cs b/Trs80.Level1Basic.VirtualMachine/Exceptions/ReturnFromGosub.cs new file mode 100644 index 0000000..3138213 --- /dev/null +++ b/Trs80.Level1Basic.VirtualMachine/Exceptions/ReturnFromGosub.cs @@ -0,0 +1,8 @@ + +using System; + +namespace Trs80.Level1Basic.VirtualMachine.Exceptions; + +public class ReturnFromGosub : Exception +{ +} \ No newline at end of file diff --git a/Trs80.Level1Basic.VirtualMachine/Interpreter/Interpreter.cs b/Trs80.Level1Basic.VirtualMachine/Interpreter/Interpreter.cs index d1d07c8..3fd93fe 100644 --- a/Trs80.Level1Basic.VirtualMachine/Interpreter/Interpreter.cs +++ b/Trs80.Level1Basic.VirtualMachine/Interpreter/Interpreter.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Text; using System.Threading; - +using Microsoft.CSharp.RuntimeBinder; using Trs80.Level1Basic.HostMachine; using Trs80.Level1Basic.VirtualMachine.Exceptions; using Trs80.Level1Basic.VirtualMachine.Machine; @@ -233,6 +233,10 @@ public void Execute(IStatement statement) { statement.Accept(this); } + catch (ReturnFromGosub) + { + throw; + } catch (ScanException) { throw; @@ -334,12 +338,10 @@ public Void VisitForStatement(For statement) public Void VisitGosubStatement(Gosub statement) { - IStatement nextStatement = _machine.GetNextStatement(_program.CurrentStatement); + IStatement resumeStatement = _machine.GetNextStatement(_program.CurrentStatement); IStatement jumpToStatement = GetJumpToStatement(statement, statement.Location, "GOSUB"); - _machine.RunStatementList(jumpToStatement, this, false); - - _machine.SetNextStatement(nextStatement); + ExecuteGosub(jumpToStatement, resumeStatement); return null!; } @@ -365,9 +367,7 @@ private IStatement GetStatementByLineNumber(int lineNumber) public Void VisitGotoStatement(Goto statement) { IStatement jumpToStatement = GetJumpToStatement(statement, statement.Location, "GOTO"); - _machine.RunStatementList(jumpToStatement, this, false); - _machine.ExecutionHalted = true; - + _machine.SetNextStatement(jumpToStatement); return null!; } @@ -375,30 +375,11 @@ public Void VisitIfStatement(If statement) { if (!IsTruthy(Evaluate(statement.Condition))) return null!; - IStatement thenStatement = statement.ThenBranch?[0]; - if (thenStatement == null) return null!; + _machine.RunThenBranch(statement.ThenBranch, this); - switch (thenStatement) - { - case Goto gotoStatement: - VisitGotoStatement(gotoStatement); - break; - default: - ExecuteThenBranch(statement.ThenBranch); - break; - } return null!; } - private void ExecuteThenBranch(CompoundStatementList thenBranch) - { - IStatement nextStatement = _machine.GetNextStatement(_program.CurrentStatement); - - _machine.RunStatementList(thenBranch[0], this, true); - - _machine.SetNextStatement(nextStatement); - } - public Void VisitInputStatement(Input statement) { foreach (Expression expression in statement.Expressions) @@ -594,8 +575,7 @@ public Void VisitOnStatement(On statement) IStatement resumeStatement = _machine.GetNextStatement(statement); Expression location = new Literal(locations[selector]); IStatement jumpToStatement = GetJumpToStatement(statement, location, "GOSUB"); - _machine.RunStatementList(jumpToStatement, this, false); - _machine.SetNextStatement(resumeStatement); + ExecuteGosub(jumpToStatement, resumeStatement); return null!; } @@ -611,6 +591,18 @@ public Void VisitOnStatement(On statement) return null!; } + private void ExecuteGosub(IStatement jumpToStatement, IStatement resumeStatement) + { + try + { + _machine.RunStatementList(jumpToStatement, this, false); + } + catch (ReturnFromGosub) + { + _machine.SetNextStatement(resumeStatement); + } + } + public Void VisitPrintStatement(Print statement) { if (statement.AtPosition != null) PrintAt(statement.AtPosition); @@ -674,9 +666,7 @@ public Void VisitRestoreStatement(Restore _) public Void VisitReturnStatement(Return statement) { - _machine.SetNextStatement(null); - - return null!; + throw new ReturnFromGosub(); } public Void VisitRunStatement(Run statement) diff --git a/Trs80.Level1Basic.VirtualMachine/Machine/IMachine.cs b/Trs80.Level1Basic.VirtualMachine/Machine/IMachine.cs index e29cc8a..7c698d5 100644 --- a/Trs80.Level1Basic.VirtualMachine/Machine/IMachine.cs +++ b/Trs80.Level1Basic.VirtualMachine/Machine/IMachine.cs @@ -26,6 +26,7 @@ public interface IMachine void LoadProgram(string path); void NewProgram(); void RunStatementList(IStatement statement, IInterpreter interpreter, bool breakOnLineChange); + void RunThenBranch(CompoundStatementList thenBranch, IInterpreter interpreter); void SetNextStatement(IStatement statement); void HaltRun(); IStatement GetStatementByLineNumber(int lineNumber); diff --git a/Trs80.Level1Basic.VirtualMachine/Machine/Machine.cs b/Trs80.Level1Basic.VirtualMachine/Machine/Machine.cs index a21c6c3..f5b73cf 100644 --- a/Trs80.Level1Basic.VirtualMachine/Machine/Machine.cs +++ b/Trs80.Level1Basic.VirtualMachine/Machine/Machine.cs @@ -139,6 +139,25 @@ public void RunStatementList(IStatement statement, IInterpreter interpreter, boo } } + public void RunThenBranch(CompoundStatementList thenBranch, IInterpreter interpreter) + { + IStatement nextStatement = _nextStatement; + + ExecutionHalted = false; + if (thenBranch == null) return; + int lineNumber = thenBranch.LineNumber; + IStatement statement = thenBranch[0]; + + while (statement != null && !ExecutionHalted) + { + _nextStatement = GetNextStatement(statement); + interpreter.Execute(statement); + if (statement is IListStatementDecorator decorated && decorated.BaseType() == typeof(Goto)) return; + statement = _nextStatement; + } + SetNextStatement(nextStatement); + } + public IStatement GetStatementByLineNumber(int lineNumber) { return Program.GetExecutableStatement(lineNumber); diff --git a/Trs80.Level1Basic.sln.DotSettings b/Trs80.Level1Basic.sln.DotSettings index 81142e9..2e3fa5c 100644 --- a/Trs80.Level1Basic.sln.DotSettings +++ b/Trs80.Level1Basic.sln.DotSettings @@ -1,5 +1,6 @@  True + True True True True diff --git a/Trs80.Level1Basic/Samples/ParserTests/recursiveFib.bas b/Trs80.Level1Basic/Samples/ParserTests/recursiveFib.bas index 891ee3c..c241529 100644 --- a/Trs80.Level1Basic/Samples/ParserTests/recursiveFib.bas +++ b/Trs80.Level1Basic/Samples/ParserTests/recursiveFib.bas @@ -9,5 +9,5 @@ 1030 gosub 1010 1040 n = n + 1 1050 a(n) = a(n-1) + a(n-2) -1060 rem runprint "#1060 n=";n;"a(";n;")=";a(n);"a(";n-1;")=";a(n-1);"a(";n-2;")=";a(n-2) +1060 rem print "#1060 n=";n;"a(";n;")=";a(n);"a(";n-1;")=";a(n-1);"a(";n-2;")=";a(n-2) 1070 return