From ab6b288e41d5ce7e2140b79758aef1d0e68e2fab Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 29 Aug 2015 04:35:33 +0100 Subject: Remove Reflection class (never was reflection) and replace it with a proper NVP lexer --- libadhocutil/Jamfile.jam | 7 +-- libadhocutil/nvpParse.h | 79 +++++++++++++++++++++++++++++++ libadhocutil/nvpParse.ll | 83 +++++++++++++++++++++++++++++++++ libadhocutil/unittests/Jamfile.jam | 10 ++++ libadhocutil/unittests/testNvpParse.cpp | 54 +++++++++++++++++++++ 5 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 libadhocutil/nvpParse.h create mode 100644 libadhocutil/nvpParse.ll create mode 100644 libadhocutil/unittests/testNvpParse.cpp 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 : : : : - "`pkg-config --cflags glibmm-2.4`" - "`pkg-config --libs glibmm-2.4`" - ; lib boost_utf : : boost_unit_test_framework ; lib Ice ; lib IceUtil ; @@ -15,7 +12,7 @@ alias ice : : : : pthread ; lib adhocutil : - [ glob *.cpp *.ice : bin ] + [ glob *.ll *.cpp *.ice : bin ] : . 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 +#include +#include +#include +#include +#include +#ifndef yyFlexLexer +#define yyFlexLexer nvpBaseFlexLexer +#include +#endif +#include + +class NvpParse : public yyFlexLexer { + public: + class ValueNotFound : public std::runtime_error { + public: + ValueNotFound(const std::string &); + }; + + typedef boost::function AssignFunc; + typedef std::map AssignMap; + + template + class TargetBase { + public: + virtual AssignFunc assign(T *) const = 0; + }; + + template + class Target : public TargetBase { + public: + Target(V T::*t) : + target(t) + { + } + + AssignFunc assign(T * t) const override + { + return [t,this](const auto & value) { + t->*target = boost::lexical_cast(value); + }; + } + + private: + V T::*target; + }; + +#define NvpTarget(T) std::map>> +#define NvpValue(c, m) { #m, boost::shared_ptr>(new NvpParse::Target(&c::m)) } + + template + 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 + +%% + +{identifier} { + name = YYText(); + BEGIN(EQUAL); +} + +{eq} { + BEGIN(VALUE); +} + +{value} { + process(YYText()); + BEGIN(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(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 + : : : + BOOST_TEST_DYN_LINK + ..//adhocutil + 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 + +#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); +} + -- cgit v1.2.3