templates support
This commit is contained in:
176
Inspectron.Epson.Templates/Interpreter/ExpressionEvaluator.cs
Normal file
176
Inspectron.Epson.Templates/Interpreter/ExpressionEvaluator.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user