diff --git a/YesNt.Interpreter.Tests/CodeFlowTests.cs b/YesNt.Interpreter.Tests/CodeFlowTests.cs index a3c0527..ff52902 100644 --- a/YesNt.Interpreter.Tests/CodeFlowTests.cs +++ b/YesNt.Interpreter.Tests/CodeFlowTests.cs @@ -200,6 +200,24 @@ public class CodeFlowTests YesNtAssert.IsLastLineEqual(lines, "ok"); } + [TestMethod] + public void GotoInsideLoopExitsLoopTest() + { + List lines = + [ + "var hit = no", + "while 1 == 1:", + "var hit = yes", + "goto done", + "end_while", + "label done:", + "${hit}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "yes"); + } + + [TestMethod] public void LabelWithoutColonFailsTest() { diff --git a/YesNt.Interpreter.Tests/FunctionStatementsTests.cs b/YesNt.Interpreter.Tests/FunctionStatementsTests.cs index aa148b9..134b6e3 100644 --- a/YesNt.Interpreter.Tests/FunctionStatementsTests.cs +++ b/YesNt.Interpreter.Tests/FunctionStatementsTests.cs @@ -132,6 +132,25 @@ public class FunctionStatementsTests YesNtAssert.ContainsTerminationMessage(lines, "Nested functions are not allowed"); } + [TestMethod] + public void LocalVariableDoesNotLeakToCallerTest() + { + List lines = + [ + "goto main", + "func modify:", + "var x = inner", + "return", + "label main:", + "var x = outer", + "call modify", + "${x}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "outer"); + } + + [TestMethod] public void ClearCallStackRunsTest() { @@ -144,5 +163,66 @@ public class FunctionStatementsTests YesNtAssert.IsLastLineEqual(lines, "ok"); } + + // --- Error path tests --- + + [TestMethod] + public void AccessInWithoutArgFailsTest() + { + List lines = + [ + "goto main", + "func noin:", + "var x = %in", + "return", + "label main:", + "call noin" + ]; + + YesNtAssert.ContainsTerminationMessage(lines, "No in argument in stack"); + } + + // --- Nested scope tests --- + + [TestMethod] + public void GlobalModifiedInsideFunctionIsVisibleAfterReturnTest() + { + List lines = + [ + "goto main", + "func setglobal:", + "global shared = modified", + "return", + "label main:", + "global shared = original", + "call setglobal", + "${shared}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "modified"); + } + + [TestMethod] + public void NestedFunctionCallsHaveIndependentLocalScopesTest() + { + List lines = + [ + "goto main", + "func outer:", + "var x = outer_val", + "call inner", + "push_out ${x}", + "return", + "func inner:", + "var x = inner_val", + "return", + "label main:", + "call outer", + "var result = %out", + "${result}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "outer_val"); + } } diff --git a/YesNt.Interpreter.Tests/ProcessingStatementsTests.cs b/YesNt.Interpreter.Tests/ProcessingStatementsTests.cs index 4d20081..c845d2c 100644 --- a/YesNt.Interpreter.Tests/ProcessingStatementsTests.cs +++ b/YesNt.Interpreter.Tests/ProcessingStatementsTests.cs @@ -63,6 +63,32 @@ public class ProcessingStatementsTests YesNtAssert.IsLastLineEqual(lines, "hello\nworld"); } + [TestMethod] + public void CalcRespectsPrecedenceTest() + { + YesNtAssert.IsLineEqual("2 + 3 * 4 calc", "14"); + } + + [TestMethod] + public void CalcParenthesesOverridePrecedenceTest() + { + YesNtAssert.IsLineEqual("(2 + 3) * 4 calc", "20"); + } + + [TestMethod] + public void SleepZeroIsValidTest() + { + List lines = + [ + "sleep 0", + "var result = ok", + "${result}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "ok"); + } + + [TestMethod] public void SleepInvalidValueFailsTest() { @@ -152,5 +178,26 @@ public class ProcessingStatementsTests YesNtAssert.IsLastLineEqual(lines, "1", timeout: 3000); } + + [TestMethod] + public void MultipleTasksRunConcurrentlyTest() + { + List lines = + [ + "global a = 0", + "global b = 0", + "global a = 1 task", + "global b = 2 task", + "while ${a} == 0:", + "sleep 10", + "end_while", + "while ${b} == 0:", + "sleep 10", + "end_while", + "${b}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "2", timeout: 3000); + } } diff --git a/YesNt.Interpreter.Tests/StringLiteralStatementsTests.cs b/YesNt.Interpreter.Tests/StringLiteralStatementsTests.cs index e6ffcda..5f623f2 100644 --- a/YesNt.Interpreter.Tests/StringLiteralStatementsTests.cs +++ b/YesNt.Interpreter.Tests/StringLiteralStatementsTests.cs @@ -58,6 +58,48 @@ public class StringLiteralStatementsTests YesNtAssert.IsLastLineEqual(lines, "hello world"); } + [TestMethod] + public void EmptyStringLiteralHasLengthZeroTest() + { + List lines = + [ + "var x = \"\"", + "length ${x}", + "var len = %out", + "${len}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "0"); + } + + [TestMethod] + public void EscapedBackslashProducesLiteralBackslashTest() + { + List lines = + [ + "var msg = \"\\\\n\"", // YesNt source: "\\n" → \n (backslash + n, 2 chars) + "length ${msg}", + "var len = %out", + "${len}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "2"); + } + + [TestMethod] + public void UnknownEscapeSequenceBecomesCharTest() + { + List lines = + [ + "var msg = \"\\q\"", // YesNt source: "\q" → q (1 char) + "length ${msg}", + "var len = %out", + "${len}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "1"); + } + [TestMethod] public void UnterminatedStringLiteralFailsTest() { diff --git a/YesNt.Interpreter.Tests/VariableStatementsTests.cs b/YesNt.Interpreter.Tests/VariableStatementsTests.cs index d7e07e3..9bf890b 100644 --- a/YesNt.Interpreter.Tests/VariableStatementsTests.cs +++ b/YesNt.Interpreter.Tests/VariableStatementsTests.cs @@ -91,6 +91,34 @@ public class VariableStatementsTests YesNtAssert.ContainsTerminationMessage(lines, "Invalid syntax"); } + [TestMethod] + public void OverwriteVariableTest() + { + List lines = + [ + "var x = first", + "var x = second", + "${x}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "second"); + } + + [TestMethod] + public void EmptyStringVariableHasLengthZeroTest() + { + List lines = + [ + "var x = \"\"", + "length ${x}", + "var len = %out", + "${len}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "0"); + } + + [TestMethod] public void MissingVariableReferenceFailsTest() {