crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

> ANTLR и С++, использование классов в парсерах ANTLR
Iron Bug
  опции профиля:
сообщение 12.3.2011, 19:24
Сообщение #1


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Вот, ещё одна моя заметка из раздела "химия и жизнь", я бы так сказала :)
В общем, наверняка кому-то пригодится.

Задача: есть парсер ANTLR3 с целевым языком С (используется libantlr3c) и очень лень(или просто некогда) писать интерфейс к нему на чистом С (это медленно и для больших проектов, обычно написанных на С++, это лишний геморрой).

Решение: сгенерированный сишный ANTLR парсер можно компилить как CPP!
Но для этого нужны некоторые хитрости, которые я и попытаюсь изложить. Я рассмотрю пример, в котором в парсере используется глобальная область переменных (можно аналогично использовать и локальные области), которая содержит указатель на С++ класс. (Про области переменных см. книжку Теренса Парра и вики на сайте ANTLR, я это тут пояснять не буду).

Пример доработанного для работы с C++ ANTLR парсера:
Раскрывающийся текст
grammar My;

options
{
    language        = C;
}

scope MyGlobalScope // определяем глобальную область видимости
{
    MyScopeClass *pInstance; // это указатель на наш CPP класс
}

@preincludes
{
// тут мы обманываем компилятор, сообщая ему, что у нас не CPP-компиляция, чтобы он не ругался на некоторые конструкции
#undef  __cplusplus
}

@postincludes
{
// возвращаемся к нормальному режиму CPP после включения заголовка
#def  __cplusplus
}

@header
{
#include "antlr3.h"
#include "MyScopeClass.h"
}
....

My_rule  // какое-то правило ANTLR
scope MyGlobalScope; // используем глобальную область видимости
@init
{
    $MyGlobalScope::pInstance = new MyScopeClass(); // создаём представитель класса
    $MyGlobalScope::pInstance->init();  // инициализируем его, если нужно
}
@after
{
    delete $MyGlobalScope::pInstance; // удаляем объект после работы
}
: variant1 { $MyGlobalScope::pInstance->reportVariant(1); } // сообщаем классу о том, что мы тут нашли
| variant2  { $MyGlobalScope::pInstance->reportVariant(2); }
;


Примечания: в области видимости ANTLR (scope) могут использоваться только переменные фиксированного размера. Т.е. мы не можем использовать, например, строку (string), а можем использовать только указатель на неё. Аналогично и с классами: обязательно используются только указатели. Ну либо типы фиксированного размера (например, int, char).
Для инициализации указателей и удаления объектов после работы используются блоки @init и @after. Там же можно выполнить какие-то другие действия, например, инициализацию созданного объекта.

Из этого парсера мы получим файлы: MyLexer.h,MyLexer.c,MyParser.h,MyParser.c
Подключаем их к проекту и указываем компилятору компилировать их как CPP.

Пишем сам класс MyScopeClass:
MyScopeClass.h
Раскрывающийся текст
#ifndef __MYSCOPECLASS_H__
#define __MYSCOPECLASS_H__

class MyScopeClass
{
   MyScopeClass() {}
  ~MyScopeClass() {}
   void init();
   void reportVariant(int var);
}
#endif


MyScopeClass.cpp
Раскрывающийся текст
#include "MyScopeClass.h" // включаем наш заголовок класса

#include "MyLexer.h" // включаем код ANTLR
#include "MyParser.h"

.... // прописываем методы класса


Собственно, всё. Всё это работает и под вендой, и под линюксом.

Однако, есть ещё некоторые нюансы:

Если в заголовочник MyScopeClass.h нужно включить другой заголовочный CPP файл, то в его начало нужно поместить что-то вроде:

#ifndef __cplusplus
#define __cplusplus
#define __remove_cplusplus
#endif

а в конец

#ifdef __remove_cplusplus
#undef __cplusplus
#undef __remove_cplusplus
#endif

Это чтобы компилятор обошёл наши объявления из парсерного кода и воспринял заголовок как код CPP.

Далее, если в проект включены сразу несколько парсеров, то нужно придерживаться строгого разделения пространств имён. Проблема в том, что libantlr3c генерит все имена правил и лексем в общем пространстве имён и нельзя использовать в разных парсерах одни и те же имена для правил и лексем, иначе потом при включении заголовков они будут перекрывать друг друга. В этом случае лучше именовать все правила как-то так: My_rule1,MY_LEXEM1.

Всё. Теперь класс CPP можно смело использовать из кода ANTLR.

Сообщение отредактировал Iron Bug - 12.3.2011, 19:29
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Сообщений в этой теме


Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 25.11.2024, 17:35