Сторінка
4

Елементи синтаксичного аналізу

Зокрема, коли ланцюжок u починається терміналом, тобто u=xu1, де xÎ X, то цикл має вигляд while ch = x do begin ch := getc; оператор-для-u1 end. Оператори, відповідні до u, u1, … , um , записуються за цими ж правилами. 3.2. Побудова аналізатора арифметичних виразів Розширена LA(1)-граматика G01 із продукціями E® T{+T}, T® F{*F}, F® (E)|a породжує мову арифметичних виразів. Згідно з наведеними правилами запишемо процедури E, T, F: procedure E; begin T; while ch = '+' do begin ch := getc; T end end; procedure T; begin F; while ch = '*' do begin ch := getc; F end end; procedure F; begin if ch = '(' then begin ch := getc; E; if ch = ')' then ch := getc else error end else if ch = 'a' then ch := getc else error end Побудовані процедури взаємно рекурсивні: тіло E містить виклики процедури T, тіло T – виклики F, а тіло F – виклики E. У мові Паскаль кожне ім'я повинно бути означеним вище від його вживання, тому незрозуміло, в якій послідовності треба записати процедури E, T, F. У таких випадках використовують конструкцію forward . Якщо в програмі є взаємно рекурсивні підпрограми, то за правилами мови Паскаль спочатку в блоці охоплюючої їх програми (підпрограми) записуються лише заголовки кількох із них (зокрема, однієї), а замість їх блоків пишеться слово forward, тобто, "попереду". Решту підпрограм розміщують так, щоб вони містили виклики підпрограм, чиї заголовки (разом із блоком чи словом forward) уже записано вище. Підпрограми, записані спочатку без блоку, розміщаються в кінці зі скороченим заголовком вигляду procedure <ім'я>; або function <ім'я>. Список параметрів, дужки й тип функції в скороченому заголовку відсутні. У даному прикладі процедури E, T, F не мають параметрів, тому скорочені заголовки не відрізняються від повних. Запишемо програму аналізу арифметичних виразів, вважаючи, що вираз набирається на клавіатурі, а читання його символів задається процедурою getc із модуля Inbuf (розділ 20): program Aexan ( input, output ); uses Inbuf; var ch : char; ok : boolean; procedure error; begin ok := false; ch := finch end; procedure E; { тут повний заголовок } forward; procedure F; … E … { виклик процедури E } end; procedure T; … F … { виклик процедури F } end; procedure E; { тут скорочений заголовок } … T … { виклик процедури T } end; begin ok := true; ch := getc; E; { виклик процедури, відповідної до } { головного нетермінала } writeln ( (ch = finch) and ok ) end. Як бачимо, всі виклики посилаються на процедури, чиї імена вже означено раніше. Уживання взаємно рекурсивних підпрограм інколи називається непрямою рекурсією.

4. Аналіз та обчислення дужкових виразів У розділі 9 розглядалися дужкові арифметичні вирази, мова яких породжується розширеною LA(1)-граматикою G2: ( { 0, … , 9, ., c, i, n, o, s, +, *, -, /, (, ) }, { E, T, F, A, C, D, N }, { E ® T { +T | -T }, T ® F { *F | /F }, F ® (E) | C | A, C ® N (E), N ® 'sin' | 'cos', A ® D { D } [ . { D } ], D ® 0 | … | 9 }, E ). Імена A, C, N є скороченнями фраз "Arithetic constant", "Call of function", "Name of function" відповідно, тобто "Арифметична стала", "Виклик функції", "Ім'я функції". Побудуємо програму Aexval аналізу та обчислення арифметичних виразів на основі програми Aexan із попереднього підрозділу. Нехай вираз подається в кількох рядках, і ознакою кінця є порожній рядок. Читання лексем виразу задається модулями Glx та Inbuf, означеними в розділі 20. Замість функції getc добування наступного символу з програми Aexan використовується функція getlx добування наступної лексеми, а замість поточного символу ch – поточна лексема lx типу Tlx. Ознака наявності лексеми, що повертається з функції getlx, присвоюється бульовій змінній islx. Підпрограми для нетерміналів A, N тут не створюються, оскільки аналіз лексем, позначених ними, уже задаєно в модулі Glx. Кожна з процедур E, T, F перетворюється на функцію обчислення тієї частини виразу, яка аналізується при виконанні її виклику. Побудуємо їх таким чином, щоб за помилкового виразу з них поверталося значення 1. Згідно з продукціями граматики G2, функція F обчислення множника у виразі має такий вигляд: function F : real; begin if ( lx.lxt = par ) and ( lx.prt = '(' ) then begin islx := getlx ( lx ); F := E; if islx and ( lx.lxt = par ) and ( lx.prt = ')' ) then islx := getlx ( lx ) else begin error; F := 1 end end else if lx.lxt = con then begin F := lx.numb; islx := getlx ( lx ) end

else if lx.lxt = nam then F := C else begin error; F := 1 end end Функція C задає обчислення значення, що має повернутися з указаного у виразі виклику функції sin чи cos: function C : real; var lx1 : Tlx; v : real; begin lx1 := lx; islx := getlx ( lx ); if islx and ( lx.lxt = par ) and ( lx.prt = '(' ) then begin islx := getlx ( lx ); v := E; if islx and ( lx.lxt = par ) and ( lx.prt = ')' ) then islx := getlx ( lx ) else begin error; C := 1 end; if ok then if lx1.name = 'sin' then C := sin ( v ) else C := cos ( v ) else begin error; C := 1 end end else begin error; C := 1 end end Функція E задає обчислення виразу, вивідного з E: function E : real; var lx1 : Tlx; v : real; begin v := T; while ok and ( lx.lxt = ops ) and ( lx.sig in ['+', '-'] ) do begin lx1 := lx; islx := getlx ( lx ); case lx1.sig of '+' : v := v + T; '-' : v := v - T end end; if ok then E := v else E := 1 end Функцію T обчислення доданка у виразі, яка має аналогічну функції E структуру, залишаємо для самостійної розробки. Головна програма подібна до програми Aexan: program Aexval ( input, output ); uses Inbuf, Glx var islx, ok : boolean;

Перейти на сторінку номер:
 1  2  3  4  5 


Інші реферати на тему «Інформатика»: