Розрахункова робота №2
Лексичні та синтаксичні аналізатори
Мета: Вивчення принципів побудови та функціонування лексичних та синтаксичних аналізаторів.
Для полегшення побудови компіляторів, мови програмування описуються в термінах деякої граматики. Наприклад, оператор присвоювання може бути визначений в граматиці як ім’я змінної, за якою слідує оператор присвоювання (:=), за яким слідує вираз. Проблема компіляції може бути сформульована як проблема пошуку відповідності написаних програмістом речень, структурам, які визначені граматикою, і генерації відповідного коду для кожного речення.
Речення вихідної програми простіше представляти у вигляді послідовності лексем (tokens), ніж просто як строку символів. Лексемою може бути ключове слово, ім’я цілої змінної, арифметичний оператор і т.д.. Прогляд вихідного тексту, розпізнавання та класифікацію різних лексем робить частина компілятора яка називається лексичним аналізатором (сканером).
Як тільки лексеми виділені, кожне речення програми може бути розпізнане як деяка конструкція мови, як, наприклад, декларативні оператори або оператор присвоювання, описані за допомогою граматики. Синтаксичний аналіз виконується частиною компілятора під назвою синтаксичний аналізатор (parser).
Останнім кроком базової схеми процесу трансляції є генерація об’єктного коду. Більшість компіляторів генерує зразу програму в машинних кодах, а не програму на асемблері, призначену для наступної трансляції в машинні коди.
Лексичний аналіз
Лексичний аналіз включає в себе сканування компілюємої програми та розпізнавання лексем. Сканери зазвичай робляться таким чином, щоби вони могли розпізнавати ключові слова, оператори та ідентифікатори так само, як і цілі числа, числа з плаваючою комою, стрічки символів та інші аналогічні конструкції. Такі об’єкти, як ідентифікатори та цілі, зазвичай розпізнаються сканером як окремі лексеми. Однак є інший підхід, в рамках якого ці лексеми описуються правилами граматики. В цьому випадку сканер буде розпізнавати в якості окремих лексем одиничні символи А, В, 0, 1, і т.д. Далі в процесі граматичного розбору послідовність цих символів буде інтерпретована як окрема конструкція мови.
Результатом роботи сканера є послідовність лексем. Для підвищення ефективності наступних дій кожна лексема зазвичай представляється деяким кодом фіксованої довжини (наприклад цілим числом), а не у вигляді стрічки символів змінної довжини.
Якщо розпізнана лексема є ключовим словом або оператором, така схема кодування дає всю необхідну інформацію. У випадку ідентифікатора додатково необхідно конкретне ім’я розпізнаного ідентифікатора. Цього можна добитися за рахунок зберігання не тільки коду лексеми, але й додаткового специфікатора лексеми. Специфікатор повинен мати в собі ім’я ідентифікатора, значення цілого числа і т.д. – всю інформацію розпізнану сканером.
Зовсім не обов’язково, щоб сканер обробляв всю програму. Частіше сканер є процедурою, викликаємою в процесі граматичного розбору для отримання наступної лексеми.
В доповнення до своєї звичайної функції – розпізнавання лексем – сканер зазвичай також виконує читання стрічки вихідної програми, проте коментарії сканером ігноруються. Сканер також повинен враховувати мовно-залежну інформацію: чи є пробіли обмежувачами лексем або ні, чи можуть речення розміщуватись в декількох стрічках вхідного тексту чи для цього потрібні спеціальні ознаки продовження.
Синтаксичний аналіз
Під час синтаксичного аналізу, речення вихідної програми розпізнаються як мовні конструкції, що описані граматикою. Ми можемо дивитись на цей процес як на побудову дерева граматичного розбору речень, що транслюються. Методи граматичного розбору можна розбити на два великі класи – висхідні и нисхідні – в відповідності з порядком побудови дерева граматичного розбору. Нисхідні методи (зверху вниз) починають з правила граматики, яке визначає кінцеву ціль аналізу з кореня дерева граматичного розбору, і намагаються так його нарощувати, щоб наступні вузли дерева відповідали синтаксису аналізуючого речення. Висхідні методи (знизу вверх) починають з кінцевих вузлів дерева граматичного розбору і намагаються об’єднати їх побудовою вузлів все більш і більш високого рівня до тих пір, поки не буде досягнутий корінь дерева.
Також нисхідний метод називають рекурсивним спуском. Процесор граматичного розбору, при цьому, складається з окремих процедур для кожного нетермінального символу, визначеного в граматиці. Кожна така процедура намагається у вхідному потоці знайти підстрічку, яка починається з текучої лексеми, яка може бути інтерпретована як нетермінальний символ, зв’язаний з даною процедурою. В процесі роботи вона може визвати інші подібні процедури або навіть рекурсивно саму себе для пошуку інших не термінальних символів. Якщо ця процедура знаходить відповідний нетермінальний символ, то вона завершує свою роботу, передає у програму яка її визвала ознаку успішного завершення і встановлює вказівник текучої лексеми на першу лексему після розпізнаної підстроки. Якщо ж процедура не може знайти підстроку, яка могла б бути інтерпретована як нетермінальний символ, вона закінчується з ознакою невдачі або ж викликає процедуру видачі діагностичного повідомлення і процедуру відновлення.
Приклад:
Данна програма шукае в вихідному коді з файлу BlocCode.txt команду множеня MUL CX і виконуе її. Ім'я файлу передаеться як параметр командної стрічки:
.model tiny
.code
org 100h
begin:
jmp start
Ident dw 0
mes1 db 'Can''t open file!',13,10,'$'
mes2 db 'Command MUL CX was executed',13,10,'$'
string db 12 dup (0)
comline db 'MUL CX',0
buffer db 50 dup (0)
start:
mov bx,82h ; з адреси 82h починається параметр стрічки
mov si,0 ; в PSP
next:
mov al,byte ptr [bx+si] ; в циклі зчитуємо з PSP ім’я файлу
cmp al,0Dh ; яке закінчується 0Dh
jz Open
mov [string+si],al
inc si
jmp next
Open: ; відкриваємо файл для читаня
mov ah,3Dh
mov al,00100000b
mov dx,offset string
int 21h
jnc Done
mov ah,9
mov dx,offset mes1
int 21h
jmp Exit
Done:
mov Ident,ax ; в ax записуємо номер ідентифікатора
Reading:
mov si,0
Read:
mov ah,3Fh ; циклічно зчитуємо з файлу в буфер символи
mov bx,Ident ; поки не зустрінемо 0Dh
mov cx,1
mov dx,offset buffer
add dx,si
int 21h
cmp buffer+si,0Dh
jz Compare
inc si
jmp Read
Compare: ; коли вся команда знаходиться в буфері
mov si,0 ; порівнюємо її з нашою лексемою
Comp:
cmp si,6 ; довжина лексеми - 6
jz Exec
mov al,byte ptr [comline+si]
mov bl,byte ptr [buffer+si]
inc si
cmp al,bl
jz Comp
mov ah,3Fh ; стрічка в коді закінчується
mov bx,Ident ; на 0Dh 0Ah, 0Dh ми вже пройшли
mov cx,1 ; ше треба пройти 0Ah
mov dx,offset buffer ; щоб зчитувати з початку команди
int 21h
jmp Reading
CloseFile:
mov ah,3Eh ; закриваємо файл
mov bx,Ident
int 21h
Exit:
mov ax,4C00h
int 21h
Exec: ; виконання команди
push ax cx dx
mul cx
mov ah,9
mov dx,offset mes2
int 21h
pop dx cx ax
jmp CloseFile
end begin
ЗАВДАННЯ:
1.Створити програму, що поєднує властивості лексичного та синтаксичного аналізатора. Програма повинна відкрити файл з програмним кодом та проаналізувавши його (блок коду складається з послідовності інструкцій – варіантів ЗАВДАННЯ) знайти в ньому та виконати як інструкцію лексему, що задається варіантом вказаним викладачем. Для відкриття файлу необхідно застосувати засоби MS-DOS-у (функція 3Dh).
2. Запустити створену програму та задокументувати результати її виконання.
3. Скласти звіт про виконану роботу.
*Примітка:
Файл з програмним кодом для аналвізу передається аналізатору у вигляді параметру командного рядка при запуску аналізатора.
Для аналізу коду необхідно застосувати метод рекурсивного спуску.
Варіанти завдання:
ЛІТЕРАТУРА:
1.П.Абель.Язык ассемблера для ІBM PC и программирования. Пер. з англ.-М.,"Высшая школа",1992.
2.Р.Джордейн.Справочник програмиста персональных компъютеров типа ІBM PC XT и AT. - M."Финансы и статистика",1992,стор.13-31.
3.Л.О.Березко,В.В.Троценко. Особливості програмування в турбо-асемблері. -Киів,НМК ВО,1992.
4.Л.Дао. Программирование микропроцессора 8088.Пер.с англ.-М."Мир",1988.