Смекни!
smekni.com

Разработка программы-компилятора (стр. 4 из 9)

WriteCode (NumLex,Lexem,'T',NumTerm)

end else WriteCode (NumLex,Lexem,'T',Termyes) // если лексема найдена

end;

'O': begin // если лексема-знак операция

NumLex: =NumLex+1;

Search_Term (1,Lexem);

if Termyes=0 then // если лексема не найдена

begin

NumTerm: =NumTerm+1;

Add_Term (1,Lexem);

Term_tab [Numterm]. razd: =0;

Term_tab [Numterm]. oper: =1;

Term_tab [Numterm]. slug: =0;

WriteCode (NumLex,Lexem,'T',NumTerm)

end else WriteCode (NumLex,Lexem,'T',Termyes) // есди лексема найдена

end;

end;

end;

procedure TForm1. N5Click (Sender: TObject);

var i,pip: integer;

begin

for k: =1 to numid do // обнуление таблицы идентификаторов

begin

id_tab [k]. lex: ='0';

id_tab [k]. nomer: =0;

id_tab [i]. ssylka: =0;

end;

for i: =1 to numlex do // обнуление выходной таблицы

begin

Code_Tab [i]. Lex: ='';

Code_Tab [i]. typ: =#0;

Code_Tab [i]. Num: =0;

Code_Tab [i]. nomer: =0;

end;

for i: =0 to numconst do // обнуление таблицы констант

begin

Const_tab [i]. nomer: =0;

Const_tab [i]. value: ='';

Const_tab [i]. Typ: ='';

Const_tab [i]. Width: ='';

Const_tab [i]. Val10: ='';

Const_tab [k]. Left: =0;

Const_tab [k]. Right: =0;

Const_tab [k]. Way: ='';

end;

for i: =1 to numterm do

begin

Term_tab [i]. nomer: =0;

Term_tab [i]. Lex: ='';

Term_tab [i]. razd: =0;

Term_tab [i]. oper: =0;

Term_tab [i]. slug: =0;

Term_tab [k]. Left: =0;

Term_tab [k]. Right: =0;

Term_tab [k]. Way: ='';

end;

// инициализация

NumLex: =0; NumId: =0; NumConst: =0; NumErr: =0; NumTerm: =0;

Error: =false; Found: =false;

i: =0; j: =0; k: =0; y: =0;

String_counter: =0;

Memo2. Lines. Clear;

N6. Enabled: =true;

while string_counter<=Memo1. Lines. Count do // цикл по строкам файла

begin

n: =1;

m: =1;

s: =Form1. Memo1. Lines. Strings [string_counter] ;

for l: =1 to 2 do

while m<=Length (s) do // цикл по строке

begin

n: =m;

m: =Select_Lex (s,Lexem,n);

if (Lexem<>'') and not (Lexem [1] in [#0. #32]) then

begin

if FConst (Lexem) =1 then WriteLex ('C') else // вызов процедуры записи

if Termin=3 then WriteLex ('K') else

if Rome (Lexem) =1 then WriteLex ('M') else

if Ident (Lexem) =1 then WriteLex ('I') else

if Termin=1 then WriteLex ('R') else

if Termin=2 then WriteLex ('O')

else Err_lex;

end;

end;

string_counter: =string_counter+1;

end;

vyvod; // вызов процедуры вывода

end;

procedure TForm1. vyvod; // Вывод результатов

var

f: textfile; // выходной файл

begin

StringGrid1. RowCount: =NumConst+1; // определение числа строк в таблицах

StringGrid2. RowCount: =NumId+1;

StringGrid3. RowCount: =NumTerm+1;

StringGrid4. RowCount: =NumLex+1;

StringGrid1. Cells [0,0]: ='№'; StringGrid1. Cells [1,0]: ='Константа'; StringGrid1. Cells [2,0]: ='Тип';

StringGrid1. Cells [3,0]: ='Ширина'; StringGrid1. Cells [4,0]: ='10-тичный формат';

StringGrid1. Cells [5,0]: ='L'; StringGrid1. Cells [6,0]: ='R';

StringGrid1. Cells [7,0]: ='Путь'; // определение заголовков

for k: =1 to NumConst do // вывод таблицы констант

begin

StringGrid1. cells [0,k]: = Inttostr (Const_Tab [k]. nomer);

StringGrid1. cells [1,k]: = Const_Tab [k]. value;

StringGrid1. cells [2,k]: = Const_Tab [k]. Typ;

StringGrid1. cells [3,k]: = Const_Tab [k]. Width;

StringGrid1. cells [4,k]: = Const_Tab [k]. Val10;

StringGrid1. cells [5,k]: = Inttostr (Const_Tab [k]. Left);

StringGrid1. cells [6,k]: = Inttostr (Const_Tab [k]. Right);

StringGrid1. cells [7,k]: = Const_Tab [k]. Way;

end;

AssignFile (F,'Const. txt'); // запись в файл таблицы констант

Rewrite (F);

for k: =1 to NumConst do

Writeln (F, StringGrid1. cells [0,k] +' '+StringGrid1. cells [1,k] +' '+StringGrid1. cells [2,k] +' '+StringGrid1. cells [3,k]);

CloseFile (F);

StringGrid2. Cells [0,0]: ='№'; StringGrid2. Cells [1,0]: ='Имя'; // определение заголовков

k: =0;

k1: =0;

while k<numid do // вывод таблицы идентификаторов

begin

if Id_tab [k1]. lex<>'' then

begin

StringGrid2. cells [0,k+1]: =IntToStr (Id_tab [k1]. nomer);

StringGrid2. cells [1,k+1]: =Id_Tab [k1]. lex;

k: =k+1;

end;

k1: =k1+1;

end;

AssignFile (F,'Ident. txt'); // запись в файл таблицы констант

Rewrite (F);

for k: =1 to NumId do Writeln (F, StringGrid2. cells [0,k] +' '+StringGrid2. cells [1,k]);

CloseFile (F);

StringGrid3. Cells [0,0]: ='№'; StringGrid3. Cells [1,0]: ='Символ'; StringGrid3. Cells [2,0]: ='Раздел. ';

StringGrid3. Cells [3,0]: ='Зн. операции'; StringGrid3. Cells [4,0]: ='Ключ. слово';

StringGrid3. Cells [5,0]: ='L'; StringGrid3. Cells [6,0]: ='R';

StringGrid3. Cells [7,0]: ='Путь'; // определение заголовков

for k: =1 to NumTerm do // вывод таблицы терминальных символов

begin

StringGrid3. cells [0,k]: = Inttostr (Term_Tab [k]. nomer);

StringGrid3. cells [1,k]: = Term_Tab [k]. lex;

StringGrid3. cells [2,k]: = Inttostr (Term_Tab [k]. razd);

StringGrid3. cells [3,k]: = Inttostr (Term_Tab [k]. oper);

StringGrid3. cells [4,k]: = Inttostr (Term_Tab [k]. slug);

StringGrid3. cells [5,k]: = Inttostr (Term_Tab [k]. Left);

StringGrid3. cells [6,k]: = Inttostr (Term_Tab [k]. Right);

StringGrid3. cells [7,k]: = Term_Tab [k]. Way;

end;

AssignFile (F,'Term. txt'); // запись в файл таблицы терминальных символов

Rewrite (F);

for k: =1 to NumTerm do Writeln (F, StringGrid3. cells [0,k] +' '+StringGrid3. cells [1,k] +' '+StringGrid3. cells [2,k] +' '+StringGrid3. cells [3,k] +' '+StringGrid3. cells [4,k]);

CloseFile (F);

StringGrid4. Cells [0,0]: ='№'; StringGrid4. Cells [1,0]: ='Тип'; StringGrid4. Cells [2,0]: ='№ в таблице'; StringGrid4. Cells [3,0]: ='Лексема'; // определение заголовков

for k: =1 to NumLex do // вывод таблицы кодов лексем

begin

StringGrid4. cells [0,k]: = Inttostr (Code_Tab [k]. nomer);

StringGrid4. cells [1,k]: = Code_Tab [k]. typ;

StringGrid4. cells [2,k]: = Inttostr (Code_Tab [k]. num);

StringGrid4. cells [3,k]: = Code_Tab [k]. lex;

end;

AssignFile (F,'Cod. txt'); // запись в файл выходной таблицы

Rewrite (F);

for k: =1 to NumLex do Writeln (F, StringGrid4. cells [0,k] +' '+StringGrid4. cells [1,k] +' '+StringGrid4. cells [2,k] +' '+StringGrid4. cells [3,k]);

CloseFile (F);

end;

procedure TForm1. Err_Lex; // процедура вывода ошибки в лексеме

begin

Memo2. Lines. Add ('В строке №'+Inttostr (String_counter+1) +' ошибочная лексема '+Lexem);

NumErr: =NumErr+1;

NumLex: =NumLex+1;

Code_Tab [NumLex]. nomer: =NumLex;

Code_Tab [NumLex]. Lex: =Lexem;

Code_Tab [NumLex]. typ: ='E';

Code_Tab [NumLex]. Num: =NumErr;

Exit;

end;

2.4.4 Тестирование лексического анализатора

Текст программы не содержит ошибок:

program var15;

var n: integer;

begin

n: =$+00;

repeat

n: =n- (-XII);

until n<$-0A;

end.

Результат - таблицы констант, идентификаторов, терминальных символов и кодов лексем (см. рис.5, б) и отсутствие сообщениий об ошибках (см. рис.5, а).

рис.5, а.


рис.5, б

рис.5. Результаты тестирования программы, не содержащей ошибок.

Текст программы содержит ошибочные лексемы var% и $+MN.

program var15;

var% n: integer;

begin

n: =$+MN;

repeat

n: =n- (-XII);

until n<$-0A;

end.

Результат - в таблицу кодов лексем эти лексемы занесены с типом Е, что означает, что они ошибочны (см. Рис.6, а, б), программа выдала также сообщения об ошибках (Рис.6, в).


Рис.6, а

Рис.6, б

Рис.6, в

Рис.6. Результаты тестирования программы, содержащей ошибочные лексемы.

3. Разработка синтаксического анализатора

3.1 Уточнение грамматики языка применительно к варианту задания

Синтаксический анализ производится методом рекурсивного спуска.

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

Правила синтаксического анализа относятся к грамматике вида LL (1), т.е. используется левосторонний просмотр и левосторонний вывод, при этом необходимо просматривать не более 1 символа.

Множество правил грамматики реализуемого языка, записанных в форме Бэкуса-Наура, имеет следующий вид:

1. <программа>→program<имя программы>;

var<список описаний>

begin<список операторов>end.

2. <имя программы>→ИМЯ

3. <список описаний>→<описание>; {<описание>; }

4. <описание>→<список имён>: <тип>

5. <тип>→real

6. <список имён>→ИМЯ{, ИМЯ}

7. <список операторов>→<оператор>; {<оператор>; }

8. <оператор>→<присваивание> | <цикл>

9. <присваивание>→ИМЯ: =<выражение>

10. <выражение>→<простое выражение>{ (=, <, <>, >, >=, <=) <простое выражение>}

11. <простое выражение>→<терм>{+<терм> | - <терм>}

12. <терм>→<множитель>{*<множитель> |/<множитель>}

13. <множитель>→ИМЯ | КОНСТАНТА | <простое выражение>

14. <цикл>→repeat<тело цикла>until<выражение>

15. <тело цикла>→<оператор>|<составной оператор>

16. <составной оператор>→begin<список операторов>end

В грамматике, помимо общепринятых, используются следующие терминальные символы: ИМЯ - идентификатор; КОНСТАНТА - 16-ричная или римская константа.

3.2 Разработка алгоритма синтаксического анализа

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

Далее рассматриваются алгоритмы отдельных функций распознавания. Общий метод их построения заключается в следующем: изначально значение функции устанавливается в FALSE. Далее происходит поиск символов входящих в распознаваемый нетерминал. Если правило содержит другой нетерминальный символ, то происходит вызов соответствующей функции. Если же необходимо проверить наличие терминального символа, то функция сама выполняет запрос на чтение следующей лексемы и сравнивает ее с той, которая должна присутствовать в конструкции. Чтение следующей лексемы состоит в выборе следующего элемента из таблицы кодов лексем, т.е. в увеличении номера текущего элемента на 1 (в блок-схеме будет обозначаться как ЧтСл). Если происходит ошибка, то выполнение функции прекращается с вызовом процедуры вывода сообщения об ошибке (в блок-схеме будет обозначаться как Ошибка). Причем при выполнении анализа такое сообщение выдается один раз, иначе следующие сообщения могут иметь недостоверную информацию. Сообщение содержит номер строки и описание обнаруженной ошибки. Если ошибок не обнаружено, то в конце работы функции ее результат становится TRUE.