Чтение онлайн

на главную - закладки

Жанры

Давайте создадим компилятор!
Шрифт:

Все это проще показать вам чем описывать. Я продемонстрирую это с самым простым циклом, циклом LOOP:

{–}

{ Parse and Translate a LOOP Statement }

procedure DoLoop;

var L1, L2: string;

begin

Match('p');

L1 := NewLabel;

L2 := NewLabel;

PostLabel(L1);

Block(L2);

Match('e');

EmitLn('BRA ' + L1);

PostLabel(L2);

end;

{–}

Заметьте, что теперь DoLoop имеет две метки а не одну. Вторая дает команде BREAK адрес перехода Если в цикле нет BREAK, то мы зря потратили метку и немного загромоздили код, но не нанесли никакого вреда.

Заметьте также, что процедура Block теперь имеет параметр, который для циклов всегда будет адресом выхода. Новая версия Block:

{–}

{ Recognize and Translate a Statement Block }

procedure Block(L: string);

begin

while not(Look in ['e', 'l', 'u']) do begin

case Look of

'i': DoIf(L);

'w': DoWhile;

'p': DoLoop;

'r': DoRepeat;

'f': DoFor;

'd': DoDo;

'b': DoBreak(L);

else Other;

end;

end;

end;

{–}

Снова заметьте, что все что Block делает с меткой это

передает ее в DoIf и DoBreak. Циклы не нуждаются в ней, потому что они в любом случае передают свою собственную метку.

Новая версия DoIf:

{–}

{ Recognize and Translate an IF Construct }

procedure Block(L: string); Forward;

procedure DoIf(L: string);

var L1, L2: string;

begin

Match('i');

Condition;

L1 := NewLabel;

L2 := L1;

EmitLn('BEQ ' + L1);

Block(L);

if Look = 'l' then begin

Match('l');

L2 := NewLabel;

EmitLn('BRA ' + L2);

PostLabel(L1);

Block(L);

end;

Match('e');

PostLabel(L2);

end;

{–}

Здесь единственное, что изменяется, это добавляется параметр у процедуры Block. Оператор IF не меняет уровень вложенности цикла, поэтому DoIf просто передает метку дальше. Независимо от того, сколько уровней вложенности IF мы имеем, будет использоваться та же самая метка.

Теперь не забудьте, что DoProgram также вызывает Block и теперь необходимо передавать ей метку. Попытка выхода из внешнего блока является ошибкой, поэтому DoProgram передает пустую метку, которая перехватывается DoBreak:

{–}

{ Recognize and Translate a BREAK }

procedure DoBreak(L: string);

begin

Match('b');

if L <> '' then

EmitLn('BRA ' + L)

else Abort('No loop to break from');

end;

{–}

{ Parse and Translate a Program }

procedure DoProgram;

begin

Block('');

if Look <> 'e' then Expected('End');

EmitLn('END')

end;

{–}

Этот код позаботится почти обо всем. Испытайте его, посмотрите, сможете ли вы «сломать» («break») его (каламбур). Аккуратней однако. К настоящему времени мы использовали так много букв, что трудно придумать символ, который не представляет сейчас какое либо зарезервированное слово. Не забудьте, перед тем, как вы протестируете программу, вы должны будете исправить каждый случай появления Block в других циклах для включения нового параметра. Сделайте это точно так же, как я сделал это для LOOP.

Я сказал выше «почти». Есть одна небольшая проблема: если вы внимательно посмотрите на код, генерируемый для DO, вы увидите, что если вы прервете этот цикл, то значение счетчика все еще остается в стеке. Мы должны исправить это! Позор... это была одна из самых маленьких наших подпрограмм, но это не помогло. Вот новая версия, которая не имеет этой проблемы:

{–}

{ Parse and Translate a DO Statement }

procedure Dodo;

var L1, L2: string;

begin

Match('d');

L1 := NewLabel;

L2 := NewLabel;

Expression;

EmitLn('SUBQ #1,D0');

PostLabel(L1);

EmitLn('MOVE D0,-(SP)');

Block(L2);

EmitLn('MOVE (SP)+,D0');

EmitLn('DBRA D0,' + L1);

EmitLn('SUBQ #2,SP');

PostLabel(L2);

EmitLn('ADDQ #2,SP');

end;

{–}

Две дополнительные инструкции SUBQ и ADDQ заботятся о сохранении стека в правильной форме.

Заключение

К этому моменту мы создали ряд управляющих конструкций... в действительности более богатый набор чем предоставляет почти любой другой язык программирования. И, за исключением цикла FOR, это было довольно легко сделать. Но даже этот цикл был сложен только потому, что сложность заключалась в ассемблере.

Я завершаю на этом урок. Чтобы можно было обернуть наш продукт красной ленточкой, в действительности мы должны иметь настоящие ключевые слова вместо этих игрушечных односимвольных. Вы уже видели, что расширить компилятор для поддержки многосимвольных слов не трудно, но в этом случае возникнут большие различия в представлении нашего входного кода. Я оставлю этот небольшой кусочек для следующей главы. В этой главе мы также рассмотрим логические выражения, так что мы сможем избавиться от фиктивной версии Condition, которую мы здесь использовали. Увидимся.

Для справочных целей привожу полный текст синтаксического анализатора для этого урока:

{–}

program Branch;

{–}

{ Constant Declarations }

const TAB = ^I;

CR = ^M;

{–}

{ Variable Declarations }

var Look : char; { Lookahead Character }

Lcount: integer; { Label Counter }

{–}

{ Read New Character From Input Stream }

procedure GetChar;

begin

Read(Look);

end;

{–}

{ Report an Error }

procedure Error(s: string);

begin

WriteLn;

WriteLn(^G, 'Error: ', s, '.');

end;

{–}

{ Report Error and Halt }

procedure Abort(s: string);

begin

Error(s);

Halt;

end;

{–}

{ Report What Was Expected }

procedure Expected(s: string);

begin

Abort(s + ' Expected');

end;

{–}

{ Match a Specific Input Character }

procedure Match(x: char);

begin

if Look = x then GetChar

else Expected('''' + x + '''');

end;

{–}

{ Recognize an Alpha Character }

function IsAlpha(c: char): boolean;

begin

IsAlpha := UpCase(c) in ['A'..'Z'];

end;

{–}

{ Recognize a Decimal Digit }

function IsDigit(c: char): boolean;

begin

IsDigit := c in ['0'..'9'];

end;

{–}

{ Recognize an Addop }

function IsAddop(c: char): boolean;

begin

IsAddop := c in ['+', '-'];

end;

{–}

{ Recognize White Space }

function IsWhite(c: char): boolean;

begin

IsWhite := c in [' ', TAB];

end;

{–}

{ Skip Over Leading White Space }

procedure SkipWhite;

begin

while IsWhite(Look) do

GetChar;

end;

{–}

{ Get an Identifier }

function GetName: char;

begin

if not IsAlpha(Look) then Expected('Name');

GetName := UpCase(Look);

GetChar;

end;

{–}

{ Get a Number }

function GetNum: char;

begin

if not IsDigit(Look) then Expected('Integer');

GetNum := Look;

GetChar;

end;

{–}

{ Generate a Unique Label }

function NewLabel: string;

var S: string;

begin

Str(LCount, S);

NewLabel := 'L' + S;

Inc(LCount);

end;

{–}

{ Post a Label To Output }

procedure PostLabel(L: string);

begin

WriteLn(L, ':');

end;

{–}

{ Output a String with Tab }

procedure Emit(s: string);

begin

Write(TAB, s);

end;

{–}

{ Output a String with Tab and CRLF }

procedure EmitLn(s: string);

begin

Emit(s);

WriteLn;

end;

{–}

{ Parse and Translate a Boolean Condition }

procedure Condition;

begin

EmitLn('<condition>');

end;

{–}

{ Parse and Translate a Math Expression }

procedure Expression;

begin

EmitLn('<expr>');

end;

{–}

{ Recognize and Translate an IF Construct }

procedure Block(L: string); Forward;

procedure DoIf(L: string);

var L1, L2: string;

begin

Match('i');

Condition;

L1 := NewLabel;

L2 := L1;

EmitLn('BEQ ' + L1);

Block(L);

if Look = 'l' then begin

Match('l');

L2 := NewLabel;

EmitLn('BRA ' + L2);

PostLabel(L1);

Block(L);

end;

Match('e');

PostLabel(L2);

end;

{–}

{ Parse and Translate a WHILE Statement }

procedure DoWhile;

var L1, L2: string;

begin

Match('w');

L1 := NewLabel;

L2 := NewLabel;

PostLabel(L1);

Condition;

EmitLn('BEQ ' + L2);

Block(L2);

Match('e');

EmitLn('BRA ' + L1);

PostLabel(L2);

end;

{–}

{ Parse and Translate a LOOP Statement }

procedure DoLoop;

var L1, L2: string;

begin

Match('p');

L1 := NewLabel;

L2 := NewLabel;

PostLabel(L1);

Block(L2);

Match('e');

EmitLn('BRA ' + L1);

PostLabel(L2);

end;

{–}

{ Parse and Translate a REPEAT Statement }

procedure DoRepeat;

var L1, L2: string;

begin

Match('r');

L1 := NewLabel;

L2 := NewLabel;

PostLabel(L1);

Block(L2);

Match('u');

Condition;

EmitLn('BEQ ' + L1);

PostLabel(L2);

end;

{–}

{ Parse and Translate a FOR Statement }

procedure DoFor;

var L1, L2: string;

Name: char;

begin

Match('f');

L1 := NewLabel;

L2 := NewLabel;

Name := GetName;

Match('=');

Expression;

EmitLn('SUBQ #1,D0');

EmitLn('LEA ' + Name + '(PC),A0');

EmitLn('MOVE D0,(A0)');

Expression;

EmitLn('MOVE D0,-(SP)');

PostLabel(L1);

EmitLn('LEA ' + Name + '(PC),A0');

EmitLn('MOVE (A0),D0');

EmitLn('ADDQ #1,D0');

EmitLn('MOVE D0,(A0)');

EmitLn('CMP (SP),D0');

EmitLn('BGT ' + L2);

Block(L2);

Match('e');

EmitLn('BRA ' + L1);

PostLabel(L2);

EmitLn('ADDQ #2,SP');

end;

{–}

{ Parse and Translate a DO Statement }

procedure Dodo;

var L1, L2: string;

begin

Match('d');

L1 := NewLabel;

L2 := NewLabel;

Expression;

EmitLn('SUBQ #1,D0');

PostLabel(L1);

EmitLn('MOVE D0,-(SP)');

Block(L2);

EmitLn('MOVE (SP)+,D0');

EmitLn('DBRA D0,' + L1);

EmitLn('SUBQ #2,SP');

PostLabel(L2);

EmitLn('ADDQ #2,SP');

end;

{–}

{ Recognize and Translate a BREAK }

procedure DoBreak(L: string);

begin

Match('b');

EmitLn('BRA ' + L);

end;

{–}

{ Recognize and Translate an

«Other» }

procedure Other;

begin

EmitLn(GetName);

end;

{–}

{ Recognize and Translate a Statement Block }

procedure Block(L: string);

begin

while not(Look in ['e', 'l', 'u']) do begin

case Look of

'i': DoIf(L);

'w': DoWhile;

'p': DoLoop;

'r': DoRepeat;

'f': DoFor;

'd': DoDo;

'b': DoBreak(L);

else Other;

end;

end;

end;

{–}

{ Parse and Translate a Program }

procedure DoProgram;

begin

Block('');

if Look <> 'e' then Expected('End');

EmitLn('END')

end;

{–}

{ Initialize }

procedure Init;

begin

LCount := 0;

GetChar;

end;

{–}

{ Main Program }

begin

Init;

DoProgram;

end.

{–}

Булевы выражения

Введение

В пятой части этой серии мы рассмотрели управляющие конструкции и разработали подпрограммы синтаксического анализа для трансляции их в объектный код. Мы закончили с хорошим, относительно богатым набором конструкций.

Однако, когда мы оставили синтаксический анализатор, в наших возможностях существовал один большой пробел: мы не обращались к вопросу условия ветвления. Чтобы заполнить пустоту, я представил вам фиктивную подпрограмму анализа Сondition, которая служила только как заменитель настоящей.

Одним из дел, которыми мы займемся на этом уроке, будет заполнение этого пробела посредством расширения Condition до настоящего анализатора/транслятора.

План

Мы собираемся подойти к этой главе немного по-другому, чем к любой другой. В других главах мы начинали немедленно с экспериментов, используя компилятор Pascal, выстраивая синтаксические анализаторы от самых элементарных начал до их конечных форм, не тратя слишком много времени на предварительное планирование. Это называется кодированием без спецификации и обычно к нему относятся неодобрительно. Раньше мы могли избегать планирования, потому что правила арифметики довольно хорошо установлены... мы знаем, что означает знак "+" без необходимости подробно это обсуждать. То же самое относится к ветвлениям и циклам. Но способы, которыми языки программирования реализуют логику, немного отличаются от языка к языку. Поэтому прежде, чем мы начнем серьезное кодирование, лучше мы сперва примем решение что же мы хотим. И способ сделать это находится на уровне синтаксических правил БНФ (грамматики).

Поделиться:
Популярные книги

Проданная невеста

Wolf Lita
Любовные романы:
любовно-фантастические романы
5.80
рейтинг книги
Проданная невеста

Сумеречный Стрелок 10

Карелин Сергей Витальевич
10. Сумеречный стрелок
Фантастика:
рпг
аниме
фэнтези
5.00
рейтинг книги
Сумеречный Стрелок 10

Картофельное счастье попаданки

Иконникова Ольга
Фантастика:
фэнтези
5.00
рейтинг книги
Картофельное счастье попаданки

Совершенно несекретно

Иванов Дмитрий
15. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Совершенно несекретно

Нечто чудесное

Макнот Джудит
2. Романтическая серия
Любовные романы:
исторические любовные романы
9.43
рейтинг книги
Нечто чудесное

Истребители. Трилогия

Поселягин Владимир Геннадьевич
Фантастика:
альтернативная история
7.30
рейтинг книги
Истребители. Трилогия

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Прометей: каменный век II

Рави Ивар
2. Прометей
Фантастика:
альтернативная история
7.40
рейтинг книги
Прометей: каменный век II

Сердце для стража

Каменистый Артем
5. Девятый
Фантастика:
фэнтези
боевая фантастика
9.20
рейтинг книги
Сердце для стража

Сделай это со мной снова

Рам Янка
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Сделай это со мной снова

Господин следователь 6

Шалашов Евгений Васильевич
6. Господин следователь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Господин следователь 6

Ваше Сиятельство 10

Моури Эрли
10. Ваше Сиятельство
Фантастика:
боевая фантастика
технофэнтези
фэнтези
эпическая фантастика
5.00
рейтинг книги
Ваше Сиятельство 10

Имперский Курьер. Том 5

Бо Вова
5. Запечатанный мир
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Имперский Курьер. Том 5

Егерь

Астахов Евгений Евгеньевич
1. Сопряжение
Фантастика:
боевая фантастика
попаданцы
рпг
7.00
рейтинг книги
Егерь