Compare commits

...

6 Commits

Author SHA1 Message Date
5c561fc69c 新增UndefinedIdentifiersAsStrings 2025-10-27 10:48:19 +08:00
47b12f4bc0 Save 2025-10-16 11:29:28 +08:00
90828989ad 加入自动类型转换 2025-10-16 10:24:34 +08:00
c9e2493796 新增赋值操作 2025-10-09 16:16:59 +08:00
de8b682330 使用双等号作为相等判断符 2025-10-09 16:04:35 +08:00
63f09b6196 修复一些错误并更新README 2025-10-09 14:40:58 +08:00
21 changed files with 315 additions and 71 deletions

View File

@@ -0,0 +1,68 @@
using System;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.MemberElements;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.ExpressionElements
{
internal class AssignmentElement : ExpressionElement
{
private readonly string _variableName;
private readonly ExpressionElement _valueExpression;
private readonly ExpressionContext _context;
public AssignmentElement(string variableName, ExpressionElement valueExpression, ExpressionContext context)
{
_variableName = variableName;
_valueExpression = valueExpression;
_context = context;
// 检查变量是否存在
if (!_context.Variables.ContainsKey(_variableName))
{
this.ThrowCompileException("UndefinedVariable", CompileExceptionReason.UndefinedName, _variableName);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
// 首先计算右侧表达式的值
_valueExpression.Emit(ilg, services);
// 获取一个临时本地变量来存储值
var valueType = _valueExpression.ResultType;
int tempIndex = ilg.GetTempLocalIndex(valueType);
// 存储值到临时变量
Utility.EmitStoreLocal(ilg, tempIndex);
// 加载变量集合 (arg 2)
ilg.Emit(System.Reflection.Emit.OpCodes.Ldarg_2); // 加载 VariableCollection
// 加载变量名
ilg.Emit(System.Reflection.Emit.OpCodes.Ldstr, _variableName);
// 加载要设置的值
Utility.EmitLoadLocal(ilg, tempIndex);
// 如果值类型是值类型,需要装箱
if (valueType.IsValueType)
{
ilg.Emit(System.Reflection.Emit.OpCodes.Box, valueType);
}
// 调用 Variables[string] 的 setter
var indexerSetter = typeof(VariableCollection).GetProperty("Item", new Type[] { typeof(string) }).GetSetMethod();
ilg.Emit(System.Reflection.Emit.OpCodes.Callvirt, indexerSetter);
// 加载返回值到栈上
Utility.EmitLoadLocal(ilg, tempIndex);
}
public override Type ResultType
{
get { return _valueExpression.ResultType; }
}
}
}

View File

@@ -8,7 +8,9 @@ using System;
namespace Flee.ExpressionElements.Base
{
[Obsolete("Base class for expression elements that operate on two child elements")]
/// <summary>
/// 操作两个子元素的表达式元素的基类
/// </summary>
internal abstract class BinaryExpressionElement : ExpressionElement
{

View File

@@ -6,7 +6,9 @@ using Flee.InternalTypes;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Encapsulates an argument list")]
/// <summary>
/// 封装参数列表的类
/// </summary>
internal class ArgumentList
{
private readonly IList<ExpressionElement> _myElements;

View File

@@ -10,7 +10,9 @@ using Flee.PublicTypes;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Represents a function call")]
/// <summary>
/// 表示函数调用的表达式元素
/// </summary>
internal class FunctionCallElement : MemberElement
{
private readonly ArgumentList _myArguments;
@@ -216,11 +218,15 @@ namespace Flee.ExpressionElements.MemberElements
return;
}
// 允许void返回类型的函数将其返回值视为0
// Void return type functions are allowed and treated as returning 0
/*
// Any function reference in an expression must return a value
if (object.ReferenceEquals(this.Method.ReturnType, typeof(void)))
{
base.ThrowCompileException("FunctionHasNoReturnValue", CompileExceptionReason.FunctionHasNoReturnValue, MyName);
}
*/
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
@@ -340,7 +346,21 @@ namespace Flee.ExpressionElements.MemberElements
this.EmitParamArrayArguments(parameters, elements, ilg, services);
}
MemberElement.EmitMethodCall(this.ResultType, nextRequiresAddress, this.Method, ilg);
// 检查方法是否返回void
bool isVoidMethod = object.ReferenceEquals(this.Method.ReturnType, typeof(void));
if (isVoidMethod)
{
// 对于void方法使用原始的ReturnType调用方法然后将0压入栈
MemberElement.EmitMethodCall(this.Method.ReturnType, nextRequiresAddress, this.Method, ilg);
// 将0压入栈作为返回值
ilg.Emit(OpCodes.Ldc_I4_0);
}
else
{
// 对于非void方法正常调用
MemberElement.EmitMethodCall(this.ResultType, nextRequiresAddress, this.Method, ilg);
}
}
private void EmitExtensionFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
@@ -395,6 +415,12 @@ namespace Flee.ExpressionElements.MemberElements
}
else
{
// 如果方法返回void将其视为返回int类型值为0
// If method returns void, treat it as returning int (value 0)
if (object.ReferenceEquals(this.Method.ReturnType, typeof(void)))
{
return typeof(int);
}
return this.Method.ReturnType;
}
}

View File

@@ -15,7 +15,9 @@ using System;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Represents an identifier")]
/// <summary>
/// 表示标识符的表达式元素
/// </summary>
internal class IdentifierElement : MemberElement
{
private FieldInfo _myField;
@@ -23,6 +25,7 @@ namespace Flee.ExpressionElements.MemberElements
private PropertyDescriptor _myPropertyDescriptor;
private Type _myVariableType;
private Type _myCalcEngineReferenceType;
private bool _isStringLiteral;
public IdentifierElement(string name)
{
this.MyName = name;
@@ -58,6 +61,13 @@ namespace Flee.ExpressionElements.MemberElements
if (MyPrevious == null)
{
// 检查是否启用了未定义标识符作为字符串的选项
if (MyOptions.UndefinedIdentifiersAsStrings)
{
// 将此标识符标记为字符串字面量
_isStringLiteral = true;
return;
}
base.ThrowCompileException("NoIdentifierWithName", CompileExceptionReason.UndefinedName, MyName);
}
else
@@ -138,6 +148,13 @@ namespace Flee.ExpressionElements.MemberElements
{
base.Emit(ilg, services);
// 如果是字符串字面量,直接发出字符串加载指令
if (_isStringLiteral)
{
ilg.Emit(OpCodes.Ldstr, MyName);
return;
}
this.EmitFirst(ilg);
if ((_myCalcEngineReferenceType != null))
@@ -352,6 +369,12 @@ namespace Flee.ExpressionElements.MemberElements
{
get
{
// 如果是字符串字面量,返回 string 类型
if (_isStringLiteral)
{
return typeof(string);
}
if ((_myCalcEngineReferenceType != null))
{
return _myCalcEngineReferenceType;

View File

@@ -9,7 +9,9 @@ using Flee.PublicTypes;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Element representing an array index")]
/// <summary>
/// 表示数组索引的表达式元素
/// </summary>
internal class IndexerElement : MemberElement
{
private ExpressionElement _myIndexerElement;

View File

@@ -4,7 +4,9 @@ using System.Reflection.Emit;
namespace Flee.InternalTypes
{
[Obsolete("Manages branch information and allows us to determine if we should emit a short or long branch")]
/// <summary>
/// 管理分支信息,允许我们确定是否应该发出短分支或长分支
/// </summary>
internal class BranchManager
{
private readonly IList<BranchInfo> MyBranchInfos;
@@ -143,7 +145,9 @@ namespace Flee.InternalTypes
}
}
[Obsolete("Represents a location in an IL stream")]
/// <summary>
/// 表示IL流中的位置
/// </summary>
internal class ILLocation : IEquatable<ILLocation>, IComparable<ILLocation>
{
private int _myPosition;
@@ -214,7 +218,9 @@ namespace Flee.InternalTypes
}
}
[Obsolete("Represents a branch from a start location to an end location")]
/// <summary>
/// 表示从起始位置到结束位置的分支
/// </summary>
internal class BranchInfo
{
private readonly ILLocation _myStart;

View File

@@ -160,7 +160,9 @@ namespace Flee.InternalTypes
public static object Instance => OurInstance;
}
[Obsolete("Helper class to resolve overloads")]
/// <summary>
/// 用于解析重载的辅助类
/// </summary>
internal class CustomMethodInfo : IComparable<CustomMethodInfo>, IEquatable<CustomMethodInfo>
{
/// <summary>
@@ -494,7 +496,9 @@ namespace Flee.InternalTypes
}
}
[Obsolete("Wraps an expression element so that it is loaded from a local slot")]
/// <summary>
/// 包装表达式元素,使其从本地槽位加载
/// </summary>
internal class LocalBasedElement : ExpressionElement
{
private readonly int _myIndex;
@@ -514,7 +518,9 @@ namespace Flee.InternalTypes
public override System.Type ResultType => _myTarget.ResultType;
}
[Obsolete("Helper class for storing strongly-typed properties")]
/// <summary>
/// 用于存储强类型属性的辅助类
/// </summary>
internal class PropertyDictionary
{
private readonly Dictionary<string, object> _myProperties;

View File

@@ -6,7 +6,9 @@ using System.Reflection.Emit;
namespace Flee.InternalTypes
{
[Obsolete("Holds various shared utility methods")]
/// <summary>
/// 包含各种共享实用方法的工具类
/// </summary>
internal class Utility
{
private Utility()

View File

@@ -3,7 +3,9 @@ using System.Collections;
namespace Flee.Parsing
{
[Obsolete("Creates a new parse tree analyzer.")]
/// <summary>
/// 创建新的解析树分析器
/// </summary>
internal class Analyzer
{
public Analyzer()

View File

@@ -589,6 +589,38 @@ namespace Flee.Parsing
return node;
}
public override Node ExitAssign(Token node)
{
node.AddValue("=");
return node;
}
public override Node ExitAssignmentExpression(Production node)
{
// 获取子节点:标识符和表达式
var children = this.GetChildValues(node);
if (children.Count != 3) // IDENTIFIER, ASSIGN, Expression
{
throw new InvalidOperationException("Assignment expression must have exactly 3 children");
}
string variableName = (string)children[0];
ExpressionElement valueExpression = (ExpressionElement)children[2];
// 获取表达式上下文
var context = _myServices.GetService(typeof(ExpressionContext)) as ExpressionContext;
if (context == null)
{
throw new InvalidOperationException("ExpressionContext not found in services");
}
// 创建赋值表达式元素
var assignmentElement = new AssignmentElement(variableName, valueExpression, context);
node.AddValue(assignmentElement);
return node;
}
public override void Child(Production node, Node child)
{
base.Child(node, child);

View File

@@ -1,9 +1,9 @@
%header%
DESCRIPTION = "A general expression grammar"
AUTHOR = "Eugene Ciloci"
VERSION = "1.0"
DATE = "May 2007"
DESCRIPTION = "A general expression grammar with assignment support"
AUTHOR = "Eugene Ciloci (Modified By LiuBai)"
VERSION = "2.0"
DATE = "October 2025"
GRAMMARTYPE = "LL"
CASESENSITIVE = "False"
@@ -37,7 +37,7 @@ LEFT_PAREN = "("
RIGHT_PAREN = ")"
LEFT_BRACE = "["
RIGHT_BRACE = "]"
EQ = "="
EQ = "=="
LT = "<"
GT = ">"
LTE = "<="
@@ -71,10 +71,13 @@ DATETIME = <<#[^#]+#>>
// Special Functions
IF = "if"
CAST = "cast"
ASSIGN = "="
%productions%
Expression = XorExpression;
Expression = AssignmentExpression | XorExpression;
AssignmentExpression = IDENTIFIER "=" XorExpression;
XorExpression = OrExpression {XOR OrExpression};
@@ -90,7 +93,7 @@ InTargetExpression = FieldPropertyExpression | InListTargetExpression;
InListTargetExpression = "(" ArgumentList ")";
CompareExpression = ShiftExpression {("=" | ">" | "<" | ">=" | "<=" | "<>") ShiftExpression};
CompareExpression = ShiftExpression {("==" | ">" | "<" | ">=" | "<=" | "<>") ShiftExpression};
ShiftExpression = AdditiveExpression {("<<" | ">>") AdditiveExpression};

View File

@@ -284,6 +284,10 @@
case (int)ExpressionConstants.EXPRESSION_GROUP:
EnterExpressionGroup((Production)node);
break;
case (int)ExpressionConstants.ASSIGNMENT_EXPRESSION:
EnterAssignmentExpression((Production)node);
break;
}
}
@@ -414,6 +418,9 @@
case (int)ExpressionConstants.CAST:
return ExitCast((Token)node);
case (int)ExpressionConstants.ASSIGN:
return ExitAssign((Token)node);
case (int)ExpressionConstants.EXPRESSION:
return ExitExpression((Production)node);
@@ -501,6 +508,9 @@
case (int)ExpressionConstants.EXPRESSION_GROUP:
return ExitExpressionGroup((Production)node);
case (int)ExpressionConstants.ASSIGNMENT_EXPRESSION:
return ExitAssignmentExpression((Production)node);
}
return node;
}
@@ -629,6 +639,10 @@
case (int)ExpressionConstants.EXPRESSION_GROUP:
ChildExpressionGroup(node, child);
break;
case (int)ExpressionConstants.ASSIGNMENT_EXPRESSION:
ChildAssignmentExpression(node, child);
break;
}
}
@@ -1391,5 +1405,24 @@
{
node.AddChild(child);
}
public virtual Node ExitAssign(Token node)
{
return node;
}
public virtual void EnterAssignmentExpression(Production node)
{
}
public virtual Node ExitAssignmentExpression(Production node)
{
return node;
}
public virtual void ChildAssignmentExpression(Production node, Node child)
{
node.AddChild(child);
}
}
}

View File

@@ -45,6 +45,7 @@
DATETIME = 1038,
IF = 1039,
CAST = 1040,
ASSIGN = 1041,
EXPRESSION = 2001,
XOR_EXPRESSION = 2002,
OR_EXPRESSION = 2003,
@@ -73,6 +74,7 @@
ARGUMENT_LIST = 2026,
LITERAL_EXPRESSION = 2027,
BOOLEAN_LITERAL_EXPRESSION = 2028,
EXPRESSION_GROUP = 2029
EXPRESSION_GROUP = 2029,
ASSIGNMENT_EXPRESSION = 2030
}
}

View File

@@ -51,6 +51,17 @@ namespace Flee.Parsing
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.EXPRESSION), "Expression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.ASSIGNMENT_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.XOR_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.ASSIGNMENT_EXPRESSION), "AssignmentExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.IDENTIFIER), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.ASSIGN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.XOR_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);

View File

@@ -58,7 +58,7 @@ namespace Flee.Parsing
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.RIGHT_BRACE), "RIGHT_BRACE", TokenPattern.PatternType.STRING, "]");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.EQ), "EQ", TokenPattern.PatternType.STRING, "=");
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.EQ), "EQ", TokenPattern.PatternType.STRING, "==");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.LT), "LT", TokenPattern.PatternType.STRING, "<");
@@ -150,6 +150,9 @@ namespace Flee.Parsing
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.CAST), "CAST", TokenPattern.PatternType.STRING, "cast");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.ASSIGN), "ASSIGN", TokenPattern.PatternType.STRING, "=");
AddPattern(pattern);
}
}
}

View File

@@ -5,8 +5,9 @@ using System.Text;
namespace Flee.Parsing
{
[Obsolete(" A base parser class. This class provides the standard parser interface, as well as token handling.")]
/// <summary>
/// 基础解析器类。此类提供标准解析器接口以及令牌处理功能
/// </summary>
internal abstract class Parser
{
private bool _initialized;

View File

@@ -8,6 +8,9 @@ using Flee.Parsing;
namespace Flee.PublicTypes
{
/// <summary>
/// 表达式上下文类,提供表达式编译和执行的环境
/// </summary>
public sealed class ExpressionContext
{
@@ -186,11 +189,22 @@ namespace Flee.PublicTypes
return this.CloneInternal(true);
}
/// <summary>
/// 编译动态表达式,返回类型在运行时确定
/// </summary>
/// <param name="expression">要编译的表达式字符串</param>
/// <returns>编译后的动态表达式</returns>
public IDynamicExpression CompileDynamic(string expression)
{
return new Flee.InternalTypes.Expression<object>(expression, this, false);
}
/// <summary>
/// 编译泛型表达式,返回指定类型的结果
/// </summary>
/// <typeparam name="TResultType">表达式结果的类型</typeparam>
/// <param name="expression">要编译的表达式字符串</param>
/// <returns>编译后的泛型表达式</returns>
public IGenericExpression<TResultType> CompileGeneric<TResultType>(string expression)
{
return new Flee.InternalTypes.Expression<TResultType>(expression, this, true);

View File

@@ -36,6 +36,7 @@ namespace Flee.PublicTypes
_myProperties.SetToDefault<Type>("ResultType");
_myProperties.SetToDefault<bool>("IsGeneric");
_myProperties.SetToDefault<bool>("IntegersAsDoubles");
_myProperties.SetToDefault<bool>("UndefinedIdentifiersAsStrings");
_myProperties.SetValue("ParseCulture", CultureInfo.CurrentCulture);
this.SetParseCulture(this.ParseCulture);
_myProperties.SetValue("RealLiteralDataType", RealLiteralDataType.Double);
@@ -149,6 +150,15 @@ namespace Flee.PublicTypes
get { return _myProperties.GetValue<RealLiteralDataType>("RealLiteralDataType"); }
set { _myProperties.SetValue("RealLiteralDataType", value); }
}
/// <summary>
/// 当标识符未定义时,是否自动将其转换为字符串字面量
/// </summary>
public bool UndefinedIdentifiersAsStrings
{
get { return _myProperties.GetValue<bool>("UndefinedIdentifiersAsStrings"); }
set { _myProperties.SetValue("UndefinedIdentifiersAsStrings", value); }
}
#endregion
#region "Properties - Non Public"

View File

@@ -7,7 +7,7 @@ using System.Reflection;
namespace Flee.PublicTypes
{
/// <summary>
///
/// 变量集合类,用于管理表达式中使用的变量
/// </summary>
public sealed class VariableCollection : IDictionary<string, object>
{
@@ -233,7 +233,21 @@ namespace Flee.PublicTypes
{
if (variable is IGenericVariable<T> generic)
{
return (T)generic.GetValue();
var genericValueResult = generic.GetValue();
var genericValueResultType = genericValueResult?.GetType();
var resultType = typeof(T);
if (genericValueResultType == resultType)
{
return (T)genericValueResult;
}
else if (resultType == typeof(string))
{
return (T)(object)genericValueResult.ToString();
}
else
{
return (T)Convert.ChangeType(genericValueResult, typeof(T));
}
}
}

View File

@@ -1,48 +1,30 @@
# Flee (Supports Net6.0, Net5.0, Netstandard2.1, Netstandard2.0)
Fast Lightweight Expression Evaluator.
Convert this project vb.net to c#.
# FLEE
快速轻量级表达式求值器。
## Project Description
Flee is an expression parser and evaluator for the .NET framework. It allows you to compute the value of string expressions such as sqrt(a^2 + b^2) at runtime. It uses a custom compiler, strongly-typed expression language, and lightweight codegen to compile expressions directly to IL. This means that expression evaluation is extremely fast and efficient.
## 项目描述
FLEE 是一个用于 .NET 框架的表达式解析器和求值器。它允许您在运行时计算字符串表达式的值,例如 sqrt(a^2 + b^2)。它使用自定义编译器、强类型表达式语言和轻量级代码生成器将表达式直接编译为 IL。这意味着表达式求值极其快速和高效。
## Features
* Fast and efficient expression evaluation
* Small, lightweight library
* Compiles expressions to IL using a custom compiler, lightweight codegen, and the DynamicMethod class
* Expressions (and the IL generated for them) are garbage-collected when no longer used
* Does not create any dynamic assemblies that stay in memory
* Backed by a comprehensive suite of unit tests
* Culture-sensitive decimal point
* Fine-grained control of what types an expression can use
* Supports all arithmetic operations including the power (^) operator
* Supports string, char, boolean, and floating-point literals
* Supports 32/64 bit, signed/unsigned, and hex integer literals
* Features a true conditional operator
* Supports short-circuited logical operations
* Supports arithmetic, comparison, implicit, and explicit overloaded operators
* Variables of any type can be dynamically defined and used in expressions
* CalculationEngine: Reference other expressions in an expression and recalculate in natural order
* Expressions can index arrays and collections, access fields and properties, and call functions on various types
* Generated IL can be saved to an assembly and viewed with a disassembler
## 功能特性
* 快速高效的表达式求值
* 小巧轻量的库
* 使用自定义编译器、轻量级代码生成器和 DynamicMethod 类将表达式编译为 IL
* 表达式(及其生成的 IL在不再使用时会被垃圾回收
* 不会创建任何保留在内存中的动态程序集
* 由全面的单元测试套件支持
* 支持区域敏感的小数点
* 对表达式可使用的类型进行细粒度控制
* 支持所有算术运算,包括幂运算符 (^)
* 支持字符串、字符、布尔值和浮点数字面量
* 支持 32/64 位、有符号/无符号和十六进制整数字面量
* 具有真正的条件运算符
* 支持短路逻辑运算
* 支持算术、比较、隐式和显式重载运算符
* 可以动态定义任何类型的变量并在表达式中使用
* 计算引擎:在表达式中引用其他表达式并按自然顺序重新计算
* 表达式可以索引数组和集合、访问字段和属性,以及调用各种类型的函数
* 生成的 IL 可以保存到程序集中并使用反汇编器查看
### Installing Flee
## 许可
本FLEE是从原Flee发布者处未经许可更改与改进得来, 用于源码编译
You should install [Flee with NuGet](https://www.nuget.org/packages/Flee):
Install-Package Flee
Or via the .NET Core command line interface:
dotnet add package Flee
## NuGet Packages
| Name | NuGet |
| :--- | :--- |
| [Flee](https://www.nuget.org/packages/Flee) | [![Flee](https://img.shields.io/badge/nuget-v2.0.0-blue.svg)](https://www.nuget.org/packages/Flee)
## More information
* [Examples](https://github.com/mparlak/Flee/wiki/Examples) to learn how to create and evaluate expressions.
## License
Flee is licensed under the LGPL. This means that as long as you dynamically link (ie: add a reference) to the officially released assemblies, you can use it in commercial and non-commercial applications.
原Flee发布者发布的Flee 使用 LGPL 许可证。这意味着只要您动态链接(即添加引用)到官方发布的程序集,就可以在商业和非商业应用程序中使用它。