diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-08-29 04:35:33 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-08-29 04:35:33 +0100 |
commit | ab6b288e41d5ce7e2140b79758aef1d0e68e2fab (patch) | |
tree | a48d81df0ae5dbfd40550f3526ef4d47654a1599 | |
parent | Move and test lazy pointer (diff) | |
download | libadhocutil-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.jam | 7 | ||||
-rw-r--r-- | libadhocutil/nvpParse.h | 79 | ||||
-rw-r--r-- | libadhocutil/nvpParse.ll | 83 | ||||
-rw-r--r-- | libadhocutil/unittests/Jamfile.jam | 10 | ||||
-rw-r--r-- | libadhocutil/unittests/testNvpParse.cpp | 54 |
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); +} + |