using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; namespace YesNt.Interpreter.Tests; [TestClass] public class FunctionStatementsTests { [TestMethod] public void FunctionCallWithInParameterTest() { List lines = [ "goto main", "func echo:", "global result = %in", "return", "label main:", "call echo with hello", "${result}" ]; YesNtAssert.IsLastLineEqual(lines, "hello"); } [TestMethod] public void HasInAndHasOutTokensTest() { List lines = [ "goto main", "func probe:", "global hasInBefore = %has_in", "var consume = %in", "global hasInAfter = %has_in", "push_out ${hasInBefore}", "push_out ${hasInAfter}", "return", "label main:", "call probe with x", "var hasOut = %has_out", "${hasOut}" ]; YesNtAssert.IsLastLineEqual(lines, "True"); } [TestMethod] public void OutParameterReadTest() { List lines = [ "goto main", "func make:", "push_out out_value", "return", "label main:", "call make with anything", "var value = %out", "${value}" ]; YesNtAssert.IsLastLineEqual(lines, "out_value"); } [TestMethod] public void OutParameterWithoutValueFailsTest() { List lines = [ "var x = %out" ]; YesNtAssert.ContainsTerminationMessage(lines, "No out argument in stack"); } [TestMethod] public void InParameterOutsideFunctionFailsTest() { List lines = [ "var x = %in" ]; YesNtAssert.ContainsTerminationMessage(lines, "Statement not allowed outside of function"); } [TestMethod] public void ReturnOutsideFunctionFailsTest() { List lines = [ "return" ]; YesNtAssert.ContainsTerminationMessage(lines, "Statement not allowed outside of function"); } [TestMethod] public void PushOutOutsideFunctionFailsTest() { List lines = [ "push_out value" ]; YesNtAssert.ContainsTerminationMessage(lines, "Statement not allowed outside of function"); } [TestMethod] public void FunctionWithoutColonFailsTest() { List lines = [ "func missing" ]; YesNtAssert.ContainsTerminationMessage(lines, "Invalid statement"); } [TestMethod] public void NestedFunctionDefinitionFailsTest() { List lines = [ "func outer:", "func inner:", "return" ]; 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() { List lines = [ "clear_call_stack", "var result = ok", "${result}" ]; 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"); } // --- end_func / return value tests --- [TestMethod] public void EndFuncReturnsToCallerTest() { List lines = [ "goto main", "func greet:", "global result = done", "end_func", "label main:", "call greet", "${result}" ]; YesNtAssert.IsLastLineEqual(lines, "done"); } [TestMethod] public void ReturnWithValueTest() { List lines = [ "goto main", "func add:", "var a = %in", "var b = %in", "var sum = ${a} + ${b} calc", "return ${sum}", "label main:", "call add with 3, 4", "var total = %out", "${total}" ]; YesNtAssert.IsLastLineEqual(lines, "7"); } [TestMethod] public void ReturnWithLiteralValueTest() { List lines = [ "goto main", "func make:", "return out_value", "label main:", "call make", "var value = %out", "${value}" ]; YesNtAssert.IsLastLineEqual(lines, "out_value"); } [TestMethod] public void ReturnEarlyExitSkipsRestOfFunctionTest() { List lines = [ "goto main", "func probe:", "push_out should_not_escape", "return", "push_out should_never_run", "label main:", "call probe", "var value = %out", "${value}" ]; YesNtAssert.IsLastLineEqual(lines, "should_not_escape"); } [TestMethod] public void ReturnWithValueOutsideFunctionFailsTest() { List lines = [ "return 42" ]; YesNtAssert.ContainsTerminationMessage(lines, "Statement not allowed outside of function"); } [TestMethod] public void EndFuncOutsideFunctionFailsTest() { List lines = [ "end_func" ]; YesNtAssert.ContainsTerminationMessage(lines, "Statement not allowed outside of function"); } // --- Named parameter tests --- [TestMethod] public void FunctionNamedParametersTest() { List lines = [ "goto main", "func add: a, b", "return ${a} + ${b} calc", "label main:", "call add with 3, 4", "var total = %out", "${total}" ]; YesNtAssert.IsLastLineEqual(lines, "7"); } [TestMethod] public void FunctionNamedParametersRespectOrderTest() { List lines = [ "goto main", "func pair: first, second", "push_out ${first}", "push_out ${second}", "end_func", "label main:", "call pair with A, B", "var f = %out", "var s = %out", "${f}-${s}" ]; YesNtAssert.IsLastLineEqual(lines, "A-B"); } [TestMethod] public void FunctionNamedParametersMissingArgumentFailsTest() { List lines = [ "goto main", "func add: a, b", "return ${a} calc", "label main:", "call add with 5" ]; YesNtAssert.ContainsTerminationMessage(lines, "No in argument in stack"); } [TestMethod] public void FunctionNamedParameterInvalidSyntaxTest() { List lines = [ "func add: a b" ]; YesNtAssert.ContainsTerminationMessage(lines, "Invalid syntax"); } [TestMethod] public void FunctionNamedParametersExtraArgumentsRemainTest() { List lines = [ "goto main", "func probe: a", "global first = ${a}", "global hasExtra = %has_in", "push_out ${hasExtra}", "end_func", "label main:", "call probe with A, B", "var extra = %out", "${extra}" ]; YesNtAssert.IsLastLineEqual(lines, "True"); } [TestMethod] public void FunctionNamedParametersSearchEntryTest() { List lines = [ "call add with 3, 4", "func add: a, b", "return ${a} + ${b} calc", "var total = %out", "${total}" ]; YesNtAssert.IsLastLineEqual(lines, "7"); } }