Пример проектирования лексического анализатора

 

Пусть {Li | Li A* , i [1,3]}– семейство из трех регулярных языков над входным алфавитом A, гдеLi – класс лексем языка Си или его ограничение (подмножество)для всехi [1, 3].

Язык L1 – множество идентификаторов языка Си; L2 - множество целочисленных констант языка Си в системе счисления с основанием 8; L3 – множество «пробельных» лексем, интерпретируемых как непустые слова над алфавитом {s, t}. Символы s и t условно обозначают «пробел» и «знак табуляции» соответственно.

Входной алфавит Aпредставляют 256 символов в 8 – разрядной (байтовой) ASCII-кодировке. Числовой код символа «пробел» - 0x20 и «знака табуляции» - 0x09.

1) ЯзыкиLi индуцируют разбиения Piалфавита A,i [1,3]:
P1={[_a-zA-Z], [0-9], [^_a-zA-Z]};
P2={[0], [1-7], [lL], [uU], [^01-7 lL uU]},
P3={[\x20\x09], [^\x20\x09]}.
Абстрактные алфавиты, соответствующие разбиениям, следующие:
B1 = {a, 9, ? },B2 = {0, 7, l, u, ? }, B3 = {s, ? },
Регулярные выражения еi представляют языки Liв соответствующих абстрактных алфавитах Bi , i [1,3] :
e1=а(а | 9)*,e2 =0(0 | 7)* (|u|l|ul|lu), e3=ss *.

2) Эквивалентные -диаграммы Di для еi ,i[1,3]:


e1 = а (а | 9)*;

 

а:

(а | 9):

(а | 9)*:

 


e1 = а (а | 9)*:

-диаграммa D1

а|9
а

e2 = 0(0 | 7)* (|u|l|ul|lu):

-диаграммa D2

e3 = s s*:

-диаграммa D3

3) Детерминированные конечные автоматы Mi, допускающие языки
L(Di), i [1,3]. Символом ‘*’ помечены финальные состояния, символом Æ - состояние “ошибки”.
M1

Замыкание q\a a ?
[0]= {0} Æ Æ
[1]={1, 2, 3} * Æ
[Æ]=Æ Æ Æ Æ
[{2,3}]={2,3} * Æ

 


M2

Замыкание q\a l u ?
[0]= {0} Æ Æ Æ Æ
[1]={1,2,3,6} * Æ
[Æ] =Æ Æ Æ Æ Æ Æ
[2]={2,3, 6} * Æ
[{4,6}]={4,6} * Æ Æ Æ Æ
[{5,6}]={5,6} * Æ Æ Æ Æ
[6]= {6} * Æ Æ Æ Æ Æ

 

M3

Замыкание q\a s ?
[0]= 0
[1]={1, 2, 3} * Æ
[Æ] =Æ Æ Æ
[{2,3}]={2,3} * Æ

 

Языки Li – не пусты, поскольку не пусты множества заключительных состоянийавтоматовMi для всехi [1,3] , и -свободны,так как начальные состояния не являются заключительными;

4) Общий алфавит B = {0, 7, 9, a, l, u, s, ?}. Соответствующее разбиениеалфавита A -
P = {[0], [1-7], [89], [_a-km-tv-zA-KM-TV-Z], [lL], [uU], [\x20\x09],
[^0-9_a-zA-Z\x20\x09]}.

Элементы алфавитов связаны следующими соотношениями:

элементы B1 = {a, 9, ?} –
a = а | l | u,

9 = 0 | 7 | 9,

? = s | ?;


элементы B2 = {0, 7, l, u, ?} –
0 = 0,

7 = 7,

l = l,

u = u,

? = 9 | a | s | ?;

элементы B3 = {s, ?} –

s = s,
t = t,
? = 0 | 7 | 9 | а | l | u | ?.


Применяя соотношения как правила подстановки к исходным выражениям, получаем регулярные выражения над общим алфавитом B:

e1 = (а | l | u)(а | l | u | 0 | 7 | 9)* ,

e2 = 0(0 | 7)* (|u|l|ul|lu),

e3 = ss*.
Чтобы получить регулярные выражения, определяющие языки в исходном алфавите A, необходимо заменить вхождения символов алфавита B в регулярные выражения на выражения, представляющие соответствующие классы разбиения P. Например, в языке C# выражения имеют следующие представления в формате строковых констант (литералов):
e1 - @”([_a-km-tv-zA-KM-TV-Z] | [lL] | [uU])”+
@”( [_a-km-tv-zA-KM-TV-Z] | [lL] | [uU] | [0] | [1-7] | [89])*” ;
e2 - @”[0]([0] | [1-7])* ( [uU] | [lL] | [uU] [lL] | [lL] [uU])?”;

e3 - @” [\x20\x09] [\x20\x09]*”.

Диаграммы Di в алфавитах Biтакже просто преобразуются в соответствующие диаграммы над общим алфавитомB:достаточно каждую
a-дугу заменить параллельнымиb-дугами.Например, в диаграммеD1 каждая дуга (i, a, j) заменяется на (i, а | l | u, j), дуга (i, 9, j) – на (i, 0 | 7 | 9, j) и (i, ?, j) – на (i, s | t |?, j).

 

Приведенные к общему алфавиту B -диаграммы Di, i [1,3]:

e1 = (а | l | u)(а| l | u | 0 | 7 | 9)* ;

 

 

e2 = 0(0 | 7)* (|u|l|ul|lu):

-диаграммa D2


e3 = ss* :

-диаграммa D3

Детерминированные конечные автоматы Mi , приведенные к общему алфавиту B, i [1,3] .
M1

Замыкание q\a a l u s ?
[0]= {0} Æ Æ Æ Æ Æ
[1]={1, 2, 3 } * Æ Æ
[Æ] =Æ Æ Æ Æ Æ Æ Æ Æ Æ
[{2,3}]={2,3} * Æ Æ

M2

Замыкание q\a a l u s ?
[0]= {0} Æ Æ Æ Æ Æ Æ Æ
[1]={1,2,3,6} * Æ Æ Æ Æ
[Æ] =Æ Æ Æ Æ Æ Æ Æ Æ Æ
[2]={2,3,6} * Æ Æ Æ Æ
[{4,6}]= {4,6} * Æ Æ Æ Æ Æ Æ Æ
[{5,6}]= {5,6} * Æ Æ Æ Æ Æ Æ Æ
[6]= {6} * Æ Æ Æ Æ Æ Æ Æ Æ

M3

Замыкание q\a a l u s ?
[0]= 0 Æ Æ Æ Æ Æ Æ Æ
[1]={1, 2, 3} * Æ Æ Æ Æ Æ Æ Æ
[Æ] =Æ Æ Æ Æ Æ Æ Æ Æ Æ
[{2,3}]={2,3} * Æ Æ Æ Æ Æ Æ Æ

 

Языки Li над общим алфавитом B так же не пусты, поскольку не пусты множества заключительных состоянийавтоматовMi для всехi [1,3] , и -свободны,так как начальные состояния не являются заключительными.


Декартово произведениедвух детерминированных конечных автоматов -aвтомат M1,2= M1x M2, допускающий объединение исходных языков: L1 L2.
M1,2

Вектор q\a a l u s ?
(0,0) Æ Æ Æ Æ
(2,1) 2 Æ Æ Æ Æ
(2,2)= Æ Æ Æ Æ Æ Æ Æ Æ Æ
(1,2) 1 Æ Æ
(2,3) 2 Æ Æ Æ Æ
(2,4) 2 Æ Æ Æ Æ Æ Æ Æ
(2,5) 2 Æ Æ Æ Æ Æ Æ Æ
(3,2) 1 Æ Æ
(2,6) 2 Æ Æ Æ Æ Æ Æ Æ Æ


 

5) Декартово произведениетрех детерминированных конечных автоматов -aвтомат M= M1x M2x M3, допускающий объединение исходных языков: L1 L2 L3.

M

Вектор q\a a l u s ?
(0,0,0) Æ Æ Æ
(2,1,2) 2 Æ Æ Æ Æ
(2,2,2)= Æ Æ Æ Æ Æ Æ Æ Æ Æ
(1,2,2) 1 Æ Æ
(2,2,1) 3              
(2,3,2) 2        
(2,4,2) 2              
(2,5,2) 2              
(3,2,2) 1    
(2,2,3) 3              
(2,6,2) 2                

ПустьM =(Q, B , g, q0, F), где F= F1 F2 F3 , F1 = {3, 8},
F2= {1, 5, 6, 7, 10}, F3 = {4, 8}. Соответствующие автоматы
(Q, B , g, q0, Fi) допускают языки Li , i [1,3] .

Cемейство подмножеств {F1, F2, F3} состоит из попарно не пересекающихся множеств, следовательно семейство языков {L1, L2, L3} состоит из трех попарно не пересекающихся регулярных языков;


Автомат M¢ , построенный методом «слияния диаграмм переходов» автоматов M1,2 и M3. Автоматы M и M¢ изоморфны, поскольку их диаграммы переходов изоморфны.
M
¢

F q\a a l u s ?
  Æ Æ 9 Æ
Æ Æ Æ Æ
Æ Æ Æ Æ Æ Æ Æ Æ Æ
Æ Æ
Æ Æ Æ Æ
Æ Æ Æ Æ Æ Æ Æ
Æ Æ Æ Æ Æ Æ Æ
Æ Æ
Æ Æ Æ Æ Æ Æ Æ Æ
Æ Æ Æ Æ Æ Æ Æ
Æ Æ Æ Æ Æ Æ Æ

 

7) Классификация состояний и переходов.
По построению все состояния достижимы из начального состояния q0.

Множество «тупиковых» или состояний, сигнализирующих о лексической ошибке, - Error ={q| g*(q, x) ÏF}.

В автомате M= M1x M2x M3 множество Error = {2}, так как из q2 не достижимо ни одно финальное состояние q Î F.

Множество активных состояний -Active = Q\ Error.
Множество активных переходов -
ActiveTransition = {(q, a)| q ÎActive и g(q, a) ÎActive,a Î B }.
Множество переходов, определяющих условие распознавания лексемы класса Li , - EndLi = {(q, a)| q Î Fi и g(q, a) ÎError,a Î B }.

Символ a такой, что (q, a) Î EndLi определяет правый контекст лексемы x Î Li.Слово x переводит автомат из начального состояния в финальное состояниеqÎFi, причем q=g*(q0, x).

Переходы множества ErrorL = {(q, a)| q ÎActive \ F и g(q, a) ÎError,a Î B } определяют условия обнаружения лексической ошибки.
Если (q, a) ÎErrorL, тогда слово x такое, что q=g*(q0, x) переводит автомат из начального состояния вактивное не финальное состояние q, определяет допустимый собственный префикс некоторой лексемы, но

слово xa – не допустимый префикс, так как не существует слова y Î B*, что xaу Î L.

Очевидно, ActiveTransition,EndL1 ,EndL2, EndL3 ,ErrorL,-попарно не пересекающиеся множества переходов.


Семантические процедуры

Определить (классифицировать) множество P «действий» (семантических процедур)значитопределить функцию выхода
f : Q
´B¢ P,гдеB¢= B È {#}.Если f(q,a)=p Î P,тогдаp – действие, выполняемое лексическим анализатором в состоянии q Î Qпривходном символеa Î B¢.Формально символ#Ï Bслужитпризнаком конца входного потока.
Фактически результатом проектирования является разработка модели детерминированного конечного преобразователяM =(Q, B¢, P, f, g)простого типа.

Пусть P = { pActiveTransition, pEndL1, … , pEndLn, pErrorL}. Определим очевидным образом функцию выхода:

f(q,a)=pActiveTransition, если(q,a)ÎActiveTransition;

 

если(q,a)ÎEndLi , то (q,#)ÎEndLi , и
f(q,a)=pEndLi, если(q,a)ÎEndLi , i Î [1, n];

 

если(q,a)ÎErrorL, то(q,#)ÎErrorL, и

f(q,a)=pErrorL, если(q,a)ÎErrorL.

 

Тогда программная реализация (интерпретация) преобразователя
M
=(Q, B¢, P, f, g) определяет основные действия (функции) лексического анализатора при сканировании входного потока символов алфавита B¢.

Ниже на псевдоязыке приводится в обобщенной форме одна из возможных программных интерпретаций.

 

Интерпретация символов выходного алфавита P.

 

pActiveTransition:

s = s + a; //добавить входной символ, расширить префикс некоторой //лексемы

// q1 = <q>

q = g[q,b]; // b = [a] – класс входного символа а ¹ #

// q2 = <q> и (q1, b, q2) in ActiveTransition`

return;


pEndLi , i Î [1, n]:

//(q, b) in EndLi , i Î [1, n]

//<s> = x – лексема, b = [a] и <a> = a – правый контекст

tokenStream.WriteLine(“<L”, i, “>”, s);

if (b == [#])

{

//выход

return;

}

else

{

inputStream.UnGetC(a); // вернуть символ <a> = a во входной поток
q = 0; // переключить автомат в начальное состояние

s=””; // очистить буфер для накопления символов очередной лексемы

return;

}

 

pErrorL:

s = s + a;

tokenStream.WriteLine(“<ErrorL>”, s); //s=xa

if (b == [#])

{

//выход

return;

}

else

{

//skip входной символ а, доставивший лексическую ошибку
q = 0;

s=””;

return;

}