- •Міністерство освіти і науки, молоді та спорту України Національний університет “Львівська політехніка”
- •Розробка системних програмних модулів та компонент систем програмування
- •Розробка системних програмних модулів та компонент систем програмування мета курсової роботи
- •Структура та обсяг курсової роботи
- •Вимоги до оформлення
- •Захист роботи
- •Теоретичні відомості
- •1. Лексичний аналіз
- •1.2. Побудова лексичного аналізатора засобами скінченного автомату
- •1.2.1. Особливості побудови лексичного аналізатора засобами скінченного автомату
- •1.2.2. Приклад побудови лексичного аналізатора засобами скінченного автомату
- •1.3. Інші способи Побудови лексичного аналізатора
- •1.3.1. Побудова лексичного аналізатора засобами регулярних виразів
- •1.3.2. Побудова лексичного аналізатора засобами спеціалізованого програмного забезпечення
- •1.3.3. Приклад файлу з правилами виділення лексем для Flex
- •2. Синтаксичний аналіз
- •2.1. Визначення синтаксичних правил мови програмування за допомогою формул Бекуса-Наура
- •2.1.1. Приклад визначення натуральних чисел за допомогою формул бнф
- •2.1.2. Формули Бекуса-Наура для мови Pascal
- •2.2. Способи побудови синтаксичного аналізатора
- •2.2.1. Низхідний аналіз
- •2.2.2. Приклад низхідного граматичного розбору
- •2.2.3. Розробка низхідного синтаксичного аналізатора
- •2.2.3.1. Опис вхідної мови в термінах нотації Бекуса-Наура
- •2.2.3.2. Розробка дерева граматичного розбору
- •2.2.4. Висхідний аналіз
- •2.2.5. Генератори синтаксичних аналізаторів
- •2.2.6. Приклад тексту специфікації для zubr
- •3. Генератор коду
- •3.1. Загальні принципи генерації коду
- •3.2. Способи внутрішнього представлення програм
- •3.2.1. Багатоадресний код з неявно іменованим результатом (тріади)
- •3.2.2. Постфіксна форма запису операцій (зворотний польський запис)
- •3.2.3. Перетворення з інфіксної нотації в зпз
- •3.3. Обчислення виразів на стековій машині
- •3.4. Генерація коду з використанням співпроцесора
- •Література
- •Завдання на курсову роботу
1.3. Інші способи Побудови лексичного аналізатора
1.3.1. Побудова лексичного аналізатора засобами регулярних виразів
Побудова лексичного аналізатора (сканера) засобами регулярних виразів можлива при використанні мов програмування з вбудованою підтримкою регулярних виразів, наприклад, C#, PHP, Python, Java, Perl, тощо. При цьому за допомогою регулярних виразів задають правила виділення лексем і ідентифікаторів з подальшим утворенням таблиці лексем і ідентифікаторів. Наприклад ідентифікатор, який починається з знаку «_» після якого іде буква після якої може зустрічатися або буква або цифра нуль або більше разів можна виділити задавши регулярний вираз: '_[a-zA-Z][a-zA-Z0-9]*'.
1.3.2. Побудова лексичного аналізатора засобами спеціалізованого програмного забезпечення
Побудова лексичного аналізатора (сканера) засобами генераторів лексичних аналізаторів, наприклад, інструменту і мови Lex або Flex відбувається в 3 етапи (рис.1.7).
Рис.1.7. Створення лексичного аналізатора засобами Lex або Flex.
Flex на вході отримує текст у вільному форматі й правила виділення лексем. На виході дає код аналізатора, в вигляді функції на мові C.
Правила задаються в вигляді регулярних виразів ліворуч і, здебільшого, коду на мові C праворуч. Правила містять три секції, відокремлені рядком «%%»:
визначення
%%
правила
%%
код користувача
Визначення містять стартові значення й визначення, правила, безпосередньо самі вирази й дії, що відповідають їм, користувацький код просто включається в вивід flex. Деякі секції можуть бути відсутніми.
Функція аналізатора отримує текст на вході й виконує заданий код для кожної знайденої лексеми. Наприклад, код:
%%
username
printf( "%s", getlogin() );
для кожного входження username в тексті, виконає код printf("%s",getlogin()).
Дана функція виведе в потік рядок, що повертається функцією getlogin(). Тобто, кожне входження username у вхідному потоці буде замінено значенням, поверненим getlogin(). Наприклад, іменем поточного користувача.
Нижче наведено правила, відповідно до яких остаточна функція має друкувати на виході тип лексеми (if, змінна, число, унарна чи бінарна операція) та значення для деяких лексем:
%%
if printf ("IF statement\n");
[a-z]+ printf ("tag, value %s\n", yytext);
{D}+ printf ("decimal number %s\n", yytext);
"++" printf ("unary op\n");
"+" printf ("binary op\n");
Приклад підрахунку кількості рядків і символів у тексті:
%{
int num_lines = 0, num_chars = 0;
%}
%%
\n
++num_lines; ++num_chars;
.
++num_chars;
%%
main()
{
yylex();
printf( "# of lines = %d, # of chars = %d\n", num_lines, num_chars );
}
Далі, функцію, створену генератором можна використати з генераторами синтаксичних аналізаторів. Зазвичай flex використовують з yacc чи bison. Синтаксичний аналізатор використовує виклик функції yylex(), створеної генератором flex для пошуку наступної лексеми.
1.3.3. Приклад файлу з правилами виділення лексем для Flex
В даному прикладі розглядається побудова файлу з правилами виділення лексем для flex для імперативної мови програмування PL/0.
Розпізнаються наступні лексеми: '+', '-', '*', '/', '=', '(', ')', ',', ';', '.', ':=', '<', '<=', '<>', '>', '>='; числа: 0-9 {0-9}; ідентифікатори: a-zA-Z {a-zA-Z0-9} та ключові слова: begin, call, const, do, end, if, odd, procedure, then, var, while.
Файл lex.l матиме наступний вигляд:
%{
#include "y.tab.h"
%}
digit [0-9]
letter [a-zA-Z]
%%
"+" { return PLUS; }
"-" { return MINUS; }
"*" { return TIMES; }
"/" { return SLASH; }
"(" { return LPAREN; }
")" { return RPAREN; }
";" { return SEMICOLON; }
"," { return COMMA; }
"." { return PERIOD; }
":=" { return BECOMES; }
"=" { return EQL; }
"<>" { return NEQ; }
"<" { return LSS; }
">" { return GTR; }
"<=" { return LEQ; }
">=" { return GEQ; }
"begin" { return BEGINSYM; }
"call" { return CALLSYM; }
"const" { return CONSTSYM; }
"do" { return DOSYM; }
"end" { return ENDSYM; }
"if" { return IFSYM; }
"odd" { return ODDSYM; }
"procedure" { return PROCSYM; }
"then" { return THENSYM; }
"var" { return VARSYM; }
"while" { return WHILESYM; }
{letter}({letter}|{digit})* {
yylval.id = (char *)strdup(yytext);
return IDENT; }
{digit}+ { yylval.num = atoi(yytext);
return NUMBER; }
[ \t\n\r] /* skip whitespace */
. { printf("Unknown character [%c]\n",yytext[0]);
return UNKNOWN; }
%%
int yywrap(void){return 1;}
