Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pascal_Unkn.doc
Скачиваний:
8
Добавлен:
03.11.2018
Размер:
1.63 Mб
Скачать

Работа с точками с запятой

Есть два различных способа работы с точками с запятой используемые в популярных языках. В Паскале точка с запятой расценивается как разделитель операторов. Точка с запятой не требуется после последнего утверждения в блоке. Синтаксис:

     <block> ::= <statement> ( ';' <statement>)*

     <statement> ::= <assignment> | <if> | <while> ... | null

(пустое утверждение важно!)

Паскаль также определяет некоторые точки с запятой в других местах, таких как после утверждения PROGRAM.

В C и Ada, с другой стороны, точка с запятой рассматривается как терминатор операторов и следует после всех утверждений (с некоторыми смущающими и путающими исключениями). Синтаксис для них простой:

     <block> ::= ( <statement> ';')*

Из двух синтаксисов, синтаксис Паскаля внешне выглядит более рациональным, но опыт показал, что он ведет к некоторым странным трудностям. Люди так привыкают ставить точку с запятой после каждого утверждения, что они также предпочитают ставить ее и после последнего утверждения в блоке. Это обычно не приносит какого-либо вреда... она просто обрабатывается как пустое утверждение. Многие программисты на Паскале, включая вашего покорного слугу,  делают точно также. Но есть одно место, в котором вы абсолютно не можете поставить точку с запятой - прямо перед ELSE. Это маленький подводный камень стоил мне множества дополнительных компиляций, особенно когда ELSE добавляется к существующему коду. Так что выбор C/Ada оказывается лучше. Очевидно, Никлаус Вирт думает также: в Modula-2 он отказался от Паскалевского подхода.

Имея эти два синтаксиса, легко (теперь, когда мы реорганизовали синтаксический анализатор!) добавить эти возможности в наш анализатор. Давайте сначала возьмем последний случай, так как он проще.

Для начала я упростил программу представив новую подпрограмму распознавания:

{--------------------------------------------------------------}

{ Match a Semicolon }

procedure Semi; begin    MatchString(';'); end;

{--------------------------------------------------------------}

Эта процедура очень похожа на наш старый Match. Она требует чтобы следующим токеном была точка с запятой. Найдя его, она переходит к следующему.

Так как точка с запятой следует за утверждением, процедура Block почти единственная, которую мы должны изменить:

{--------------------------------------------------------------}

{ Parse and Translate a Block of Statements }

procedure Block; begin    Scan;    while not(Token in ['e', 'l']) do begin       case Token of        'i': DoIf;        'w': DoWhile;        'R': DoRead;        'W': DoWrite;        'x': Assignment;       end;       Semi;       Scan;    end; end;

{--------------------------------------------------------------}

Внимательно взгляните на тонкие изменения в операторе case. Вызов Assigment теперь ограничивается проверкой Token. Это позволит избежать вызова Assigment когда токен является точкой с запятой (что случается когда утверждение пустое).

Так как объявления - тоже утверждения, мы также должны добавить вызов Semi в процедуру TopDecl:

{--------------------------------------------------------------}

{ Parse and Translate Global Declarations }

procedure TopDecls; begin    Scan;    while Token = 'v' do begin       Alloc;       while Token = ',' do          Alloc;       Semi;    end; end;

{--------------------------------------------------------------}

Наконец нам нужен вызов для утверждения PROGRAM:

{--------------------------------------------------------------}

{ Main Program }

begin    Init;    MatchString('PROGRAM');    Semi;    Header;    TopDecls;    MatchString('BEGIN');    Prolog;    Block;    MatchString('END');    Epilog; end.

{--------------------------------------------------------------}

Проще некуда. Испробуйте это с копией TINY и скажите как вам это нравится.

Версия Паскаля немного сложнее, но она все равно требует только небольших изменений и то только в процедуре Block. Для максимальной простоты давайте разобьем процедуру на две части. Следующая процедура обрабатывает только одно утверждение:

{--------------------------------------------------------------}

{ Parse and Translate a Single Statement }

procedure Statement; begin    Scan;    case Token of     'i': DoIf;     'w': DoWhile;     'R': DoRead;     'W': DoWrite;     'x': Assignment;    end; end;

{--------------------------------------------------------------}

Используя эту процедуру мы можем переписать Block так:

{--------------------------------------------------------------}

{ Parse and Translate a Block of Statements }

procedure Block; begin    Statement;    while Token = ';' do begin       Next;       Statement;    end; end;

{--------------------------------------------------------------}

Это, уверен, не повредило, не так ли? Теперь мы можем анализировать точки с запятой в Паскаль подобном стиле.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]