27. Мал золотник,
или...
КОМПИЛЯТОР ДЛЯ АЛГЕБРАИЧЕСКОГО ЯЗЫКА
Компилятор — это всегда большая программа. Написать его, практически на пустом месте, даже под опекой преподавателей вовсе не просто. И хотя при создании Мини ставилась цель свести все муки к минимуму, во всяком случае, в той мере, в какой это позволяло его просветительское назначение, предлагаемая задача все же оказалась самой трудной в книге. Если вы (и ваши верные друзья) не обладаете достаточной энергией и временем, то не стоит за нее и браться.
Мини — универсальный, процедурный, алгебраический язык программирования. Он уходит своими корнями в Алгол, Алгол 68 и Паскаль. Подобно этим языкам, Мини предназначен для компиляции, загрузки и выполнения на обычных ЭВМ (хорошим примером такой машины служит УМ-1, см. гл. 25). Синтаксис задается контекстно-свободной грамматикой, пригодной для разбора методами LRA). Семантика аналогична алгольной и паскалевой, и нам кажется достаточным ее неформальное описание. Квалификация читателя позволит ему домыслить все недосказанное[54]. Ниже приведены логически связанные части грамматики и их семантика.
<единица компиляции>::=<программный сегмент>
| <единица компиляции> <программный сегмент>
<программный сегмент>::= <главная программа>
| <внешняя процедура>
<Единица компиляции> — это цепочка замкнутых <программных сегментов>[55]. Каждый <программный сегмент> есть либо <главная программа>, либо <внешняя процедура>. Все сегменты <единицы компиляции> свяжет друг с другом загрузчик, однако не обязательно, чтобы все сегменты, нужные для полной загрузки, компилировались вместе. При загрузке должна присутствовать ровно одна <главная программа>.
<главная программа> ::= <заголовокпрограммы><тело программы> <конец программы>
<заголовок программы>::= PROGRAM <идентификатор>
<тело программы>::= <тело сегмента>
<конец программы>::= END PROGRAM <идентификатор>;
Каждая <главная программа> именована, и закрывающее имя должно совпадать с открывающим. <Тело программы>, подобно большинству других сгруппированных инструкций, есть <тело сегмента> и может содержать все, что обычно свойственно программам. Заметим также, что резервированные слова, идентификаторы и константы не должны разрываться на границах записей. Их следует отделять друг от друга пробелами, знаками операций, комментариями или границами записей.
<внешняя процедура> ::= <внешняя подпрограмма>
| <внешняя функция>
<внешняя подпрограмма> ::= <заголовок внешней подпрограммы>:
<тело внешней подпрограммы>
<конец внешней подпрограммы>
<внешняя функция> ::= <заголовок внешней функции>:
<тело внешней функции> <конец внешней функции>
<заголовок внешней подпрограммы> ::= EXTERNAL PROCEDURE <имя внешней процедуры>
<заголовок внешней функции>::= EXTERNAL FUNCTION <имя внешней процедуры>
<внешний тип>
<имя внешней процедуры> ::= <идентификатор>
| <идентификатор> <список внешних параметров>
<список внешних параметров>::= <заголовок внешних параметров>)
<заголовок внешних параметров>::= (<внешний параметр>
| <заголовок внешних параметров>, <внешний параметр>
<внешний параметр>::= <идентификатор> <внешний тип>
| <идентификатор> <внешний тип> NAME
<внешний тип>::= <базовый тип>
<тело внешней подпрограммы>::= <тело сегмента>
<тело внешней функции>::= <тело сегмента>
<конец внешней подпрограммы>::= END EXTERNAL PROCEDURE <идентификатор>;
<конец внешней функции>::= END EXTERNAL FUNCTION <идентификатор>;
<Внешние процедуры> позволяют компилировать модули раздельно и собирать их вместе во время загрузки. При загрузке допускается лишь одна <внешняя процедура> с данным именем. <Внешние параметры> и возвращаемые <внешними функциями> значения должны иметь <базовый тип>. <Внешние параметры> передаются по значению, если они не помечены резервированным словом NAME; при наличии этого слова параметры передаются по имени. Перед <концом внешней подпрограммы> неявно располагается <инструкция возврата>, однако <внешние функции> должны завершать работу явной <инструкцией возврата> с возвращаемым значением; попытка выхода из <внешней функции> через <конец внешней функции> является семантической ошибкой, которую иногда можно обнаружить при компиляции. Единственной связью между переменными <внешней процедуры> и переменными вызывающей процедуры служит механизм передачи параметров. Однако во время сборки сегментов (например, с помощью загрузчика, подобного описанному в гл. 26) будет проверено совпадение типов формальных и фактических параметров, функций и возвращаемых значений.
54
Стиль последующего изложения показывает, сколь высоко оценивает автор квалификацию своих читателей. — Прим. перев.
55
В русском переводе приходится склонять нетерминальные символы грамматики. — Прим. перев.