Renamed to QR Script
- Split into three projects: Library, Console, and Tests - Library compiles as QRScript.Interpreter to avoid conflicts
This commit is contained in:
parent
e681e6144c
commit
6501b72973
16 changed files with 82 additions and 20 deletions
14
QRScript/QRScript.csproj
Normal file
14
QRScript/QRScript.csproj
Normal file
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>QRScript.Interpreter</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DynamicExpresso.Core" Version="2.19.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
131
QRScript/Runner.cs
Normal file
131
QRScript/Runner.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
namespace QRScript;
|
||||
|
||||
using DynamicExpresso;
|
||||
|
||||
public static class Runner
|
||||
{
|
||||
public static void Interpret(string[] lines)
|
||||
{
|
||||
var env = new Dictionary<string, object>();
|
||||
var labels = new Dictionary<string, int>();
|
||||
var stack = new Stack<int>();
|
||||
var exp = new Interpreter();
|
||||
int pc = 0;
|
||||
|
||||
// First pass: record labels
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
string line = lines[i].Trim();
|
||||
if (line.StartsWith("@"))
|
||||
labels[line[1..]] = i;
|
||||
}
|
||||
|
||||
// Main execution loop
|
||||
while (pc < lines.Length)
|
||||
{
|
||||
string raw = lines[pc].Trim();
|
||||
pc++;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(raw) || raw.StartsWith("#") || raw.StartsWith("@"))
|
||||
continue;
|
||||
|
||||
var parts = raw.Split(' ', 4, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 0) continue;
|
||||
|
||||
string cmd = parts[0].ToLowerInvariant();
|
||||
string arg1 = parts.Length > 1 ? parts[1] : null;
|
||||
string arg2 = parts.Length > 2 ? parts[2] : null;
|
||||
string arg3 = parts.Length > 3 ? parts[3] : null;
|
||||
|
||||
object Eval(string expr)
|
||||
{
|
||||
foreach (var (k, v) in env)
|
||||
exp.SetVariable(k, v);
|
||||
return exp.Eval(expr);
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case "say":
|
||||
Console.WriteLine(string.Join(' ', parts.Skip(1).Select(p =>
|
||||
env.TryGetValue(p, out var val) ? val.ToString() : p)));
|
||||
break;
|
||||
|
||||
case "set":
|
||||
env[arg1] = Eval(arg2);
|
||||
break;
|
||||
|
||||
case "add":
|
||||
case "sub":
|
||||
case "mul":
|
||||
case "div":
|
||||
case "mod":
|
||||
if (!env.TryGetValue(arg1, out var val)) val = 0;
|
||||
var op = cmd switch
|
||||
{
|
||||
"add" => "+",
|
||||
"sub" => "-",
|
||||
"mul" => "*",
|
||||
"div" => "/",
|
||||
"mod" => "%",
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
env[arg1] = Eval($"{val} {op} {arg2}");
|
||||
break;
|
||||
|
||||
case "if":
|
||||
// Format: if x > 10 goto label
|
||||
if (arg2 == "goto" && arg3 != null)
|
||||
{
|
||||
var cond = string.Join(' ', parts.Skip(1).Take(2)); // x > 10
|
||||
var result = Eval(cond);
|
||||
if (result is bool b && b)
|
||||
pc = labels[arg3];
|
||||
}
|
||||
break;
|
||||
|
||||
case "goto":
|
||||
if (arg1 != null && labels.TryGetValue(arg1, out var target))
|
||||
pc = target;
|
||||
break;
|
||||
|
||||
case "inp":
|
||||
Console.Write($"{arg1}: ");
|
||||
env[arg1] = Console.ReadLine();
|
||||
break;
|
||||
|
||||
case "def":
|
||||
// No-op in this basic form, handled via label + call/ret
|
||||
break;
|
||||
|
||||
case "call":
|
||||
if (arg1 != null && labels.TryGetValue(arg1, out var fnStart))
|
||||
{
|
||||
stack.Push(pc);
|
||||
pc = fnStart + 1;
|
||||
if (arg2 != null)
|
||||
env["x"] = Eval(arg2);
|
||||
}
|
||||
break;
|
||||
|
||||
case "ret":
|
||||
if (stack.Count > 0)
|
||||
pc = stack.Pop();
|
||||
else
|
||||
return;
|
||||
break;
|
||||
|
||||
case "cmp":
|
||||
Console.WriteLine($"Compare: {Eval($"{arg1} == {arg2}")}");
|
||||
break;
|
||||
|
||||
case "end":
|
||||
return;
|
||||
|
||||
default:
|
||||
Console.WriteLine($"Unknown command: {cmd}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
QRScript/StringExtensions.cs
Normal file
56
QRScript/StringExtensions.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
namespace QRScript.Extensions;
|
||||
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for common string transformations and validations.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Encodes the current string instance to Base64 using Unicode encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string to encode.</param>
|
||||
/// <returns>A Base64-encoded version of the input string.</returns>
|
||||
/// <remarks>
|
||||
/// This uses Unicode (UTF-16) encoding, which is standard in .NET strings.
|
||||
/// </remarks>
|
||||
public static string ToBase64(this string input)
|
||||
{
|
||||
if (input is null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
byte[] bytes = Encoding.Unicode.GetBytes(input);
|
||||
return Convert.ToBase64String(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Joins an array of strings into a single string separated by the system's newline.
|
||||
/// </summary>
|
||||
/// <param name="lines">Array of strings to join.</param>
|
||||
/// <returns>A single string composed of all input lines, separated by newlines.</returns>
|
||||
public static string JoinLines(this IEnumerable<string> lines)
|
||||
{
|
||||
if (lines is null)
|
||||
throw new ArgumentNullException(nameof(lines));
|
||||
|
||||
return string.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the string exceeds the specified character length limit.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string to evaluate.</param>
|
||||
/// <param name="limit">The maximum number of allowed characters.</param>
|
||||
/// <returns><c>true</c> if the string's length is greater than or equal to <paramref name="limit"/>; otherwise, <c>false</c>.</returns>
|
||||
public static bool ExceedsLengthLimit(this string text, int limit)
|
||||
{
|
||||
if (text is null)
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
|
||||
if (limit < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(limit), "Limit must be non-negative.");
|
||||
|
||||
return text.Length >= limit;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue