Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Bykiev committed Jul 15, 2024
2 parents 693b0f2 + 83c84e5 commit 796ac32
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 65 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# NCalc

![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ncalc/ncalc/dotnet.yml)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ncalc/ncalc/build-test.yml)
[![Coverage](https://img.shields.io/codecov/c/github/ncalc/ncalc.svg)](https://codecov.io/gh/ncalc/ncalc)
[![NuGet](https://img.shields.io/nuget/v/NCalcSync.signed.svg?label=nuget&color=004880
)](https://nuget.org/packages/NCalcSync.signed)
![NuGet Downloads](https://img.shields.io/nuget/dt/NCalcSync.svg?color=004880)
[![Discord](https://img.shields.io/discord/1237181265426387005?color=5b62ef&label=discord
)](https://discord.gg/TeJkmXbqFk)

Expand Down Expand Up @@ -165,9 +166,9 @@ A TypeScript/JavaScript port of NCalc.

NCalc 101 is a simple web application that allows you to try out the NCalc expression evaluator, developed by [Panoramic Data](https://github.com/panoramicdata).

### [JJMasterData.NCalc](https://md.jjconsulting.tech/articles/plugins/ncalc.html)
### [JJMasterData](https://github.com/JJConsulting/JJMasterData/)

Plugin of NCalc used to evaluate [JJMasterData](https://github.com/jjconsulting/jjmasterdata) expressions. JJMasterData is a runtime form generator from database metadata.
JJMasterData is a runtime form generator from database metadata. It uses NCalc to evaluate expressions used in field visibility and other dynamic behaviors.

# NCalc versioning

Expand Down
20 changes: 18 additions & 2 deletions src/NCalc.Async/AsyncExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,28 @@ public bool HasErrors()
}

/// <summary>
/// Returns a list with all parameters names from the expression.
/// Returns a list with all parameter names from the expression.
/// </summary>
public List<string> GetParametersNames()
public List<string> GetParameterNames()
{
var parameterExtractionVisitor = new ParameterExtractionVisitor();
LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
return LogicalExpression.Accept(parameterExtractionVisitor);
}

[Obsolete("Please use GetParameterNames (correct english spelling).")]
public List<string> GetParametersNames()
{
return GetParameterNames();
}

/// <summary>
/// Returns a list with all function names from the expression.
/// </summary>
public List<string> GetFunctionNames()
{
var functionExtractionVisitor = new FunctionExtractionVisitor();
LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
return LogicalExpression.Accept(functionExtractionVisitor);
}
}
63 changes: 63 additions & 0 deletions src/NCalc.Core/Visitors/FunctionExtractionVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using NCalc.Domain;

namespace NCalc.Visitors;

/// <summary>
/// Visitor dedicated to extract <see cref="Function"/> names from a <see cref="LogicalExpression"/>.
/// </summary>
public sealed class FunctionExtractionVisitor : ILogicalExpressionVisitor<List<string>>
{
public List<string> Visit(Identifier identifier) => [];

public List<string> Visit(LogicalExpressionList list)
{
var functions = new List<string>();
foreach (var value in list)
{
if (value is Function function)
{
if (!functions.Contains(function.Identifier.Name))
{
functions.Add(function.Identifier.Name);
}
}
}
return functions;
}

public List<string> Visit(UnaryExpression expression) => expression.Expression.Accept(this);

public List<string> Visit(BinaryExpression expression)
{
var leftParameters = expression.LeftExpression.Accept(this);
var rightParameters = expression.RightExpression.Accept(this);

leftParameters.AddRange(rightParameters);
return leftParameters.Distinct().ToList();
}

public List<string> Visit(TernaryExpression expression)
{
var leftParameters = expression.LeftExpression.Accept(this);
var middleParameters = expression.MiddleExpression.Accept(this);
var rightParameters = expression.RightExpression.Accept(this);

leftParameters.AddRange(middleParameters);
leftParameters.AddRange(rightParameters);
return leftParameters.Distinct().ToList();
}

public List<string> Visit(Function function)
{
var functions = new List<string> { function.Identifier.Name };

foreach (var expression in function.Expressions)
{
var exprParameters = expression.Accept(this);
functions.AddRange(exprParameters);
}
return functions.Distinct().ToList();
}

public List<string> Visit(ValueExpression expression) => [];
}
12 changes: 6 additions & 6 deletions src/NCalc.Core/Visitors/ParameterExtractionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace NCalc.Visitors;

/// <summary>
/// Visitor dedicated to extract parameters from a <see cref="LogicalExpression"/>.
/// Visitor dedicated to extract <see cref="Identifier"/> names from a <see cref="LogicalExpression"/>.
/// </summary>
public sealed class ParameterExtractionVisitor : ILogicalExpressionVisitor<List<string>>
{
Expand All @@ -22,12 +22,12 @@ public List<string> Visit(LogicalExpressionList list)
var parameters = new List<string>();
foreach (var value in list)
{
if (value is not Identifier identifier)
continue;

if (!parameters.Contains(identifier.Name))
if (value is Identifier identifier)
{
parameters.Add(identifier.Name);
if (!parameters.Contains(identifier.Name))
{
parameters.Add(identifier.Name);
}
}
}
return parameters;
Expand Down
2 changes: 1 addition & 1 deletion src/NCalc.Core/Visitors/SerializationVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace NCalc.Visitors;

/// <summary>
/// Class responsible to converting an <see cref="LogicalExpression"/> into a <see cref="string"/> representation.
/// Class responsible to converting a <see cref="LogicalExpression"/> into a <see cref="string"/> representation.
/// </summary>
public class SerializationVisitor : ILogicalExpressionVisitor<string>
{
Expand Down
20 changes: 18 additions & 2 deletions src/NCalc.Sync/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,28 @@ public bool HasErrors()
}

/// <summary>
/// Returns a list with all parameters names from the expression.
/// Returns a list with all parameter names from the expression.
/// </summary>
public List<string> GetParametersNames()
public List<string> GetParameterNames()
{
var parameterExtractionVisitor = new ParameterExtractionVisitor();
LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
return LogicalExpression.Accept(parameterExtractionVisitor);
}

[Obsolete("Please use GetParameterNames (correct english spelling).")]
public List<string> GetParametersNames()
{
return GetParameterNames();
}

/// <summary>
/// Returns a list with all function names from the expression.
/// </summary>
public List<string> GetFunctionNames()
{
var functionExtractionVisitor = new FunctionExtractionVisitor();
LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
return LogicalExpression.Accept(functionExtractionVisitor);
}
}
65 changes: 65 additions & 0 deletions test/NCalc.Tests/ExtractionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
namespace NCalc.Tests;

[Trait("Category","Extraction")]
public class ExtractionTests
{
[Fact]
public void ShouldGetParametersIssue103()
{
var expression = new Expression("PageState == 'LIST' && a == 1 && customFunction() == true || in(1 + 1, 1, 2, 3)", ExpressionOptions.CaseInsensitiveStringComparer)
{
Parameters =
{
["a"] = 1
}
};
expression.DynamicParameters["PageState"] = _ => "List";
expression.Functions["customfunction"] = _ => true;

var parameters = expression.GetParameterNames();
Assert.Contains("a", parameters);
Assert.Contains("PageState", parameters);
Assert.Equal(2, parameters.Count);
}

[Fact]
public void ShouldGetParametersOneTimeIssue141()
{
var expression =
new Expression("if(x=0,x,y)",
ExpressionOptions.CaseInsensitiveStringComparer);
var parameters = expression.GetParameterNames();

Assert.Equal(2,parameters.Count);
}

[Fact]
public void ShouldGetParametersWithUnary()
{
var expression = new Expression("-0.68");
var p = expression.GetParameterNames();
Assert.Empty(p);
}

[Theory]
[InlineData("(a, b, c)", 3)]
[InlineData("725 - 1 == result * secret_operation(secretValue)", 2)]
[InlineData("getLightsaberColor(selectedJedi) == selectedColor", 2)]
public void ShouldGetParameters(string formula, int expectedCount)
{
var expression = new Expression(formula);
var p = expression.GetParameterNames();
Assert.Equal(expectedCount, p.Count);
}

[InlineData("(a, drop_database(), c) == toUpper(getName())", 3)]
[InlineData("Abs(523/2) == Abs(523/2)", 1)]
[InlineData("getLightsaberColor('Yoda') == selectedColor", 1)]
[Theory]
public void ShouldGetFunctions(string formula, int expectedCount)
{
var expression = new Expression(formula);
var functions = expression.GetFunctionNames();
Assert.Equal(expectedCount, functions.Count);
}
}
51 changes: 0 additions & 51 deletions test/NCalc.Tests/ParameterExtractionTests.cs

This file was deleted.

0 comments on commit 796ac32

Please sign in to comment.