Add string literal parsing with escapes and related tests

This commit is contained in:
Stone_Red
2026-03-04 16:21:49 +01:00
parent 7f58fb4aaf
commit 86819900a0
3 changed files with 152 additions and 0 deletions
+3
View File
@@ -14,6 +14,8 @@ This document describes the current, word-based YesNt syntax.
- Statements are line-based. - Statements are line-based.
- `# ...` remains a comment. - `# ...` remains a comment.
- Variable interpolation inside text uses `${name}`. - Variable interpolation inside text uses `${name}`.
- String literals use double quotes (`"..."`) with escapes like `\n`, `\t`, `\"`, `\\`.
Interpolation is not evaluated inside string literals.
- Function and label declarations are block markers with a trailing `:`. - Function and label declarations are block markers with a trailing `:`.
- Conditions retain current evaluator expressions (for example: `a == b`, `x > 5`, `10 + 2 == 12`). - Conditions retain current evaluator expressions (for example: `a == b`, `x > 5`, `10 + 2 == 12`).
@@ -22,6 +24,7 @@ This document describes the current, word-based YesNt syntax.
```ynt ```ynt
var name = world var name = world
func greet: func greet:
print_line "Hello world"
print_line Hello ${name} print_line Hello ${name}
return return
call greet call greet
@@ -0,0 +1,71 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
namespace YesNt.Interpreter.Tests;
[TestClass]
public class StringLiteralStatementsTests
{
[TestMethod]
public void StringLiteralWithSpacesWorksTest()
{
List<string> lines =
[
"var msg = \"hello world\"",
"${msg}"
];
YesNtAssert.IsLastLineEqual(lines, "hello world");
}
[TestMethod]
public void StringLiteralEscapesWorkTest()
{
List<string> lines =
[
"var msg = \"a\\n\\t\\\"b\"",
"${msg}"
];
YesNtAssert.IsLastLineEqual(lines, "a\n\t\"b");
}
[TestMethod]
public void StringLiteralPreventsVariableInterpolationTest()
{
List<string> lines =
[
"var x = hidden",
"print_line \"${x}\""
];
YesNtAssert.ContainsDebugOutput(lines, "${x}");
}
[TestMethod]
public void StringLiteralWorksWithListAddTest()
{
List<string> lines =
[
"list items new",
"list items add \"hello world\"",
"list items get 0",
"var result = %out",
"${result}"
];
YesNtAssert.IsLastLineEqual(lines, "hello world");
}
[TestMethod]
public void UnterminatedStringLiteralFailsTest()
{
List<string> lines =
[
"var msg = \"hello"
];
YesNtAssert.ContainsTerminationMessage(lines, "Invalid string literal");
}
}
@@ -0,0 +1,78 @@
using System.Text;
using YesNt.Interpreter.Attributes;
using YesNt.Interpreter.Enums;
using YesNt.Interpreter.Runtime;
using YesNt.Interpreter.Utilities;
namespace YesNt.Interpreter.Statements;
internal class StringLiteralStatements : StatementRuntimeInformation
{
[Statement("\"", SearchMode.Contains, SpaceAround.None, System.ConsoleColor.DarkYellow, Priority = Priority.PreProcessing, KeepStatementInArgs = true)]
public void ParseStringLiterals(string args)
{
if (!args.Contains('"'))
{
return;
}
StringBuilder output = new StringBuilder(args.Length);
for (int i = 0; i < args.Length; i++)
{
char current = args[i];
if (current != '"')
{
_ = output.Append(current);
continue;
}
StringBuilder literal = new StringBuilder();
bool closed = false;
i++;
for (; i < args.Length; i++)
{
char ch = args[i];
if (ch == '\\' && i + 1 < args.Length)
{
i++;
_ = literal.Append(ParseEscape(args[i]));
continue;
}
if (ch == '"')
{
closed = true;
break;
}
_ = literal.Append(ch);
}
if (!closed)
{
RuntimeInfo.Exit("Invalid string literal", true);
return;
}
_ = output.Append(literal.ToString().ToSafeString());
}
RuntimeInfo.CurrentLine = output.ToString();
}
private static char ParseEscape(char escapeChar)
{
return escapeChar switch
{
'n' => '\n',
'r' => '\r',
't' => '\t',
'"' => '"',
'\\' => '\\',
_ => escapeChar
};
}
}