3468
.pdfЛАБОРАТОРНАЯ РАБОТА № 11. РАЗРАБОТКА КОМПИЛЯТОРОВ. ПРОЕКТИРОВАНИЕ ЛЕКСИЧЕСКОГО АНАЛИЗАТОРА
Цель работы: изучение основных понятий теории регулярных грамматик, ознакомление с назначением и принципами работы лексических анализаторов (сканеров), получение практических навыков построения сканера на примере заданного простейшего входного языка.
Для выполнения лабораторной работы требуется написать программу, выделяющую лексемы языка программирования и строящую таблицу идентификаторов.
Краткие теоретические сведения
Лексема (лексическая единица языка) - это структурная единица языка, которая состоит из элементарных символов языка и не содержит в своем составе других структурных единиц языка.
Пример разбора предложения на лексемы представлен на рис. 86.
Рис. 86. Пример разбора предложения на лексемы
211
Лексемами языков программирования являются:
–идентификаторы,
–константы,
–ключевые слова языка,
–знаки операций и т. п.
Состав лексем каждого языка программирования определяется синтаксисом этого языка.
Лексический анализатор (или сканер) - это часть ком-
пилятора, которая читает исходную программу и выделяет в ее тексте лексемы входного языка (рис 87).
Рис. 87. Работа ЛА
Причины включения ЛА в компилятор
1.Упрощает работу с текстом исходной программы на этапе синтаксического разбора и сокращает объем обрабатываемой информации, так как ЛА:
– структурирует исходный текст;
– отбрасывает всю незначащую информацию.
2.Для выделения в тексте и разбора лексем возможно применять простую, эффективную и теоретически хорошо проработанную технику анализа (на этапе СА конструкций исходного языка используются достаточно сложные алгоритмы разбора).
212
3. Сканер отделяет сложный по конструкции синтаксический анализатор от работы непосредственно с текстом исходной программы, структура которого может варьироваться в зависимости от версии входного языка (для перехода от одной версии языка к другой достаточно только перестроить относительно простой ЛА).
ЛА исключает из текста:
комментарии;
незначащие пробелы;
символы табуляции и перевода строки.
ЛА выделяет лексемы:
идентификаторы;
строковые, символьные и числовые константы;
ключевые (служебные) слова входного языка;
знаки операций и разделители.
Результатом работы лексического анализатора является таблица лексем, в которой представлена следующая информация (рис. 88).
|
уникальный условный код, |
дополнительная |
лексема |
зависящий от типа |
служебная |
|
лексемы |
информация |
Рис. 88. Пример информации в таблице лексем
Информация о некоторых типах лексем, найденных в исходной программе, должна помещаться в таблицу идентифи-
каторов.
Таблица лексем и таблица идентификаторов - это две принципиально разные таблицы, обрабатываемые ЛА.
Информация о некоторых типах лексем, найденных в исходной программе, должна помещаться в таблицу идентифи-
каторов.
213
Таблица лексем и таблица идентификаторов - это две принципиально разные таблицы, обрабатываемые ЛА.
Лексемы в таблице лексем обязательно располагаются в том же порядке, как и в исходной программе (порядок лексем
вней не меняется).
Втаблице идентификаторов лексемы располагаются в любом порядке так, чтобы обеспечить удобство поиска.
Определение границ лексем
Втерминах ЛА - это выделение тех строк в общем потоке входных символов, для которых надо выполнять распознавание.
Пример оператора на языке С
k = i+++++j;
Существует только одна единственно верная трактовка этого оператора:
k = i++ + ++j; k = (i++) + (++j);
Неверные варианты могут быть обнаружены только на этапе семантического анализа
вариант:
k = (i++)++ + j;
является синтаксически правильным, но семантикой языка С не допускается.
Пример фрагмента текста программы на языке Паскаль и соответствующей ему таблицы лексем:
...
begin
for i:=1 to N do
fg := fg * 0.5;
...
214
|
Лексемы программы |
Таблица 10 |
|
|
|
Лексема |
Тип лексемы |
Значение |
begin |
Ключевое слово |
X1 |
for |
Ключевое слово |
X2 |
i |
Идентификатор |
i : 1 |
:= |
Знак присваивания |
S1 |
1 |
Целочисленная константа |
1 |
to |
Ключевое слово |
X3 |
N |
Идентификатор |
N : 2 |
do |
Ключевое слово |
X4 |
fg |
Идентификатор |
fg : 3 |
:= |
Знак присваивания |
S1 |
fg |
Идентификатор |
fg : 3 |
* |
Знак арифметической операции |
A1 |
0.5 |
Вещественная константа |
0.5 |
; |
Разделитель операторов |
S2 |
Однако в общем случае задача сканера несколько шире, чем просто проверка цепочки символов лексемы на соответствие ее входному языку. Сканер должен выполнить те или иные действия по запоминанию распознанной лексемы (занесение ее в таблицу лексем). Набор действий определяется реализацией компилятора. Обычно эти действия выполняются сразу же по обнаружению конца распознаваемой лексемы, поэтому их несложно вставить в соответствующие места рассмотренной выше программы-сканера.
Таким образом, алгоритм работы простейшего сканера можно описать так:
просматривается входной поток символов программы на исходном языке до обнаружения очередного символа, ограничивающего лексему;
215
для выбранной части входного потока выполняется функция распознавания лексемы;
при успешном распознавании информация о выделенной лексеме заносится в таблицу лексем, и алгоритм возвращается к первому этапу;
при неуспешном распознавании выдается сообщение об ошибке, а дальнейшие действия зависят от реализации сканера - либо его выполнение прекращается, либо делается попытка распознать следующую лексему (идет возврат к первому этапу алгоритма).
Работа программы-сканера продолжается до тех пор, пока не будут просмотрены все символы программы на исходном языке из входного потока.
Задание. Составить программу, которая выполняет лексический анализ входного текста в соответствии с заданием и порождает таблицу лексем с указанием их типов и значений.
Варианты заданий
1.Входной язык содержит арифметические выражения, разделенные символом ;(точка с запятой). Арифметические выражения состоят из идентификаторов, десятичных чисел с плавающей точкой, знака присваивания (:=), знаков операций +, -, *, / и круглых скобок.
2.Входной язык содержит логические выражения, разделенные символом ;(точка с запятой). Логические выражения состоят из идентификаторов, констант true и false, знака присваивания (:=), знаков операций or, xor, and, not и круглых скобок.
3.Входной язык содержит операторы условия типа if … then … else и if … then, разделенные символом ;(точка с запятой). Операторы условия содержат идентификаторы, знаки
216
сравнения <, >, =, десятичные числа с плавающей точкой, знак присваивания (:=).
4.Входной язык содержит операторы цикла типа for … do, разделенные символом ;(точка с запятой). Операторы цикла содержат идентификаторы, знаки сравнения <, >, =, десятичные числа с плавающей точкой, знак присваивания (:=).
5.Входной язык содержит арифметические выражения, разделенные символом ;(точка с запятой). Арифметические выражения состоят из идентификаторов, римских чисел, знака присваивания (:=), знаков операций +, -, *, / и круглых скобок.
6.Входной язык содержит логические выражения, разделенные символом ;(точка с запятой). Логические выражения состоят из идентификаторов, констант 0 и 1, знака присваивания (:=), знаков операций or, xor, and, not и круглых скобок.
7.Входной язык содержит операторы условия типа if … then … else и if … then, разделенные символом ;(точка с запятой). Операторы условия содержат идентификаторы, знаки сравнения <, >, =, римские числа, знак присваивания (:=).
8.Входной язык содержит операторы цикла типа for … do, разделенные символом ;(точка с запятой). Операторы цикла содержат идентификаторы, знаки сравнения <, >, =, римские числа, знак присваивания (:=).
9.Входной язык содержит арифметические выражения, разделенные символом ;(точка с запятой). Арифметические выражения состоят из идентификаторов, шестнадцатеричных чисел, знака присваивания (:=), знаков операций +, -, *, / и круглых скобок.
10.Входной язык содержит логические выражения, разделенные символом ;(точка с запятой). Логические выражения состоят из идентификаторов, шестнадцатеричных чисел, знака присваивания (:=), знаков операций or, xor, and, not и круглых скобок.
217
11.Входной язык содержит операторы условия типа if
…then … else и if … then, разделенные символом ;(точка с запятой). Операторы условия содержат идентификаторы, знаки сравнения <, >, =, шестнадцатеричные числа, знак присваивания (:=).
12.Входной язык содержит операторы цикла типа for … do, разделенные символом ;(точка с запятой). Операторы цикла содержат идентификаторы, знаки сравнения <, >, =, шестнадцатеричные числа, знак присваивания (:=).
Примечание:
- римскими числами считать последовательности больших латинских букв X, V и I;
- шестнадцатеричными числами считать последовательность цифр и символов ‘a’, ‘b’, ‘c’,’d’, ’e’ и ‘f’, начинающуюся с цифры (например: 89, 45ac9, 0abc4);
Ход выполнения работы:
1.Написать программу, которая выполняет лексический анализ входного текста в соответствии с заданием и порождает таблицу лексем с указанием их типов и значений.
2.Текст на входном языке задается в виде символьного (текстового) файла.
3.Программа должна выдавать сообщения о наличии во входном тексте ошибок, которые могут быть обнаружены на этапе лексического анализа.
4.Длину идентификаторов и строковых констант считать ограниченной 32 символами.
Пример листинга программы:
//Основное окно программы Form1 using System;
using System.Collections.Generic; using System.Collections;
218
using System.ComponentModel; using System.Data;
using System.Drawing; using System.Linq; using System.Text;
using System.Windows.Forms; using System.IO;
namespace spo2_1
{
public partial class Form1 : Form
{
FileStream FS; bool fl = false; int al2count;
//переменные для хранения номера значения лексемы int number;
int number_i; int number_pr; int number_ao;
int number_sk; int number_kn; int number_vsfunk;
bool spec;
//массив для хранения таблицы идентификаторов element[] mas = new element [50];
//массив для хранения элементов таблицы идентификаторов
ArrayList al = new ArrayList(); //ArrayList al2 = new ArrayList(); //массив для храниеия значений лексем
leksem [] al2 = new leksem [1000]; bool find;
219
//массивы со значениями ключевых слов, операторов, методов, скобок
string[] keyword = { "uint", "int", "string", "char", "long", "ulong","bool" };
string[] keyword_all = { "using", "namespace", "public", "class", "static", "object", "try", "catch", "const", "void", "object", "ArrayList", "new", "for",
"false", "private", "foreach", "in", "if", "true", "else","do","break","while","continue"}; string[] arifm_op = {"+","-","*","/" };
string[] b_class = { "EventArgs", "Convert", "Thread", "ParameterizedThreadStart" };
string[] vs_funk = { "ToString", "ToInt32","InitializeComponent","Length","Add", "Value", "Cells","Rows" };
string[] log_op = { "!=", "==", "||", "&&","++","-=","+=" }; string[] sr_op = { ">", "<", "<=", ">=" ,"%"};
string[] ct = { "0","1", "2", "3", "4", "5", "6", "7", "8", "9","100","1000000"};
string[] sk = { "[", "]", "(", ")" }; string[] fun = { "sender", "e" };
string[] str; public Form1()
{
InitializeComponent();
}
//обработка откратия файла
private void button1_Click(object sender, EventArgs e)
{
//переменная для хранения имени файла string s;
//расширения выбираемых в диалоге файлов
220