Безопасный выражающий парсер в 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 щедростью и приму их ответ.