diff --git a/README.md b/README.md
index 86cc690..f513c4c 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
# NCalc
-
+
[](https://codecov.io/gh/ncalc/ncalc)
[](https://nuget.org/packages/NCalcSync.signed)
+
[](https://discord.gg/TeJkmXbqFk)
@@ -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
diff --git a/src/NCalc.Async/AsyncExpression.cs b/src/NCalc.Async/AsyncExpression.cs
index e733ba9..b4a245a 100644
--- a/src/NCalc.Async/AsyncExpression.cs
+++ b/src/NCalc.Async/AsyncExpression.cs
@@ -260,12 +260,28 @@ public bool HasErrors()
}
///
- /// Returns a list with all parameters names from the expression.
+ /// Returns a list with all parameter names from the expression.
///
- public List GetParametersNames()
+ public List GetParameterNames()
{
var parameterExtractionVisitor = new ParameterExtractionVisitor();
LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
return LogicalExpression.Accept(parameterExtractionVisitor);
}
+
+ [Obsolete("Please use GetParameterNames (correct english spelling).")]
+ public List GetParametersNames()
+ {
+ return GetParameterNames();
+ }
+
+ ///
+ /// Returns a list with all function names from the expression.
+ ///
+ public List GetFunctionNames()
+ {
+ var functionExtractionVisitor = new FunctionExtractionVisitor();
+ LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
+ return LogicalExpression.Accept(functionExtractionVisitor);
+ }
}
\ No newline at end of file
diff --git a/src/NCalc.Core/Visitors/FunctionExtractionVisitor.cs b/src/NCalc.Core/Visitors/FunctionExtractionVisitor.cs
new file mode 100644
index 0000000..4e5eea7
--- /dev/null
+++ b/src/NCalc.Core/Visitors/FunctionExtractionVisitor.cs
@@ -0,0 +1,63 @@
+using NCalc.Domain;
+
+namespace NCalc.Visitors;
+
+///
+/// Visitor dedicated to extract names from a .
+///
+public sealed class FunctionExtractionVisitor : ILogicalExpressionVisitor>
+{
+ public List Visit(Identifier identifier) => [];
+
+ public List Visit(LogicalExpressionList list)
+ {
+ var functions = new List();
+ 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 Visit(UnaryExpression expression) => expression.Expression.Accept(this);
+
+ public List Visit(BinaryExpression expression)
+ {
+ var leftParameters = expression.LeftExpression.Accept(this);
+ var rightParameters = expression.RightExpression.Accept(this);
+
+ leftParameters.AddRange(rightParameters);
+ return leftParameters.Distinct().ToList();
+ }
+
+ public List 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 Visit(Function function)
+ {
+ var functions = new List { function.Identifier.Name };
+
+ foreach (var expression in function.Expressions)
+ {
+ var exprParameters = expression.Accept(this);
+ functions.AddRange(exprParameters);
+ }
+ return functions.Distinct().ToList();
+ }
+
+ public List Visit(ValueExpression expression) => [];
+}
\ No newline at end of file
diff --git a/src/NCalc.Core/Visitors/ParameterExtractionVisitor.cs b/src/NCalc.Core/Visitors/ParameterExtractionVisitor.cs
index da4ba31..0ac8e19 100644
--- a/src/NCalc.Core/Visitors/ParameterExtractionVisitor.cs
+++ b/src/NCalc.Core/Visitors/ParameterExtractionVisitor.cs
@@ -3,7 +3,7 @@
namespace NCalc.Visitors;
///
-/// Visitor dedicated to extract parameters from a .
+/// Visitor dedicated to extract names from a .
///
public sealed class ParameterExtractionVisitor : ILogicalExpressionVisitor>
{
@@ -22,12 +22,12 @@ public List Visit(LogicalExpressionList list)
var parameters = new List();
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;
diff --git a/src/NCalc.Core/Visitors/SerializationVisitor.cs b/src/NCalc.Core/Visitors/SerializationVisitor.cs
index c9c37a6..998de23 100644
--- a/src/NCalc.Core/Visitors/SerializationVisitor.cs
+++ b/src/NCalc.Core/Visitors/SerializationVisitor.cs
@@ -4,7 +4,7 @@
namespace NCalc.Visitors;
///
-/// Class responsible to converting an into a representation.
+/// Class responsible to converting a into a representation.
///
public class SerializationVisitor : ILogicalExpressionVisitor
{
diff --git a/src/NCalc.Sync/Expression.cs b/src/NCalc.Sync/Expression.cs
index 95efe8e..9a17f1f 100644
--- a/src/NCalc.Sync/Expression.cs
+++ b/src/NCalc.Sync/Expression.cs
@@ -254,12 +254,28 @@ public bool HasErrors()
}
///
- /// Returns a list with all parameters names from the expression.
+ /// Returns a list with all parameter names from the expression.
///
- public List GetParametersNames()
+ public List GetParameterNames()
{
var parameterExtractionVisitor = new ParameterExtractionVisitor();
LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
return LogicalExpression.Accept(parameterExtractionVisitor);
}
+
+ [Obsolete("Please use GetParameterNames (correct english spelling).")]
+ public List GetParametersNames()
+ {
+ return GetParameterNames();
+ }
+
+ ///
+ /// Returns a list with all function names from the expression.
+ ///
+ public List GetFunctionNames()
+ {
+ var functionExtractionVisitor = new FunctionExtractionVisitor();
+ LogicalExpression ??= LogicalExpressionFactory.Create(ExpressionString!, Context.Options);
+ return LogicalExpression.Accept(functionExtractionVisitor);
+ }
}
\ No newline at end of file
diff --git a/test/NCalc.Tests/ExtractionTests.cs b/test/NCalc.Tests/ExtractionTests.cs
new file mode 100644
index 0000000..353ed57
--- /dev/null
+++ b/test/NCalc.Tests/ExtractionTests.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/test/NCalc.Tests/ParameterExtractionTests.cs b/test/NCalc.Tests/ParameterExtractionTests.cs
deleted file mode 100644
index c2429e5..0000000
--- a/test/NCalc.Tests/ParameterExtractionTests.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-namespace NCalc.Tests;
-
-[Trait("Category","Parameter Extraction")]
-public class ParameterExtractionTests
-{
- [Fact]
- public void Should_Get_Parameters_Issue_103()
- {
- 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.GetParametersNames();
- Assert.Contains("a", parameters);
- Assert.Contains("PageState", parameters);
- Assert.Equal(2, parameters.Count);
- }
-
- [Fact]
- public void Should_Get_Parameters_One_Time_Issue_141()
- {
- var expression =
- new Expression("if(x=0,x,y)",
- ExpressionOptions.CaseInsensitiveStringComparer);
- var parameters = expression.GetParametersNames();
-
- Assert.Equal(2,parameters.Count);
- }
-
- [Fact]
- public void Should_Get_Parameters_With_Unary()
- {
- var expression = new Expression("-0.68");
- var p = expression.GetParametersNames();
- Assert.Empty(p);
- }
-
- [Fact]
- public void ShouldGetParametersInsideArray()
- {
- var expression = new Expression("(a,b,c)");
- var p = expression.GetParametersNames();
- Assert.Equal(3, p.Count);
- }
-}
\ No newline at end of file