Создание компиляторов: реализация работы с процедурами с параметрами
Принцип работы компиляторов. Синтаксически-неоpиентиpованные алгоритмы, достоинства этого класса. Метод нисходящего разбора - рекурсивный спуск, суть данного метода. Преобразования программы компилятором с реализацией работы с процедурами с параметрами.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | курсовая работа |
Язык | русский |
Дата добавления | 25.03.2011 |
Размер файла | 31,6 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Размещено на http://www.allbest.ru/
Федеральное Агентство по образованию Российской Федерации
Государственное образовательное учреждение высшего профессионального образования
Курсовая работа
По курсу: «СПО»
Тема: «Создание компиляторов: реализация работы с процедурами с параметрами»
Содержание
Введение
Описание предметной области
Описание работы программы
1. Описание языка
2. Грамматика языка
3. Работа с процедурами
Заключение
Список использованных источников
Введение
В данный момент существует множество компиляторов. Компилятором называется системная программа, выполняющая преобразование программы, написанной на одном алгоритмическом языке, в программу на языке близком к машинному.
На создание компилятора с реализацией работы с процедурами с параметрами направлена данная работа.
Описание предметной области
Преобразования программы выполняется в несколько этапов (фаз), взаимосвязь между которыми отображена на рисунке 1.
Размещено на http://www.allbest.ru/
Размещено на http://www.allbest.ru/
Рисунок 1. Основные этапы (фазы) обработки программы компилятором
Лексический анализ.
Лексический анализатор представляет собой модуль разбора текста программы. Разбор происходит в зависимости от имеющихся у него в памяти терминальных символов и правилами определения типов данных. Каждый язык имеет свои ограничения по типам данных, допущения и особенности создания (строения) конструкций, применению операторов и т.п.
Лексический анализатор может работать или как самостоятельная фаза трансляции, или как подпрограмма, работающая по принципу "дай лексему". В первом случае выходом лексического анализатора является файл лексем, во втором лексема выдается при каждом обращении к лексическому анализатору (при этом, как правило, тип лексемы возвращается как значение функции "лексический анализатор", а значение передается через глобальную переменную).
В данной работе реализован второй способ реализаций лексического анализа. Синтаксический анализатор вызывает сканер каждый раз, как только ему необходима новая лексема. С точки зрения формирования значений лексем, принадлежащих классам лексем, лексический анализатор выдает значение каждой лексемы, а построение таблицы идентификаторов переносится на более поздние фазы.
В своей работе сканера используются две таблицы: таблица зарезервированных слов (терминальных символов), таблица специальных символов. Таблица зарезервированных слов хранит все ключевые слова языка, в другой таблице храниться специальные символы, используемые в языке, а также коды, соответствующие каждому символу.
Распознавание по таблицам происходит следующим образом:
Сканер в процессе анализа текста программы выделяет один из элементов текста (лексему) и сравнивает с каждым зарезервированным словом. Если такой символ найден, то сканер передает синтаксическому анализатору код лексемы и её значение. Например: если сканер находит зарезервированное слово «Program», он передаст синтаксическому анализатору - тип лексемы равной 3 и её значение «Program».
В случае если этот элемент не является зарезервированным словом, проверяется, является ли он идентификатором (первый символ обычно буква, остальные могут быть либо буквой, либо цифрой), если такой определен, то сканер передаст синтаксическому анализатору тип лексемы равной 2 и её имя.
Выходные данные, сформированные сканером, передаются на следующую стадию обработки - синтаксический анализ.
Синтаксический анализ.
Синтаксический анализ - это процесс, в котором исследуется цепочка лексем и устанавливается, удовлетворяет ли она структурным условиям, явно сформулированным в определений синтаксиса языка.
Синтаксический разбор - это построение дерева грамматического разбора. Методы грамматического разбора разбиваются на два класса восходящие и нисходящие.
Кроме этого, алгоритмы синтаксического (грамматического) разбора делят на синтаксически-ориентированные и синтаксически-неориентированные. Синтаксически-ориентированные алгоритмы являются универсальными для некоторого семейства языков и переход к распознаванию предложений другого языка выполняется путем смены грамматики, т.е. грамматика играет роль некоей "программы" распознавания предложений языка.
Синтаксически-неоpиентиpованные алгоритмы отличаются тем, что порядок действий в них определяется правилами грамматики данного конкретного языка. Достоинством этого класса алгоритмов является отсутствие избыточности, а недостатком - невозможность перенастройки на распознавание предложений другого языка.
В данной работе используются синтаксически-неориентированные алгоритмы, т.к. компилятор работает лишь с одним.
В качестве метода грамматического разбора был применён метод нисходящего разбора - рекурсивный спуск. Суть данного метода заключается в том, чтобы организовать правила так, чтобы они согласовались с процедурами синтаксического и семантического анализа, к тому же этот метод наиболее пригоден для ручного написания компилятора.
Генерация кода
После того как синтаксис программы проанализирован, последним шагом процесса компиляции является генерация объектного кода. Генерация объектного кода - это перевод компилятором внутреннего представления исходной программы в результирующую объектную программу на языке ассемблера или непосредственно на машинном языке (машинных кодах).
Генерация объектного кода выполняется после того, как выполнен синтаксический анализ программы и все необходимые действия по подготовке к генерации кода: распределено адресное пространство под функции и переменные, проверено соответствие имен и типов переменных, констант и функций в синтаксических конструкциях исходной программы и т.д.
Процедуры генераций кода встроены в тела процедур синтаксического анализа, как только распознана некоторая конструкция, синтаксическим анализатором вызывается определённая процедура генераций кода.
Описание работы программы
1. Описание языка
Основные символы языка - буквы, цифры и специальные символы - составляют его алфавит.
Компилятор распознает только:
· 26 латинских строчных и 26 латинских прописных букв: A..Z, a..z.
· 10 цифр - 0 1 2 3 4 5 6 7 8 9
· знаки операций: + - * / = < > <> <= >= :=
· ограничители: . , ` ( ) { } : ;
· символ пробела
· служебные (зарезервированные) слова и операторы:
PROGRAM, VAR, BEGIN, INTEGER, STRING, END, READ, WRITE, IF, THEN, ELSE, FOR, TO, DO, WHILE, UNTIL, REPEAT, AND, OR, NOT, PROCEDURE.
Язык включает в себя элементарные конструкции - это имена, числа и строки. На элементарные конструкций накладываются некоторые ограничения описанные ниже.
Имя (идентификатор) называет какой-либо из элементов языка (переменную, тип и т.д.) и представляет собой последовательность букв и цифр, начинающихся с буквы. Имя может содержать не более 16 символов. Не разрешается использовать в качестве имён служебные слова. Нельзя использовать пробел внутри имени и числа. По крайней мере, один пробел необходимо вставить между двумя последовательными именами.
Пример имени: m1, alfa, b, gamma, z512.
Числа записываются в десятичной системе. Они могут быть только целыми. Положительный знак числа может быть опущен. Компилятор работает только с целыми числами, которые записываются в форме без десятичной точки, например: 1, 33, 215, 5, -45, +23.
Строка - это последовательность символов, записанная между апострофами. В строке нельзя использовать знак апострофа. Пример строк: Строка` `Program`, `Привет`, `134567`.
Типы данных.
Компилятор работает только с двумя типами данных:
INTEGER - целое число.
STRING - строка. Переменная этого типа содержит последовательность символов.
Над целыми операндами можно выполнять следующие арифметические операции:
· Сложение +
· Вычитание -
· Умножение *
· Деление /
Результатом арифметической операции над целыми операндами есть величина целого типа. Результатом выполнения операций деления есть целая часть частного.
В учебном языке имеются следующие операций отношения:
· Равенство =
· Неравенство <>
· Больше или равно >=
· Меньше или равно <=
· Больше >
· Меньше <
К переменным строкового типа определены операции:
· Присваивания
· Слияния
Рассмотри операторы учебного языка.
· Для ввода исходных данных оператор ввода : READ(а1,а2, .. аi);
Оператор реализует чтение I значений исходных данных, пропуск остальных значений до начала следующей строки, присвоение считанных значений переменным a1,a2, … ai.
· Для вывода данных используется оператор: WRITE(a1,a2,…ai);
Оператор реализует вывод значений переменных.
· Условный оператор IF: If <условие> Then <оператор1> Else <оператор2>- оператор условия, предназначен для выполнения операторов, в зависимости от истинности условия. Если <условие> - "истина", то выполняется <оператор1>, иначе выполняется <оператор2>, стоящий после Else (Else и <оператор2> могут опускаться, в этом случае при значении <условие> - "ложь", программа продолжит выполнение, минуя <оператор1>). <Условие> может состоять из последовательности простых условий, связанных логическими операциями AND (логическое И) и OR (логическое ИЛИ).
If a>0 Then b=3
Если значение переменной a больше 0, то b присваивается значение 3.
· Оператор цикла с предусловием. While <условие> Do <оператор>. <Оператор> будет выполнятся до тех пор, пока <условие> истинно. Так же как и в предыдущем примере, <условие> может состоять из последовательности простых условий, связанных операторами AND и OR.
While a<100 do a = a+1
Операция a = a +1 будет выполнятся до тех пор, пока значение переменной a будет меньше 100.
· Цикл с постусловием. Repeat <оператор> Until <условие>. <Оператор> будет выполнятся до тех, пор пока <условие> не примет значение "истина".
Repeat a=a+1 Until a>100
Выполнение оператора а = а+1 закончится, когда значение переменной a будет больше 100.
· Цикл с параметром. For <переменная> := <выражение1> To <выражение2> Do <оператор> - оператор цикла с параметром. <Оператор> выполняется заданное количество итераций. <Переменная> - счетчик, начальное значение которого задается <выражением1>, <выражение2> задает конечное значение счетчика.
For i=1 To 5 Do Read ( I );
После выполнения этого примера получится вот такой результат:
1
2
3
4
5,т.е. выводятся все числа от 1 до 5.
· Составной оператор. Begin End - составной оператор. В приведенных выше примерах вместо <оператор> может быть на только один оператор, но и целая группа операторов, заключенная в операторные скобки. Роль операторных скобок в языке выполняют два ключевых слова - Begin и End.
Begin
<оператор 1>
<оператор 2>
...
<оператор n>
End
2. Грамматика языка
<prog> ::= PROGRAM <prog-name> VAR <dec-list> <proc-list> BEGIN <stmt-list> END.
<prog-name> ::= id ;
<dec-list> ::= <dec> { ; <dec> } ;
<dec> ::= <id-list> : <type>
<type> ::= INTEGER | STRING
<id-list> ::= id { , id }
<proc-list> ::= <proc>{;<proc>}
<proc> ::= PROCEDURE <proc-name>(<dec-list>) BEGIN <stmt-list> END;
<proc-name> ::=id
<stmt-list> ::= <stmt> { ; <stmt> } ;
<stmt> ::= <assign> | <for> | <read> | <write> | <while> | <repeat> | <if>
<assign> ::= id := <exp>
<exp> ::= - <term> { + <term> | - <term> }
<term> ::= <factor> { * <factor> | / <factor> }
<factor> ::= id | int | (<b-exp>)
<b-exp> ::= <b-term> { OR <b-term>}
<b-term> ::= <not-factor> {AND <not-factor>}
<not-factor> ::= NOT <b-factor>
<b-factor> ::= id | int | (<exp>)
<read> ::= READ ( <id-list> )
<write> ::= WRITE ( <value> { , <value>} )
<for> ::= FOR <index-exp> DO <body>
<index-exp> ::= id := <exp> TO <exp>
<body> ::= <stmt> | BEGIN <stmt-list> END
<value> ::= <id-list> | <text-val> , <id-list>
<text-val> ::= ? <text> ?
<text> ::= символы
<if> ::= IF <condition> THEN <body> ELSE <body>
<condition> ::= <сравнение> | (<сравнение>) AND (<сравнение>) | (<сравнение>) OR (<сравнение>)
<сравнение>::= <factor> <условие> <factor>
<условие> ::= > | < | = | >= | <= | <>
<while> ::= WHILE <condition> DO <body>
<repeat> ::= REPEAT <body> UNTIL <condition>
3 Работа с процедурами
Процедуры обеспечивают возможность практической реализации принципов структурного программирования. Ориентируясь на эти принципы, можно разделить большую программу на несколько небольших, оформив как процедуры или функции.
При создании процедур и функции преследуют следующие цели:
1. разделение некоторой общей задачи на несколько частных задач, меньших по объему и менее сложных;
2. уменьшение объема программы за счет оформления многократно повторяющихся действий в виде отдельной процедуры или функции;
3. создание универсальных программных модулей - обобщения полученного решения частной задачи с целью его использования для решения других задач.
Для реализации работы с процедурами с параметрами были использованы следующие переменные и процедуры:
1. pere:array[1..10] of array[1..2,1..10] of string[8]; в этом массиве хранится название процедур, входные параметры и их тип;
2. Num:integer; переменная, которая используется в роли счётчика, указывающего на номер текущей процедуры в массиве pere;
3. DoProcedure - эта процедура разбирает правило
<proc> ::= PROCEDURE <proc-name>(<dec-list>) BEGIN <stmt-list> END;
и генерирует код процедуры.
procedure DoProcedure;
var x,y,i,j,x1,y1:integer;
Name:string;
begin
i:=0;
if token = 23 then
begin
CloseFileOut;
OpenFileOut('Temp.tmp');
while token = 23 do
begin
i:=i+1;
num:=i;
if i>10 then Expected('Ошибка в строке ' + inttostr(LineError));
y1:=1;
x1:=0;
Scan;
pere[i][1,y1]:=value;
y1:=y1+2;
VstId;
Emitln(value+' proc');
Name:=value;
token:=23;
IdList1;
Scan;
if value = '(' then
while value<>')' do
begin
Emit('LOCAL ');
value:=',';
while value=',' do
begin
Scan;
pere[i][1,y1]:=value;
Emit(value+'_');
Scan;
if value = ':' then
begin
Scan;
if (Token<>9) and (Token<>13) then Expected('Ошибка в строке ' + inttostr (LineError)+': Type');{???}
pere[i][2,y1]:=value;
EmitLn(':WORD');
{???}
y1:=y1+1;
end
else Expected('Ошибка в строке ' + inttostr (LineError)+': Type');
x1:=x1+1;
Scan;
end;
if value<>')' then Expected('Ошибка в строке ' + inttostr (LineError)+': Type');
end;
Scan;
if Value<>';' then Expected('Ошибка в строке ' + inttostr (LineError)+': Type');
Scan;
if token <> 8 then Expected('Ошибка в строке ' + inttostr(LineError)+': BEGIN ');
check:=1;
pere[i][1,2]:=IntToStr(x1);
for j:=3 to 2+x1 do
{EmitLn(' pop _'+pere[num][1,j]);}
EmitLn(' push bp');
EmitLn('mov bp, sp');
for j:=3 to 2+x1 do
begin
Emit(' mov AX, [BP+');Emit(IntToStr(4+(j-3)*2));Emitln(']');
EmitLn(' mov '+pere[num][1,j]+'_ ,AX');
end;
StmtList;
if token <> 10 then Expected('Ошибка в строке ' + inttostr(LineError)+': END ');
EmitLn(' mov SP, BP');
EmitLn(' pop BP');
EmitLn(' ret');
EmitLn(Name+' endp');
Scan;
if value <> ';' then Expected('Ошибка в строке ' + inttostr(LineError)+': ; ');
Scan;
end;
CloseFileOut;
Assign(output,'Out.asm');
Append(output);
writeln(output);
Check:=0;
end;
end;
В этой процедуре были использованы следующие переменные: счетчики x,y,i,j,x1,y1 и переменная Name строкового типа для передачи имени процедуры.
DoProcedure выполняет следующие действия указанные на рисунке 2.
Рисунок 2. основные блоки процедуры DoProcedure
В первом блоке выполняется закрытие файла Out.asm и открытие файла Temp.tmp - это делается для того, что бы в последствии из файла Temp.tmp скопировать текст процедуры в файл Out.asm.
Во втором блоке выполняется фиксирование имени процедуры и её параметров, которые записываются в массив pere.
В третьем блоке генерации тела процедуры на язык ассемблер выполняется непосредственная генерация кода по описанным правилам.
В четвертом блоке закрывается файл Temp.tmp и открывается Out.asm, позиция устанавливается в конец файла.
В языке ассемблер для передачи параметров используется директива LOCAL.
LOCAL аргумент [,аргумент] . [=идентификатор]
Отдельные аргументы имеют следующий синтаксис:
имя_аргумента [[выражение_счетчик_1]]
[: сложный_тип [:выражение_счетчик_2]]
где "сложный_тип" - это тип данных аргумента. Он может быть либо простым типом, либо сложным выражением-указателем.
Значения объявленным локальным переменным передаются через стек.
4. процедура copyf копирует данные из файла Temp.tmp в файл Out.asm
procedure copyf;
var
s:string;
i:integer;
begin
Assign(output,'temp.tmp');
reset(output);
assign(f,'out.asm');
append(f);
while not EOF(output) do
begin
readln(output,s);
writeln(f,s);
end;
writeln(f,'end main');
erase(output);
close(f);
end;
5. процедура Assign осуществляющая разбор правила:<exp> ::= id:= <exp>
procedure Assignn;
var
i,u,r:integer;
begin
u:=2;
r:=1;
Name := Value; {Присваиваем имя идентификатора переменной NAME}
IdUnknown; {Проверяем описан ли идентификатор}
If TypeId = '3' then
begin
for i:=1 to 10 do
begin
if pere[i][1,1]=value then
begin
EmitLn(' pusha');
Scan;
if value <> '(' then Expected('Ошибка в строке ' + inttostr(LineError)+': (');
While value <> ')' do
begin
value:=',';
while value = ',' do
begin
u:=u+1;
Scan;
EmitLn('push _'+value);
temp[r]:=value;
if token <> 2 then Expected('Ошибка в строке ' + inttostr(LineError));
IdUnknown;
if typeId='1' then if pere[i][2,u]<>'INTEGER' then
Expected('Ошибка в строке ' + inttostr(LineError));
if typeid='2'then if pere[i][2,u]<>'STRING' then
Expected('Ошибка в строке ' + inttostr(LineError));
Scan;
end;
end;
Scan;
Emitln('CALL '+pere[num][1,1]);
EmitLn(' popa');
break;
end;
if i=10 then Expected('Ошибка в строке ' + inttostr(LineError));
end;
end
else
begin
Scan; {Запрос лексемы}
{Проверяем является ли лексема символом :=}
if Value <> ':=' then Expected('Ошибка в строке ' + inttostr(LineError)+': :=');
Scan; {Запрос лексемы}
if TypeId='1' then
begin
BoolExpression;
CodeAssign(Name); {процедура генерации кода}
end;
if TypeId='2' then
begin
StrExpression;
CodeAssign1(Name); {процедура генерации кода}
end;
Процедура используется при вызове в теле программы. Она проверяет, существует ли процедура и выдает ошибку, в случае если процедуре будет передаваться параметров больше, чем описано, либо если эти параметры будут иметь не верный тип. В процедуре ASSIGN использовались переменные-счётчики - i, u, r целого типа.
6. Процедура IdList1 вставляет в таблицу идентификаторов IdTable тип идентификатора.
7.
Procedure IdList1;
var i,j:integer;
begin
{Если тип равен INTEGER}
if token=9 then begin
for i:=1 to 210 do begin
if IdTable[2,i] = '25' then begin IdTable[2,i]:='1'; {Вставляем тип идентификатора}
Emitln('_' + IdTable[1,i] +' DW ?');
end;
end;
end;
{Если тип равен STRING}
if token=13 then begin
for i:=1 to 210 do begin
if IdTable[2,i] = '25' then begin IdTable[2,i]:='2'; {Вставляем тип идентификатора}
Emitln('_' + IdTable[1,i] +' DB 256 dup (?)');
end;
end;
end;
{Если тип равен PROCEDURE}
f token=23 then begin
for i:=1 to 210 do begin
if IdTable[2,i] = '25' then IdTable[2,i]:='3'; {Вставляем тип идентификатора}
end;
end;
В таблице IdTable хранятся все переменные и их типы, а также названия процедур. Процедура записывает в таблицу IdTable тип. Генерирует код, если тип не равен 3.
компилятор процедура параметр алгоритм
Заключение
В данной работе была в полном объёме выполнена поставленная задача написание компилятора с реализацией работы с процедурами с параметрами. В ходе выполнения задачи было изучен принцип работы компиляторов, что позволило беспроблемно решить поставленную задачу.
Список использованных источников
Бек Л. Введение в системное программирование.: Пер. с англ. - М.: Мир, 1998.
Грис Д. Конструирование компиляторов для цифровых вычислительных машин. - М.: Мир, 1975.
Карпов В.Э. Классическая теория компиляторов. - http://itlab.net.ru/ materials/compiler/compiler.html
Размещено на Allbest.ru
Подобные документы
Описание компиляторов языка С/С++: MinGW, Borland Builder, Watcom, Intel C++, Visual. Сравнение характеристик выполнения программ на примере простого кода. Проведение тестов для компиляторов, оценка скорости выполнения и компилирования, их опций.
курсовая работа [1,6 M], добавлен 05.10.2012Составные части, основные принципы построения и функционирования компиляторов. Практическое освоение методов разработки их составных частей. Этапы и особенности создания программы для выполнения лексического анализа входного текста по заданной грамматике.
курсовая работа [294,0 K], добавлен 04.11.2014Компиляторы - инструменты для решения вычислительных задач с использованием бинарного кода. Проблема выбора "правильного" компилятора. Применение компиляторов языка С++. Оценка MinGW, Borland Builder, Intel C++ Professional Edition, Watcom и Visual Studi.
контрольная работа [4,5 M], добавлен 05.10.2012Сведения о интерполяторе. Принцип работы схемы сложения и вычитания единицы. Выбор элементной базы. Расчет эквивалентной передаточной функции схемы. Создание программы в коде ISO 7bit и ее реализация на низкоуровневом языке программирования Ассемблер.
курсовая работа [363,2 K], добавлен 15.04.2014Виртуальная функция как метод класса, который может быть переопределён в классах-наследниках так, что конкретная реализация метода для вызова будет определяться во время исполнения. Порядок разработки программы обработки массивов, работы со строками.
контрольная работа [847,3 K], добавлен 19.03.2012Принцип работы транслятора. Исследование формата данных объектного файла шестнадцатиразрядной системы DOS для последующего преобразования его в файл программы. Используемые директивы и команды ассемблера. Алгоритмы программы и таблицы компилятора.
контрольная работа [35,9 K], добавлен 07.07.2012Доэлектронный период создания механических счетных устройств. Появление первых электронных машин и их недостатки. Начало коммерческого применения ЭВМ для обработки данных. Разработка программного обеспечения, компиляторов. Принципы работы современных ЭВМ.
презентация [226,9 K], добавлен 19.12.2014Особенности работы с процедурами и двумерными массивами, последовательность вызова процедур. Способы описания и использования многомерных массивов, назначение процедур, их описание и обращение к ним. Набор программы, ее отладка и тестирование данных.
лабораторная работа [112,1 K], добавлен 03.10.2010Методы решения задач линейного программирования. Вектор коэффициентов целевой функции. Простой однооконный интерфейс с набором всех необходимых инструментов для работы с программой. Структура программного модуля. Автоматический режим работы программы.
контрольная работа [1,6 M], добавлен 11.06.2011Изучение этапов возникновения компьютерных операционных систем. Особенности их прикладного программного интерфейса и конфигурации. Характеристика набора вспомогательных программ - редакторов, компиляторов, программ работы с файлами (системные утилиты).
презентация [98,0 K], добавлен 29.05.2010