Безопасный выражающий парсер в Python

Как я могу позволить пользователям безопасно выполнять математические выражения? Нужно ли писать полный парсер?

Есть ли что-то вроде ast.literal_eval(), но для выражений?

Ответы

Ответ 1

В странице примеров Pyparsing перечислены несколько парсеров выражений:

http://pyparsing.wikispaces.com/file/view/fourFn.py - обычная реализация синтаксического анализа/оценки оценщика арифметических инфикс с использованием pyparsing (несмотря на свое имя, на самом деле это 5-функциональная арифметика, плюс несколько функций триггера)

http://pyparsing.wikispaces.com/file/view/simpleBool.py - логический синтаксический анализатор/оценщик булевых инфикс, используя вспомогательный метод pyparsing operatorPrecedence, который упрощает определение инфиксного оператора нотации

http://pyparsing.wikispaces.com/file/view/simpleArith.py http://pyparsing.wikispaces.com/file/view/eval_arith.py - Пара примеров, переработавших fourFn.py с помощью operatorPrecedence. Первый просто анализирует и возвращает дерево синтаксического анализа, второе добавляет логику оценки.

Ответ 2

Какие выражения вы хотите? Переменное назначение? Оценка функции?

SymPy стремится стать полноценным CAS Python.

Ответ 3

Несколько недель назад я сделал аналогичную вещь, но для логических выражений (или, а, нет, сравнений, круглых скобок и т.д.). Я сделал это, используя парсер Ply. Я создал простой лексер и парсер. Parser создал дерево AST, которое позже использовалось для выполнения вычислений. Выполняя это, вы сможете полностью контролировать, что вводит пользователь, потому что будут анализироваться только выражения, совместимые с грамматикой.

Ответ 4

Да. Даже если для выражений был эквивалент выражения ast.literal_eval(), выражение Python может быть большим, чем просто чистое математическое выражение, например, произвольный вызов функции.

Меня не удивит, если в каком-то модуле с открытым исходным кодом есть хороший математический анализатор/оценщик математических выражений, но если нет, довольно легко написать один из своих.

Ответ 5

Функции maths состоят из числовых символов и символов пунктуации, возможно "E" или "e", если вы допускаете научную нотацию для рациональных чисел, и единственным (другим) легальным использованием альфа-символов будет, если вы разрешаете/предоставляете конкретные математические данные функций (например, stddev). Итак, должно быть тривиально бегать по строке для альфа-символов и проверять следующий маленький бит не является подозрительным, а затем просто вычислять строку в блоке try/except.

Повторите этот комментарий, полученный... Я согласен, что этот подход играет с огнем. Тем не менее, это не значит, что это невозможно сделать безопасно. Я новичок в python (< 2 месяца), поэтому может не знать обходные пути, для которых это уязвимо (и, конечно, новая версия Python всегда может сделать код небезопасным в будущем), но - за то, что мало стоит (в основном мое собственное развлечение) - вот моя трещина на нем:

def evalMaths(s):
    i = 0
    while i < len(s):
        while s[i].isalpha() and i < len(s):
            idn += s[i]
            i += 1
        if (idn and idn != 'e' and idn != 'abs' and idn != 'round'):
            raise Exception("you naughty boy: don't " + repr(idn))
        else:
            i += 1
    return eval(s)

Мне было бы очень интересно узнать, как и как его можно обойти... (^_^) BTW/Я знаю, что вы можете вызывать функции, такие как abs2783 или _983, - если они существуют, но они не будут. Я имею в виду что-то практическое.

На самом деле, если кто-то может это сделать, я создам вопрос с 200 щедростью и приму их ответ.