Пример проектирования лексического анализатора
Пусть {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
|
|
|
|
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;
}