From a5f9c5a178433fe753cb87274c50d5e8646962d3 Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Fri, 7 Aug 2026 01:03:00 +0200 Subject: [PATCH] Make end_func the only func ending block --- docs/editor.md | 3 +- src/YesNt.CodeEditor/Editor.cs | 10 +--- src/YesNt.Interpreter.Tests/CodeFlowTests.cs | 2 + .../FunctionStatementsTests.cs | 60 ++++++++++++++++++- src/YesNt.Interpreter/Runtime/ExitMessages.cs | 5 ++ .../Statements/FunctionStatements.cs | 25 +++++++- 6 files changed, 93 insertions(+), 12 deletions(-) diff --git a/docs/editor.md b/docs/editor.md index 02921d8..10b9108 100644 --- a/docs/editor.md +++ b/docs/editor.md @@ -47,6 +47,7 @@ If you pass a file path, it is loaded on startup. ## Formatter behavior (quick summary) - Indents `func`, `if`, `else`, and `while` blocks. -- Dedents on `return`, `end_func`, `end_if`, and `end_while`. +- Dedents on `end_func`, `end_if`, and `end_while`. +- `return` / `return ` are regular body statements and stay indented with the block they are in. - `exit` / `throw` / `error` close active non-function blocks for following lines. - Comment lines (`# ...`) are kept unindented. diff --git a/src/YesNt.CodeEditor/Editor.cs b/src/YesNt.CodeEditor/Editor.cs index abdd8af..a8ed4cd 100644 --- a/src/YesNt.CodeEditor/Editor.cs +++ b/src/YesNt.CodeEditor/Editor.cs @@ -261,15 +261,11 @@ internal class TextEditor } } } - else if (trimmed == "return" || trimmed == "end_func" || trimmed.StartsWith("return ", StringComparison.Ordinal)) + else if (trimmed == "end_func") { - for (int j = blockStack.Count - 1; j >= 0; j--) + if (blockStack.Count > 0 && blockStack[^1] == "func") { - if (blockStack[j] == "func") - { - blockStack.RemoveAt(j); - break; - } + blockStack.RemoveAt(blockStack.Count - 1); } } diff --git a/src/YesNt.Interpreter.Tests/CodeFlowTests.cs b/src/YesNt.Interpreter.Tests/CodeFlowTests.cs index 66df648..5f64817 100644 --- a/src/YesNt.Interpreter.Tests/CodeFlowTests.cs +++ b/src/YesNt.Interpreter.Tests/CodeFlowTests.cs @@ -16,6 +16,7 @@ public class CodeFlowTests "func yes:", "global result = 1", "return", + "end_func", "${result}" ]; YesNtAssert.IsLastLineEqual(lines, "1"); @@ -193,6 +194,7 @@ public class CodeFlowTests "func set_result:", "global result = ok", "return", + "end_func", "if 1 == 1 call set_result", "${result}" ]; diff --git a/src/YesNt.Interpreter.Tests/FunctionStatementsTests.cs b/src/YesNt.Interpreter.Tests/FunctionStatementsTests.cs index e453e66..f37ee2b 100644 --- a/src/YesNt.Interpreter.Tests/FunctionStatementsTests.cs +++ b/src/YesNt.Interpreter.Tests/FunctionStatementsTests.cs @@ -124,9 +124,13 @@ public class FunctionStatementsTests { List lines = [ + "goto main", "func outer:", "func inner:", - "return" + "return", + "end_func", + "label main:", + "call outer" ]; YesNtAssert.ContainsTerminationMessage(lines, "Nested functions are not allowed"); @@ -414,10 +418,64 @@ public class FunctionStatementsTests "call add with 3, 4", "func add: a, b", "return ${a} + ${b} calc", + "end_func", "var total = %out", "${total}" ]; YesNtAssert.IsLastLineEqual(lines, "7"); } + + // --- Top-down function body skip tests --- + + [TestMethod] + public void FunctionDeclaredFirstThenCalledTest() + { + List lines = + [ + "func add: a, b", + "return ${a} + ${b} calc", + "end_func", + "call add with 3, 7", + "var total = %out", + "${total}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "10"); + } + + [TestMethod] + public void FunctionBodyWithNestedReturnSkippedTest() + { + List lines = + [ + "func test: a, b", + "if ${a} == ${b}:", + "print_line %in", + "return 999", + "end_if", + "return ${a} + ${b} calc", + "end_func", + "call test with 1, 2, 4", + "var value = %out", + "${value}" + ]; + + YesNtAssert.IsLastLineEqual(lines, "3"); + } + + [TestMethod] + public void FunctionWithoutEndFuncFailsTest() + { + List lines = + [ + "func answer:", + "return 42", + "call answer", + "var value = %out", + "${value}" + ]; + + YesNtAssert.ContainsTerminationMessage(lines, "no matching \"end_func\""); + } } \ No newline at end of file diff --git a/src/YesNt.Interpreter/Runtime/ExitMessages.cs b/src/YesNt.Interpreter/Runtime/ExitMessages.cs index 44591c8..ab88025 100644 --- a/src/YesNt.Interpreter/Runtime/ExitMessages.cs +++ b/src/YesNt.Interpreter/Runtime/ExitMessages.cs @@ -36,6 +36,11 @@ internal static class ExitMessages return $"Function \"{function}\" not found"; } + internal static string FunctionWithoutEndFunc(string function) + { + return $"Function \"{function}\" has no matching \"end_func\""; + } + internal static string VariableNotFound(string variable) { return $"Variable \"{variable}\" not found"; diff --git a/src/YesNt.Interpreter/Statements/FunctionStatements.cs b/src/YesNt.Interpreter/Statements/FunctionStatements.cs index 1b49ff8..feeba90 100644 --- a/src/YesNt.Interpreter/Statements/FunctionStatements.cs +++ b/src/YesNt.Interpreter/Statements/FunctionStatements.cs @@ -10,7 +10,7 @@ namespace YesNt.Interpreter.Statements; internal class FunctionStatements : StatementRuntimeInformation { - [Statement("func", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.DarkYellow, ExecuteInSearchMode = true, Separator = ":")] + [Statement("func", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.DarkYellow, ExecuteInSearchMode = true, Separator = ":", BlockPair = "end_func")] public void FindFunction(string args) { if (RuntimeInfo.InternalIsInFunction) @@ -32,7 +32,26 @@ internal class FunctionStatements : StatementRuntimeInformation RuntimeInfo.SearchFunction = string.Empty; } - RuntimeInfo.IsInFunction = true; + bool isDefinitionScan = RuntimeInfo.FunctionCallStack.Count == 0 + && string.IsNullOrEmpty(RuntimeInfo.SearchFunction) + && string.IsNullOrEmpty(RuntimeInfo.SearchLabel); + + if (isDefinitionScan) + { + if (RuntimeInfo.BlockBoundaries.TryGetValue(RuntimeInfo.LineNumber, out int endLine) && endLine > RuntimeInfo.LineNumber) + { + RuntimeInfo.IsInFunction = false; + RuntimeInfo.LineNumber = endLine; + } + else + { + RuntimeInfo.Exit(ExitMessages.FunctionWithoutEndFunc(key), true); + } + } + else + { + RuntimeInfo.IsInFunction = true; + } } [Statement("push_in", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Yellow)] @@ -126,7 +145,7 @@ internal class FunctionStatements : StatementRuntimeInformation RuntimeInfo.FunctionCallStack.Peek().Results.Push(args); } - [Statement("end_func", SearchMode.Exact, SpaceAround.None, ConsoleColor.DarkYellow, ExecuteInSearchMode = true)] + [Statement("end_func", SearchMode.Exact, SpaceAround.None, ConsoleColor.DarkYellow, ExecuteInSearchMode = true, IsBlockEnd = true)] public void EndFunction(string _) { HandleReturn(string.Empty);