summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2015-05-29 21:26:15 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2015-05-29 21:26:15 +0100
commit3eceb7e1d04cd38f123eb05b5d2e6773b2b29feb (patch)
tree3be128f57c111beb4648b197d5a7b733076de1ff
parentInitialize the gcrypt library on start up (diff)
downloadproject2-3eceb7e1d04cd38f123eb05b5d2e6773b2b29feb.tar.bz2
project2-3eceb7e1d04cd38f123eb05b5d2e6773b2b29feb.tar.xz
project2-3eceb7e1d04cd38f123eb05b5d2e6773b2b29feb.zip
Parse .p2 config files with flex
-rw-r--r--project2/Jamfile.jam1
-rw-r--r--project2/files/Jamfile.jam4
-rw-r--r--project2/files/configFlexLexer.cpp28
-rw-r--r--project2/files/configFlexLexer.h27
-rw-r--r--project2/files/configuration.ll64
-rw-r--r--project2/files/optionsSource.cpp47
-rw-r--r--project2/files/unittests/Jamfile.jam18
-rw-r--r--project2/files/unittests/test.config20
-rw-r--r--project2/files/unittests/testConfig.cpp65
9 files changed, 235 insertions, 39 deletions
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 : : : :
<cflags>"`pkg-config --cflags glibmm-2.4`"
<linkflags>"`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 ]
:
<include>.
<include>../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 <stdexcept>
+
+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 <glibmm/ustring.h>
+#include <optionsSource.h>
+
+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
+
+%%
+
+<INITIAL>{begindomain} {
+ domain.clear();
+ yy_push_state(DOMAIN);
+}
+
+<DOMAIN>{identifier} {
+ domain = YYText();
+}
+
+<DOMAIN>{enddomain} {
+ yy_pop_state();
+}
+
+<INITIAL>{identifier} {
+ option = YYText();
+ platform.clear();
+ value.clear();
+ BEGIN(POSTOPT);
+}
+
+<POSTOPT>{beginplatform} {
+ BEGIN(PLATFORM);
+}
+
+<POSTOPT,EQUAL>{eq} {
+ BEGIN(VALUE);
+}
+
+<PLATFORM>{identifier} {
+ platform = YYText();
+ BEGIN(EQUAL);
+}
+
+<VALUE>{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 <pch.hpp>
#include <boost/filesystem/convenience.hpp>
#include "optionsSource.h"
-#include <glibmm/iochannel.h>
-#include <glibmm/fileutils.h>
-#include <boost/algorithm/string/trim.hpp>
+#include <FlexLexer.h>
+#include "configFlexLexer.h"
+#include <boost/shared_ptr.hpp>
+#include <fstream>
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<Glib::IOChannel> 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
+ : : :
+ <library>..//p2files
+ <library>../../ut//p2ut
+ <library>../../common//p2common
+ <library>boost_filesystem
+ <define>ROOT=\"$(me)\"
+ <dependency>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 <boost/test/unit_test.hpp>
+
+#include <definedDirs.h>
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+#include "../optionsSource.h"
+#include <safeMapFind.h>
+
+class TestConfigConsumer : public ConfigConsumer {
+ public:
+ typedef boost::tuple<Glib::ustring, Glib::ustring> OptionPlatform;
+ typedef std::map<OptionPlatform, Glib::ustring> 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<NotFound>(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", ""));
+}
+