diff --git a/ExpressionElements/Assignment.cs b/ExpressionElements/Assignment.cs new file mode 100644 index 0000000..56a4331 --- /dev/null +++ b/ExpressionElements/Assignment.cs @@ -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; } + } + } +} diff --git a/Parsing/CustomExpressionAnalyzer.cs b/Parsing/CustomExpressionAnalyzer.cs index 9dc1710..6ca5bf3 100644 --- a/Parsing/CustomExpressionAnalyzer.cs +++ b/Parsing/CustomExpressionAnalyzer.cs @@ -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); diff --git a/Parsing/Expression.grammar b/Parsing/Expression.grammar index 22aaa67..ce650a1 100644 --- a/Parsing/Expression.grammar +++ b/Parsing/Expression.grammar @@ -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" @@ -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}; diff --git a/Parsing/ExpressionAnalyzer.cs b/Parsing/ExpressionAnalyzer.cs index bbb73c3..7abe104 100644 --- a/Parsing/ExpressionAnalyzer.cs +++ b/Parsing/ExpressionAnalyzer.cs @@ -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); + } } } diff --git a/Parsing/ExpressionConstants.cs b/Parsing/ExpressionConstants.cs index c5c7d0a..6c1d16f 100644 --- a/Parsing/ExpressionConstants.cs +++ b/Parsing/ExpressionConstants.cs @@ -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 } } \ No newline at end of file diff --git a/Parsing/ExpressionParser.cs b/Parsing/ExpressionParser.cs index dfb8f3e..bb03fd9 100644 --- a/Parsing/ExpressionParser.cs +++ b/Parsing/ExpressionParser.cs @@ -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); diff --git a/Parsing/ExpressionTokenizer.cs b/Parsing/ExpressionTokenizer.cs index 4f6a47d..c269171 100644 --- a/Parsing/ExpressionTokenizer.cs +++ b/Parsing/ExpressionTokenizer.cs @@ -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); } } }