Add support for block-style if/else and while loop statements with tests

This commit is contained in:
Stone_Red
2026-03-04 14:28:04 +01:00
parent 9d43f1373f
commit 299558df70
3 changed files with 349 additions and 12 deletions
+20 -12
View File
@@ -19,18 +19,6 @@ This document describes the current, word-based YesNt syntax.
## Quick Example
v1:
```ynt
<name = world
fnc greet
cwl Hello >name
ret
cal greet
```
v2:
```ynt
let name = world
func greet:
@@ -39,6 +27,26 @@ return
call greet
```
## Block Conditionals
```ynt
if 10 > 5:
print_line yes
else:
print_line no
end_if
```
## While Loops
```ynt
let i = 3
while ${i} > 0:
print_line ${i}
let i = ${i} - 1 calc
end_while
```
## Full v1 -> v2 Mapping
| Area | v1 Syntax | v2 Syntax | Notes |
@@ -34,4 +34,106 @@ public class CodeFlowTests
];
YesNtAssert.IsLastLineEqual(lines, "1");
}
[TestMethod]
public void IfBlockTrueTest()
{
List<string> lines =
[
"let result = low",
"if 6 > 5:",
"let result = high",
"end_if",
"${result}"
];
YesNtAssert.IsLastLineEqual(lines, "high");
}
[TestMethod]
public void IfElseFalseBranchTest()
{
List<string> lines =
[
"let result = low",
"if 6 < 5:",
"let result = high",
"else:",
"let result = medium",
"end_if",
"${result}"
];
YesNtAssert.IsLastLineEqual(lines, "medium");
}
[TestMethod]
public void NestedIfElseTest()
{
List<string> lines =
[
"let result = 0",
"if 1 == 1:",
"if 2 == 3:",
"let result = 1",
"else:",
"let result = 2",
"end_if",
"end_if",
"${result}"
];
YesNtAssert.IsLastLineEqual(lines, "2");
}
[TestMethod]
public void WhileLoopTest()
{
List<string> lines =
[
"let i = 3",
"while ${i} > 0:",
"let i = ${i} - 1 calc",
"end_while",
"${i}"
];
YesNtAssert.IsLastLineEqual(lines, "0");
}
[TestMethod]
public void WhileSkipBodyWhenFalseTest()
{
List<string> lines =
[
"let i = 0",
"while ${i} > 0:",
"let i = 99",
"end_while",
"${i}"
];
YesNtAssert.IsLastLineEqual(lines, "0");
}
[TestMethod]
public void NestedWhileLoopTest()
{
List<string> lines =
[
"let outer = 2",
"let count = 0",
"while ${outer} > 0:",
"let inner = 2",
"while ${inner} > 0:",
"let count = ${count} + 1 calc",
"let inner = ${inner} - 1 calc",
"end_while",
"let outer = ${outer} - 1 calc",
"end_while",
"${count}"
];
YesNtAssert.IsLastLineEqual(lines, "4");
}
}
@@ -140,6 +140,105 @@ internal class CodeFlowStatements : StatementRuntimeInformation
}
}
[Statement("if", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Green, Priority = Priority.VeryLow, Separator = ":")]
public void IfBlock(string args)
{
args = args.Trim();
if (!args.EndsWith(':'))
{
RuntimeInfo.Exit("Invalid syntax", true);
return;
}
string condition = args[..^1].Trim();
bool? result = Evaluator.EvaluateCondition(condition);
if (result is null)
{
RuntimeInfo.Exit("Invalid operation", true);
return;
}
if (result == true)
{
return;
}
(int targetLine, _) = FindElseOrEndIf(RuntimeInfo.LineNumber);
if (targetLine < 0)
{
RuntimeInfo.Exit("No matching end_if found", true);
return;
}
RuntimeInfo.LineNumber = targetLine;
}
[Statement("else:", SearchMode.Exact, SpaceAround.None, ConsoleColor.Green)]
public void Else(string _)
{
int targetLine = FindEndIf(RuntimeInfo.LineNumber);
if (targetLine < 0)
{
RuntimeInfo.Exit("No matching end_if found", true);
return;
}
RuntimeInfo.LineNumber = targetLine;
}
[Statement("end_if", SearchMode.Exact, SpaceAround.None, ConsoleColor.Green)]
public void EndIf(string _)
{
}
[Statement("while", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Green, Priority = Priority.VeryLow, Separator = ":")]
public void While(string args)
{
args = args.Trim();
if (!args.EndsWith(':'))
{
RuntimeInfo.Exit("Invalid syntax", true);
return;
}
string condition = args[..^1].Trim();
bool? result = Evaluator.EvaluateCondition(condition);
if (result is null)
{
RuntimeInfo.Exit("Invalid operation", true);
return;
}
if (result == true)
{
return;
}
int endWhileLine = FindEndWhile(RuntimeInfo.LineNumber);
if (endWhileLine < 0)
{
RuntimeInfo.Exit("No matching end_while found", true);
return;
}
RuntimeInfo.LineNumber = endWhileLine;
}
[Statement("end_while", SearchMode.Exact, SpaceAround.None, ConsoleColor.Green)]
public void EndWhile(string _)
{
int whileLine = FindWhile(RuntimeInfo.LineNumber);
if (whileLine < 0)
{
RuntimeInfo.Exit("No matching while found", true);
return;
}
RuntimeInfo.LineNumber = whileLine - 1;
}
[Statement("exit", SearchMode.Exact, SpaceAround.None, ConsoleColor.Red, ExecuteInSearchMode = true)]
public void End(string _)
{
@@ -198,4 +297,132 @@ internal class CodeFlowStatements : StatementRuntimeInformation
{
return value.Trim().TrimEnd(':').Trim();
}
private (int TargetLine, bool IsElse) FindElseOrEndIf(int currentLine)
{
int depth = 0;
for (int i = currentLine + 1; i < RuntimeInfo.Lines.Count; i++)
{
string line = RuntimeInfo.Lines[i].Content.Trim().Replace("\r", string.Empty);
if (IsIfStart(line))
{
depth++;
continue;
}
if (line == "end_if")
{
if (depth == 0)
{
return (i, false);
}
depth--;
continue;
}
if (line == "else:" && depth == 0)
{
return (i, true);
}
}
return (-1, false);
}
private int FindEndIf(int currentLine)
{
int depth = 0;
for (int i = currentLine + 1; i < RuntimeInfo.Lines.Count; i++)
{
string line = RuntimeInfo.Lines[i].Content.Trim().Replace("\r", string.Empty);
if (IsIfStart(line))
{
depth++;
continue;
}
if (line == "end_if")
{
if (depth == 0)
{
return i;
}
depth--;
}
}
return -1;
}
private static bool IsIfStart(string line)
{
return line.StartsWith("if ", StringComparison.Ordinal) && line.EndsWith(':');
}
private int FindEndWhile(int currentLine)
{
int depth = 0;
for (int i = currentLine + 1; i < RuntimeInfo.Lines.Count; i++)
{
string line = RuntimeInfo.Lines[i].Content.Trim().Replace("\r", string.Empty);
if (IsWhileStart(line))
{
depth++;
continue;
}
if (line == "end_while")
{
if (depth == 0)
{
return i;
}
depth--;
}
}
return -1;
}
private int FindWhile(int currentLine)
{
int depth = 0;
for (int i = currentLine - 1; i >= 0; i--)
{
string line = RuntimeInfo.Lines[i].Content.Trim().Replace("\r", string.Empty);
if (line == "end_while")
{
depth++;
continue;
}
if (IsWhileStart(line))
{
if (depth == 0)
{
return i;
}
depth--;
}
}
return -1;
}
private static bool IsWhileStart(string line)
{
return line.StartsWith("while ", StringComparison.Ordinal) && line.EndsWith(':');
}
}