Она содержит также декларации приоритета, представленных % 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;
public:
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 ). Фактически ее тип является ее состоянием и определяется в момент открытия.
class hbBasetbl
{
// нужен для того чтобы set_flags вызывалась ровно один раз
uint Set_flg_Counter;
ushort state;
// флаг, показывающ. открыта ли сама таблица, необходим для экстр. закрытия в случае некоректного
// открытия
bool tableopen;
hbInit ini;
protected:
uint recsize;
uint keysize; //толькодля DB_HASH
Db *table;
virtual void UsrOpen(hbTxn *tx,FileConf& conf,bool openidx,hbInitRt* irt = 0,u_int32_t op_flags = 0);
virtual void UsrClose();
void SetRecSize(uint recsize1){recsize = recsize1;}
void SetKeySize(uint keysize1){keysize = keysize1;}
uint GetType() {return ini.type;}
bool IsDup() {return (ini.st_flags & DB_DUP | ini.st_flags & DB_DUPSORT)>0;}
public:
hbEnv& env;
operator Db*(){return table;}
Db* operator ->(){return table;}
const char* GetDbName(){return ini.dbname;}
hbBasetbl(hbEnv& e,hbInit&);
virtual ~hbBasetbl(){ if(state) Close();}
void Open(hbTxn *tx,FileConf& conf,bool openidx,hbInitRt* irt = 0,u_int32_t op_flags = 0);
void Close();
virtual void Create(hbTxn *tx,FileConf& conf,hbInitRt* irt = 0,u_int32_t op_flags = 0);
virtual int Get(hbTxn *tx,hbObj *key,hbObj *val,u_int32_t flags=0); // в стиле С ( без исключений )
virtual int Pget(hbTxn *tx,hbObj *fkey,hbObj *pkey, // в стиле С ( без исключений )
hbObj *val, u_int32_t flags=0);
virtual int Del(hbTxn *tx,hbObj *key,u_int32_t flags=0); // в стиле С ( без исключений )
virtual int tGet(hbTxn *tx,hbObj *key,hbObj *val,u_int32_t flags=0); // в стиле С ++
virtual int tPget(hbTxn *tx,hbObj *fkey,hbObj *pkey, hbObj *val, u_int32_t flags=0); // в стиле С ++
virtual int tDel(hbTxn *tx,hbObj *key,u_int32_t flags=0); // в стиле С ++
virtual int Put(hbTxn *tx,hbObj *key,hbObj *val,u_int32_t flags=0);
bool IsOpen (){ return state ;}
};
Для ускорения доступа по какому-то критерию к данным в таблицах вводятся индексные таблицы. Ими могут быть любые из перечисленных, конечно в соответствии с их особенностями. Класс hbBasetbl является с одной стороны базовым классом, содержащим всю рутинную работу с флагами и основными операциями с таблицей, а с другой стороны -финальным классом для индексной таблицы .
Этот класс является базовым , и совсем неудобен для работы , если эта таблица является индексированной (то есть имеет индексы – другие индексные таблицы ). Необходим еще один класс , который будет обобщением понятия индексируемой таблицы и являться контейнером для таких индексных таблиц . Этот класс представлен ниже .
class hbPTable:public hbBasetbl{
void ErrorClose();
void eee();
void FixIdx(uint bulk_ret_buffer_size,int i,FileConf& conf);
void FixIdxForQueue(uint bulk_ret_buffer_size,int i,FileConf& conf);
void FixIdxForHash(uint bulk_ret_buffer_size,int i,FileConf& conf);
void CheckMainToIdx(uint bulk_ret_buffer_size,bool fix,FileConf& conf);
void CheckMainToIdxForQueue(uint bulk_ret_buffer_size,bool fix,FileConf& conf);
void CheckMainToIdxForHash(uint bulk_ret_buffer_size,bool fix,FileConf& conf);
void CheckIdxToMain(uint bulk_ret_buffer_size,bool fix,FileConf& conf);
void CheckIdxToMainForQueue(uint bulk_ret_buffer_size,bool fix,FileConf& conf);
void CheckIdxToMainForHash(uint bulk_ret_buffer_size,bool fix,FileConf& conf);
inline void ExportForQueue(uint bulk_ret_buffer_size,FILE* f, hbTxn* tx);
inline void ExportForHash(uint bulk_ret_buffer_size,FILE* f, hbTxn* tx);
inline void Import3(Dbt* key,Dbt* data);
inline void Import2(char* buf);
inline void Import1(FILE* f,char*& buf1,uint&);
inline void CheckForRefForQueue(uint bulk_ret_buffer_size);
inline void CheckForRefForHash(uint bulk_ret_buffer_size);
inline uint GetMaxRefRecBuf();
protected:
int sz;
IdxItem *idx;
RefItems ref;
virtual void UsrOpen(hbTxn *tx,FileConf& conf,bool openidx,hbInitRt* irt = 0,u_int32_t flags = 0);
virtual void UsrClose();
inline virtual void ExportDBTemplate(FILE*,const char*,const char*) = 0;
inline virtual void ImportDBTemplate( char* buf1,
uint buf1len,
char* buf2,
uint buf2len,
hbObj*& Key,
hbObj*& Val) = 0;
public:
//! конструктор принимает массив инициализаторов (в тч индексов)
hbPTable(hbEnv& env,hbInit& ini1);
virtual ~hbPTable();
// проверка индексной целостности
void CheckIdx(uint bulk_ret_buffer_size,bool fix);
// проверка ссылочной целостности
void CheckForRef(uint bulk_ret_buffer_size);
void Export(uint bulk_ret_buffer_size,FILE* f, hbTxn* tx);
void Import(FILE* f,char*& buf,uint&);
virtual int Pget(hbTxn *tx,int n,hbObj *fkey, hbObj* pkey, hbObj *val, u_int32_t flags=0)
{return idx[n].table.Pget(tx,fkey,pkey,val,flags);}
hbBasetbl& GetIdx(int n)
{return idx[n].table;}
inline uint GetIdxCount() {return sz;}
inline uint GetRecSize() {return recsize;}
};
Как видим, этот класс расширяет старый интерфейс путем введения утилитарных методов экспорта, импорта, различного рода проверок и операциями с индексными таблицами. Однако этот класс также не удобен в работе, так как не знает ничего о типах структур и ее характеристиках. Введение этих типов как параметров шаблона позволило бы очень упростить работу с интерфейсом индексируемой таблицы (но не расширить!). Результат приведен ниже: