пятница, 20 января 2012 г.

ТЯП часть 1. Лексический анализатор.

В первой части необходимо написать лексический анализатор.
Чтобы упростить жизнь студентам, используется программа Flex(...На входе получает текст в свободном формате и правила выделения лексем. На выходе даёт код анализатора, в виде функции на языке C...).



Попросту говоря, нужно из исходного файла программы выделить ключевые слова языка(например слова classint и т.д.), идентификаторы(имена переменных, функций, классов) и спецсимволы(например  []{},. и т.д.).


Файл правил для Flex генерируется специальным образом. Он состоит из трёх секций:
определения
%%
правила
%%
пользовательский код


В первой секции нужно сделать все #include'ы и объявить все переменные, а так же объявить все стартовые состояния и задать настройки.
Во второй мы опишем правила для языка в заданном формате.
В третьей - собственные функции.


Рассмотрим их подробнее.


Начнём с секции #2. Я решил сначала разобрать её, потому что в ней сосредоточена самая главная часть  будушего анализатора - правила. 


Правила, в принципе, бывают для двух типов данных: постоянных и переменных.


Что такое постоянные данные? Это те данные, которые никогда в языке не изменятся и несут служебную информацию - if всегда будет if'ом, а { никогда не станет [.


Что такое переменные данные? Это те данные, которые могут изменяться программистом по усмотрению. Это имена классов / функций / переменных перечислителей(далее всё это будем называть идентификаторами), строки, символы и числа.


Правила описываются в виде формате значение действие; по правилу одна строка - одно правило.
Если в действие попадает > 1 оператора, необходимо заключить их в {}.


Рассмотрим пример для постоянных данных:


"return" printf("Ключевое слово %s\n", yytext);
"if"        printf("Ключевое слово %s\n", yytext);
"="       printf("Операция %s\n", yytext);


В самом начале нужно лишь проверить, что анализатор правильно парсит текст и просто выводить данные о найденых лексемах.

Что касается переменных данных, то для их выделения используются регулярные выражения.
Рассмотрим пример для переменных данных:


0x[A-Fa-f0-9]+ {
    sscanf(yytext, "%x", &myint);
    printf("Шестнадцатеричная константа %x\n", myint);
} 
[A-Za-z_][A-Za-z_0-9]printf("Идентификатор %s\n", yytext);




В примере с шестнадцатеричным числом мы используем переменную myint. Её необходимо объявить в секции #1. Все переменные, которые будут использоваться в действиях, 
необходимо объявлять именно там.


Для считывания строк и комментариев в Flex'е введены понятия стартовых значений. 
Они бывают двух типов, включающие и исключающие, об этом подробнее в методичке.


Что нужно помнить о них:
а) Их нужно объявить в секции #1
б) По окончанию считывания в состоянии, отличном от начального, нужно переходить в стартовое состояние командой BEGIN(INITIAL);


Рассмотрим пример для считывания строк(в Objective-C строки имеют вид @"string"):


%{
//в секции #1 объявляем строку
char str[100] //хранит строковые константы
%}


В начале секции #2 объявляем стартовое состояние для строки
%x STRING
....


Объявим условие, по которому анализатор будет переходить в состояние STRING


"@\"" {
    BEGIN(STRING);
    str[0] = 0;
}


Таким образом программа перейдёт из начального состояния в состояние STRING. Так как состояние исключающее(о чём говорит %x в объявлении), то будут работать только те правила, перед которыми указано это состояние. Другие правила будут игнорироваться!


любые символы, кроме специальных
<STRING>[^\"\\\n]+  strcat(str, yytext);
<STRING>\\n  strcat (str, "\n");
....другие спецсимволы
<STRING>\" {
    printf ("Cтрока %s\n", str);
    BEGIN(INITIAL);
}


В последней строке обрабатывается ситуация, когда мы добрались до последней кавычки(") . На этом этапе заканчивается считывание строки, мы выводим её и снова переходим в стартовое состояние.


Для комментариев ситуация похожа, только для компиляции программы нам они не потребуются, поэтому их нужно выбросить.


Ещё довольно важные замечания: пробелы и табы должны съедаться, в правилах это будет выглядеть так:
[[:blank:]]+
[\n\t]+
(т.е. при обнаружении соответствии с этими правилами анализатор не производит никаких действий).


На все остальные символы нужно ругаться, выводя ошибку
{ printf("Eror, unexpected symbol %s\n", yytext); error = 1}




В секции #3 я описал небольшой код, который открывает файл c исходным кодом программы и скармливает его функции, производящей лексический анализ - yylex(). Не забываем объявить переменные в секции #1.


void main(int argc, char* argv[])
{
    setlocale(LC_CTYPE, ".1251");
    if (argc < 2)
    {
        yyin = fopen("test.txt", "r");
    }
    else if((yyin = fopen(argv[1], "r")) == 0)
    {
        printf("Невозможно открыть файл %s\n", argv[1]);
        exit(1);
    }
    yylex();
    system("pause");
}


Чтобы заставить flex сделать вам выходной файл, нужно в командной строке запустить команду flex.exe <путь к файлу с правилами>. На выходе мы получим файл lex.yy.c, на основе которого можно сделать компилируемый проект(просто создав пустой Visual Studio проект и подключив к нему этот файл).


Ссылка на репозиторий
http://code.google.com/p/vstu-objective-c/
Путь к файлу с лексемами
trunk/Lex/LA/LA/lexems.flex
Ссылка на homepage проекта Flex
http://flex.sourceforge.net

Комментариев нет:

Отправить комментарий