177 lines
5.0 KiB
C#
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;
|
|
}
|
|
}
|