В первой части необходимо написать лексический анализатор.
Чтобы упростить жизнь студентам, используется программа Flex(...На входе получает текст в свободном формате и правила выделения лексем. На выходе даёт код анализатора, в виде функции на языке C...).
Попросту говоря, нужно из исходного файла программы выделить ключевые слова языка(например слова class, int и т.д.), идентификаторы(имена переменных, функций, классов) и спецсимволы(например []{},. и т.д.).
Файл правил для 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, "\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
Чтобы упростить жизнь студентам, используется программа Flex(...На входе получает текст в свободном формате и правила выделения лексем. На выходе даёт код анализатора, в виде функции на языке C...).
Попросту говоря, нужно из исходного файла программы выделить ключевые слова языка(например слова class, int и т.д.), идентификаторы(имена переменных, функций, классов) и спецсимволы(например []{},. и т.д.).
Файл правил для 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
Комментариев нет:
Отправить комментарий