{ // Обработчик событий vert->value(b->yvalue()); horiz->value(b->xvalue());
CrossInformation();
}
static void cb_vert(Fl_Roller* r, void*)
{ // Обработчик событий
ClientArea->yvalue(r->value()); CrossInformation();
}
//функция проверяет, пересекаются ли два заданных
//отрезка
bool OterezokXOtrezok(Point Dot_11,Point
Dot_21,Point Dot_12,Point Dot_22)
{
. . . . . . . . . . . . .
return true;
return false;
}
static void cb_horiz(Fl_Roller* r, void*)
{
ClientArea->xvalue(r->value()); CrossInformation();
}
//Функция кнопки открытия файла
static void cb_btnfile(Fl_Button*, void*)
{
char *newfile =
fl_file_chooser("Открыть файл", "*.txt", ""); if (newfile!= NULL)
{
StartLoadPolygon(newfile);
}
ClientArea->xvalue(ClientArea->w()/2);
ClientArea->yvalue(ClientArea->h()/2); ClientArea->do_callback(); ClientArea->draw(); CrossInformation();
}
//Находится ли новая точка внутри многоугольника void CrossInformation()
{
char b[30];
int x=(int)(ClientArea->xvalue()); int y=(int)(ClientArea->yvalue());
26
if(NumPoint==0)
{
sprintf(b,"Файл не загружен");
}
else
{
if(DotIsInPoligon(Poly,NumPoint,x,y)) sprintf(b,"Точка внутри полигона");
else sprintf(b,"Точка снаружи полигона");
}
txtdispl->buffer()->text(b);
}
static void cb_btnext(Fl_Button*, void*)
{
exit(0);
}
int main(int argc, char **argv)
{
// Собственно создание интерфейсного окна
Fl_Window* w= new Fl_Window(450, 550);
ClientArea = new dwin(10, 10, 430, 430);
ClientArea->callback((Fl_Callback*)cb_brow); vert = new Fl_Roller(420,450, 20, 85); vert->callback((Fl_Callback*)cb_vert);
horiz = new Fl_Roller(10, 450, 400, 20); horiz->type(1); horiz->callback((Fl_Callback*)cb_horiz);
btnfile = new Fl_Button(10,510,190,25,”Загрузить"); btnfile->tooltip("Загрузить многоугольник"); btnfile->callback((Fl_Callback*)cb_btnfile);
btnext = new Fl_Button(220, 510, 190, 25, "Выход"); btnext->tooltip("Выход из программы"); btnext->callback((Fl_Callback*)cb_btnext); btnext->shortcut(FL_ALT+'x');
txtdispl = new Fl_Text_Display(10, 480,350,25); txtbuf=new Fl_Text_Buffer(10); txtdispl->buffer(txtbuf); w->insert((Fl_Widget&)*ClientArea, 0); w->show(argc, argv);
horiz->range(0, ClientArea->w()); vert->range(0, ClientArea->h()); horiz->step(1);
vert->step(1);
ClientArea->xbounds(0, ClientArea->w()); ClientArea->ybounds(0, ClientArea->h());
// Вход в цикл событий return Fl::run();
}
27
4. ИСПОЛЬЗОВАНИЕ СРЕДСТВ АВТОМАТИЧЕСКОЙ ГЕНЕРАЦИИ ПАРСЕРОВ
Для чего используется синтаксический анализ? Во-первых, достаточно часто возникает необходимость в приложениях просто осуществить вычисления по заданной формуле. Во-вторых, необходимость в крупных программных продуктах (офисные приложения, компиляторы, бухгалтерские программы) наличия возможности расширения программными средствами удобств пользователя заставляют разработчиков встраивать в приложения так называемые скрипт-языки.
И в том и в другом случаях не обойтись без программ синтаксического разбора (парсера). Но написание такой программы – долгий и утомительный труд, на котором вас подстерегают многочисленные отладочные проблемы.
К счастью, есть программы, автоматически генерирующие синтаксические распознаватели, да еще с возможностью генерации кода, описываемого на языке высокого уровня. Простейшими примерами являются программы yacc и bison. В ОС UNIX – это стандартные компоненты системы. Для DOS и Windows существуют их аналоги. К ним относится, например, программа pcyacc разработки фирмы Abraxas software.
Что же представляет из себя yacc?
Генератор распознавателей yacc
Yacc – это универсальные средства для структуризации исходных данных программ. Пользователь задает спецификацию (грамматику языка), которая включает:
множество правил, описывающих составные части исходных данных;
действия, выполняемые при применении правила;
определение или описание процедуры нижнего уровня, анализирующей исходные данные.
Yacc отображает спецификацию в функцию yyparse() на языке C, обрабатывающую входной поток данных. Эта функция является процедурой синтаксического разбора и при выполнении обращается к низкоуровневому сканеру входных данных (лексическому распознавателю). Сканер извлекает из входного потока элементарные конструкции языка – лексемы. Лексемы сопоставляются с правилами, описывающими структуру входного текста, то есть с грамматическими правилами.
Если правило оказывается подходящим, то выполняется ассоциированное с ним действие. Действие – это фрагмент программы на языке C. Действия могут возвращать значения, а также использовать значения, возвращаемые другими действиями, например сканером.
28
Имена обозначают лексемы или нетерминальные символы. YACC требует, чтобы имена лексем были указаны явно. Хотя лексический анализатор можно включить в файл спецификаций, определение его в отдельном файле, вероятно, более соответствует принципам модульного проектирования. Подобно лексическому анализатору, в файл спецификаций могут быть также включены и другие подпрограммы.
Таким образом, каждый файл спецификаций (yacc-программа) теоретически состоит из трех секций: определений, (грамматических) правил и подпрограмм.
Структура спецификации yacc
Секции отделяются двумя знаками процента %% (знак % используется в yacc-спецификациях как универсальный управляющий). Если используются все секции, полный файл спецификаций выглядит следующим образом:
определения
%%
правила
%%
подпрограммы
Секции определений и подпрограмм являются необязательными. Минимальная допустимая yacc-спецификация – это
%%
правила
%%
Пробелы, табуляции и переводы строки, которые встречаются вне имен и зарезервированных слов, игнорируются. Комментарии могут быть везде, где допустимо имя. Они оформляются как многострочный комментарий на языке Си: /*...*/
Секция правил
Секция правил составляется из одного или большего числа грамматических правил. Грамматическое правило имеет вид
нетерминал: определение;
где определение – это последовательность из нуля или нескольких альтернатив, разделенных метасимволом «ИЛИ», в роли которого выступает символ – вертикальная черта(|). Каждая альтернатива представляет набор имен и литералов. Каждые двоеточие и точка с запятой – знаки препинания yacc'а.
Имена могут иметь произвольную длину и должны состоять из букв, точек, подчеркиваний и цифр, однако имя не может начинаться с
29
цифры. Прописные и строчные буквы различаются. Имена, используемые в теле грамматического правила, могут представлять лексемы или нетерминальные символы.
Секция определений
Всекции определений можно объявлять: терминальные символы, начальный символ, операции,
типы нетерминалов.
Терминальные символы объявляются при помощи декларатора
%token: %token имя1 имя2.
Считается, что всякое имя, не описанное в секции определений, представляет нетерминальный символ. Каждый нетерминальный символ должен встретиться в левой части по крайней мере одного правила.
Из всех нетерминальных символов особую роль играет начальный символ. По умолчанию начальным считается символ, стоящий в левой части первого грамматического правила в секции правил.
Из начального символа можно вывести любую правильную фразу языка. Можно явно объявить начальный символ в секции определений при помощи ключевого слова %start:
%start начальный_символ
Операции можно объявить деклараторами %left, %right, %noassoc:
%left '+' '-'
%left '*' '/' %right '='
%noassoc UNARYMINUS
Лексемы, встречающиеся в одном деклараторе, считаются равноприоритетными операциями.
Приоритет операций увеличивается от декларатора к декларатору. Например, операции „*‟ и „/‟ в приведенном примере имеют более высокий приоритет, чем „+‟ и „-„.
Тип нетерминального символа задается декларатором %type:
% type expr double
Секция процедур
В этой секции должна быть описана функция main(), вызывающая процедуру разбора, ее имя – yyparse(). Здесь же можно поместить подпрограммы обработки ошибок, служебные функции и лексический распознаватель (сканер).
30