EasyExpression.Core 1.0.1

dotnet add package EasyExpression.Core --version 1.0.1
                    
NuGet\Install-Package EasyExpression.Core -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EasyExpression.Core" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EasyExpression.Core" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="EasyExpression.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EasyExpression.Core --version 1.0.1
                    
#r "nuget: EasyExpression.Core, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EasyExpression.Core@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EasyExpression.Core&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=EasyExpression.Core&version=1.0.1
                    
Install as a Cake Tool

EasyExpression

English | δΈ­ζ–‡

License: MIT .NET Standard 2.0

A lightweight, extensible expression engine for .NET that supports scripting with variables, control flow, and built-in functions.

✨ Features

  • Lightweight & Portable - Targets .NET Standard 2.0, compatible with .NET Framework 4.6.1+, .NET Core 2.0+, and .NET 5+
  • Safe Execution - Built-in timeout, max depth, and node limits to prevent infinite loops and resource exhaustion
  • Rich Expression Support - Arithmetic, comparison, logical operators, and string concatenation
  • Control Flow - if/elseif/else, local blocks, return, return_local, and assert statements
  • Built-in Functions - String, Math, and DateTime functions out of the box
  • Extensible - Register custom functions and type converters via IEngineContributor
  • Type Annotations - Optional type hints for field references like [fieldName:decimal]
  • Compilation Cache - Improved performance for repeated script execution
  • Detailed Error Reporting - Line/column positions and code snippets in error messages

πŸ“¦ Installation

Install from NuGet:

dotnet add package EasyExpression.Core

Or via Package Manager:

Install-Package EasyExpression.Core

Or via PackageReference:

<ItemGroup>
  <PackageReference Include="EasyExpression.Core" Version="1.0.0" />
</ItemGroup>

Build from source (optional)

git clone https://github.com/yang-sanmu/EasyExpression.git
cd EasyExpression
dotnet build

πŸš€ Quick Start

Basic Usage

using EasyExpression.Core.Engine;

// Create engine
var factory = new DefaultExpressionEngineFactory();
var engine = factory.Create();

// Define input fields
var inputs = new Dictionary<string, object?>
{
    { "price", 100m },
    { "quantity", 5 }
};

// Execute script
var script = @"
{
    set(total, [price:decimal] * [quantity:decimal])
    set(discount, [total] * 0.1)
    set(finalPrice, [total] - [discount])
}";

var result = engine.Execute(script, inputs);

// Access results
Console.WriteLine(result.Assignments["total"]);      // 500
Console.WriteLine(result.Assignments["discount"]);   // 50
Console.WriteLine(result.Assignments["finalPrice"]); // 450

Script Validation

var validationResult = engine.Validate(script);
if (!validationResult.Success)
{
    Console.WriteLine($"Error at line {validationResult.ErrorLine}: {validationResult.ErrorMessage}");
}

πŸ“– Language Syntax

Data Types

Type Example
Number (decimal) 123, 45.67, -10
String 'hello', "world"
Boolean true, false
DateTime now (current time)
Null null

Operators

Category Operators
Arithmetic +, -, *, /, %
Comparison ==, !=, >, <, >=, <=
Logical &&, \|\|, !

Field References

Access input fields using square brackets:

[fieldName]              // Basic reference
[fieldName:decimal]      // With type annotation
[fieldName:datetime]     // DateTime type
[fieldName:bool]         // Boolean type
[fieldName:string]       // String type

Statements

Set Statement
set(variableName, expression)
set(variableName:type, expression)  // With type annotation
If/ElseIf/Else
if(condition) {
    // statements
} elseif(condition) {
    // statements
} else {
    // statements
}
Local Block
local {
    // Isolated scope
    return_local  // Exit only this block
}
Assert
assert(condition, 'return', 'Error message', 'error')
assert(condition, 'continue', 'Warning message', 'warn')
Message
msg('Information message')
msg('Warning message', 'warn')
msg('Error message', 'error')
Return
return        // Exit entire script
return_local  // Exit current local block only

Comments

// Single-line comment

/* 
   Multi-line 
   comment 
*/

πŸ”§ Built-in Functions

String Functions

Function Description Example
ToString(value) Convert to string ToString(123) β†’ "123"
StartsWith(str, prefix, [ignoreCase]) Check prefix StartsWith('Hello', 'He') β†’ true
EndsWith(str, suffix, [ignoreCase]) Check suffix EndsWith('Hello', 'lo') β†’ true
Contains(str, sub, [ignoreCase]) Check contains Contains('Hello', 'ell') β†’ true
ToUpper(str) Uppercase ToUpper('hello') β†’ "HELLO"
ToLower(str) Lowercase ToLower('HELLO') β†’ "hello"
Trim(str) Remove whitespace Trim(' hi ') β†’ "hi"
Len(str) String length Len('hello') β†’ 5
Replace(str, old, new, [ignoreCase]) Replace text Replace('hello', 'l', 'L') β†’ "heLLo"
Substring(str, start, [length]) Extract substring Substring('hello', 1, 3) β†’ "ell"
RegexMatch(str, pattern, [flags]) Regex matching RegexMatch('test123', '\\d+') β†’ true
Coalesce(a, b, ...) First non-null Coalesce(null, 'default') β†’ "default"
Iif(cond, trueVal, falseVal) Inline if Iif(true, 'yes', 'no') β†’ "yes"
FieldExists(name, ...) Check field exists FieldExists('price') β†’ true/false

Math Functions

Function Description Example
ToDecimal(value) Convert to decimal ToDecimal('123.45') β†’ 123.45
Max(a, b, ...) Maximum value Max(1, 5, 3) β†’ 5
Min(a, b, ...) Minimum value Min(1, 5, 3) β†’ 1
Sum(a, b, ...) Sum of values Sum(1, 2, 3) β†’ 6
Average(a, b, ...) Average value Average(1, 2, 3) β†’ 2
Round(value, [digits]) Round number Round(3.14159, 2) β†’ 3.14
Abs(value) Absolute value Abs(-5) β†’ 5

DateTime Functions

Function Description Example
ToDateTime(str) Parse datetime ToDateTime('2024-01-01 00:00:00')
FormatDateTime(dt, [format]) Format datetime FormatDateTime(now, 'yyyy-MM-dd')
AddDays(dt, days) Add days AddDays(now, 7)
AddHours(dt, hours) Add hours AddHours(now, 24)
AddMinutes(dt, minutes) Add minutes AddMinutes(now, 30)
AddSeconds(dt, seconds) Add seconds AddSeconds(now, 60)
TimeSpan(dt1, dt2, [unit]) Time difference TimeSpan(dt1, dt2, 'd') (days)

TimeSpan units: ms (milliseconds), s (seconds), m (minutes), h (hours, default), d (days)

βš™οΈ Configuration Options

var engine = factory.Create(options =>
{
    // Execution limits
    options.MaxDepth = 64;                    // Max nesting depth
    options.MaxNodes = 2000;                  // Max AST nodes
    options.MaxNodeVisits = 10000;            // Max node visits
    options.TimeoutMilliseconds = 2000;       // Execution timeout
    
    // String handling
    options.StringComparison = StringComparison.OrdinalIgnoreCase;
    options.CaseInsensitiveFieldNames = true;
    
    // Number handling
    options.RoundingDigits = 2;
    options.MidpointRounding = MidpointRounding.AwayFromZero;
    
    // DateTime
    options.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
    options.NowUseLocalTime = true;
    
    // Null handling
    options.TreatNullStringAsEmpty = true;
    options.TreatNullDecimalAsZero = false;
    options.TreatNullBoolAsFalse = false;
    
    // Equality comparison mode
    options.EqualityCoercion = EqualityCoercionMode.Strict;
    
    // String concatenation behavior
    options.StringConcat = StringConcatMode.PreferStringIfAnyString;
    
    // Other
    options.EnableComments = true;
    options.EnableCompilationCache = true;
    options.RegexTimeoutMilliseconds = 0;     // 0 = no timeout
});

StringConcat Modes

StringConcat only affects the + operator when at least one side is a string.

Mode Behavior Examples
PreferStringIfAnyString If either operand is a string, always convert both sides to string (via converters when available) and concatenate. '1' + 2 β†’ "12", ToDateTime('2024-01-01 00:00:00') + ' UTC' β†’ "2024-01-01 00:00:00 UTC"
PreferNumericIfParsable If either operand is a string, first try parsing both sides as decimal. If both are parsable, do numeric addition; otherwise fall back to string concatenation. '1' + '2' β†’ 3, '1' + 'b' β†’ "1b"

Equality Coercion Modes

Mode Behavior
Strict No type coercion; type mismatch throws error
NumberFriendly Try numeric comparison when strings involved
Permissive Fall back to string comparison on mismatch
MixedNumericOnly Numeric coercion only for number-string pairs

πŸ”Œ Extensibility

Custom Functions

public class MyFunction : IFunction
{
    public string Name => "MyFunc";
    
    public object? Invoke(object?[] args, InvocationContext ctx)
    {
        // Implementation
        return args[0]?.ToString()?.ToUpperInvariant();
    }
}

// Register via contributor
public class MyContributor : IEngineContributor
{
    public void Configure(EngineServices services)
    {
        services.Functions.Register(new MyFunction());
    }
}

// Use contributor
var engine = factory.Create(contributors: new[] { new MyContributor() });

Custom Type Converters

public class MyConverter : ITypeConverter
{
    public Type InputType => typeof(string);
    public Type OutputType => typeof(MyType);
    
    public bool TryConvert(object? value, out object? result)
    {
        // Conversion logic
    }
}

// Register in contributor
services.Converters.Register(new MyConverter());

πŸ“Š Execution Result

var result = engine.Execute(script, inputs);

// Check for errors
if (result.HasError)
{
    Console.WriteLine($"Error: {result.ErrorMessage}");
    Console.WriteLine($"Location: Line {result.ErrorLine}, Column {result.ErrorColumn}");
    Console.WriteLine($"Code: {result.ErrorSnippet}");
    Console.WriteLine($"Error Code: {result.ErrorCode}");
}

// Access assigned variables
foreach (var kvp in result.Assignments)
{
    Console.WriteLine($"{kvp.Key} = {kvp.Value}");
}

// Access messages
foreach (var msg in result.Messages)
{
    Console.WriteLine($"[{msg.Level}] {msg.Text}");
}

// Execution time
Console.WriteLine($"Elapsed: {result.Elapsed}");

πŸ§ͺ Running Tests

cd EasyExpression
dotnet test

πŸ“ Project Structure

EasyExpression/
β”œβ”€β”€ EasyExpression.Core/
β”‚   └── Engine/
β”‚       β”œβ”€β”€ Ast/              # Abstract Syntax Tree nodes
β”‚       β”œβ”€β”€ Conversion/       # Type converters
β”‚       β”œβ”€β”€ Functions/        # Built-in and custom functions
β”‚       β”‚   └── BuiltIns/     # String, Math, DateTime functions
β”‚       β”œβ”€β”€ Parsing/          # Lexer and Parser
β”‚       β”œβ”€β”€ Runtime/          # Execution context and results
β”‚       β”œβ”€β”€ ExpressionEngine.cs
β”‚       β”œβ”€β”€ ExpressionEngineFactory.cs
β”‚       └── ExpressionEngineOptions.cs
└── EasyExpression.Core.Tests/    # Unit tests

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 120 2/2/2026
1.0.0 250 12/15/2025
0.1.0 235 12/15/2025