delegateas / expressionengine Goto Github PK
View Code? Open in Web Editor NEWExpression Engine for Power Automate expressions written in C# using Sprache!
License: MIT License
Expression Engine for Power Automate expressions written in C# using Sprache!
License: MIT License
Use logging from DI
We had the following expression
"expression": "@and(equal(length(formvalue(logicalName())[idx()]['cvr']),8),isDigits(formvalue(logicalName())[idx()]['cvr']))"
and wanted to simplify the whole formvalue(logicalName())[idx()]['cvr']
into a currentCellValue
function and coded this up:
public class CurrentCellValueFunction : Function
{
private readonly ILogger _logger;
private readonly ManifestAccessor _validationContext;
private readonly FormDataAccessor _formDataAccessor;
private readonly FormValueFunction _formValueFunction;
private readonly IdxFunction _idxFunction;
private readonly LogicalNameFunction _logicalNameFunction;
public CurrentCellValueFunction(
ILogger<CurrentCellValueFunction> logger,
ManifestAccessor validationContext,
FormDataAccessor formDataAccessor,
FormValueFunction formValueFunction,
IdxFunction idxFunction,
LogicalNameFunction logicalNameFunction) : base("currentCellValue")
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_validationContext = validationContext ?? throw new ArgumentNullException(nameof(validationContext));
_formDataAccessor = formDataAccessor ?? throw new ArgumentNullException(nameof(formDataAccessor));
_formValueFunction = formValueFunction;
_idxFunction = idxFunction;
_logicalNameFunction = logicalNameFunction;
}
public override async ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
_logger.LogDebug("{FunctionName} entered", nameof(CurrentCellValueFunction));
try
{
//currentCellValue = formvalue(logicalName())[idx()]['cvr']
var logicalName = await _logicalNameFunction.ExecuteFunction();
var idx = await _idxFunction.ExecuteFunction();
var formvalue = await _formValueFunction.ExecuteFunction(logicalName);
var currentCell = formvalue[idx.GetValue<int>()][_validationContext.CurrentAttributeLogicalName];
return currentCell;
}
catch (Exception ex)
{
_logger.LogDebug(ex,"{FunctionName} failed", nameof(CurrentCellValueFunction));
throw;
}
}
}
however it bascially could have been automated in the framework to do Function expression Definitions:
services.AddFunctionExpresssionDefinition("currentCellValue", "@formvalue(logicalName())[idx()][attributeLogicalName()]")
or
services.AddFunctionExpresssionDefinition("currentCellValue", "formvalue(logicalName())[idx()][attributeLogicalName()]")
Would be nice to have implicit casts / conversions for ValueContainer, simplifying constructing a ValueContainer like below:
public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
var relationship = parameters[0]?.ToString();
return new ValueTask<ValueContainer>(new ValueContainer(relationship));
}
with implicit conversions between simple types and ValueContainer one might do this instead
public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
var relationship = parameters[0]?.ToString();
return new ValueTask<ValueContainer>(relationship);
}
string
int
double
In the below example, getting the values of the array ValueContainer type is abit bloated and requires one to explicite do GetValue<IList<ValueContainer>>()
, using ValueContainer[]
or Object[]
would throw exceptions.
public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
var relationship = parameters[0]?.ToString()?.ToLower();
var data = _formDataAccessor.Data;
if (data.ContainsKey(relationship))
{
var array = data[relationship];
if(array.Type() == ValueType.Array)
{
var arrayData = array.GetValue<IList<ValueContainer>>();
return new ValueTask<ValueContainer>(new ValueContainer(arrayData.Count > 0));
}
}
return new ValueTask<ValueContainer>(new ValueContainer(false));
}
consider ValueContainer to implement IEnumerable ? as short cuts to return properties for objects or items for arrays. Or just a better AsArray?
The concat
function only takes strings according to the documentaiton, however it works on every type.
Go through types and their generated string and update PrettyPrint
, maybe look into replacing prettyprint with toString.
Go through string functions to see if there are more errors.
Consider the if function, ale three parameters are evaluated, the guard, true and false branch.
But consider the case where the false branch is guarded by the guard, but is evaluated anyways because it is not short circuit.
Consider changing this to object - CLR TYPE
public class ValueContainer : IComparable, IEquatable<ValueContainer>
{
private readonly dynamic _value; // TODO: Consider changing this to object - CLR TYPE
private readonly ValueType _type;
public ValueContainer(string value, bool tryToParse = false)
ndex 8b1553e..093837d 100644
++ b/Test/ExpressionGrammarTest.cs
b36f84e1c85b68a55aabc64cf7c9cdd7c53a2a1a
from preFix in allowedString
from exp in enclosedExpression.Optional()
select exp.IsEmpty ? preFix : preFix + exp.Get().Result)
// TODO - fix real async
// @assignees: thygesteffensen
.Many()
select Task.FromResult(new ValueContainer(string.Concat(e)));
446caa3703632f1ad99360dcdb375bcead2468c3
See sample internal project using ExpressionEngine and write more elaborate tests.
In ExpressionRule.cs
use metadata and service provider to resolve functions, instead of resolving every function. Prior, functions were cheap to create due to their simple logic, new functions may contain HTTP clients or DB clients, which are more expensive to create. To make it faster and lighter, we can instead use mete data classes to locate a function and resolve it. See AddPlugin
Not needed to fix, but small performance increate using a string builder to append x strings, otherwise you will allokate so0 = s0+s1, then so1 = so0 + s2, so2 = so1 +s3 ect.
can be optimized later (would be cool to have some performance tests/messures) fore optimizing things.
Originally posted by @pksorensen in #75 (comment)
When using dot indexing, the parser does not stop when meeting a comma ,
, when using a dotindexing inside a function call, it keeps reading the other arguments, as a part of the dot indexing
The format and basic function offering are based heavily on Power Automate and I think we should keep it that way.
However, to the purpose of this project is to have functionality used by PowerAutomateMockUp and to make it easier for others to use the same expressions in their projects, why it is a standalone project.
To not break the Power Automate version and to still offer simple functions to other users, we could add an extension NuGet, where some extra functions could be added to the Dependency Injection, such as pow()
or sqrt()
. This could be done by:
// ...
services.AddExpressionEngine();
services.AddExpressionEngineExtensions();3
// ...
Then AddExpressionEngineExtensions
could be located in Delegate.ExpressionEngine.Extensions
namespace.
@pksorensen what do you think?
from preFix in allowedString
from exp in enclosedExpression.Optional()
select exp.IsEmpty ? preFix : preFix + exp.Get().Result)
// TODO - fix real async
// @assignees: thygesteffensen
.Many()
select Task.FromResult(new ValueContainer(string.Concat(e)));
32dc81a18f6684e0246a73740c88f0e96fa92ea6
Right now, only netcoreapp3.1
is published as an artifact due to the test matrix, try to figure out how to cache and publish both in a proper manner
No matter how the expression is written, it will output as it is
Using Visual Studio 2022
create ASP.NET Core Web
/ .Net 6.0
and install Delegate.ExpressionEngine 4.1.2
Code :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
var services = new ServiceCollection();
services.AddExpressionEngine();
var sp = services.BuildServiceProvider();
var ee = sp.GetRequiredService<IExpressionEngine>();
var str = ee.Parse("1 + 1").Result;
Console.WriteLine(str); // output "1 + 1"
app.Run();
2
1 + 1
The .csproj
is :
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Delegate.ExpressionEngine" Version="4.1.2" />
</ItemGroup>
</Project>
@mul(10,-1)
are not parsed correctly and the same string is returned, i.e. the parsing is incomplete.
Incomplete parsing
How do we detect incomplete parsing? An exception shouldn't be thrown just because no expression is in the input string, but if a string contains the @{
and no }
or if there's an error in-between, should an exception be thrown?
We based alot of functions of Power Automate, which makes sense. But generally writing my expressions i write eq()
as a shorthand for equal
.
Would properly be good to be able to just rename or alias functions in a project without having to implement it again.
I suggest we implement a service that can registered to map one function name to another.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.