и синтаксически-управляемый перевод:
REPEAT { L = NewLabel;
PostLabel(L) }
UNTIL
Как обычно, код вытекает отсюда довольно легко:
{–}
{ Parse and Translate a REPEAT Statement }
procedure DoRepeat;
var L: string;
begin
Match('r');
L := NewLabel;
PostLabel(L);
Block;
Match('u');
Condition;
EmitLn('BEQ ' + L);
end;
{–}
Как и прежде, мы должны добавить вызов DoRepeat в Block. Хотя на этот раз есть различия. Я решил использовать "r" вместо REPEAT (естественно), но я также решил использовать "u" вместо UNTIL. Это означает, что "u" должен быть добавлен к множеству символов в условии while. Это символы, которые сигнализируют о выходе из текущего блока... символы «follow», на жаргоне разработчиков компиляторов.
{–}
{ Recognize and Translate a Statement Block }
procedure Block;
begin
while not(Look in ['e', 'l', 'u']) do begin
case Look of
'i': DoIf;
'w': DoWhile;
'p': DoLoop;
'r': DoRepeat;
else Other;
end;
end;
end;
{–}
Цикл FOR очень удобен, но он тяжел для трансляции. Не столько потому, что сама конструкция трудна... в конце концов это всего лишь цикл... но просто потому, что она трудна для реализации на ассемблере. Как только код придуман, трансляция достаточно проста.
Фаны Си любят цикл FOR этого языка (фактически он проще для кодирования), но вместо него я выбрал синтаксис очень похожий на синтаксис из старого доброго Бейсика:
FOR
Сложность трансляции цикла «FOR» зависит от выбранного вами способа его реализации, от пути, которым вы решили определять правила обработки ограничений. Рассчитывается ли expr2 каждый раз при прохождении цикла, например, или оно обрабатывается как постоянное ограничение? Всегда ли вы проходите цикл хотя бы раз, как в Fortran, или нет. Все становится проще, если вы приверженец точки зрения что эта конструкция эквивалентна:
TEMP =
WHILE
ENDWHILE
Заметьте, что с этим определением цикла
Код 68000, необходимый для этого, сложней чем все что мы делали до сих пор. Я сделал несколько попыток, помещая и счетчик и верхний предел в стек, в регистры и т.д. В конечном итоге я остановился на гибридном варианте размещения, при котором счетчик помещается в памяти (поэтому он может быть доступен внутри цикла) а верхний предел – в стеке. Оттранслированный код получился следующий:
LEA
SUBQ #1,D0 ; предварительно уменьшить его
MOVE D0,(A0) ; сохранить его
MOVE D0,-(SP) ; сохранить его в стеке
L1: LEA
MOVE (A0),D0 ; извлечь его в D0
ADDQ #1,D0 ; увеличить счетчик
MOVE D0,(A0) ; сохранить новое значение
CMP (SP),D0 ; проверить диапазон
BLE L2 ; пропустить если D0 > (SP)
BRA L1 ; цикл для следующего прохода
L2: ADDQ #2,SP ; очистить стек
Ничего себе! Это же куча кода... строка, содержащая
Однако, подпрограмма анализа довольно проста теперь, когда у нас есть код: