Материал: Разработка элементов учебной системы программирования

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

·        Загрузчик может загружать только одну программу;

·        Поддерживаются программы максимальной длиной в 50 карт;

4. Компилятор с языка высокого уровня с использованием Flex и Bison


Построение компилятора с языка высокого уровня (ЯВУ), являющегося одним из элементов системы программирования, образующих в совокупности следующий технологический конвейер:


При этом предполагается то, что данная система программирования работает на технологической ЭВМ (IBM PC) и является по существу кросс-системой для объектной ЭВМ (ЕС ЭВМ). В этой системе:

·        в качестве языка высокого уровня (ЯВУ) выбран язык, образованный из подмножества языковых конструкций ПЛ1, а исходная программа готовится в виде текстового файла технологической ЭВМ с расширением *.pli;

·        язык АССЕМБЛЕРА сформирован из языковых конструкций АССЕМБЛЕРА ЕС ЭВМ, а ассемблеровский эквивалент исходной программы формируется в виде текстового файла технологической ЭВМ с расширением *.ass;

·        объектный эквивалент исходной программы готовится в формате объектных файлов операционной системы ОС ЕС ЭВМ и хранится в виде двоичного файла технологической ЭВМ с расширением *.tex;

·        загрузочный эквивалент исходной программы представляет собой машинный код ЕС ЭВМ, запоминаемый в области ОЗУ технологической ЭВМ, являющейся зоной загрузки для эмулятора объектной ЭВМ.

Постановка задачи

Необходимо выполнить доработку элементов макета учебной системы программирования до уровня, позволяющего обрабатывать “новые” для макета конструкции языка высокого уровня, примененные в соответствующем варианте:


Где на входе имеется текст программы на ЯВУ PL/I:

EX04:         PROC OPTIONS ( MAIN );   A       DECIMAL FIXED INIT ( 2 );          B         DECIMAL FIXED;       P       POINTER; D       DECIMAL FIXED BASED ( P );= ADDR ( A );= D;EX04;

На выходе строится эквивалент программы на ассемблере архитектуры IBM 370:

ex04START 0Programm startRBASE,0Base initialization*,RBASEBase declarationRRAB,A Load addressRRAB,P Store address,D Variable value loading,0(0,RRAB) Load by addressB(3),0(RRAB) Move numveric15,RVIXReturn from programmEQU 5143PL3'2C'Variable declaration with initialization PL3 Variable declaration without initializationAVariable declaration without initializationPProgramm end

Анализ поставленной задачи

В поставленной задаче на входе в компилятор ЯВУ представлена программа на языке PL/I, которая выполняет присваивание одной десятичной переменной другой через указатель. Для этого были введены новые лексемы, отсутствовавшие в исходном макете:

·        DECIMAL - десятичные переменные с инициализацией и без

·        POINTER - переменная указатель

·        BASED-переменные - переменная, базирующаяся на данных, на которые ссылается указатель, указанный при инициализации

·        Оператор ADDR - получение адреса переменной

·        Оператор присваивания ‘=’ - присваивание значения одной десятичной переменной другой

Набор новых конструкций языка PL/I

Соответствующий набор конструкций был добавлен в компилятор ЯВУ.

3.      Форматы объявления переменных

3.1    Целые десятичные переменные:

Формат: DCL <IPE> DECIMAL FIXED; или DCL <IPE> DECIMAL FIXED INIT ( <NUM> );

·        <IPE> - имя переменной

·        <NUM> - значение при инициализации, напр. ‘234’

·        Могут быть как инициализированными, так и не инициализированными

3.2    Указатели:

Формат: DCL <IPE> POINTER;

·        <IPE> - имя переменной

3.3    BASED-переменные:

Формат: DCL <IPE> DECIMAL FIXED BASED ( <IPE_POI> );

·        <IPE> - имя переменной

·        <IPE_POI> - имя переменной - указателя

4.      Форматы использования операторов

4.1    Оператор присваивания:

Формат: <IPE1> = <IPE2>

·        предназначен для назначения нового значения переменной, идентификатор которой использован в левой части оператора;

4.2    Оператор ADDR:

Формат: <IPE> = ADDR ( <IPE2> );

·        предназначен для получения адреса переменной, указанной в качестве операнда в скобках. Переменная <IPE> должна иметь тип POINTER;

Входные ограничения компилятора ЯВУ

В поставленной задаче не рассмотрены следующие моменты:

·        не описано, каким образом работать с целые десятичными переменными, а именно:

o   не указан знак числа,

o   не указана длина мантиссы.

·        не описано, каким образом представлять BASED-переменные в ассемблеровском эквиваленте программы (на выходе).

·        отсутствуют арифметические операции

По этому, для определенности, были введены следующие ограничения на языковые конструкции компилятора ЯВУ:

·        Ограничения на целые десятичные переменные:

o   имеют тип ‘P’ в ассемблеровском эквиваленте программы;

o   определяются как без знаковые, т.е. всегда положительные;

o   имеют длину 3 байта (мантисса равна 5), т.е. это числа от 0 до 99999;

o   не инициализированные десятичные переменные на ЯВУ инициализируются нулем в ассемблеровском эквиваленте программы.

·        BASED-переменные:

o   только целые десятичные

o   не имеют свое представление в памяти программы, для этого они объявляются через оператор EQU ассемблера ЕС ЭВМ.

·        Оператор присваивания кодируется в 6 байтовую команду типа SS (Storage - Storage) языка Ассемблера. Это означает, что присваивание работает без использования вспомогательных регистров.

·        Указатели объявляются как тип ‘A’ в ассемблеровском эквиваленте программы, имеют размер 4 байта (длину адреса в ЕС ЭВМ).

Описание разработанного синтаксиса языка

Синтаксис существующего макета был расширен следующим образом:

·        добавлено правило декларирования десятичных переменных с инициализацией и без

·        добавлено правило декларирования указателей и десятичных переменных, базирующихся на данных, на которые ссылается этот указатель

·        добавлено правило получения адреса существующей переменной

·        добавлено правило работы оператора присваивания

Таким образом, грамматика языка имеет следующий вид:

1. <PRO> ::= <OPR><TEL><OEN>

. <OPR> ::= <IPR>:PROC_OPTIONS(MAIN);

. <IPR> ::= <IDE>

. <IDE> ::= <BUK> | <IDE><BUK> | <IDE><CIF>

. <BUK> ::= A | B | C | D | E | M | P | X

. <CIF> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

. <TEL> ::= <ODC> | <TEL><ODC> | <TEL><OPA>

. <ODC> ::= DCL_<IPE>_BIN_FIXED(<RZR>); |_<IPE>_BIN_FIXED(<RZR>)INIT(<LIT>); |_<IPE>_DECIMAL_FIXED_INIT(<RZR>); |_<IPE>_DECIMAL_FIXED; |_<IPE>_POINTER; |_<IPE>_DECIMAL_FIXED_BASED(<IPE>);

. <IPE> ::= <IDE>

. <RZR> ::= <CIF> | <RZR><CIF>

. <LIT> ::= <MAN>B

. <MAN> ::= 1 | <MAN>0 | <MAN>1

. <OPA> ::= <IPE>=ADDR(<IPE>); |

<IPE>=<AVI>;

. <AVI> ::= <LIT> | <IPE> | <AVI><ZNK><LIT> |

<AVI><ZNK><IPE>

. <ZNK> ::= + | -

. <OEN> ::= END_<IPR>

Рис. 3 Распознавание грамматических правил в формате продукций

Преобразования в код ассемблера ЕС ЭВМ

Следуя введенным ограничениям, получаем следующий ассемблеровский эквивалент на выходе компилятора ЯВУ:

.        Переменные:

.1.     Целые десятичные переменные объявляются как тип PL3’X’ через команду DC, где Х - значение при инициализации. ‘P’ - стандартный тип десятичных переменных в ассемблере IBM 370. Литера ‘L’ означает длину числа в байтах, которая соответствует 3 байтам - фиксированное значение, оговоренное в ограничениях к компилятору ЯВУ.

.2.     Указатели объявляются как тип A через команду DS. Команда DS ассемблера IBM 370 выделяет память на 4 байта не инициализированных данных, т.к. указатели не могут быть иницилизированы, а длина 4 байта соответствует длине адреса архитектуры IBM 370.

5.3.   BASED-переменные объявляются через псевдокоманду EQU:

<имя переменной> EQU <имя указателя>. Т.к. BASED-переменные являются лишь представлением уже существующего десятичного числа через указатель, логично использование ассемблерной псевдокоманды EQU, которая не выделяет память под переменную. В дальнейшем, при компиляции с ассемблера в байт-код, будет использоваться подстановка соответствующего значения псевдокоманды EQU для соответствующей BASED-переменной.

.        Операторы:

.1.     Оператор присваивания кодируется в 6ти-байтовую SS команду MVN, имеющую формат:

MVN D1(L, B1), D2(B2)

где:

·        D1 и D2 - смещение относительно базового адреса, содержащегося в регистре общего назначения;

·        B1, B2 - регистры РОН, содержащие адрес данных;

·        L - длина операндов в байтах

Команда MVN специализирована под копирование числа из одного адреса памяти в другой. Эта SS-команда требует большее число тактов, чем RX-команда, однако упрощает выходной эквивалент программы и не требует дополнительного вмешательства для правильного копирования чисел.

.2.     Оператор ADDR кодируется в две 4ех-байтовые RX команды: LA и ST, т.к. в ассемблере IBM 370 отсутствует команда, которая бы копировала адрес переменной в соответствующее место памяти. Эти команды имеют одинаковый формат:

LA R1, D2(X2, B2)R1, D2(X2, B2)

где:

·        R1- регистр РОН;

·        D2 - смещение относительно базового адреса, содержащегося в регистре общего назначения;

·        X2 -регистр РОН, используемый в качестве индекса;

·        B2 - регистр РОН, содержащие адрес данных;

Команда LA загружает адрес переменной, указанной в качестве второго операнда в первый операнд - регистр. А команда ST заносит значение первого операнда - регистра во второй операнд - переменную. Таким образом сначала считывается адрес переменной из памяти в регистр, а затем это значение заносится в переменную-указатель.

.3.     Оператор присваивания - ‘=’. Переменные, расположенные по правую сторону от оператора кодируются в зависимости от типа:

.3.1.  Для BASED-переменных кодирование происходит в два этапа: 1) Загрузка значения указателя в регистр с помощью уже имеющейся в исходном макете команды L; 2) Загрузка значения переменной, расположенной по полученному адресу с помощью той же команды L.

.3.2.  Загрузка десятичных переменных происходит с помощью комманды L. Таким образом, если идет присвоение одной десятичной переменной другой, то сначала идет загрузка второй десятичной переменной в регистр с помощью комманды L, а затем происходит копирование памяти с помощью комманды MVN, описанной ранее.

Модификация базы данных исходного макета

В исходный код для flex был добавлен разбор новых лексем BASED, DECIMAL, ADDR и POINTER. Эти же лексемы добавлены в набор токенов для bison.

Модификация алгоритма исходного макета

Разработанные синтаксические правила были добавлены в набор правил для bison.

Были добавлены функции декларирования десятичных переменных, уакзателей и BASED-переменных.

Добавлена функция poi, которая обрабатывает лексему ADDR. Эта функция генерирует две ассемблерные команды - LA и ST.

Функция opa модифицирована для того, чтобы оператор присвоения заменялся на ассемблерную функцию MVN, если операндом является десятичное число.

В функцию avi_ipe добавлена возможность подстановки вместо слова, объявленного псевдокомандой EQU, имени переменной.

В результате был разработан компилятор ЯВУ, позволяющи справляться с поставленной задачей.

Плюсы данной реализации:

·        использование встроенных функций ассемблера для работы с числами и адресами;

·        BASED-переменные имеют представление в выходном файле, что позволяет более точно восстановить исходный код по выходному эквиваленту;

·        не используется память для BASED-переменных;

Минусы данной реализации:

·        беззнаковые десятичные числа;

·        фиксированная мантиса десятичных чисел;

·        отсутствие арифметических операций над десятичными числами;

·        выходой файл совместим только с ассемблером ЭВМ IBM 370;

Основные преимущества Flex и Bison над первой реализацией компилятора ЯВУ:

·        отсутствие таблицы проекций

·        отсутствие стека целей и достижений

·        отсутствие матрицы смежности и генерации матрицы связности

·        отсутствие 2ух проходов при разборе программы

·        обработка программы последовательно сразу лексическим и грамматическим анализатором, а не поэтапно - сначала лексическим, затем синтаксическим и потом семантическим

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