Внутри вашего p_function_expression
вы делаете рекурсивный вызов метода parse
:
p[0] = parser.parse(functions[key].replace('x', '9'))
(Примечание: как указано в вашем вопросе, в этой строке произошла синтаксическая ошибка из постороннего .
после последней круглой скобки.)
Этот вызов parse
неявно использует глобальный лексер; то есть текущее значение lex.lexer
(которое является последним лексером, созданным lex.lex()
). Но лексеры Ply (и, действительно, большинство лексеров) с состоянием ; лексер поддерживает текущую строку ввода и текущую позицию ввода, а также некоторую другую информацию (например, текущее состояние лексера, если вы используете несколько состояний ).
Как следствие, рекурсивный вызов parse
оставляет (глобальный) лексер указанным в конце строки. Поэтому, когда внешний parse
пытается прочитать следующий токен (фактически второй следующий токен, поскольку у него уже есть токен предпросмотра), он получает EOF от лексера, что вызывает синтаксическую ошибку.
Вы можете это можно увидеть, включив отладку парсера :
answer = parser.parse(question, debug=True)
Эта проблема кратко описана в руководстве Ply , где указано, что вам следует clone
лексер для реентерабельный лексический анализ.
К сожалению, в руководстве Ply не упоминается, что объект Ply parser также не реентерабелен. В случае синтаксического анализатора проблемы повторного входа несколько менее очевидны; они действительно применяются только во время восстановления синтаксической ошибки (если только вы не сохраните свои постоянные данные в объекте синтаксического анализатора, что вам разрешено делать). У синтаксического анализатора нет метода клонирования, главным образом потому, что таблицы синтаксического анализа уже были предварительно вычислены и кэшированы, поэтому создание нового синтаксического анализатора не так дорого, как создание нового лексера.
Короче говоря, вам нужно было выполнить внутренний анализ с объектом синтаксического анализатора fre sh, который использует объект лексера fre sh:
p[0] = yacc.yacc().parse(functions[key].replace('x', '9'),
lexer=p.lexer.clone())
(объект синтаксического анализатора не имеет постоянного атрибута, который хранит текущий лексер, но он доступен в аргументе, передаваемом функциям действия синтаксического анализатора как p.lexer
. См. очень полезное руководство Ply .)
Кроме того, вы действительно должны изучить цель из Python словарей. Они точно разработаны таким образом, что вам не нужно l oop для всех записей, чтобы найти нужный ключ. Вы можете найти ключ в простой операции O (1). Гораздо более простая версия (также значительно более быстрая, если у вас есть несколько функций):
def p_function_expression(p):
'''function : FUNCTION '''
if p[1] in functions:
p[0] = yacc.yacc().parse(functions[p[1]].replace('x', '9'),
lexer=p.lexer.clone())
else:
print("Variable '{}' not found".format(p[1]))
, которая ищет имя функции дважды, что по-прежнему всего лишь две линейные операции. Но если вы хотите выполнить поиск только один раз, вы можете использовать метод словаря get
, который возвращает значение по умолчанию:
def p_function_expression(p):
'''function : FUNCTION '''
funcbody = functions.get(p[1], None)
if funcbody is not None:
p[0] = yacc.yacc().parse(funcbody.replace('x', '9'),
lexer=p.lexer.clone())
else:
print("Variable '{}' not found".format(p[1]))
Вы также можете сделать это, посмотрев имя внутри try
блок, который ловит KeyError
.
Я думаю, это должно go, не говоря, что это , а не хороший способ выполнить sh задачу, которую вы поставили перед собой. Было бы гораздо лучше представить функциональные тела в виде предварительно проанализированного AST, который впоследствии можно было бы оценить с использованием фактических параметров. В этой модели вам вообще не требуется немедленная оценка; все анализируется в AST, и когда (если) вы хотите оценить проанализированный текст, вы вызываете для этого метод eval
AST.