Лексический анализ
Сначала составим программу на языке Си, которая предназначена только лишь для распознавания лексем в тексте программы на языке SPL, а также для размещения идентификаторов в специальной таблице TNM. Эта таблица представляет собой глобальный одномерный массив char TNM[400]. В процессе лексического анализа распознаются лексемы: · служебные слова; · знаки операций и разделители; · целые числа; · идентификаторы; · признак конца файла. В программе каждой лексеме ставится в соответствие целое число. Знаки операций и разделители закодированы литеральными константами ‘+’, ‘-‘, ‘*’, ‘/’, ‘%’, ‘=’, ‘(‘, ‘)’, ‘, ’, ‘; ’. Каждой из них соответствует целое число из таблицы кодов ASCII (М. Уэйт, С. Прата, Д. Мартин. Язык Си.- М.: Изд-во “Мир”, 1988.- 512 с.). В таблице кодов ASCII также закодированы целыми числами символы букв и цифр. Обычно в этой таблице приводятся коды 256 символов. Для служебных слов имена лексем выбирает программист. Предлагается эти лексемы обозначать соответствующими заглавными буквами с буквой L в конце. Например: служебными словами if и while имена лексем соответственно IFL, WHILEL и т.д. А связь с целыми числами определяется с помощью перечисляемых констант целого типа. enum {BEGINL=257, ENDL, READL, PRITL, RETRL, IFL, THENL, WHILEL, DOL, INTL, CONSTL, NUMB, IDEN },
где NUMB – лексема для целого числа; IDEN – лексема для идентификатора. Благодаря применению enum {}, лексема BEGINL получила числовое значение 257, ENDL – 258 и т.д. Таким образом, при написании программы нет необходимости помнить, что лексема BEGINL закодирована числом 257, а ENDL – 258. Везде, где нужно сослаться на эти лексемы, удобнее писать BEGINL, ENDL и т.д., а вместо них будут представляться числа 257, 258 и т.д. Кроме перечисленных лексем, также используется признак конца файла EOF. В файле stdio.h есть директива препроцессора #define EOF – 1. Из нее видно, что EOF закодирован – 1. Текст программы на Си для лексического анализа программы на SPL приведен ниже. Целесообразно привести некоторые пояснения к этой программе. В ней используются глобальные переменные и приведенные выше в enum {} перечисленные константы: int lex; //распознанная лексема (целое число); int lval; // значение лексемы. Для константы – это числовое значение, а для идентификатора – адрес в таблице идентификаторов TNM; int nst=0; // номер считываемой строки в программе на языке SPL; char nch=’\n’; // считываемый символ из программы на SPL; char TNM [400]; // таблица идентификаторов; char * ptn=TNM; // указатель на первый свободный элемент в таблице идентификаторов. В самом начале он стоит на TNM [0], т.к. имя массива TNM в языке Си является адресом его самого первого (т.е. нулевого) элемента; FILE * PF; // указатель на файл с текстом программы на SPL; FILE * padres // указатель на файл, в который будут заноситься адреса идентификаторов в таблице TNM. В программе лексического анализа при вызове функции main () используются параметры. Заголовок функции main () имеет вид: void main (int av, char * av [ ]). Здесь ac – количество параметров (символьных строк); char* av [ ] – массив показателей. Конкретно, в av [0] автоматически заносятся имя файла с готовой для исполнения программой (например, part1.exe). В av [1] следует занести самостоятельно имя файла с текстом программы на языке SPL. Например, var2.s Для этого необходимо, находясь в среде Borland C++, вызвать в главном меню Run/arguments и в появившееся диалоговое окно ввести требуемое имя файла на SPL. В данном примере – это var2.s. Предполагается, что var2.s находится в текущей директории. Чтобы обеспечить обращение к этому файлу при запуске Borland C++ из другой директории, надо ввести полный путь, например, C: \\USER\\SPL\\var2.s part1.s ¿. Для лучшего понимания работы лексического анализа part1.c предлагается рассмотреть блок-схемы отдельных его функций. Вначале проверяется количество параметров ac. Должно быть не меньше двух, например, part1.exe в av[0] и var2.s в av [1]. Если параметров меньше двух, происходит остановка работы программы. Иначе открывается файл для чтения с текстом на SPL. В примере это – var2.s и его имя находится в av [1]. Если файл открылся, то происходит вызов функции get (), которая является “поставщиком” лексем.
4. 1 Блок-схема функции void main (int ac, char * av [ ])
В функции get () блок №1 в интерпретаторе должен быть if(nch==EOF) { lex=EOF; .-.-.-.-.-.-.-.- return; } Однако программа part1.c предназначена только лишь для того, чтобы прочитать текст программы на SPL, распознать лексемы, а идентификаторы занести в специальную таблицу идентификаторов char TNM [400]. Поэтому в данной программе в функции get () блок №1 являет собой цикл while (nch! =EOF). Он позволяет прочитать весь текст программы на SPL до конца файла. Далее идет цикл while (isspace(nch)), позволяющий пропускать символы пробела, табуляции, перехода на новую строку и на новую страницу. Если символ nch, прочитанный из файла с помощью функции get (), не является одним из перечисленных выше, то происходит его распознавание с вызовом функции number() или word(). Если nch является одним из перечисленных специальных символов, например, ‘(’, ‘)’, ‘+’ и т.д., то лексема получает соответствующее значение, считывается новый символ и происходит возврат в цикл (блок №1). В противном случае выдается сообщение о недопустимом символе и происходит выход из программы.
|