-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expression.HasErrors() not flagging error with if function #44
Comments
We ended up writing a custom visitor that implemented some parameter checking for us. Just in case it helps anyone else, this is roughly what we are using: //example usage
public List<string> ValidateExpression(string expression)
{
var expr = new Expression(expression);
if (expr.HasErrors())
return new List<string>() {"Expression has Invalid Syntax"};
var functionParamValidator = new FunctionParamValidator();
expr.ParsedExpression.Accept(functionParamValidator);
return functionParamValidator.Errors;
}
public enum NCalcFunctionParamRequirementType
{
Exact,
Minimum
}
class NCalcFunctionParamRequirement
{
public NCalcFunctionParamRequirementType Type { get; private set; }
public int Value { get; private set; }
public NCalcFunctionParamRequirement(int value, NCalcFunctionParamRequirementType type)
{
Value = value;
Type = type;
}
public bool RequirementMet(int parameterCount) =>
Type switch
{
NCalcFunctionParamRequirementType.Exact => parameterCount == Value,
NCalcFunctionParamRequirementType.Minimum => parameterCount >= Value,
_ => throw new ArgumentOutOfRangeException("Invalid Enum Value")
};
}
class FunctionParamValidator : LogicalExpressionVisitor
{
public List<string> Errors { get; private set; } = new List<string>();
private readonly Dictionary<string, NCalcFunctionParamRequirement> CustomFunctionParamCount;
private readonly Dictionary<string, NCalcFunctionParamRequirement> NCalcuFunctionParamCount = new Dictionary<string, NCalcFunctionParamRequirement>()
{
{"Abs", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Acos", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Asin", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Atan", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Ceiling", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Cos", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Exp", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Floor", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"IEEERemainder", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Exact) },
{"Log", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Exact) },
{"Log10", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Pow", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Exact) },
{"Round", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Exact) },
{"Sign", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Sin", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Sqrt", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Tan", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Truncate", new NCalcFunctionParamRequirement(1, NCalcFunctionParamRequirementType.Exact) },
{"Max", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Exact) },
{"Min", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Exact) },
{"if", new NCalcFunctionParamRequirement(3, NCalcFunctionParamRequirementType.Exact) },
{"in", new NCalcFunctionParamRequirement(2, NCalcFunctionParamRequirementType.Minimum) }
};
public FunctionParamValidator(Dictionary<string, NCalcFunctionParamRequirement> customFunctionParamCount = null)
{
CustomFunctionParamCount = customFunctionParamCount ?? new Dictionary<string, NCalcFunctionParamRequirement>();
}
public override void Visit(NCalc.Domain.Identifier function)
{
}
public override void Visit(NCalc.Domain.UnaryExpression expression)
{
expression.Expression.Accept(this);
}
public override void Visit(NCalc.Domain.BinaryExpression expression)
{
//Visit left and right
expression.LeftExpression.Accept(this);
expression.RightExpression.Accept(this);
}
public override void Visit(NCalc.Domain.TernaryExpression expression)
{
//Visit left, right and middle
expression.LeftExpression.Accept(this);
expression.RightExpression.Accept(this);
expression.MiddleExpression.Accept(this);
}
public override void Visit(Function function)
{
if (CustomFunctionParamCount.ContainsKey(function.Identifier.Name))
{
ValidateFunctionParamCount(function, CustomFunctionParamCount[function.Identifier.Name]);
}
else if (NCalcuFunctionParamCount.ContainsKey(function.Identifier.Name))
{
ValidateFunctionParamCount(function, NCalcuFunctionParamCount[function.Identifier.Name]);
}
else
{
//in this case, its possible a function name in the expression has a typo. It's also possible
//the function exists, but we don't have parameter info for it. In either case, I think it would
//be good to report a validation issue!
Errors.Add($"No function parameter count info was found for the function '{function.Identifier.Name}'. This may be a typo" +
$"or we may need to add additional parameter count data for a custom function for this validation to work!.");
}
foreach (var expr in function.Expressions)
{
expr.Accept(this);
}
}
public override void Visit(LogicalExpression expression)
{
}
public override void Visit(ValueExpression expression)
{
}
private void ValidateFunctionParamCount(Function function, NCalcFunctionParamRequirement paramRequirement)
{
if (!paramRequirement.RequirementMet(function.Expressions.Length))
{
var requirementTypeToEnglishPhrase = paramRequirement.Type == NCalcFunctionParamRequirementType.Minimum
? "a minimum of"
: "exactly";
Errors.Add($"function '{function.Identifier.Name}' is expected to have " +
$"{requirementTypeToEnglishPhrase} {paramRequirement.Value} parameters," +
$" but {function.Expressions.Length} were specified! " +
$"Parameters: ({string.Join(",", function.Expressions.Select(x => x.ToString()).ToList())})");
}
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Running the above code with the latest version (2.2.80), the expression will not show as having errors ( the second line), but when evaluating it. it willk throw an argument exception (third line).
Why is this happening? is there a better way to ensure a given expression does not have errors?
The text was updated successfully, but these errors were encountered: