我正在寻找一种定义文本表达式的通用方法,允许对值进行验证。
例如,我有一个值,只能设置为1,2,3,10,11或12。
其约束可能定义为:(value >= 1 && value <= 3) || (value >= 10 && value <= 12)
或者其他值可以是1,3,5,7,9等......会有value % 2 == 1
或IsOdd(value)
这样的约束。
(为了帮助用户更正无效值,我想显示约束 - 因此最好使用像IsOdd
这样的描述性内容。)
这些约束将在客户端(用户输入之后)和服务器端进行评估。 因此,多平台解决方案将是理想的(特别是Win C#/ Linux C ++)。
是否存在允许评估或解析类似简单表达式的现有语言/项目?
如果没有,我可以从哪里开始创建自己的?
我意识到这个问题有些模糊,因为我不完全确定我所追求的是什么。搜索结果没有结果,因此即使某些术语作为起点也会有所帮助。然后我可以相应地更新/标记问题。
答案 0 :(得分:6)
此类语言的类型系统允许在类型中编码值约束。无法保证约束的程序根本无法编译。通常的例子是矩阵乘法,其中维度必须匹配。但这就是说依赖类型语言的“hello world”,类型系统可以为你做更多的事情。
答案 1 :(得分:4)
如果你最终开始使用自己的语言,我会尽可能地保持与实现无关。寻找合适的编程语言(例如C)的正式表达式语法,并根据需要添加特殊的关键字/函数。一旦有了正式的语言定义,就可以使用自己喜欢的解析器生成器实现解析器。
这样,即使您的解析器无法移植到某个平台,您至少也可以从哪里开始单独的解析器实现。
答案 2 :(得分:3)
您可能还想看一下在Ruby中创建域特定语言(DSL)。 (这是一篇关于这意味着什么以及它会是什么样子的好文章:http://jroller.com/rolsen/entry/building_a_dsl_in_ruby)
这肯定会为您提供所需的可移植性,包括在您的C#环境中使用IronRuby,并且您将能够利用Ruby的现有逻辑和数学运算。然后,您可以拥有如下所示的约束定义文件:
constrain 'wakeup_time' do
6 <= value && value <= 10
end
constrain 'something_else' do
check (value % 2 == 1), MustBeOdd
end
# constrain is a method that takes one argument and a code block
# check is a function you've defined that takes a two arguments
# MustBeOdd is the name of an exception type you've created in your standard set
但实际上,DSL的优点在于你可以很好地控制约束文件的外观。
答案 3 :(得分:1)
不确定它是否是您所寻找的,但从您的起始条件(Win C#/ Linux C ++)判断,您可能不需要它完全与语言无关。您可以在C ++中使用所有需要的功能自己实现这样的解析器,然后只在C ++和C#项目中使用它 - 因此也绕过了添加外部库的需要。
在应用程序设计级别上,它(相对)简单 - 您可以创建一个可跨平台构建的库,并在两个项目中使用它。界面可能很简单:
bool VerifyConstraint_int(int value, const char* constraint);
bool VerifyConstraint_double(double value, const char* constraint);
// etc
此类接口既可用于Linux C ++(通过静态或动态链接),也可用于Windows C#(使用P / Invoke)。您可以在两个平台上进行相同的代码库编译。
解析器(再次,根据您在问题中描述的内容判断)可能非常简单 - 一个包含Variable
和Expression
类型元素的树,可以是Evaluate
d具有给定的Variable
值。
示例类定义:
class Entity {public: virtual VARIANT Evaluate() = 0;} // boost::variant may be used typedef'd as VARIANT
class BinaryOperation: public Entity {
private:
Entity& left;
Entity& right;
enum Operation {PLUS,MINUS,EQUALS,AND,OR,GREATER_OR_EQUALS,LESS_OR_EQUALS};
public:
virtual VARIANT Evaluate() override; // Evaluates left and right operands and combines them
}
class Variable: public Entity {
private:
VARIANT value;
public:
virtual VARIANT Evaluate() override {return value;};
}
或者,你可以用C ++编写验证代码,并在C#和C ++应用程序中使用它:)
答案 4 :(得分:1)
有多种方法可以验证多种语言的值列表。我首选的方法是列出允许的值并将其加载到dictionary/hashmap/list/vector
(取决于语言和您的偏好)并编写一个简单的isIn()
或isValid()
函数,检查提供的值是否有效,具体取决于它在数据结构中的存在。这样做的好处是代码很简单,可以很容易地用几乎任何语言实现。对于奇数或偶数数字有效性,一个不同语言isOdd()
函数的小型库就足够了:如果它不是奇数,它必须是偶数(除0
之外,然后是可以设置简单的异常来处理它,或者您可以在代码文档中简单地指定出于逻辑目的,您的代码将0评估为奇数/偶数(您的选择))。
我通常会推出一组c ++和c#函数来评估isOdd(),原因与您所提到的类似,代码如下:
<强> C ++ 强>
bool isOdd( int integer ){ return (integer%2==0)?false:true; }
您还可以根据需要或偏好向功能添加inline
和/或fastcall
;我倾向于将它用作inline
和fastcall
,除非有必要这样做(在xeon处理器上有巨大的性能提升)。
<强> C#强>
如果它不是另一个类的一部分,那么在C#中使用相同的行就可以添加静态:
static bool isOdd( int integer ){ return (integer%2==0)?false:true; }
希望这有帮助,无论如何让我知道您是否需要任何进一步的信息:)
答案 5 :(得分:1)
我个人的选择是Lua。任何DSL的缺点是新语言的学习曲线以及如何将代码与脚本粘合,但我发现Lua有很多来自用户群的支持和一些好书可以帮助你学习。
如果您正在制作一些非通用代码,非程序员可以为允许的输入注入规则,那么无论您采用何种路线,都需要进行一些前期工作。我强烈建议不要自己动手,因为你可能会发现人们想要更多已经制作过DSL的功能。
答案 6 :(得分:1)
如果您使用的是Java,则可以使用Object Graph Navigation Library。
它使您能够编写可以解析,编译和评估OGNL表达式的Java应用程序。
OGNL表达式包括基本的java,C,C ++,C#表达式。
您可以编译使用某些变量的表达式,然后计算该表达式 对于某些给定的变量。
答案 7 :(得分:1)
实现表达式验证的一种简单方法是使用Python的eval
方法。它可以用来评估表达式,就像你写的那样。 Python的语法很容易学习简单的表达式和英语。您的表达式示例已转换为:
(value >= 1 and value <= 3) or (value >= 10 and value <= 12)
用户提供的代码评估可能会带来安全风险,因为某些功能可用于在主机上执行(例如open
功能,以打开文件)。但是eval
函数需要额外的参数来限制允许的函数。因此,您可以创建一个安全的评估环境。
# Import math functions, and we'll use a few of them to create
# a list of safe functions from the math module to be used by eval.
from math import *
# A user-defined method won't be reachable in the evaluation, as long
# as we provide the list of allowed functions and vars to eval.
def dangerous_function(filename):
print open(filename).read()
# We're building the list of safe functions to use by eval:
safe_list = ['math','acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# Let's test the eval method with your example:
exp = "(value >= 1 and value <= 3) or (value >= 10 and value <= 12)"
safe_dict['value'] = 2
print "expression evaluation: ", eval(exp, {"__builtins__":None},safe_dict)
-> expression evaluation: True
# Test with a forbidden method, such as 'abs'
exp = raw_input("type an expression: ")
-> type an expression: (abs(-2) >= 1 and abs(-2) <= 3) or (abs(-2) >= 10 and abs(-2) <= 12)
print "expression evaluation: ", eval(exp, {"__builtins__":None},safe_dict)
-> expression evaluation:
-> Traceback (most recent call last):
-> File "<stdin>", line 1, in <module>
-> File "<string>", line 1, in <module>
-> NameError: name 'abs' is not defined
# Let's test it again, without any extra parameters to the eval method
# that would prevent its execution
print "expression evaluation: ", eval(exp)
-> expression evaluation: True
# Works fine without the safe dict! So the restrictions were active
# in the previous example..
# is odd?
def isodd(x): return bool(x & 1)
safe_dict['isodd'] = isodd
print "expression evaluation: ", eval("isodd(7)", {"__builtins__":None},safe_dict)
-> expression evaluation: True
print "expression evaluation: ", eval("isodd(42)", {"__builtins__":None},safe_dict)
-> expression evaluation: False
# A bit more complex this time, let's ask the user a function:
user_func = raw_input("type a function: y = ")
-> type a function: y = exp(x)
# Let's test it:
for x in range(1,10):
# add x in the safe dict
safe_dict['x']=x
print "x = ", x , ", y = ", eval(user_func,{"__builtins__":None},safe_dict)
-> x = 1 , y = 2.71828182846
-> x = 2 , y = 7.38905609893
-> x = 3 , y = 20.0855369232
-> x = 4 , y = 54.5981500331
-> x = 5 , y = 148.413159103
-> x = 6 , y = 403.428793493
-> x = 7 , y = 1096.63315843
-> x = 8 , y = 2980.95798704
-> x = 9 , y = 8103.08392758
因此,您可以控制eval
方法应该使用的允许函数,并具有可以计算表达式的沙箱环境。
这是我在之前工作的项目中使用的。我们在自定义Eclipse IDE插件中使用Python表达式,使用Jython在JVM中运行。 您可以使用IronPython在CLR中运行。
我在Lybniz项目中解释了如何运行安全的Python eval环境,从而部分启发/复制了这些示例。 Read it for more details!
答案 8 :(得分:0)