mirror of
https://github.com/Stone-Red-Code/YesNt-Interpreter.git
synced 2026-09-04 00:56:31 +02:00
Add string literal parsing with escapes and related tests
This commit is contained in:
@@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user