Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекция СП5.DOC
Скачиваний:
3
Добавлен:
28.04.2019
Размер:
384.51 Кб
Скачать

Лекция 5 Характеристика процесса сканирования

Лексический анализ ( или сканирование) образует первый этап процесса компиляции. На этом этапе символы, составляющие исходную программу, считываются и группируются в отдельные лексические элементы, называемые лексемами. Лексический анализ важен для процесса компиляции по следующим причинам:

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

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

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

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

При непрямом лексическом анализе требуется, прочитав цепочку символов, определить, образует ли эта цепочка лексему некоторого конкретного типа. В этом случае сканер работает вместе с синтаксическим анализатором, как некоторая программная процедура SCAN

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

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

Рис.

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

Распознавание лексем выполняется следующим образом:

- входная цепочка считывается до тех пор, пока КА не достигнет заключительного состояния;

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

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

Рассмотрим способы описания лексем.

Построение недетерминированного конечного автомата по расширенному регулярному выражению

Рассмотрим теперь метод получения КА, распознающих лексемы языка, заданные регулярными выражениями.

Вход А1 : расширенное регулярное выражение R в алфавите , не содержащее символа  и операций «  » «  ».

Выход А1 : недетерминированный КА (НКА) - М, для которого L(М)=R.

Описание А1: Получим автомат М0 такой, что L(М0)= R 0 , выполняя рекурсивно следующие действия:

1. Если R0, тогда М0=( {q} , , q, {q} ), где q - новое состояние;

2. Если R 0 , где a , тогда М0= ( {q1,q2}, ,0 , q1, {q2} ), где 0 (q1,a) = {q2}, в остальных случаях 0 не определена; q1 и q2- новые состояния;

3. Если R0=R1| R2, тогда применяем весь алгоритм к R1 и R2 и получаем соответственно М1= ( Q 1,, 1, q1, F1 ) и

М2=( Q2 , , 2, q2 , F2 ) , где Q1 и Q2 не пересекаются, а затем построим М0=(Q1Q2 {q0}, ,  0 ,q0 ,F0), где

а) q0-новый символ;

б) 0 включает 1 и 2, т.е. 0 (q0 ,a)= 1 (q1 ,a) 2 (q2 ,a);

в) F0=F1F2 , если q1 F1 и q2 F2 , в противном случае F0 =F1F2 { q0 }.

4. Если R0=R1 R2 , то применим весь алгоритм к R1 и R2 и получим М1 и М2 , как в п.3 .Построим М0=(Q1 Q2 , , 0 , q1, F0 ) , где

а) 0 включает 2 ; 0 (q ,a)= 1 (q ,a) для всех q Q и a , если q F1, и 0 (q ,a)= 1 (q ,a) 2 (q2 ,a) в противном случае;

б) F0=F2 , если q2 F2 , и F0=F1 F2 в противном случае.

5. Если R0=R1* , то применим весь алгоритм к R1 и получим М1=( Q1 ,, 1 , q1 , F1).

Построим М0=( Q1 {q0}, , 0 , q0 , F1 {q0}), где q0 - новый символ и 0 определяется соотношениями:

а) 0 (q0 ,a)=1 (q1 , a);

б) если q F1 , то 0 (q ,a)=1 (q ,a);

в) если q F1 , то 0 (q ,a)=1 (q,a) 1 (q1 , a).

6. Если R0=R1+ , то применим весь алгоритм к R1 и получим М1, как в п.5. Построим М0=( Q1, , 0 , q1 ,F1), где 0 (q,a)=1(q,a), если q F1 , и 0 (q ,a)=1 (q ,a) 1 (q1 ,a), если q F1.

7. Если R0=R1*n, то применим весь алгоритм к R1 и получим М1 , как в п.5. Построим М0 =(Q1 {1,..., n}, , 0 , [q1 ,1], F0), где

а) если q F1 или i=n, то 0 ([q ,i], a )={[p , i] |1 (q ,a) содержит p};

б) если q F1 и i<n, то 0 ( [ q , i ] , a)={ [p , i] | 1( q , a) содержит p}U{ [p ,i +1] | 1(q1 ,a) содержит p }

в) F0={ [q , i ] | q F1 , 1 i n } U { [q1 ,1] }.

8. Если R0=R1+n , выполним то же , что и в (7) , но пункт (7,в) заменим на F0={ [q , i ] | q F1 , 1 i n }.

Пример . Пусть R = (0 | 1) , преобразуем его в НКА :

1. R можно представить как R = (R1 | R2) , где R1 = 0 , R2 .

2. Применяя п. 2 для R1 и R2 , получаем :

3. Применяя п. 3 к R = (R1 | R2), объединяем состояния автоматов М1 , М2 и получаем результирующий автомат М

Пример . Пусть R = (0 | 1)* , преобразуем его в НКА :

1. Используя результаты , полученные в примере 4.2, и применяя п.5 для R , получим автомат М:

2. Согласно п. 5 , объединяем q1 и q2 и получаем

Пример . Пусть R = (a | b) (a | b |0 | 1)* , выполним его преобразование в НКА :

1. Представим r как R = R1 R2 ,

где R1 = (R3 | R4) , R2 = ( )* , R3 = a , R4 = b ;

= (R5 | R6 | R7 | R8) , R5 = a , R6 = b , R7 = 0 , R8 = 1 ;

2. Автоматы , соответствующие выражениям R1 и R2 легко получить , используя результаты примеров 4.2 , 4.3. Для R1 получим

для R2

3. Применяем п. 4 к выражению R = R1 R2 и получаем результирующий автомат М:

Пример Пусть R = (00 | 11)* , преобразуем его в НКА :

1. Представим R следующим образом :

R = R1* , R1 = R2 | R3

R2 = R4 R5 , R3 = R6 R7 ,

R4 = 0 , R5 = 0, R6 = 1 , R7 = 1

2. Для R2 и R3 применяем пп. 2 и 4 ( можно использовать результаты предыдущих примеров ) и получаем автоматы М2 и М3 :

3. Применяя п. 3 к R1 = R2 | R3 , получаем из М2 и М3 результирующий автомат М1 :

4. Применяя п. 5 к R = R1* , получаем из М1 автомат М

Для того, чтобы лучше усвоить и закрепить навыки преобразований, полученные в этих примерах, и использовать их в дальнейшем, приведём соответствия между некоторыми простыми регулярными выражениями и КА: