mirror of
https://github.com/Stone-Red-Code/YesNt-Interpreter.git
synced 2026-09-09 16:06:08 +02:00
Performance improvements and code cleanup
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -19,11 +19,13 @@ internal static partial class Evaluator
|
||||
/// </returns>
|
||||
public static bool? EvaluateCondition(string input)
|
||||
{
|
||||
if (input.ToLower().FromSafeString().Trim() == "true")
|
||||
input = input.FromSafeString();
|
||||
string lower = input.ToLower().Trim();
|
||||
if (lower == "true")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (input.ToLower().FromSafeString().Trim() == "false")
|
||||
else if (lower == "false")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -31,16 +33,16 @@ internal static partial class Evaluator
|
||||
string[] parts = input.Split("==");
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
string part1 = parts[0].FromSafeString().Trim();
|
||||
string part2 = parts[1].FromSafeString().Trim();
|
||||
string part1 = parts[0].Trim();
|
||||
string part2 = parts[1].Trim();
|
||||
return part1 == part2;
|
||||
}
|
||||
|
||||
parts = input.Split("!=");
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
string part1 = parts[0].FromSafeString().Trim();
|
||||
string part2 = parts[1].FromSafeString().Trim();
|
||||
string part1 = parts[0].Trim();
|
||||
string part2 = parts[1].Trim();
|
||||
return part1 != part2;
|
||||
}
|
||||
|
||||
@@ -89,23 +91,26 @@ internal static partial class Evaluator
|
||||
/// <returns>The result as a culture-invariant numeric string, or <c>"NaN"</c> if evaluation failed.</returns>
|
||||
public static string Calculate(string input)
|
||||
{
|
||||
input = input.FromSafeString();
|
||||
input = PlusPlusRegex().Replace(input, "+");
|
||||
input = MinusMinusRegex().Replace(input, "+");
|
||||
input = MinusPlusRegex().Replace(input, "-");
|
||||
input = PlusMinusRegex().Replace(input, "-");
|
||||
|
||||
string yes = Calculate(input, '+');
|
||||
return yes;
|
||||
return CalculateInternal(input, '+');
|
||||
}
|
||||
|
||||
private static string Calculate(string input, char op)
|
||||
private static string CalculateInternal(string input, char op)
|
||||
{
|
||||
if (input is null)
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
input = input.FromSafeString();
|
||||
if (input.ToStandardizedNumber(out double quickNum))
|
||||
{
|
||||
return quickNum.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
MatchCollection matches = ParenthesesRegex().Matches(input);
|
||||
while (matches.Count > 0)
|
||||
@@ -113,7 +118,7 @@ internal static partial class Evaluator
|
||||
for (int i = 0; i < matches.Count; i++)
|
||||
{
|
||||
string calc = matches[i].Value.Substring(1, matches[i].Length - 2);
|
||||
string ret = Calculate(calc);
|
||||
string ret = CalculateInternal(calc, '+');
|
||||
input = input.Replace(matches[i].Value, ret);
|
||||
}
|
||||
matches = ParenthesesRegex().Matches(input);
|
||||
@@ -136,11 +141,11 @@ internal static partial class Evaluator
|
||||
|
||||
part = op switch
|
||||
{
|
||||
'+' => Calculate(part, '-'),
|
||||
'-' => Calculate(part, '*'),
|
||||
'*' => Calculate(part, '/'),
|
||||
'/' => Calculate(part, '%'),
|
||||
'%' => Calculate(part, '^'),
|
||||
'+' => CalculateInternal(part, '-'),
|
||||
'-' => CalculateInternal(part, '*'),
|
||||
'*' => CalculateInternal(part, '/'),
|
||||
'/' => CalculateInternal(part, '%'),
|
||||
'%' => CalculateInternal(part, '^'),
|
||||
_ => part
|
||||
};
|
||||
|
||||
@@ -208,4 +213,4 @@ internal static partial class Evaluator
|
||||
|
||||
[GeneratedRegex("(\\+ +\\-)+")]
|
||||
private static partial Regex PlusMinusRegex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ internal delegate void UserCallBack(string data);
|
||||
/// <see cref="FixedProcess"/> flushes whatever is in the read buffer immediately, enabling real-time
|
||||
/// output forwarding for interactive child processes.
|
||||
/// </summary>
|
||||
public class FixedProcess : Process
|
||||
internal class FixedProcess : Process
|
||||
{
|
||||
public new event DataReceivedEventHandler OutputDataReceived;
|
||||
|
||||
|
||||
@@ -64,13 +64,30 @@ public static class StringExtensions
|
||||
/// <returns>The safe-string encoded representation.</returns>
|
||||
public static string ToSafeString(this string input)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
foreach (char c in input)
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
_ = output.Append($"\v{c}\v");
|
||||
return input;
|
||||
}
|
||||
|
||||
return ReplaceOnce(output.ToString(), ReplacementRules);
|
||||
StringBuilder output = new StringBuilder(input.Length * 3);
|
||||
foreach (char c in input)
|
||||
{
|
||||
string s = c.ToString();
|
||||
if (ReplacementRules.TryGetValue(s, out string replacement))
|
||||
{
|
||||
_ = output.Append('\v');
|
||||
_ = output.Append(replacement);
|
||||
_ = output.Append('\v');
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = output.Append('\v');
|
||||
_ = output.Append(c);
|
||||
_ = output.Append('\v');
|
||||
}
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,7 +97,35 @@ public static class StringExtensions
|
||||
/// <returns>The decoded plain string.</returns>
|
||||
public static string FromSafeString(this string input)
|
||||
{
|
||||
return ReplaceOnce(input.Replace("\v", string.Empty), reverseReplacementRules);
|
||||
if (string.IsNullOrEmpty(input) || (!input.Contains('\v') && !input.Contains('\x01')))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
string stripped = input.Replace("\v", string.Empty);
|
||||
if (!stripped.Contains('\x01'))
|
||||
{
|
||||
return stripped;
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder(stripped.Length);
|
||||
for (int i = 0; i < stripped.Length; i++)
|
||||
{
|
||||
if (stripped[i] == '\x01' && i + 4 < stripped.Length && stripped[i + 4] == '\x01')
|
||||
{
|
||||
string code = stripped.Substring(i, 5);
|
||||
if (reverseReplacementRules.TryGetValue(code, out string value))
|
||||
{
|
||||
_ = output.Append(value);
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_ = output.Append(stripped[i]);
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,7 +137,11 @@ public static class StringExtensions
|
||||
/// <returns><see langword="true"/> if parsing succeeded; otherwise <see langword="false"/>.</returns>
|
||||
public static bool ToStandardizedNumber(this string input, out double result)
|
||||
{
|
||||
return double.TryParse(input.FromSafeString().Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||
if (input.IndexOf(',') != -1)
|
||||
{
|
||||
input = input.Replace(',', '.');
|
||||
}
|
||||
return double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||
}
|
||||
|
||||
/// <summary>Replaces only the first occurrence of <paramref name="oldValue"/> in the string.</summary>
|
||||
@@ -131,24 +180,4 @@ public static class StringExtensions
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static string ReplaceOnce(string input, Dictionary<string, string> replacementRules)
|
||||
{
|
||||
// \x01emp\x01/string.Empty is a special case, it is used to represent empty strings and won't work with the normal rules because an empty string always matches and causes an infinite loop 3 letter abbreviation
|
||||
IEnumerable<KeyValuePair<string, string>> matches = replacementRules.Where(rule => rule.Key != string.Empty && input.Contains(rule.Key, StringComparison.Ordinal));
|
||||
if (!matches.Any())
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
KeyValuePair<string, string> match = matches.First();
|
||||
int startIndex = input.IndexOf(match.Key, StringComparison.Ordinal);
|
||||
int endIndex = startIndex + match.Key.Length;
|
||||
|
||||
string before = ReplaceOnce(input[..startIndex], replacementRules);
|
||||
string replaced = match.Value;
|
||||
string after = ReplaceOnce(input[endIndex..], replacementRules);
|
||||
|
||||
return before + replaced + after;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using YesNt.Interpreter.Runtime;
|
||||
|
||||
namespace YesNt.Interpreter.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides high-performance template substitution for variables and stack parameters.
|
||||
/// </summary>
|
||||
internal static class TemplateProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of ${variableName} with their current values.
|
||||
/// </summary>
|
||||
public static string ProcessVariables(string input, RuntimeInformation runtimeInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
|
||||
int startIdx = input.IndexOf("${", StringComparison.Ordinal);
|
||||
if (startIdx == -1) return input;
|
||||
|
||||
StringBuilder sb = new StringBuilder(input.Length);
|
||||
int lastIdx = 0;
|
||||
|
||||
while (startIdx != -1)
|
||||
{
|
||||
sb.Append(input, lastIdx, startIdx - lastIdx);
|
||||
int endIdx = input.IndexOf('}', startIdx + 2);
|
||||
if (endIdx == -1)
|
||||
{
|
||||
sb.Append("${");
|
||||
lastIdx = startIdx + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
string varName = input.Substring(startIdx + 2, endIdx - (startIdx + 2));
|
||||
if (runtimeInfo.Variables.TryGetValue(varName, out string value))
|
||||
{
|
||||
sb.Append(value);
|
||||
}
|
||||
else if (runtimeInfo.GlobalVariables.TryGetValue(varName, out value))
|
||||
{
|
||||
sb.Append(value);
|
||||
}
|
||||
else if (!runtimeInfo.IsSearching)
|
||||
{
|
||||
runtimeInfo.Exit(ExitMessages.VariableNotFound(varName), true);
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("${");
|
||||
sb.Append(varName);
|
||||
sb.Append('}');
|
||||
}
|
||||
|
||||
lastIdx = endIdx + 1;
|
||||
}
|
||||
|
||||
startIdx = input.IndexOf("${", lastIdx, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
sb.Append(input, lastIdx, input.Length - lastIdx);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of a placeholder (e.g., %in, %out) with values popped from a stack.
|
||||
/// </summary>
|
||||
public static string ProcessStackParameters(string input, string placeholder, Stack<string> stack, RuntimeInformation runtimeInfo, string emptyStackMessage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
|
||||
int startIdx = input.IndexOf(placeholder, StringComparison.Ordinal);
|
||||
if (startIdx == -1) return input;
|
||||
|
||||
StringBuilder sb = new StringBuilder(input.Length);
|
||||
int lastIdx = 0;
|
||||
int placeholderLen = placeholder.Length;
|
||||
|
||||
while (startIdx != -1)
|
||||
{
|
||||
sb.Append(input, lastIdx, startIdx - lastIdx);
|
||||
if (stack.Count == 0)
|
||||
{
|
||||
runtimeInfo.Exit(emptyStackMessage, true);
|
||||
return input;
|
||||
}
|
||||
sb.Append(stack.Pop());
|
||||
lastIdx = startIdx + placeholderLen;
|
||||
startIdx = input.IndexOf(placeholder, lastIdx, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
sb.Append(input, lastIdx, input.Length - lastIdx);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of a placeholder with a fixed value.
|
||||
/// </summary>
|
||||
public static string ProcessSimplePlaceholders(string input, string placeholder, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
|
||||
return input.Replace(placeholder, value, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of a placeholder with values generated by a provider function.
|
||||
/// </summary>
|
||||
public static string ProcessDynamicPlaceholders(string input, string placeholder, Func<string> valueProvider)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
|
||||
int startIdx = input.IndexOf(placeholder, StringComparison.Ordinal);
|
||||
if (startIdx == -1) return input;
|
||||
|
||||
StringBuilder sb = new StringBuilder(input.Length);
|
||||
int lastIdx = 0;
|
||||
int placeholderLen = placeholder.Length;
|
||||
|
||||
while (startIdx != -1)
|
||||
{
|
||||
sb.Append(input, lastIdx, startIdx - lastIdx);
|
||||
sb.Append(valueProvider());
|
||||
lastIdx = startIdx + placeholderLen;
|
||||
startIdx = input.IndexOf(placeholder, lastIdx, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
sb.Append(input, lastIdx, input.Length - lastIdx);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of arithmetic expressions with their results.
|
||||
/// </summary>
|
||||
public static string ProcessCalculations(string input, RuntimeInformation runtimeInfo, Regex calculationRegex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
|
||||
MatchCollection matches = calculationRegex.Matches(input);
|
||||
if (matches.Count == 0) return input;
|
||||
|
||||
StringBuilder sb = new StringBuilder(input.Length);
|
||||
int lastIdx = 0;
|
||||
|
||||
for (int i = 0; i < matches.Count; i++)
|
||||
{
|
||||
Match match = matches[i];
|
||||
sb.Append(input, lastIdx, match.Index - lastIdx);
|
||||
|
||||
string res = Evaluator.Calculate(match.Value);
|
||||
if (res is null)
|
||||
{
|
||||
runtimeInfo.Exit(ExitMessages.InvalidOperation, true);
|
||||
return input;
|
||||
}
|
||||
sb.Append(res);
|
||||
lastIdx = match.Index + match.Length;
|
||||
}
|
||||
|
||||
sb.Append(input, lastIdx, input.Length - lastIdx);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user