Язык общего назначения для указания ограничений значения

Я ищу универсальный способ определения текстовых выражений, которые позволяют проверять значение.

Например, у меня есть значение, которое должно быть установлено только 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 С#/Linux С++).

Существует ли существующий язык/проект, который позволяет оценивать или анализировать подобные простые выражения?

Если нет, то где я могу начать создавать свои собственные?

Я понимаю, что этот вопрос несколько расплывчатый, поскольку я не совсем уверен, что я за ним. Поиск не принес результатов, поэтому даже некоторые термины в качестве отправной точки были бы полезными. Затем я могу обновить/пометить вопрос.

Ответ 1

Возможно, вам захочется исследовать навязчивые языки, такие как Idris или Agda.

Система типов таких языков допускает кодирование ограничений значений в типах. Программы, которые не могут гарантировать ограничения, просто не будут компилироваться. Обычный пример - это пример матричного умножения, в котором должны соответствовать размеры. Но это, так сказать, "мир привет" языков, типично типизированных, система типов может сделать для вас гораздо больше.

Ответ 2

Если вы начнете свой собственный язык, я постараюсь оставаться независимым от реализации как можно дольше. Найдите формальные грамматики выражения подходящего языка программирования (например, C) и добавьте специальные ключевые слова/функции по мере необходимости. После того, как у вас есть формальное определение вашего языка, выполните парсер, используя ваш любимый генератор синтаксического анализатора.

Таким образом, даже если ваш синтаксический анализатор не переносится на определенную платформу, вы, по крайней мере, имеете формальный стандарт, с которого можно начать отдельную реализацию парсера.

Ответ 3

Вы также можете посмотреть, как создать язык домена (DSL) в Ruby. (Здесь хорошая статья о том, что это означает и как это будет выглядеть: http://jroller.com/rolsen/entry/building_a_dsl_in_ruby)

Это определенно даст вам мобильность, которую вы ищете, в том числе, возможно, используя 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 заключается в том, что у вас есть большой контроль над тем, как выглядят файлы ограничений.

Ответ 4

Не уверен, что это то, что вы ищете, но, судя по вашим исходным условиям (Win С#/Linux С++), вам может и не понадобиться быть полностью языковым агностиком. Вы можете реализовать такой синтаксический анализатор самостоятельно на С++ со всеми желаемыми функциями, а затем просто использовать его как в проектах С++, так и в С#, что также обходит необходимость добавления внешних библиотек.

На уровне разработки приложений было бы (относительно) просто - вы создаете библиотеку, которая является построенной кросс-платформой и использует ее в обоих проектах. Интерфейс может быть простым:

bool VerifyConstraint_int(int value, const char* constraint);
bool VerifyConstraint_double(double value, const char* constraint);
// etc

Такой интерфейс будет использоваться как в Linux С++ (путем статической или динамической компоновки), так и в Windows С# (с использованием 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;};
}

Или вы можете просто написать код проверки на С++ и использовать его как в приложениях С#, так и на С++:)

Ответ 5

существует несколько способов проверки списка значений на нескольких языках. Мой предпочтительный метод состоит в том, чтобы составить список допустимых значений и загрузить их в dictionary/hashmap/list/vector (в зависимости от языка и ваших предпочтений) и написать простую функцию isIn() или isValid(), которая будет проверять, что указанное значение действительным в зависимости от его присутствия в структуре данных. Красота заключается в том, что код тривиальный и может быть реализован практически на любом языке очень легко. для нечетной или четной числовой проверки снова будет достаточно маленькой библиотеки различных языковых функций isOdd(): если она не является нечетной, она должна по определению быть четной (кроме 0, но тогда может быть простое исключение настроенный для этого, или вы можете просто указать в своей документации кода, что для логических целей ваш код оценивает 0 как нечетный/четный (ваш выбор)).

Обычно я использую набор функций С++ и С# для оценки isOdd() по тем же причинам, на которые вы ссылались, и код выглядит следующим образом:

С++

bool isOdd( int integer ){  return (integer%2==0)?false:true;  }

вы также можете добавить inline и/или fastcall к функции в зависимости от потребности или предпочтения; Я обычно использую его как inline и fastcall, если нет необходимости делать иначе (огромный прирост производительности на процессорах xeon).

С#

Красиво одна и та же линия работает на С#, просто добавляет статику в начало, если она не будет частью другого класса:

static bool isOdd( int integer ){  return (integer%2==0)?false:true;  }

Надеюсь, что это поможет, в любом случае, дайте мне знать, если вам нужна дополнительная информация:)

Ответ 6

Мой личный выбор - это Lua. Недостатком любого DSL является кривая обучения нового языка и способ склеивания кода со сценариями, но я нашел, что Lua имеет большую поддержку от базы пользователей и несколько хороших книг, которые помогут вам узнать.

Если вы сделаете несколько общий код, который не программист может вводить правила для допустимого ввода, он будет заниматься некоторой предварительной работой независимо от используемого вами маршрута. Я настоятельно рекомендую не сворачивать ваши собственные, потому что вы, скорее всего, найдете людей, желающих получить больше функций, которые уже сделали DSL.

Ответ 7

Если вы используете Java, вы можете использовать библиотеку навигации по графическому объекту.

Он позволяет вам писать java-приложения, которые могут анализировать, компилировать и оценивать выражения OGNL.

OGNL-выражения включают основные выражения java, C, С++, С#.

Вы можете скомпилировать выражение, которое использует некоторые переменные, и затем оценить это выражение для некоторых заданных переменных.

Ответ 8

Простым способом получения достоверности выражений является использование метода 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 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 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 ask the user a function:
user_func = raw_input("type a function: y = ")
-> type a function: y = exp(x)

# Let 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, и иметь среду песочницы, которая может оценивать выражения.

Это то, что мы использовали в предыдущем проекте, в котором я работал. Мы использовали выражения Python в пользовательских плагинах Eclipse IDE, используя Jython для запуска в JVM. Вы можете сделать то же самое с IronPython для запуска в среде CLR.

Примеры, которые я использовал, частично вдохновили/скопировали из проекта проекта Lybniz о том, как запустить безопасную среду eval Python. Прочтите его для более подробной информации!

Ответ 9

Вы можете посмотреть Regular-Expressions или RegEx. Это доказано и было вокруг в течение долгого времени. Там есть библиотека регулярных выражений всех основных языков программирования / script.

Библиотеки:

Использование