Simple.Interpreter
8.0.2.3
See the version list below for details.
dotnet add package Simple.Interpreter --version 8.0.2.3
NuGet\Install-Package Simple.Interpreter -Version 8.0.2.3
<PackageReference Include="Simple.Interpreter" Version="8.0.2.3" />
<PackageVersion Include="Simple.Interpreter" Version="8.0.2.3" />
<PackageReference Include="Simple.Interpreter" />
paket add Simple.Interpreter --version 8.0.2.3
#r "nuget: Simple.Interpreter, 8.0.2.3"
#addin nuget:?package=Simple.Interpreter&version=8.0.2.3
#tool nuget:?package=Simple.Interpreter&version=8.0.2.3
Simple.Interpreter
Simple.Interpreter is a lightweight C# library providing a straightforward interpreter for a simple, custom expression language. This NuGet package enables developers to empower their customers or clients to define dynamic expressions for various purposes, such as business rules, conditional logic, and data filtering, without requiring code recompilation. Notably, as demonstrated in the unit tests, the interpreter can also handle complex objects as variables within the expression context.
Features
- Simple Expression Syntax: The interpreted language offers a minimal and intuitive syntax, easy for non-technical users to grasp.
- Validation: The interpreter validates the expression syntax and ensures that it is well-formed and can be evaluated.
- Arithmetic Operations: Includes standard arithmetic operations (+, -, *, /).
- Comparison Operators: Supports a full suite of comparison operators (==, !=, >, <, >=, ⇐).
- Logical Operators: Implements logical AND (
and
) and OR (or
) operations for building complex conditions. - Array Operators:
in
(array contains),not in
(array does not contain) to check against arrays. - Variable Resolution: Allows expressions to reference external variables provided at runtime, including complex objects and their properties.
- Extensible Function Support: Designed to allow developers to easily register custom functions that can be called within the expressions.
- Lightweight and Embeddable: The library has minimal dependencies and can be easily integrated into any .NET application.
- Complex Object Support: As showcased in the unit tests, the interpreter can access properties, fields and methods of complex objects passed within the evaluation context.
Getting Started
Installation via NuGet
You can install the Simple.Interpreter NuGet package using the NuGet Package Manager in Visual Studio or the .NET CLI:
dotnet add package Simple.Interpreter
Basic Usage with Complex Objects
Example 1: Accessing Properties of Complex Objects
using Simple.Interpreter;
using System;
using System.Collections.Generic;
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
public string SayHi(string to)
{
return $"Hi there {to}!, I'm {Name}";
}
}
// Create an instance of the interpreter
var interpreter = new ExpressionInterpreter();
// Define the expression accessing properties of a complex object
string expressionString = "user.Age > 18 and user.City == 'Johannesburg'";
// Create a complex object to use as a variable
var user = new User
{
Name = "Alice",
Age = 25,
City = "Pretoria"
};
// Provide the context (including the complex object) for the expression
var context = new Dictionary<string, object>
{
{"user", user}
};
// Parse the expression
var expression = interpreter.GetExpression(expressionString);
// Set its scope
expression.SetScope(context);
// Evaluate the expression
object result = expression.Evaluate();
if (result is bool isAdultInJohannesburg && isAdultInJohannesburg)
{
Console.WriteLine($"{user.Name} meets the criteria.");
}
else
{
Console.WriteLine($"{user.Name} does not meet the criteria.");
}
Example 2: Using Conditional Logic and method call
using Simple.Interpreter;
using System;
using System.Collections.Generic;
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
public string SayHi(string to)
{
return $"Hi there {to}!, I'm {Name}";
}
}
// Create an instance of the interpreter
var interpreter = new ExpressionInterpreter();
// Define the expression accessing properties of a complex object
string expressionString = "alice.SayHi('Frank') if(alice.Age > 18+51) else bob.SayHi('Frank')";
var alice = new User
{
Name = "Alice",
Age = 25,
City = "Pretoria"
};
var bob = new User
{
Name = "Bob",
Age = 19,
City = "Cape Town"
};
// Provide the context (including the complex object) for the expression
var context = new Dictionary<string, object>
{
{"alice", alice},
{"bob", bob}
};
// Parse the expression
var expression = interpreter.GetExpression(expressionString);
// Set its scope
expression.SetScope(context);
// Evaluate the expression
object result = expression.Evaluate();
Console.WriteLine($"{result}"); //Outputs: Hi there Frank!, I'm Bob
For more see the Simple.Interpreter.Demo
project
Validation
// Create an instance of the interpreter
var interpreter = new ExpressionInterpreter();
// Define the expression
string expressionString = "user.Age > 18 and user.City == 'Johannesburg'";
// Define valid variable types allowed in expressions
var validVariableTypes = new Dictionary<string, Type>
{
{ "user", typeof(User) },
};
// validate the expression
var isValid = interpreter.Validate(expressionString, validVariableTypes, out var errors); //Returns true
if (isValid)
{
Console.WriteLine($"The expression is valid!");
}
else
{
Console.WriteLine($"There were issues with the expression: ");
foreach (var err in errors)
{
Console.WriteLine(err.Message);
}
}
For more see the Simple.Interpreter.Demo
project
Defining Custom Functions
Custom functions can be defined and used by Expressions
created with the ExpressionInterpreter
.
Custom functions can accept up to 4 arguments.
using Simple.Interpreter;
using System;
using System.Collections.Generic;
private static bool IsUserOlderThan(User user, int age)
{
bool result = user?.Age > age;
return result;
}
ExpressionInterpreter interpreter = new ExpressionInterpreter();
//Register custom Function
// PS: Due to limitations, custom functions must accept either an `object` or an `object[]` and return an `object`. Sorry :/
interpreter.RegisterFunction("isUserOlderThan", IsUserOlderThan);
var frank = new User
{
Name = "Frank",
Age = 40,
City = "Cape Town"
};
var ageToTest = 30;
var scope = new Dictionary<string, object>()
{
{"user", frank },
{"age", ageToTest }
};
string expressionString = "isUserOlderThan(user, age)";
Expression expression = interpreter.GetExpression(expressionString);
// Set its scope
expression.SetScope(scope);
var result = expression.Evaluate(); //Returns true
if(result is bool isOldEnough && isOldEnough)
{
Console.WriteLine($"{frank} is Older than {ageToTest}");
}
else
{
Console.WriteLine($"{frank} is not older than {ageToTest}");
}
For more see the Simple.Interpreter.Demo
project
Language Syntax (Brief Overview for Expressions)
- Variables: Identifiers (e.g.,
age
,productName
,user
). These will be resolved from the provided context. You can access properties, fields and methods of complex object variables using dot notation (e.g.,user.Age
,order.Name
,user.DoSomething()
). The expression can only access variables 1 level deep (e.g.user.FullAddress.PostalCode
is 2 level deep and will fail validation) - Literals:
- Numbers: Integers and decimals (e.g.,
10
,3.14
). - Strings: Enclosed in single quotes (e.g.,
'Hello'
). - Booleans:
true
orfalse
. - Arrays: The
ExpressionInterpreter
can parse array literals ofstring
,double
andint
(e.g.[1, 2, 3, 4, 5]
or['a', 'b', 'c']
).
- Numbers: Integers and decimals (e.g.,
- Arithmetic Operators:
+
(addition),-
(subtraction),*
(multiplication),/
(division). - Comparison Operators:
==
(equals),!=
(not equals),>
(greater than),<
(less than),>=
(greater than or equal to),<=
(less than or equal to). - Logical Operators:
and
(logical AND),or
(logical OR). - Array Operators:
in
(array contains),not in
(array does not contain). - Parentheses: Used to group expressions and control operator precedence (e.g.,
(a + b) * c
). - Function Calls: Registered custom functions can be called using their name followed by arguments in parentheses (e.g.,
startsWith(name, 'Prefix')
,isOlderThan(currentUser, 30)
). By default, theExpressionInterpreter
includes a set of built-in functions includingstartsWith
,endsWith
,min
andmax
. - Ternary Expressions: Python-style ternary support (e.g.,
'Over 21' if (user.Age > 21) else 'Under 21'
).
Contributing
Contributions to this project are welcome! If you have ideas for improvements, bug fixes, or new features, especially those enhancing the expression language or the interpreter's ability to handle complex data structures, please feel free to:
- Fork the repository.
- Create a new branch for your changes (
git checkout -b feature/your-feature
). - Commit your changes (
git commit -am 'Add some feature'
). - Push to the branch (
git push origin feature/your-feature
). - Open a pull request.
License
This project is licensed under the MIT License. See the LICENSE
file for more details.
Acknowledgements
- This project aims to provide a simple and effective solution for dynamic expression evaluation in .NET applications, with the added capability of handling complex object variables.
- Special thanks to the .NET community for its valuable resources and support.
Contact
Donations
If you find this project useful, please consider making a donation to support its development. Your support will help me continue to maintain and improve this project.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. |
-
net8.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.