From 3eceb7e1d04cd38f123eb05b5d2e6773b2b29feb Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 29 May 2015 21:26:15 +0100 Subject: Parse .p2 config files with flex --- project2/Jamfile.jam | 1 + project2/files/Jamfile.jam | 4 +- project2/files/configFlexLexer.cpp | 28 ++++++++++++++ project2/files/configFlexLexer.h | 27 ++++++++++++++ project2/files/configuration.ll | 64 ++++++++++++++++++++++++++++++++ project2/files/optionsSource.cpp | 47 +++++------------------- project2/files/unittests/Jamfile.jam | 18 +++++++++ project2/files/unittests/test.config | 20 ++++++++++ project2/files/unittests/testConfig.cpp | 65 +++++++++++++++++++++++++++++++++ 9 files changed, 235 insertions(+), 39 deletions(-) create mode 100644 project2/files/configFlexLexer.cpp create mode 100644 project2/files/configFlexLexer.h create mode 100644 project2/files/configuration.ll create mode 100644 project2/files/unittests/Jamfile.jam create mode 100644 project2/files/unittests/test.config create mode 100644 project2/files/unittests/testConfig.cpp diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam index 07681d1..cb99d35 100644 --- a/project2/Jamfile.jam +++ b/project2/Jamfile.jam @@ -40,6 +40,7 @@ build-project basics//unittests ; build-project ice//unittests ; build-project sql//unittests ; build-project xml//unittests ; +build-project files//unittests ; explicit install ; package.install install : : finalbin : finallib ; diff --git a/project2/files/Jamfile.jam b/project2/files/Jamfile.jam index 91272e0..23fd351 100644 --- a/project2/files/Jamfile.jam +++ b/project2/files/Jamfile.jam @@ -1,3 +1,5 @@ +import lex ; + alias glibmm : : : : "`pkg-config --cflags glibmm-2.4`" "`pkg-config --libs glibmm-2.4`" @@ -12,7 +14,7 @@ cpp-pch pch : pch.hpp : ; lib p2files : pch - [ glob-tree *.cpp ] + [ glob-tree *.cpp *.ll : unittests bin ] : . ../libmisc diff --git a/project2/files/configFlexLexer.cpp b/project2/files/configFlexLexer.cpp new file mode 100644 index 0000000..fe44b0c --- /dev/null +++ b/project2/files/configFlexLexer.cpp @@ -0,0 +1,28 @@ +#include "FlexLexer.h" +#include "configFlexLexer.h" +#include + +configFlexLexer::configFlexLexer(std::istream & in, const ConfigConsumer & c, const Options::CurrentPlatform & cp) : + yyFlexLexer(&in, NULL), + consume(c), + currentPlatform(cp) +{ +} + +void +configFlexLexer::process() const +{ + consume(domain.empty() ? option : domain + "." + option, platform, value, currentPlatform); +} + +void +configFlexLexer::LexerOutput(const char *, int) +{ +} + +void +configFlexLexer::LexerError(const char * msg) +{ + throw std::runtime_error(msg); +} + diff --git a/project2/files/configFlexLexer.h b/project2/files/configFlexLexer.h new file mode 100644 index 0000000..177f11c --- /dev/null +++ b/project2/files/configFlexLexer.h @@ -0,0 +1,27 @@ +#ifndef CONFIGFLEXLEXER_H +#define CONFIGFLEXLEXER_H + +#include +#include + +class configFlexLexer : public yyFlexLexer { + public: + configFlexLexer(std::istream & in, const ConfigConsumer & consume, const Options::CurrentPlatform &); + int yylex(); + + void LexerOutput(const char *, int) override; + void LexerError(const char * msg) override; + + private: + void process() const; + const ConfigConsumer & consume; + const Options::CurrentPlatform & currentPlatform; + + Glib::ustring domain; + Glib::ustring platform; + Glib::ustring option; + Glib::ustring value; +}; + +#endif + diff --git a/project2/files/configuration.ll b/project2/files/configuration.ll new file mode 100644 index 0000000..b3c8de7 --- /dev/null +++ b/project2/files/configuration.ll @@ -0,0 +1,64 @@ +%option batch +%option c++ +%option noyywrap +%option yyclass="configFlexLexer" + +%{ +#include "configFlexLexer.h" +%} + +element [a-zA-Z][a-zA-Z0-9_-]* +identifier {element}("."{element})* +begindomain "[" +enddomain "]" +beginplatform "/" +eq "=" +value [^ ][^\r\n]* + +%x DOMAIN +%x PLATFORM +%x POSTOPT +%x VALUE +%x EQUAL + +%% + +{begindomain} { + domain.clear(); + yy_push_state(DOMAIN); +} + +{identifier} { + domain = YYText(); +} + +{enddomain} { + yy_pop_state(); +} + +{identifier} { + option = YYText(); + platform.clear(); + value.clear(); + BEGIN(POSTOPT); +} + +{beginplatform} { + BEGIN(PLATFORM); +} + +{eq} { + BEGIN(VALUE); +} + +{identifier} { + platform = YYText(); + BEGIN(EQUAL); +} + +{value} { + value = YYText(); + process(); + BEGIN(INITIAL); +} + diff --git a/project2/files/optionsSource.cpp b/project2/files/optionsSource.cpp index c09132a..2ebebdb 100644 --- a/project2/files/optionsSource.cpp +++ b/project2/files/optionsSource.cpp @@ -1,9 +1,10 @@ #include #include #include "optionsSource.h" -#include -#include -#include +#include +#include "configFlexLexer.h" +#include +#include FileOptions::FileOptions(const boost::filesystem::path & f) : file(boost::filesystem::absolute(f)) @@ -14,42 +15,12 @@ void FileOptions::loadInto(const ConfigConsumer & consume, const Options::CurrentPlatform & platform) const { if (boost::filesystem::exists(file)) { - Glib::RefPtr cfg = Glib::IOChannel::create_from_file(file.string(), "r"); - Glib::ustring line; - Glib::ustring prefix; - while (cfg->read_line(line) != Glib::IO_STATUS_EOF) { - switch (line[0]) { - case '#': - continue; - case '[': - prefix = line.substr(1, line.find(']', 2) - 1); - boost::algorithm::trim_if(prefix, Glib::Unicode::isspace); - if (!prefix.empty()) { - prefix += '.'; - } - break; - default: - Glib::ustring name, plat; - Glib::ustring::size_type eq = line.find('='); - Glib::ustring::size_type sl = line.find('/'); - if (sl < eq && sl != (Glib::ustring::size_type)-1) { - name = line.substr(0, sl); - plat = line.substr(sl + 1, eq - sl - 1); - } - else { - name = line.substr(0, eq); - } - boost::algorithm::trim_if(name, Glib::Unicode::isspace); - if (name.empty()) { - continue; - } - Glib::ustring val = line.substr(eq + 1); - boost::algorithm::trim_if(plat, Glib::Unicode::isspace); - boost::algorithm::trim_if(val, Glib::Unicode::isspace); - consume(prefix + name, plat, val, platform); - break; - } + std::ifstream in(file.string()); + if (!in.good()) { + throw std::runtime_error("Couldn't open configuration file " + file.string()); } + configFlexLexer flex(in, consume, platform); + while (flex.yylex()) ; } } diff --git a/project2/files/unittests/Jamfile.jam b/project2/files/unittests/Jamfile.jam new file mode 100644 index 0000000..236938d --- /dev/null +++ b/project2/files/unittests/Jamfile.jam @@ -0,0 +1,18 @@ +import testing ; + +lib boost_system ; +lib boost_filesystem ; + +path-constant me : . ; + +run + testConfig.cpp + : : : + ..//p2files + ../../ut//p2ut + ../../common//p2common + boost_filesystem + ROOT=\"$(me)\" + test.config + : testConfig ; + diff --git a/project2/files/unittests/test.config b/project2/files/unittests/test.config new file mode 100644 index 0000000..aefcce9 --- /dev/null +++ b/project2/files/unittests/test.config @@ -0,0 +1,20 @@ +noscope1 = 1 +noscope2 = two +noscope3and4 = 3 & four + +some.scope = scoped +some.more.scope = more scoped + +[domain] +subscope = 1 +sub.scope = 2 + +[ other.domain ] +subscope = 1 +sub.scope = 2 + +[] +noscope5 = 5 +platform / test = testonly +platform = all + diff --git a/project2/files/unittests/testConfig.cpp b/project2/files/unittests/testConfig.cpp new file mode 100644 index 0000000..45fafac --- /dev/null +++ b/project2/files/unittests/testConfig.cpp @@ -0,0 +1,65 @@ +#define BOOST_TEST_MODULE TestFilesConfig +#include + +#include +#include +#include +#include "../optionsSource.h" +#include + +class TestConfigConsumer : public ConfigConsumer { + public: + typedef boost::tuple OptionPlatform; + typedef std::map OptionsMap; + class NotFound : public std::runtime_error { + public: + NotFound(const OptionPlatform & op) : std::runtime_error("NotFound: " + op.get<0>() + " / " + op.get<1>()) { } + }; + void operator()(const Glib::ustring & option, const Glib::ustring & platform, const Glib::ustring & value, const Options::CurrentPlatform &) const override + { + options[OptionPlatform(option, platform)] = value; + } + + const Glib::ustring & operator()(const Glib::ustring & name, const Glib::ustring & platform) const + { + return safeMapLookup(options, {name, platform}); + } + const Options::Option * get(const Glib::ustring &) const override { return NULL; } + mutable OptionsMap options; +}; + +BOOST_AUTO_TEST_CASE( readMissingFile ) +{ + TestConfigConsumer options; + OptionsSourcePtr file = OptionsSourcePtr(new FileOptions(RootDir / "nothere.config")); + file->loadInto(options, []() { return Glib::ustring(); }); + BOOST_REQUIRE(options.options.empty()); +} + +BOOST_AUTO_TEST_CASE( readConfigFile ) +{ + TestConfigConsumer options; + BOOST_REQUIRE(boost::filesystem::exists(RootDir / "test.config")); + OptionsSourcePtr file = OptionsSourcePtr(new FileOptions(RootDir / "test.config")); + file->loadInto(options, []() { return Glib::ustring(); }); + // Count + BOOST_REQUIRE_EQUAL(12, options.options.size()); + // Simple + BOOST_REQUIRE_EQUAL("1", options("noscope1", "")); + BOOST_REQUIRE_EQUAL("two", options("noscope2", "")); + BOOST_REQUIRE_EQUAL("3 & four", options("noscope3and4", "")); + BOOST_REQUIRE_EQUAL("scoped", options("some.scope", "")); + BOOST_REQUIRE_EQUAL("more scoped", options("some.more.scope", "")); + // Domain + BOOST_REQUIRE_EQUAL("1", options("domain.subscope", "")); + BOOST_REQUIRE_EQUAL("2", options("domain.sub.scope", "")); + // Other Domain + BOOST_REQUIRE_EQUAL("1", options("other.domain.subscope", "")); + BOOST_REQUIRE_EQUAL("2", options("other.domain.sub.scope", "")); + // No domain + BOOST_REQUIRE_EQUAL("5", options("noscope5", "")); + // Platform + BOOST_REQUIRE_EQUAL("testonly", options("platform", "test")); + BOOST_REQUIRE_EQUAL("all", options("platform", "")); +} + -- cgit v1.2.3