/* -*- C++ -*- * Copyright ©2004 Hugo Mills * * This software is distributed under the terms of the GNU GPL v3 * For more information on the GPL, see the file COPYING or * visit http://www.gnu.org/ * * This software is distributed without warranty */ #ifndef PARSERTOOLS_H #define PARSERTOOLS_H #include #include #include #include "magellan/configlexer.h" #include "values.h" #include "plugin.h" // For the Plugin exceptions #undef max // Exceptions class ParseException { public: ParseException(ConfigLexer* l) : lex(l) { } ConfigLexer* lex; }; class UnknownConfigToken : public ParseException { public: UnknownConfigToken(ConfigLexer* l) : ParseException(l) { } }; class MissingSemicolon : public ParseException { public: MissingSemicolon(ConfigLexer* l) : ParseException(l) { } }; class NotAToken : public ParseException { public: NotAToken(ConfigLexer* l) : ParseException(l) { } }; // Reading & setting keyed values bool readbooleanvalue(ConfigLexer* lex, const char* key, bool* var); /* Try reading a keyword plus a triple, semicolon terminated * Returns true if successful * Returns false, and leaves the lexer untouched, if not interested * Throws exceptions on mis-parsing */ template bool readkeyedvalue(ConfigLexer* lex, const char* key, T1* var1, T2* var2, T3* var3) { if(lex->type() != TOKEN) // Syntax error -- not a token throw NotAToken(lex); if(lex->value() != key) return false; convert(var1, lex->next()->value()); // Note: no check for lex->type() if(var2 != NULL) convert(var2, lex->next()->value()); if(var3 != NULL) convert(var3, lex->next()->value()); // Read the terminating ; lex->next(); if(lex->type() != CHAR || lex->value() != ";") { // Syntax error -- missing semicolon? throw(MissingSemicolon(lex)); } lex->next(); return true; } /* Try reading a keyword plus a pair of values, semicolon terminated * Returns true if successful */ template bool readkeyedvalue(ConfigLexer* lex, const char* key, T1* var1, T2* var2) { return readkeyedvalue(lex, key, var1, var2, static_cast(NULL)); } /* Try reading a keyword plus a single value, semicolon terminated * returns true if successful */ template bool readkeyedvalue(ConfigLexer* lex, const char* key, T1* var1) { return readkeyedvalue(lex, key, var1, static_cast(NULL), static_cast(NULL)); } /* Try reading a value which is one of a fixed set of keywords * Pass the beginning and ending of the list of keywords in * "start" and "end". * Returns true if successful, * Returns false if not interested * Throws exceptions if mis-parsing */ template bool readoptionvalue(ConfigLexer* lex, const char* key, T* var, const std::string* start, const std::string* end) { if(lex->type() != TOKEN) { // Syntax error -- not a token std::cerr << "Syntax error -- not a token: '" << lex->value() << "' (parser.h " << __LINE__ << ")" << std::endl; return false; } if(lex->value() != key) return false; lex->next(); const std::string* pos = find(start, end, lex->value()); if(pos != end) *var = T(pos - start); else { // Syntax error -- unknown key std::cerr << "Syntax error -- not a valid keyword here: '" << lex->value() << "' (parser.h " << __LINE__ << ")" << std::endl; } // Read the terminating ; lex->next(); if(lex->type() != CHAR || lex->value() != ";") { // Syntax error -- missing semicolon? throw(MissingSemicolon(lex)); } lex->next(); return true; } /* Try to read a complete plugin definition (of a given type/subtype) * by asking all plugins in the range [begin, end) whether they want * to handle it. */ template T* readplugin(ConfigLexer* lex, const std::string& type, const std::string& subtype, Iterator begin, Iterator end) { Iterator it; for(it = begin; it != end; ++it) { try { T* res = (*it)->parser(lex, type, subtype); if(res) { lex->next(); return res; } } catch(UnknownConfigToken UCT) { std::cerr << "Unknown configuration keyword '" << lex->value() << "' at line " << lex->lineno() << " of config file" << std::endl; } catch(NotAToken NAT) { std::cerr << "Expecting a keyword and found '" << lex->value() << "' at line " << lex->lineno() << " of config file" << std::endl; } catch(MissingSemicolon MS) { std::cerr << "Semicolon missing at line " << lex->lineno() << " of config file" << std::endl; } catch(PluginInitError PIE) { std::cerr << "Plugin failed to initialise (" << PIE.text << ") at line " << lex->lineno() << " of config file" << std::endl; } } if(it == end) { std::cerr << "No plugin accepted section '" << type << "' type '" << subtype << "'" << std::endl; lex->section_next(); } return NULL; } #endif