Files
Print_server/Inspectron.Epson.Templates/Interpreter/ExpressionEvaluator.cs
2026-01-21 15:56:37 +01:00

177 lines
5.0 KiB
C#

using System.Text.RegularExpressions;
namespace Inspectron.Epson.Templates.Interpreter;
public class ExpressionEvaluator
{
private static readonly Regex ComparisonRegex = new(
@"^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$",
RegexOptions.Compiled);
public bool Evaluate(string expression, DataContext context)
{
expression = expression.Trim();
if (string.IsNullOrEmpty(expression))
{
return false;
}
// Handle negation: !property
if (expression.StartsWith("!"))
{
var inner = expression[1..].Trim();
return !Evaluate(inner, context);
}
// Handle comparison operators
var match = ComparisonRegex.Match(expression);
if (match.Success)
{
var left = match.Groups[1].Value.Trim();
var op = match.Groups[2].Value;
var right = match.Groups[3].Value.Trim();
return EvaluateComparison(left, op, right, context);
}
// Handle logical AND: property1 && property2
if (expression.Contains("&&"))
{
var parts = expression.Split("&&", 2);
return Evaluate(parts[0], context) && Evaluate(parts[1], context);
}
// Handle logical OR: property1 || property2
if (expression.Contains("||"))
{
var parts = expression.Split("||", 2);
return Evaluate(parts[0], context) || Evaluate(parts[1], context);
}
// Simple truthy check
return context.IsTruthy(expression);
}
private bool EvaluateComparison(string left, string op, string right, DataContext context)
{
var leftValue = ResolveValue(left, context);
var rightValue = ResolveValue(right, context);
// Handle null comparisons
if (leftValue == null && rightValue == null)
{
return op == "==" || op == ">=" || op == "<=";
}
if (leftValue == null || rightValue == null)
{
return op switch
{
"==" => false,
"!=" => true,
_ => false
};
}
// Try numeric comparison first
if (TryGetNumeric(leftValue, out var leftNum) && TryGetNumeric(rightValue, out var rightNum))
{
return op switch
{
"==" => Math.Abs(leftNum - rightNum) < 0.0001,
"!=" => Math.Abs(leftNum - rightNum) >= 0.0001,
">" => leftNum > rightNum,
"<" => leftNum < rightNum,
">=" => leftNum >= rightNum,
"<=" => leftNum <= rightNum,
_ => false
};
}
// Fall back to string comparison
var leftStr = leftValue.ToString() ?? "";
var rightStr = rightValue.ToString() ?? "";
return op switch
{
"==" => leftStr.Equals(rightStr, StringComparison.OrdinalIgnoreCase),
"!=" => !leftStr.Equals(rightStr, StringComparison.OrdinalIgnoreCase),
">" => string.Compare(leftStr, rightStr, StringComparison.OrdinalIgnoreCase) > 0,
"<" => string.Compare(leftStr, rightStr, StringComparison.OrdinalIgnoreCase) < 0,
">=" => string.Compare(leftStr, rightStr, StringComparison.OrdinalIgnoreCase) >= 0,
"<=" => string.Compare(leftStr, rightStr, StringComparison.OrdinalIgnoreCase) <= 0,
_ => false
};
}
private object? ResolveValue(string expression, DataContext context)
{
expression = expression.Trim();
// Check for quoted string literal
if ((expression.StartsWith("\"") && expression.EndsWith("\"")) ||
(expression.StartsWith("'") && expression.EndsWith("'")))
{
return expression[1..^1];
}
// Check for numeric literal
if (double.TryParse(expression, out var num))
{
return num;
}
// Check for boolean literal
if (expression.Equals("true", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (expression.Equals("false", StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Check for null literal
if (expression.Equals("null", StringComparison.OrdinalIgnoreCase))
{
return null;
}
// Resolve as path
return context.GetValue(expression);
}
private bool TryGetNumeric(object? value, out double result)
{
result = 0;
if (value == null) return false;
if (value is double d)
{
result = d;
return true;
}
if (value is long l)
{
result = l;
return true;
}
if (value is int i)
{
result = i;
return true;
}
if (value is string s && double.TryParse(s, out result))
{
return true;
}
return false;
}
}