C# 中的自定义表达式解析器
Custom Expression Parser in C#
我正在为财务应用开发计算器程序。
我需要解析和评估如下所述的复杂财务表达式。
该表达式是自定义函数和算术表达式的混合体。
我正在使用 NCalc
来解析算术表达式。但是,我在解析自定义函数时遇到了问题。
IF((COALESCE(X1,X2)-X3+IF(X4<=0,0,X5))>0, CUSTOM_FUNCTION(X6), X7)
对最佳方法有什么建议吗?
我目前正在研究涉及递归调用和堆栈的复杂逻辑Push/Pop。但它不起作用。
这是一个老问题,很久以前就解决了。解决方案是使用解析器生成器,而不是从头开始编写您自己的解析器。有许多可用的选项,ANTLR 是最受欢迎的选项之一。
使用像 ANTLR 这样的解析器生成器,您可以使用易于理解的 EBNF 类生产规则来描述您的问题。解析器生成器将生成复杂的逻辑,看起来你现在正试图用你选择的语言手写,在你的例子中是 C#。可能您的语言的语法已经以这种格式提供,或者您已经使用这种格式向您的用户和您的开发团队描述该语言。
我认为你必须使用有限状态机。编译器基于它。
您必须逐个字母地解析字符串,并定义您的操作:
类似的东西:
Do
{
//your parser
i++;
}
While(i
Mishax 是对的,你写规则的方式比直接用 C# 编码更清晰。如果您在使用 ANTLR 和 C# 时遇到问题(我遇到过),还有其他专用于 C# 的生成器 -- Irony, Coco/R, GOLD, ANTLR, LLLPG, Sprache, or my NLT。
不要让你只是坚持信念(如果你只想要结果):
expr -> e1:expr "+" e2:expr { e1+e2 }
| e1:expr "*" e2:expr { e1*e2 }
... and so on ...
;
原始 C# 中的等效部分会长得多。
我会推荐使用 JavaScript 解释器的替代方法。
https://github.com/sebastienros/jint
JInt 有一个强大的解析器,但是您需要更改的东西很少。
IF 和 COALESCE 在 JavaScript 中不存在,但是它们可以转换,例如。
IF 表达式
x = IF(CONDITION,THEN,ELSE)
JavaScript等价于
x = CONDITION ? THEN : ELSE
COALESCE 表达式
x = COALESCE(x1,x2,x3)
JavaScript等价于
x = x1 != null ? x1 :
(x2 != null ? x2 :
(x3 != null ? x3 : null ))
JInt 会给你一个解析后的 AST,你可以操作 AST 并转换相应的 IF 和 COALESCE 运算符。
使用组织良好的 JInt 解析器并简单地 post 处理 AST 将比重新发明轮子容易得多。
通过使用 JInt,您还可以使用各种现有的 .NET 方法,并且可以轻松地注入您自己的 类 来表示上下文值。
我正在为财务应用开发计算器程序。 我需要解析和评估如下所述的复杂财务表达式。
该表达式是自定义函数和算术表达式的混合体。
我正在使用 NCalc
来解析算术表达式。但是,我在解析自定义函数时遇到了问题。
IF((COALESCE(X1,X2)-X3+IF(X4<=0,0,X5))>0, CUSTOM_FUNCTION(X6), X7)
对最佳方法有什么建议吗?
我目前正在研究涉及递归调用和堆栈的复杂逻辑Push/Pop。但它不起作用。
这是一个老问题,很久以前就解决了。解决方案是使用解析器生成器,而不是从头开始编写您自己的解析器。有许多可用的选项,ANTLR 是最受欢迎的选项之一。
使用像 ANTLR 这样的解析器生成器,您可以使用易于理解的 EBNF 类生产规则来描述您的问题。解析器生成器将生成复杂的逻辑,看起来你现在正试图用你选择的语言手写,在你的例子中是 C#。可能您的语言的语法已经以这种格式提供,或者您已经使用这种格式向您的用户和您的开发团队描述该语言。
我认为你必须使用有限状态机。编译器基于它。 您必须逐个字母地解析字符串,并定义您的操作: 类似的东西:
Do { //your parser i++; } While(i
Mishax 是对的,你写规则的方式比直接用 C# 编码更清晰。如果您在使用 ANTLR 和 C# 时遇到问题(我遇到过),还有其他专用于 C# 的生成器 -- Irony, Coco/R, GOLD, ANTLR, LLLPG, Sprache, or my NLT。
不要让你只是坚持信念(如果你只想要结果):
expr -> e1:expr "+" e2:expr { e1+e2 }
| e1:expr "*" e2:expr { e1*e2 }
... and so on ...
;
原始 C# 中的等效部分会长得多。
我会推荐使用 JavaScript 解释器的替代方法。
https://github.com/sebastienros/jint
JInt 有一个强大的解析器,但是您需要更改的东西很少。
IF 和 COALESCE 在 JavaScript 中不存在,但是它们可以转换,例如。
IF 表达式
x = IF(CONDITION,THEN,ELSE)
JavaScript等价于
x = CONDITION ? THEN : ELSE
COALESCE 表达式
x = COALESCE(x1,x2,x3)
JavaScript等价于
x = x1 != null ? x1 :
(x2 != null ? x2 :
(x3 != null ? x3 : null ))
JInt 会给你一个解析后的 AST,你可以操作 AST 并转换相应的 IF 和 COALESCE 运算符。
使用组织良好的 JInt 解析器并简单地 post 处理 AST 将比重新发明轮子容易得多。
通过使用 JInt,您还可以使用各种现有的 .NET 方法,并且可以轻松地注入您自己的 类 来表示上下文值。