Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабораторная работа №2

.pdf
Скачиваний:
18
Добавлен:
03.05.2015
Размер:
309.26 Кб
Скачать

9

Лабораторная работа № 2

ПРОЕКТИРОВАНИЕ ЛЕКСИЧЕСКОГО АНАЛИЗАТОРА

Цель работы: изучение основных понятий теории регулярных грамматик, ознакомление с назначением и принципами работы лексиче-

ских анализаторов (сканеров), получение практических навыков по-

строения сканера на примере заданного простейшего входного языка.

Для выполнения лабораторной работы требуется написать програм-

му, которая выполняет лексический анализ входного текста в соответст-

вии с заданием и порождает таблицу лексем с указанием их типов и значений. Текст на входном языке задается в виде символьного (тексто-

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

сического анализа.

Краткие теоретические сведения

Лексический анализатор (или сканер) - это часть компилятора, ко-

торая читает литеры программы на исходном языке и строит из них слова (лексемы) исходного языка. На вход лексического анализатора поступает текст исходной программы, а выходная информация переда-

ется для дальнейшей обработки компилятором на этапе синтаксическо-

го анализа и разбора.

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

10

сколько причин, исходя из которых в состав практически всех компиля-

торов включают лексический анализ. Эти причины заключаются в сле-

дующем:

упрощается работа с текстом исходной программы на после-

дующих этапах работы компилятора и сокращается объем обрабаты-

ваемой информации, так как лексический анализатор структурирует поступающий на вход исходный текст программы и убирает всю незна-

чащую информацию;

для выделения в тексте и разбора лексем можно применять про-

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

сканер отделяет сложный по конструкции синтаксический ана-

лизатор от работы непосредственно с текстом исходной программы,

структура которой может варьироваться в зависимости от версии вход-

ного языка - при такой конструкции компилятора при переходе от од-

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

Функции, выполняемые лексическим анализатором, и состав лексем,

которые он выделяет в тексте исходной программы, могут меняться в зависимости от версии компилятора. В основном лексические анализа-

торы выполняют исключение из текста исходной программы коммента-

риев и незначащих пробелов, а также выделение лексем следующих типов: идентификаторов, строковых, символьных и числовых констант,

ключевых (служебных) слов входного языка.

11

Не следует путать таблицу лексем и таблицу идентификаторов – это две принципиально разные таблицы, обрабатываемые лексическим анализатором.

Таблица лексем фактически содержит весь текст исходной програм-

мы, обработанный лексическим анализатором. В нее входят все воз-

можные типы лексем, кроме того, любая лексема может встречаться в ней любое количество раз. Таблица идентификаторов содержит только определенные типы лексем – идентификаторы и константы. В нее не попадают такие лексемы, как ключевые (служебные) слова входного языка, знаки операций и разделители. Кроме того, каждая лексема

(идентификатор или константа) может встречаться в таблице идентифи-

каторов только один раз. Также можно отметить, что лексемы в таблице лексем обязательно располагаются в том же порядке, что и в исходной программе (порядок лексем в ней не меняется), а в таблице идентифика-

торов лексемы располагаются в любом порядке так, чтобы обеспечить удобство поиска.

В простейшем случае фазы лексического и синтаксического анализа могут выполняться компилятором последовательно. Однако, в боль-

шинстве компиляторов лексический и синтаксический анализаторы -

это взаимосвязанные части. Лексический разбор исходного текста в та-

ком варианте выполняется поэтапно так, что синтаксический анализа-

тор, выполнив разбор очередной конструкции языка, обращается к ска-

неру за следующей лексемой. При этом он может сообщить информа-

цию о том, какую лексему следует ожидать. В процессе разбора может даже происходить “откат назад”, чтобы выполнить анализ текста на другой основе. В дальнейшем будем исходить из предположения, что

12

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

ческого разбора.

Работу синтаксического и лексического анализаторов можно изобра-

зить в виде схемы (рис. 1.)

 

 

 

Очередная лексема

 

Исходная

 

Сканер

 

Синтаксический

программа

 

(лексический

Обращение за очередным

анализатор

 

 

анализатор)

 

 

 

словом (лексемой)

 

 

 

 

 

 

Рис. 1. Взаимодействие синтаксического и лексического анализаторов

В данной работе будем использовать последовательную модель ра-

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

ная организация сложнее и требует знаний работы синтаксического анализатора, который буде изучаться в следующейлабораторной работе.

Пример фрагмента текста программы на языке Паскаль и соответст-

вующей ему таблицы лексем даны ниже и в таблице 1:

---

begin

for i:=1 to N do fg := fg * 0.5

...

 

 

 

Таблица 1

 

Таблица лексем программы

 

 

 

 

 

 

 

Лексема

Тип лексемы

 

Значение

 

begin

Ключевое слово

 

X1

 

for

Ключевое слово

 

X2

 

I

Идентификатор

 

i : 1

:=

Знак присваивания

 

 

 

1

Целочисленная константа

 

1

 

to

Ключевое слово

 

X3

 

N

Идентификатор

 

N : 2

 

do

Ключевое слово

 

X4

 

Fg

Идентификатор

 

fg : 3

:=

Знак присваивания

 

 

 

Fg

Идентификатор

 

fg : 3

*

Знак арифметической операции

 

 

 

0.5

Вещественная константа

 

0.5

 

13

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

значении. Обычно такая таблица имеет два столбца: первый - строка лексемы, второй - указатель на информацию о лексеме. Может быть включен и третий столбец - тип лексем.

Лексический анализатор имеет дело с такими объектами, как раз-

личного рода константы и идентификаторы (к последним относятся и ключевые слова). Язык констант и идентификаторов в большинстве случаев является регулярным, то есть, может быть описан с помощью регулярных (праволинейных или леволинейных) грамматик.

Распознавателями для регулярных языков являются конечные ав-

томаты. Существуют правила, с помощью которых для любой регу-

лярной грамматики может быть построен недетерминированный конеч-

ный автомат, распознающий цепочки языка, заданного этой граммати-

кой.

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

т.е. такие, из которых выходят две или более дуг, помеченных одним и тем же символом. Очевидно, что программирование работы такого ав-

томата - нетривиальная задача.

Доказано, что любой недетерминированный автомат может быть преобразован в детерминированный так, чтобы их языки совпадали (го-

ворят, что автоматы эквивалентны).

14

Детерминированный конечный автомат задается пятеркой: M’=(Q’, , ’,q0’,F’),

где: Q’ - конечное множество состояний автомата;

- конечное множество допустимых входных символов автомата; q0’ Q’ - начальное состояние автомата;

’ - заданное отображение множества Q’* во множество Q’ :

Q’* Q’;

F Q’ - множество заключительных состояний автомата.

После построения конечный детерминированный автомат может быть минимизирован, т.е. построен эквивалентный ему автомат с мини-

мальным числом состояний.

Работа автомата выполняется по тактам. На каждом очередном такте i автомат, находясь в некотором состоянии qi Q, считывает очередной символ w из входной цепочки символов и изменяет свое состояние на qi+1= (qi,w), после чего указатель в цепочке входных символов пе-

редвигается на следующий символ и начинается такт i+1. Так продол-

жается до тех пор, пока цепочка входных символов не закончится. Ко-

нец цепочки символов часто помечают особым символом . Считается также, что перед тактом 1 автомат находится в начальном состоянии q0.

Говорят, что автомат допускает цепочку *, если в результате выполнения всех тактов работы над этой цепочкой он окажется в со-

стоянии q F. Язык, определяемый автоматом, является множеством всех цепочек, допускаемых автоматом. Для анализа цепочки с помощью конечного автомата достаточно подать ее на вход автомата, выполнить

15

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

Графически автомат отображается нагруженным однонаправленным графом, в котором вершины представляют состояния, дуги отображают переходы из одного состояния в другое, а символы нагрузки (пометки)

дуг соответствуют функции перехода. Если функция перехода преду-

сматривает переход из состояния q в q’ по нескольким символам, то между ними строится одна дуга, которая помечается всеми символами,

по которым происходит переход из q в q’.

Можно написать функцию, отражающую функционирование любого детерминированного конечного автомата. Чтобы запрограммировать такую функцию, достаточно иметь переменную, которая бы отображала текущее состояние автомата, а переходы автомата из одного состояния в другое на основе символов входной цепочки могут быть построены с помощью операторов выбора. Работа функции должна продолжаться до тех пор, пока не будет достигнут конец входной цепочки. Для вычисле-

ния результата функции необходимо по ее завершению проанализиро-

вать состояние автомата. Если это одно из конечных состояний, то функция выполнена успешно, и входная цепочка принимается, если нет

- то входная цепочка не принадлежит заданному языку.

Однако в общем случае задача сканера несколько шире, чем просто проверка цепочки символов лексемы на соответствие ее входному язы-

ку. Сканер должен выполнить те или иные действия по запоминанию распознанной лексемы (занесение ее в таблицу лексем). Набор действий определяется реализацией компилятора. Обычно эти действия выпол-

няются сразу же по обнаружению конца распознаваемой лексемы, по-

16

этому их несложно вставить в соответствующие места рассмотренной выше программы-сканера (в те операторы, где обнаруживается символ

#0).

Вторая проблема, которая уже обсуждалась выше - это выделение границ лексем. Ведь во входном тексте лексемы не ограничены специ-

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

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

ных языков границы лексем распознаются по заданным терминальным символам. Эти символы - пробелы, знаки операций, символы коммента-

риев, а также разделители (запятые, точки с запятой и др.). Набор таких терминальных символов может варьироваться в зависимости от входно-

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

Другой способ выделения лексем – распознавание по набору допус-

тимых символов. Хотя таких символов может быть достаточно много,

но они все точно известны, в отличие от символов-разделителей, в каче-

стве которых могут выступать разнообразные спецсимволы, символы языка и их комбинации. Кроме того, список допустимых символов тре-

буется для задания грамматики языка и его распознавателя.

Таким образом, алгоритм работы простейшего сканера можно опи-

сать так:

просматривается входной поток символов программы на исход-

ном языке до обнаружения очередного символа, ограничивающего лек-

сему;

17

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

при успешном распознавании информация о выделенной лек-

семе заносится в таблицу лексем, и алгоритм возвращается к первому

этапу;

при неуспешном распознавании выдается сообщение об ошиб-

ке, а дальнейшие действия зависят от реализации сканера - либо его выполнение прекращается, либо делается попытка распознать следую-

щую лексему (идет возврат к первому этапу алгоритма).

Работа программы-сканера продолжается до тех пор, пока не будут просмотрены все символы программы на исходном языке из входного потока.

Порядок выполнения работы

1.Получить вариант задания у преподавателя.

2.Разработать грамматику входного языка в соответствии с заданием.

3.Описать алгоритм работы сканера или граф переходов КА для распознавания цепочек.

4.Написать и отладить программу на ЭВМ.

5.Сдать работающую программу преподавателю.

6.Сдать отчет преподавателю.

7.Защитить отчет.

18

Варианты заданий

1.Входной язык содержит арифметические выражения, разделен-

ные символом «;» (точка с запятой). Арифметические выражения состо-

ят из идентификаторов, десятичных чисел с плавающей точкой (в обыч-

ной и логарифмической форме), знаков операций и скобок.

2.Входной язык содержит арифметические выражения, разделен-

ные символом «;» (точка с запятой). Арифметические выражения состо-

ят из идентификаторов, римских чисел, знаков операций и скобок. (Римскими считать числа, записанные большими буквами X, V и I).

3.Входной язык содержит упрощенные операторы цикла типа

while <логическое выражение> do <оператор присваивания>; Логиче-

ское выражение может содержать идентификаторы, знаки операций сравнения, целые десятичные числа без знака, скобки и логические опе-

рации and и or. Оператор присваивания должен состоять из идентифи-

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

4.Входной язык содержит упрощенные условные операторы типа

if <логическое выражение> then <оператор присваивания> else <опера-

тор присваивания>; (часть else в операторе может отсутствовать). Логи-

ческое выражение может содержать идентификаторы, знаки операций сравнения, целые десятичные числа без знака, скобки и логические опе-

рации and и not. Оператор присваивания должен состоять из двух иден-

тификаторов, разделенных знаком присваивания.

5.Входной язык содержит выражения над строковыми константа-

ми, разделенные символом «;» (точка с запятой). Выражения состоят из

идентификаторов, строковых констант, заключенных в двойные кавыч-