Commit bd6b703e authored by Simon Morlat's avatar Simon Morlat

Add GrammarLoader class, that helps loading grammars from installation directories.

parent 8b218971
......@@ -31,7 +31,6 @@ option(ENABLE_STRICT "Build with strict compile options." YES)
option(ENABLE_TOOLS "Turn on or off compilation of tools." YES)
option(ENABLE_UNIT_TESTS "Enable compilation of unit tests." YES)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
if(NOT CPACK_GENERATOR AND NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX)
......@@ -119,6 +118,11 @@ else()
set(EXPORT_TARGETS_NAME "belr")
endif()
set(BELR_GRAMMARS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/belr/grammars")
set(BELR_GRAMMARS_RELATIVE_DIR "${CMAKE_INSTALL_DATADIR}/belr/grammars")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
add_subdirectory(include)
add_subdirectory(src)
if(ENABLE_UNIT_TESTS AND BCTOOLBOX_TESTER_FOUND)
......
/*
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef belr_config_h
#define belr_config_h
#define BELR_GRAMMARS_DIR "${BELR_GRAMMARS_DIR}"
#define BELR_GRAMMARS_RELATIVE_DIR "${BELR_GRAMMARS_RELATIVE_DIR}"
#endif
......@@ -279,30 +279,20 @@ public:
**/
BELR_PUBLIC void include(const std::shared_ptr<Grammar>& grammar);
/**
* Add arule to the grammar.
* Add a rule to the grammar.
* @param name the name of the rule
* @param rule the rule recognier, must be an instance of belr::Recognizer.
* @return the rule (the recognizer). The recognizer is given the name of the rule.
* @param rule the rule recognizer, must be an instance of belr::Recognizer.
* @note The grammar takes ownership of the recognizer, which must not be used outside of this grammar.
* TODO: use unique_ptr to enforce this, or make a copy ?
**/
template <typename _recognizerT>
std::shared_ptr<_recognizerT> addRule(const std::string & name, const std::shared_ptr<_recognizerT> &rule){
assignRule(name, rule);
return rule;
}
void addRule(const std::string & name, const std::shared_ptr<Recognizer> &rule);
/**
* Extend a rule from the grammar.
* This corresponds to the '/=' operator of ABNF definition.
* @param name the name of the rule to extend.
* @param rule the recognizer of the extension.
* @return the rule.
**/
template <typename _recognizerT>
std::shared_ptr<_recognizerT> extendRule(const std::string & name, const std::shared_ptr<_recognizerT> &rule){
_extendRule(name, rule);
return rule;
}
void extendRule(const std::string & name, const std::shared_ptr<Recognizer> &rule);
/**
* Find a rule from the grammar, given its name.
* @param name the name of the rule
......@@ -343,8 +333,6 @@ public:
**/
BELR_PUBLIC int load(const std::string &filename);
private:
void assignRule(const std::string &name, const std::shared_ptr<Recognizer> &rule);
void _extendRule(const std::string &name, const std::shared_ptr<Recognizer> &rule);
std::map<std::string,std::shared_ptr<Recognizer>> mRules;
//The recognizer pointers create loops in the chain of recognizer, preventing shared_ptr<> to be released.
//We store them in this list so that we can reset them manually to break the loop of reference.
......@@ -354,7 +342,6 @@ private:
}//end of namespace
#endif
......@@ -202,6 +202,47 @@ public:
private:
Parser<std::shared_ptr<ABNFBuilder>> mParser;
};
}
/**
* The GrammarLoader creates Grammar objects from binary files lookup and loaded from a pre-defined set of paths.
* The binary files have to be created from the ABNF grammar files using the belr-compiler tool.
**/
class GrammarLoader{
public:
/**
* Obtain the GrammarLoader singleton.
**/
BELR_PUBLIC static GrammarLoader &get();
/**
* Clear all specific lookup paths previously set in the GrammarLoader.
* The system paths are not erased.
**/
BELR_PUBLIC void clear();
/**
* Add a specific path to lookup for grammar binary files.
* Any specific paths added by this method is actually prepended to the list of paths.
* @param path a path
**/
BELR_PUBLIC void addPath(const std::string &path);
/**
* Build a grammar loaded from a binary file whose name is 'fileName' argument.
* The file searched from the list of specific paths eventually added by addPath(), and the system paths.
* That the GrammarLoader is initialized with two system paths used for searching:
* - ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/belr/grammars
* - ${CMAKE_INSTALL_DATADIR}/belr/grammars
* @param fileName the file name of grammar
**/
BELR_PUBLIC std::shared_ptr<Grammar> load(const std::string &fileName);
private:
GrammarLoader();
GrammarLoader(const GrammarLoader & other); //forbids copy constructor
std::string lookup(const std::string &fileName, const std::list<std::string> &dirs);
std::list<std::string> mSystemPaths;
std::list<std::string> mAppPaths;
bool isAbsolutePath(const std::string &path);
static GrammarLoader *sInstance;
};
}//end of namespace
#endif
......@@ -639,7 +639,7 @@ Grammar::~Grammar() {
}
}
void Grammar::assignRule(const string &argname, const shared_ptr<Recognizer> &rule){
void Grammar::addRule(const string &argname, const shared_ptr<Recognizer> &rule){
string name=tolower(argname);
rule->setName(name);
auto it=mRules.find(name);
......@@ -655,7 +655,7 @@ void Grammar::assignRule(const string &argname, const shared_ptr<Recognizer> &ru
mRules[name]=rule;
}
void Grammar::_extendRule(const string &argname, const shared_ptr<Recognizer> &rule){
void Grammar::extendRule(const string &argname, const shared_ptr<Recognizer> &rule){
string name=tolower(argname);
rule->setName("");
auto it=mRules.find(name);
......@@ -795,7 +795,7 @@ int Grammar::load(const std::string &filename){
break;
}
BCTBX_SLOGD<<"Added rule "<< rule->getName();
assignRule(rule->getName(), rule);
addRule(rule->getName(), rule);
}
ifs.close();
if (!isComplete()){
......
......@@ -22,9 +22,10 @@
#include "belr/abnf.h"
#include "belr/parser.h"
#include "belr/grammarbuilder.h"
#include "config.h"
using namespace std;
// =============================================================================
......@@ -357,4 +358,67 @@ shared_ptr<Grammar> ABNFGrammarBuilder::createFromAbnfFile(const string &path, c
return createFromAbnf(sstr.str(), gram);
}
GrammarLoader * GrammarLoader::sInstance = nullptr;
GrammarLoader::GrammarLoader(){
mSystemPaths.push_back(BELR_GRAMMARS_DIR);
mSystemPaths.push_back(BELR_GRAMMARS_RELATIVE_DIR);
}
GrammarLoader &GrammarLoader::get(){
if (sInstance == nullptr){
sInstance = new GrammarLoader();
}
return *sInstance;
}
void GrammarLoader::addPath(const string &path){
mAppPaths.push_front(path);
}
void GrammarLoader::clear(){
mAppPaths.clear();
}
string GrammarLoader::lookup(const string &fileName, const list<string> & paths){
for(auto & it : paths){
ostringstream absFilename;
absFilename<<it<<"/"<<fileName;
if (bctbx_file_exist(absFilename.str().c_str()) == 0){
return absFilename.str();
}
}
return "";
}
bool GrammarLoader::isAbsolutePath(const string &fileName){
if (fileName[0] == '/') return TRUE;
#ifdef _WIN32
/* for windows:*/
if (fileName.size() > 2 && fileName[1] == ':') return TRUE;
#endif
return FALSE;
}
shared_ptr<Grammar> GrammarLoader::load(const string &fileName){
string absFilename;
if (isAbsolutePath(fileName)){
absFilename = fileName;
}
if (absFilename.empty()){
absFilename = lookup(fileName, mAppPaths);
}
if (absFilename.empty()){
absFilename = lookup(fileName, mSystemPaths);
}
if (absFilename.empty()){
bctbx_error("Could not load grammar %s because the file could not be located.", fileName.c_str());
return nullptr;
}
shared_ptr<Grammar> ret = make_shared<Grammar>(fileName);
if (ret->load(absFilename) == 0) return ret;
return nullptr;
}
}//end of namespace
......@@ -60,3 +60,9 @@ if(NOT IOS)
endif()
install(FILES ${GRAMMAR_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/belr-tester/res")
set(BINARY_GRAMMAR_FILES
res/belr-grammar-example.blr
)
install(FILES BINARY_GRAMMAR_FILES DESTINATION "${CMAKE_INSTALL_DATADIR}/belr/grammars")
......@@ -131,10 +131,17 @@ static void aliases_rules(void) {
}
static void test_grammar_loader(void) {
GrammarLoader & loader = GrammarLoader::get();
loader.addPath("res");
shared_ptr<Grammar> grammar = loader.load("belr-grammar-example.blr");
BC_ASSERT_PTR_NOT_NULL(grammar);
}
static test_t tests[] = {
TEST_NO_TAG("SIP grammar save and load", sipgrammar_save_and_load),
TEST_NO_TAG("SIP grammar with aliases rules", aliases_rules)
TEST_NO_TAG("SIP grammar with aliases rules", aliases_rules),
TEST_NO_TAG("Grammar loader", test_grammar_loader)
};
test_suite_t grammar_suite = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment