Пользователь формирует файл с грамматикой, описывающей некоторый желаемый язык. Этот файл подается на вход к yapp
yapp grammar_file.yp
На выходе получаем perl-модуль, выполняющий синтаксический анализатор языка, описываемого пользователем. То есть, фактически, yapp и генерирует синтаксические анализаторы.
Чтобы подключить синтаксический анализатор, пользователь должен подготовить уже своими силами лексический анализатор и использовать примерно такой код:
use MyParser;
$parser=new MyParser();
$value=$parser->YYParse(yylex => \&lexer_sub, yyerror => \&error_sub);
Файл грамматики
1) Комментарии бывают в стиле Perl # или в стиле С // , /* */.
2) Признаки литералов и строк.
В любом грамматическом файле могут появиться только два типа символов: нетерминальные символы, назвавшие также лево-лежащие символы (имена правил), и терминальные символы названные также лексемами. Лексемы являются символами, получаемыми на выходе лексического анализатора.
Синтаксис нетерминальных символов и символьных лексем:
[A-Za-z][A-Za-z0-9_]*.
Запрещено использование название «error» для литералов.
Структура его выглядит следующим образом (очень похожа на yacc, фактически является ее подмножеством)
Файл состоит из трех секций, разделенных %%:
заголовок
%%
секция правил
нижняя секция
Заголовочная секция содержит любой корректный код Perl, который копируется дословно в самое начало будущего модуля синтаксического анализатора. Это полезная вещь, например для объявления глобальных переменных.
Она содержит также декларации приоритета, представленных %left, %right и %nonassoc(определяющ. ассоциативность).
%start указывает на правило(левую часть), выполняющееся первым.
Секция правил содержит грамматические правила:
Каждое правило состоит из слева лежащего символа (нетерминального), разделенного ':' и одним или несколькими возможными правилами, разделенными '|' и завершенными ';':
exp: exp '+' exp
| exp '-' exp
;
Правило справа может быть пустым
input: #empty
| input line
Для задания явного приоритета в случае неоднозначности следует использовать директиву %prec, дающую правилу высокий приоритет.
exp: '-' exp %prec NEG { -$_[1] }
| exp '+' exp { $_[1] + $_[3] }
| NUM
Примечательно, что YAPP позволяет встраивать в синтаксический анализатор семантику. Это организуется путем добавления в конце правила конструкций {…}, ограничивающих Perl-команды. Они встраиваются в синтаксический анализатор и выполняются после применения анализатором этого правила. Такой код может возвращать некоторую величину, используемую в определении следующего правила по дереву.
Переменные $_[1] , $_[n] являются параметрами и хранят значения разобранного правила.
Нижняя секция может содержать корректный Perl-код, встраиваемый в конце сформированного синтаксического анализатора. Там можно указать лексер, процедуру анализа ошибок.
5.Структура разрабатываемой программы
Идеология оболочки состоит в том, что пользователь будущей базы данных сначала описывает на специальном языке структуры данных, из которых должны состоять таблицы в базе (то есть фактически интерфейсы) их ссылочные связи, индексы и т.п. Затем при помощи специального транслятора он получает готовый С++ код, реализующий интерфейс работы с базой данных, определенный пользователем, включая саму базу, ее таблицы, записи данных, транзакции и некоторые другие объекты. Код, генерируемый транслятором, на самом деле, является тоже оболочкой. Дело в том, что многие части транслятора имеют под собой общее основание. Эти статические классы и функции можно выделить в библиотеку. Еще одна причина для этого – семантика самого транслятора должна быть как можно проще. И как следствие этого, сокращаются размеры генерируемых файлов.
Полученный программный код остается только включить в проект и использовать уже готовые объекты базы данных и таблиц.
Новая база данных должна располагать такими возможностями:
· Добавление пользовательских данных их модификация и удаление.
· Открытие базы в нескольких режимах: например, в нормальном многопользовательском транзакционном режиме, безопасном режиме (как правило, для монопольного доступа и используется утилитами), а также в режиме восстановления базы данных.
· Импорта данных, то есть, представления данных в некотором текстовом формате, и их перемещение в пустую базу данных
· Экспорта данных, то есть, перемещение данных из базы, и представление их в определенном текстовом формате в файле, удобном для чтения. Является взаимно обратной операцией предыдущей.
· Проверки индексной целостности. Дело в том, что иногда, вследствие различных внешних факторов (например, перепад напряжения), теряется актуальность и корректность данных в индексных таблицах, и их необходимо периодически проверять и в случае необходимости восстанавливать.
· Проверки ссылочной целостности. То есть проверка корректности логических зависимостей между таблицами в базе.
Итак, вся оболочка состоит из следующих частей:
1. Собственно, базовая библиотека статических классов, для высокоуровневой работы с Berkeley, необходимая транслятору.
2. Транслятор, который, также является генерируемой оболочкой под типы данных пользователя вокруг библиотеки.
Библиотека классов
«Движок» представляет собой библиотеку классов, которые с одной стороны являются надстройками вокруг стандартных соответствующих структур, а с другой стороны делают их интерфейс более удобным и инкапсулируют часть работы транслятора. Основными компонентами являются:
· Транзакции
· Исключения
· Базовые записи
· Таблицы
· Базы данных
· Курсоры
Базовые записи
Базовая запись – это элементарная единица хранения в таблице. Описание ее класса:
//! базовый класс для записей с vtable pointer
class hbObj{
Dbt dbt;
protected:
void dbtInit(uint s,void* d)
{
dbt.set_flags(DB_DBT_USERMEM);
dbt.set_ulen(s);
dbt.set_size(s);
dbt.set_data(d);
}
public:
operator Dbt*(){return &dbt;}
void* getData(void) {return dbt.get_data();};
uint getSize(void) {return dbt.get_size();};
hbObj() {}
virtual ~hbObj() {}
};
Этот класс не совсем удобен для непосредственного использования. Дело в том, что он ничего не знает об исходных данных, которые будет в себе содержать. Этими данными могут быть, например, размер структуры в памяти и некоторые ее методы. Простейшим решением будет введение шаблона с передачей типа хранимой структуры как его параметра.
//! реальный класс, который приводится к Dbt
template <class A> class hbRec:public hbObj
A data;
A* getPnt(void) { return &data;} // если в в A массив то можно переопределить операцию & для А
hbRec() { memset(&data,0,sizeof(A));dbtInit(sizeof(A),&data);}
hbRec(const hbRec<A>& a):data(a.data) { dbtInit(sizeof(A),&data);}
hbRec(const A& a) :data(a) { dbtInit(sizeof(A),&data);}
void SetData(const A& a) { data = a;dbtInit(sizeof(A),&data);}
virtual ~hbRec() {}
Таблицы
Диаграмма отношений существующих таблиц приведена ниже:
По аналогии с записями существует базовый класс таблиц hbBasetbl, который поддерживает работу со всеми стандартными типами таблиц (Hash, Btree, Queue). Фактически ее тип является ее состоянием и определяется в момент открытия.
Страницы: 1, 2, 3, 4, 5, 6, 7