Make end_func the only func ending block

This commit is contained in:
Stone_Red
2026-08-07 01:03:00 +02:00
parent 6bc5d01a2e
commit a5f9c5a178
6 changed files with 93 additions and 12 deletions
+2 -1
View File
@@ -47,6 +47,7 @@ If you pass a file path, it is loaded on startup.
## Formatter behavior (quick summary) ## Formatter behavior (quick summary)
- Indents `func`, `if`, `else`, and `while` blocks. - 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 <value>` are regular body statements and stay indented with the block they are in.
- `exit` / `throw` / `error` close active non-function blocks for following lines. - `exit` / `throw` / `error` close active non-function blocks for following lines.
- Comment lines (`# ...`) are kept unindented. - Comment lines (`# ...`) are kept unindented.
+3 -7
View File
@@ -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(blockStack.Count - 1);
{
blockStack.RemoveAt(j);
break;
}
} }
} }
@@ -16,6 +16,7 @@ public class CodeFlowTests
"func yes:", "func yes:",
"global result = 1", "global result = 1",
"return", "return",
"end_func",
"${result}" "${result}"
]; ];
YesNtAssert.IsLastLineEqual(lines, "1"); YesNtAssert.IsLastLineEqual(lines, "1");
@@ -193,6 +194,7 @@ public class CodeFlowTests
"func set_result:", "func set_result:",
"global result = ok", "global result = ok",
"return", "return",
"end_func",
"if 1 == 1 call set_result", "if 1 == 1 call set_result",
"${result}" "${result}"
]; ];
@@ -124,9 +124,13 @@ public class FunctionStatementsTests
{ {
List<string> lines = List<string> lines =
[ [
"goto main",
"func outer:", "func outer:",
"func inner:", "func inner:",
"return" "return",
"end_func",
"label main:",
"call outer"
]; ];
YesNtAssert.ContainsTerminationMessage(lines, "Nested functions are not allowed"); YesNtAssert.ContainsTerminationMessage(lines, "Nested functions are not allowed");
@@ -414,10 +418,64 @@ public class FunctionStatementsTests
"call add with 3, 4", "call add with 3, 4",
"func add: a, b", "func add: a, b",
"return ${a} + ${b} calc", "return ${a} + ${b} calc",
"end_func",
"var total = %out", "var total = %out",
"${total}" "${total}"
]; ];
YesNtAssert.IsLastLineEqual(lines, "7"); YesNtAssert.IsLastLineEqual(lines, "7");
} }
// --- Top-down function body skip tests ---
[TestMethod]
public void FunctionDeclaredFirstThenCalledTest()
{
List<string> 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<string> 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<string> lines =
[
"func answer:",
"return 42",
"call answer",
"var value = %out",
"${value}"
];
YesNtAssert.ContainsTerminationMessage(lines, "no matching \"end_func\"");
}
} }
@@ -36,6 +36,11 @@ internal static class ExitMessages
return $"Function \"{function}\" not found"; 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) internal static string VariableNotFound(string variable)
{ {
return $"Variable \"{variable}\" not found"; return $"Variable \"{variable}\" not found";
@@ -10,7 +10,7 @@ namespace YesNt.Interpreter.Statements;
internal class FunctionStatements : StatementRuntimeInformation 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) public void FindFunction(string args)
{ {
if (RuntimeInfo.InternalIsInFunction) if (RuntimeInfo.InternalIsInFunction)
@@ -32,7 +32,26 @@ internal class FunctionStatements : StatementRuntimeInformation
RuntimeInfo.SearchFunction = string.Empty; 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)] [Statement("push_in", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Yellow)]
@@ -126,7 +145,7 @@ internal class FunctionStatements : StatementRuntimeInformation
RuntimeInfo.FunctionCallStack.Peek().Results.Push(args); 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 _) public void EndFunction(string _)
{ {
HandleReturn(string.Empty); HandleReturn(string.Empty);