Как отформатирован файл parsetab.py от PLY? - PullRequest
0 голосов
/ 13 марта 2019

Я работаю над проектом по преобразованию кода MATLAB в Python, и был несколько успешным после сборки работы других .Инструмент использует PLY (реализация инструментов синтаксического анализа lex и yacc для Python) для анализа входных данных MATLAB.К сожалению, это требование, чтобы мой код был написан на Python 3, а не Python 2. Инструмент работает без проблем в Python 2, но я получаю странную ошибку в Python 3 (при условии, что A - это массив):

    log_idx = A <= 16;
                  ^
SyntaxError: Unexpected "=" (parser)

Код MATLAB, который я пытаюсь преобразовать:

idx = A <= 16;

, который должен преобразовывать почти в одно и то же в Python 3:

idx = A <= 16

Единственная реальная разницамежду кодом Python 3 и кодом Python 2 находится PLY-генерируемый файл parsetab.py, который имеет существенные различия в следующих переменных:

_tabversion
_lr_signature
_lr_action_items
_lr_goto_items

У меня проблемы с пониманием назначения этих переменных ипочему они могут отличаться, когда единственным отличием была версия Python, используемая для генерации файла parsetab.py.

Я попытался найти документацию по этому вопросу, но безуспешно.Первоначально я подозревал, что может быть разница в способах форматирования строк между Python 2 и Python 3, но это тоже ничего не дало.Кто-нибудь знаком с PLY, который мог бы дать некоторое представление о том, как генерируются эти переменные, или почему версия Python создает эту разницу?

Редактировать: я не уверен, будет ли это кому-нибудь полезно, потому чтоФайл очень длинный и загадочный, но ниже приведен пример части первых строк _lr_action_items и _lr_goto_items

Python 2:

_lr_action_items = {'DOTDIV':([6,9,14,20,22,24,32,34,36,42,46,47,52,54,56,57,60,71,72,73,74,75 ...
_lr_goto_items = {'lambda_args':([45,80,238,],[99,161,263,]),'unwind':([1,8,28,77,87,160,168,177 ...

Python 3:

_lr_action_items = {'END_STMT':([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,39,41,48,50 ...
_lr_goto_items = {'top':([0,],[1,]),'stmt':([1,44,46,134,137,207,212,214,215,244,245,250 ...

1 Ответ

2 голосов
/ 13 марта 2019

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

В этом сообщении об ошибке есть важный ключ:

log_idx = A <= 16;
              ^
SyntaxError: Unexpected "=" (parser)

Очевидно, <= - это , а не , сканируемый как один токен; в противном случае парсер не увидит токен = в этой точке ввода. Это может означать только то, что сканер возвращает два токена, < и =, и если это так, то это, безусловно, синтаксическая ошибка, как и следовало ожидать от

log_idx = A < = 16;

Чтобы выяснить, почему лексер сделает это, важно понять, как работает лексер Ply (по умолчанию). Он собирает все шаблоны лексеров из переменных, имена которых начинаются с t_, которые должны быть либо функциями, либо переменными, значения которых являются строками. Затем он сортирует их следующим образом:

  1. Строки документации функций, в порядке по номеру строки в исходном файле.
  2. строковые значения, в обратном порядке по длине.

См. Спецификация токенов в руководстве Ply.

Это обычно делает правильные вещи, но не всегда. Цель сортировки в обратном порядке по длине состоит в том, что шаблон префикса будет следовать за шаблоном, который соответствует более длинной строке. Таким образом, если бы у вас были шаблоны '<' и '<=', сначала попытались бы '<=', и поэтому в случае, когда на входе было <=, шаблон < никогда не будет пробоваться. Это важно, поскольку, если '<' будет пробоваться первым, '<=' никогда не будет распознан.

Однако эта простая эвристика не всегда работает. Тот факт, что регулярное выражение короче, не обязательно означает, что его совпадение будет короче. Так что, если вы ожидаете семантику "максимального хруста", вам иногда нужно быть осторожным с вашими шаблонами. (Или вы можете предоставить их как строки документов, потому что тогда у вас будет полный контроль над заказом.)

И тот, кто создал этот файл lexer.py, был , а не осторожно относился к их шаблонам, потому что он включает (среди прочих вопросов):

t_LE          = r"<="
t_LT          = r"\<"

Обратите внимание, что, поскольку это необработанные строки, обратная косая черта сохраняется во второй строке, поэтому оба шаблона имеют длину 2:

>>> len(r"\<")
2
>>> len(r"<=")
2

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

< не имеет особого значения в регулярном выражении Python, поэтому нет необходимости использовать обратную косую черту в определении t_LT. (Очевидно, поскольку в t_LE он не экранирован от обратной косой черты.) Поэтому простейшим решением было бы сделать порядок сортировки однозначным, удалив обратную косую черту:

t_LE          = r"<="
t_LT          = r"<"

Теперь t_LE длиннее и обязательно будет пробоваться первым.

Это не единственный случай этой проблемы в файле лексера, поэтому вы можете тщательно его пересмотреть.

Примечание. Вы также можете решить проблему, добавив ненужную обратную косую черту в шаблон t_LE; Есть аргумент для того, чтобы занять позицию: «Если сомневаешься, сбегай». Однако полезно знать, какие символы необходимо экранировать в регулярном выражении Python, и документация Python для пакета re содержит полный список. Кроме того, рассмотрите возможность использования длинных необработанных строк для шаблонов, которые включают в себя кавычки, поскольку ни ", ни ' не нужно экранировать обратной косой чертой в регулярном выражении Python.

...