用于指定值约束的通用语言

时间:2013-12-18 09:41:15

标签: validation language-agnostic arithmetic-expressions

我正在寻找一种定义文本表达式的通用方法,允许对值进行验证。

例如,我有一个值,只能设置为1,2,3,10,11或12。 其约束可能定义为:(value >= 1 && value <= 3) || (value >= 10 && value <= 12)

或者其他值可以是1,3,5,7,9等......会有value % 2 == 1IsOdd(value)这样的约束。

(为了帮助用户更正无效值,我想显示约束 - 因此最好使用像IsOdd这样的描述性内容。)

这些约束将在客户端(用户输入之后)和服务器端进行评估。 因此,多平台解决方案将是理想的(特别是Win C#/ Linux C ++)。

是否存在允许评估或解析类似简单表达式的现有语言/项目?

如果没有,我可以从哪里开始创建自己的?

我意识到这个问题有些模糊,因为我不完全确定我所追求的是什么。搜索结果没有结果,因此即使某些术语作为起点也会有所帮助。然后我可以相应地更新/标记问题。

9 个答案:

答案 0 :(得分:6)

您可能需要调查依赖类型的语言,例如IdrisAgda

此类语言的类型系统允许在类型中编码值约束。无法保证约束的程序根本无法编译。通常的例子是矩阵乘法,其中维度必须匹配。但这就是说依赖类型语言的“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)。您可以在两个平台上进行相同的代码库编译。

解析器(再次,根据您在问题中描述的内容判断)可能非常简单 - 一个包含VariableExpression类型元素的树,可以是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;我倾向于将它用作inlinefastcall,除非有必要这样做(在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)

您可能需要查看Regular-Expressions or RegEx。它已被证明并且已存在很长时间了。有一个正则表达式库,所有主要的编程/脚本语言都在那里。

库:

用法