summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2015-08-29 04:35:33 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2015-08-29 04:35:33 +0100
commitab6b288e41d5ce7e2140b79758aef1d0e68e2fab (patch)
treea48d81df0ae5dbfd40550f3526ef4d47654a1599
parentMove and test lazy pointer (diff)
downloadlibadhocutil-ab6b288e41d5ce7e2140b79758aef1d0e68e2fab.tar.bz2
libadhocutil-ab6b288e41d5ce7e2140b79758aef1d0e68e2fab.tar.xz
libadhocutil-ab6b288e41d5ce7e2140b79758aef1d0e68e2fab.zip
Remove Reflection class (never was reflection) and replace it with a proper NVP lexer
-rw-r--r--libadhocutil/Jamfile.jam7
-rw-r--r--libadhocutil/nvpParse.h79
-rw-r--r--libadhocutil/nvpParse.ll83
-rw-r--r--libadhocutil/unittests/Jamfile.jam10
-rw-r--r--libadhocutil/unittests/testNvpParse.cpp54
5 files changed, 228 insertions, 5 deletions
diff --git a/libadhocutil/Jamfile.jam b/libadhocutil/Jamfile.jam
index b689242..53fe82a 100644
--- a/libadhocutil/Jamfile.jam
+++ b/libadhocutil/Jamfile.jam
@@ -1,9 +1,6 @@
import package ;
+import lex ;
-alias glibmm : : : :
- <cflags>"`pkg-config --cflags glibmm-2.4`"
- <linkflags>"`pkg-config --libs glibmm-2.4`"
- ;
lib boost_utf : : <name>boost_unit_test_framework ;
lib Ice ;
lib IceUtil ;
@@ -15,7 +12,7 @@ alias ice : : : :
<library>pthread ;
lib adhocutil :
- [ glob *.cpp *.ice : bin ]
+ [ glob *.ll *.cpp *.ice : bin ]
:
<include>.
<library>ice
diff --git a/libadhocutil/nvpParse.h b/libadhocutil/nvpParse.h
new file mode 100644
index 0000000..bb5afd8
--- /dev/null
+++ b/libadhocutil/nvpParse.h
@@ -0,0 +1,79 @@
+#ifndef ADHOCUTIL_REFLECTION_H
+#define ADHOCUTIL_REFLECTION_H
+
+#include <string.h>
+#include <string>
+#include <map>
+#include <istream>
+#include <boost/function.hpp>
+#include <boost/lexical_cast.hpp>
+#ifndef yyFlexLexer
+#define yyFlexLexer nvpBaseFlexLexer
+#include <FlexLexer.h>
+#endif
+#include <visibility.h>
+
+class NvpParse : public yyFlexLexer {
+ public:
+ class ValueNotFound : public std::runtime_error {
+ public:
+ ValueNotFound(const std::string &);
+ };
+
+ typedef boost::function<void(const std::string &)> AssignFunc;
+ typedef std::map<std::string, AssignFunc> AssignMap;
+
+ template <typename T>
+ class TargetBase {
+ public:
+ virtual AssignFunc assign(T *) const = 0;
+ };
+
+ template <typename T, typename V>
+ class Target : public TargetBase<T> {
+ public:
+ Target(V T::*t) :
+ target(t)
+ {
+ }
+
+ AssignFunc assign(T * t) const override
+ {
+ return [t,this](const auto & value) {
+ t->*target = boost::lexical_cast<V>(value);
+ };
+ }
+
+ private:
+ V T::*target;
+ };
+
+#define NvpTarget(T) std::map<std::string, boost::shared_ptr<NvpParse::TargetBase<T>>>
+#define NvpValue(c, m) { #m, boost::shared_ptr<NvpParse::Target<c, decltype(c::m)>>(new NvpParse::Target<c, decltype(c::m)>(&c::m)) }
+
+ template <typename T>
+ static void Parse(std::istream & in, const NvpTarget(T) & tm, T & t)
+ {
+ NvpParse::AssignMap am;
+ for (const auto & v : tm) {
+ am[v.first] = v.second->assign(&t);
+ }
+ return Parse(in, am);
+ }
+
+ DLL_PUBLIC static void Parse(std::istream & in, const AssignMap & m);
+
+ private:
+ NvpParse(std::istream & in, const AssignMap &);
+ ~NvpParse() = default;
+
+ int yylex() override;
+ void LexerError(const char * msg) override;
+
+ void process(const std::string & value) const;
+ std::string name;
+ const AssignMap values;
+};
+
+#endif
+
diff --git a/libadhocutil/nvpParse.ll b/libadhocutil/nvpParse.ll
new file mode 100644
index 0000000..b149b92
--- /dev/null
+++ b/libadhocutil/nvpParse.ll
@@ -0,0 +1,83 @@
+%option batch
+%option c++
+%option noyywrap
+%option 8bit
+%option stack
+%option yyclass="NvpParse"
+%option prefix="nvpBase"
+
+%{
+#include "nvpParse.h"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+%}
+
+element [a-zA-Z][a-zA-Z0-9_-]*
+identifier {element}("."{element})*
+eq "="
+value [^ ][^;]*
+semi ";"
+
+%x VALUE
+%x EQUAL
+%x SEMI
+
+%%
+
+<INITIAL>{identifier} {
+ name = YYText();
+ BEGIN(EQUAL);
+}
+
+<EQUAL>{eq} {
+ BEGIN(VALUE);
+}
+
+<VALUE>{value} {
+ process(YYText());
+ BEGIN(SEMI);
+}
+
+<SEMI>{semi} {
+ BEGIN(INITIAL);
+}
+
+<*>[ \t\r\n\f] {
+}
+
+<*>. {
+ throw std::runtime_error(std::string("Lex error at: ") + YYText());
+}
+
+%%
+#include "safeMapFind.h"
+
+NvpParse::ValueNotFound::ValueNotFound(const std::string & vn) :
+ std::runtime_error("Value not found: " + vn)
+{
+}
+
+NvpParse::NvpParse(std::istream & in, const AssignMap & v) :
+ yyFlexLexer(&in),
+ values(v)
+{
+}
+
+void
+NvpParse::process(const std::string & value) const
+{
+ safeMapLookup<ValueNotFound>(values, name)(value);
+}
+
+void
+NvpParse::LexerError(const char * msg)
+{
+ throw std::runtime_error(msg);
+}
+
+void
+NvpParse::Parse(std::istream & in, const AssignMap & m)
+{
+ NvpParse p(in, m);
+ p.yylex();
+}
+
diff --git a/libadhocutil/unittests/Jamfile.jam b/libadhocutil/unittests/Jamfile.jam
index 4a6b430..724db34 100644
--- a/libadhocutil/unittests/Jamfile.jam
+++ b/libadhocutil/unittests/Jamfile.jam
@@ -96,3 +96,13 @@ run
testLazyPointer
;
+run
+ testNvpParse.cpp
+ : : :
+ <define>BOOST_TEST_DYN_LINK
+ <library>..//adhocutil
+ <library>boost_utf
+ :
+ testNvpParse
+ ;
+
diff --git a/libadhocutil/unittests/testNvpParse.cpp b/libadhocutil/unittests/testNvpParse.cpp
new file mode 100644
index 0000000..09f3ff4
--- /dev/null
+++ b/libadhocutil/unittests/testNvpParse.cpp
@@ -0,0 +1,54 @@
+#define BOOST_TEST_MODULE NvpParse
+#include <boost/test/unit_test.hpp>
+
+#include "nvpParse.h"
+
+class TestTarget {
+ public:
+ std::string a;
+ std::string b;
+ int c;
+ double d;
+};
+
+NvpTarget(TestTarget) TestTargetMap {
+ NvpValue(TestTarget, a),
+ NvpValue(TestTarget, b),
+ NvpValue(TestTarget, c),
+ NvpValue(TestTarget, d),
+};
+
+BOOST_AUTO_TEST_CASE ( targetmap )
+{
+ BOOST_REQUIRE(TestTargetMap.find("a") != TestTargetMap.end());
+ BOOST_REQUIRE(TestTargetMap.find("b") != TestTargetMap.end());
+ BOOST_REQUIRE(TestTargetMap.find("c") != TestTargetMap.end());
+ BOOST_REQUIRE(TestTargetMap.find("d") != TestTargetMap.end());
+ BOOST_REQUIRE(TestTargetMap.find("e") == TestTargetMap.end());
+}
+
+BOOST_AUTO_TEST_CASE ( parse )
+{
+ TestTarget tt;
+ std::stringstream i("a = foo;b=bar; c=3;d=3.14");
+ NvpParse::Parse(i, TestTargetMap, tt);
+ BOOST_REQUIRE_EQUAL("foo", tt.a);
+ BOOST_REQUIRE_EQUAL("bar", tt.b);
+ BOOST_REQUIRE_EQUAL(3, tt.c);
+ BOOST_REQUIRE_CLOSE(3.14, tt.d, 0.01);
+}
+
+BOOST_AUTO_TEST_CASE ( missing )
+{
+ TestTarget tt;
+ std::stringstream i("missing=nothing;");
+ BOOST_REQUIRE_THROW(NvpParse::Parse(i, TestTargetMap, tt), NvpParse::ValueNotFound);
+}
+
+BOOST_AUTO_TEST_CASE ( bad )
+{
+ TestTarget tt;
+ std::stringstream i("{bad=");
+ BOOST_REQUIRE_THROW(NvpParse::Parse(i, TestTargetMap, tt), std::runtime_error);
+}
+