Ваша do
форма грамматики (разбита на строки и использует односимвольные литералы для удобства чтения):
do_expression : '(' DO '(' ID constant '(' symbol ID expression ')' ')'
comparison_expression expression ')'
( Примечание: Это нена самом деле правильная грамматика по ряду причин, одна из которых отмечена в другом ответе. Но это не относится к этому вопросу.)
В вашем семантическом действии p[12]
и p[13]
соответствуют comparison_expression
и expression
.Разобравшись с основами, ваше семантическое действие выполняет следующее:
# create a new bound variable with the indicated initial value (`p[5]`).
aux = p[12]
while True:
expr = p[13] # You have a typo; I assume you meant p[13], not [13]
# Update the index variable's value
aux = p[12] # This value is still the same as it was at entry
if aux == '#t':
break
p[0] = expr
Теперь важно подумать о том, что такое p[12]
и p[13]
.Ply не делает никакой магии под капотом Python;это просто генерирование кода Python.Таким образом, p[12]
и p[13]
являются обычными значениями Python, которые являются результатом выполнения семантических действий для comparison_expression
и expression
нетерминалов.И эти семантические действия оцениваются до того, как do_expression
было уменьшено, поэтому их значения вычисляются без какой-либо ссылки на do_expression
.И comparison_expression
, и expression
относятся к связанной переменной i
(что является естественным для конструкции итерации), но эта переменная не привязывается при оценке этих семантических действий.Отсюда и сообщение об ошибке.
Но логика более фундаментальна, чем эта.В вашей модели выражения сравнения и действия вычисляются ровно один раз, когда они анализируются.Но это не семантика конструкции цикла;в семантике цикла выражение сравнения оценивается многократно до тех пор, пока оно не покажет, что цикл завершен, и выражение действия может вообще не оцениваться, если сравнение не выполнено с первым связанным значением.
Похоже, вы предполагаете, что доступ к p[12]
и p[13]
каким-то образом переоценит связанные семантические действия.Но у Python нет такой возможности, и Ply тоже волшебным образом не реализует ее.Это ваша ответственность, основанная на предполагаемой семантике языка, который вы пытаетесь скомпилировать (или, в данном случае, интерпретировать).
Для этого вам необходимо преобразовать анализируемый ввод в некие данныеструктура, которую вы можете позже оценить (или нет, в зависимости от обстоятельств).Таким образом, вы должны сделать так, чтобы значение, возвращаемое семантическими действиями, было описанием проанализированного кода, а не немедленной оценкой (которая в любом случае не имела бы смысла при отсутствии привязок переменных).
В случаеСхемы, разбор действительно самая маленькая из проблем.Хотя специальные формы немного усложняют задачу, программы Scheme в основном являются S-выражениями, которые можно почти тривиально преобразовать в списки без необходимости в сложной технологии синтаксического анализа.Таково было первоначальное намерение синтаксиса Scheme (или, скорее, Lisp).Если у вас есть структура списка или некоторый функциональный эквивалент (абстрактное синтаксическое дерево или даже трехадресный код), вы можете при необходимости оценить текст программы при необходимости и с правильными привязками переменных.
Однаждывремя, никто бы не подумал о назначении такой задачи без ссылки на превосходный (и до сих пор актуальный) учебник Abelson & Sussman Структура и интерпретация компьютерных программ , ласково именуемая какSICP.Благодаря щедрости авторов и издателей, полный текст этой книги свободно доступен в Интернете.Я не могу рекомендовать это достаточно.
PS: Также обратите внимание, что привязка переменной управления цикла (в данном случае i
) присутствует только во время оценки цикла.После завершения цикла переменная должна быть удалена.Но вы не можете смоделировать это с помощью простого словаря, потому что может быть внешняя переменная с тем же именем.Это все объясняется и в SICP.