From ea1f1e8b65e619b8d13b67a7753ad934307837e4 Mon Sep 17 00:00:00 2001 From: Alex Cunningham Date: Thu, 15 Dec 2011 19:39:11 +0000 Subject: [PATCH] Parser now handles both namespace and class headers --- wrap/Module.cpp | 44 +++++++++---- wrap/pop_actor.h | 59 ----------------- wrap/spirit_actors.h | 123 ++++++++++++++++++++++++++++++++++++ wrap/tests/testNamespaces.h | 5 ++ wrap/tests/testWrap.cpp | 81 ++++++++++++++++-------- wrap/utilities.cpp | 32 +++++++++- wrap/utilities.h | 1 + 7 files changed, 244 insertions(+), 101 deletions(-) delete mode 100644 wrap/pop_actor.h create mode 100644 wrap/spirit_actors.h diff --git a/wrap/Module.cpp b/wrap/Module.cpp index c9a9c97d1..af8bd6696 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -16,10 +16,9 @@ #include "Module.h" #include "utilities.h" -#include "pop_actor.h" +#include "spirit_actors.h" //#define BOOST_SPIRIT_DEBUG -#include #include #include #include @@ -53,7 +52,11 @@ Module::Module(const string& interfacePath, Method method0(enable_verbose), method(enable_verbose); StaticMethod static_method0(enable_verbose), static_method(enable_verbose); Class cls0(enable_verbose),cls(enable_verbose); - vector namespaces, namespaces_return; + vector namespaces, /// current namespace tag + namespace_includes, /// current set of includes + namespaces_return; /// namespace for current return type + string include_path = ""; + const string null_str = ""; //---------------------------------------------------------------------------- // Grammar with actions that build the Class object. Actions are @@ -169,20 +172,32 @@ Module::Module(const string& interfacePath, [push_back_a(cls.static_methods, static_method)] [assign_a(static_method,static_method0)]; - Rule includes_p = str_p("#include") >> ch_p('<') >> (*(anychar_p - '>'))[push_back_a(cls.includes)] >> ch_p('>'); - Rule functions_p = constructor_p | method_p | static_method_p; - Rule class_p = !includes_p >> (str_p("class") >> className_p[assign_a(cls.name)] >> '{' >> - *(functions_p | comments_p) >> - str_p("};"))[assign_a(cls.namespaces, namespaces)][push_back_a(classes,cls)][assign_a(cls,cls0)]; + Rule include_p = str_p("#include") >> ch_p('<') >> (*(anychar_p - '>'))[assign_a(include_path)] >> ch_p('>'); - Rule namespace_def_p = str_p("namespace") >> - namespace_name_p[push_back_a(namespaces)] - >> ch_p('{') >> - *(class_p | namespace_def_p | comments_p) >> - str_p("}///\\namespace") >> !namespace_name_p // end namespace, avoid confusion with classes - [pop_a(namespaces)]; + Rule class_p = + (!include_p + >> str_p("class")[push_back_a(cls.includes, include_path)][assign_a(include_path, null_str)] + >> className_p[assign_a(cls.name)] + >> '{' + >> *(functions_p | comments_p) + >> str_p("};")) + [assign_a(cls.namespaces, namespaces)] + [append_a(cls.includes, namespace_includes)] + [push_back_a(classes,cls)] + [assign_a(cls,cls0)]; + + Rule namespace_def_p = + (!include_p + >> str_p("namespace")[push_back_a(namespace_includes, include_path)][assign_a(include_path, null_str)] + >> namespace_name_p[push_back_a(namespaces)] + >> ch_p('{') + >> *(class_p | namespace_def_p | comments_p) + >> str_p("}///\\namespace") // end namespace, avoid confusion with classes + >> !namespace_name_p) + [pop_a(namespaces)] + [pop_a(namespace_includes)]; Rule using_namespace_p = str_p("using") >> str_p("namespace") >> namespace_name_p[push_back_a(using_namespaces)] >> ch_p(';'); @@ -210,6 +225,7 @@ Module::Module(const string& interfacePath, BOOST_SPIRIT_DEBUG_NODE(methodName_p); BOOST_SPIRIT_DEBUG_NODE(method_p); BOOST_SPIRIT_DEBUG_NODE(class_p); + BOOST_SPIRIT_DEBUG_NODE(namespace_def_p); BOOST_SPIRIT_DEBUG_NODE(module_p); # endif //---------------------------------------------------------------------------- diff --git a/wrap/pop_actor.h b/wrap/pop_actor.h deleted file mode 100644 index 97bfdcca6..000000000 --- a/wrap/pop_actor.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file pop_actor.h - * - * @brief An actor to pop a vector/container, based off of the clear_actor - * - * @date Dec 8, 2011 - * @author Alex Cunningham - */ - -#pragma once - -#include - -namespace boost { namespace spirit { - -BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN - - /////////////////////////////////////////////////////////////////////////// - // Summary: - // A semantic action policy that calls pop_back method. - // (This doc uses convention available in actors.hpp) - // - // Actions (what it does): - // ref.pop_back(); - // - // Policy name: - // clear_action - // - // Policy holder, corresponding helper method: - // ref_actor, clear_a( ref ); - // - // () operators: both. - // - // See also ref_actor for more details. - /////////////////////////////////////////////////////////////////////////// - struct pop_action - { - template< - typename T - > - void act(T& ref_) const - { - ref_.pop_back(); - } - }; - - /////////////////////////////////////////////////////////////////////////// - // helper method that creates a and_assign_actor. - /////////////////////////////////////////////////////////////////////////// - template - inline ref_actor pop_a(T& ref_) - { - return ref_actor(ref_); - } - -BOOST_SPIRIT_CLASSIC_NAMESPACE_END - -}} - diff --git a/wrap/spirit_actors.h b/wrap/spirit_actors.h new file mode 100644 index 000000000..c109c40fe --- /dev/null +++ b/wrap/spirit_actors.h @@ -0,0 +1,123 @@ +/** + * @file spirit_actors.h + * + * @brief Additional actors for the wrap parser + * + * @date Dec 8, 2011 + * @author Alex Cunningham + */ + +#pragma once + +#include +#include + +namespace boost { +namespace spirit { + +BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////// +// Summary: +// A semantic action policy that calls pop_back method. +// (This doc uses convention available in actors.hpp) +// Note that this action performs a "safe" pop, that checks the size +// before popping to avoid segfaults +// +// Actions (what it does): +// ref.pop_back(); +// +// Policy name: +// pop_action +// +// Policy holder, corresponding helper method: +// ref_actor, clear_a( ref ); +// +// () operators: both. +// +// See also ref_actor for more details. +/////////////////////////////////////////////////////////////////////////// +struct pop_action +{ + template< typename T> + void act(T& ref_) const + { + if (!ref_.empty()) + ref_.pop_back(); + } +}; + +/////////////////////////////////////////////////////////////////////////// +// helper method that creates a and_assign_actor. +/////////////////////////////////////////////////////////////////////////// +template +inline ref_actor pop_a(T& ref_) +{ + return ref_actor(ref_); +} + +/////////////////////////////////////////////////////////////////////////// +// Summary: +// +// A semantic action policy that appends a set of values to the back of a +// container. +// (This doc uses convention available in actors.hpp) +// +// Actions (what it does and what ref, value_ref must support): +// ref.push_back( values ); +// ref.push_back( T::value_type(first,last) ); +// ref.push_back( value_ref ); +// +// Policy name: +// append_action +// +// Policy holder, corresponding helper method: +// ref_value_actor, append_a( ref ); +// ref_const_ref_actor, append_a( ref, value_ref ); +// +// () operators: both +// +// See also ref_value_actor and ref_const_ref_actor for more details. +/////////////////////////////////////////////////////////////////////////// + +struct append_action +{ + template< typename T, typename ValueT > + void act(T& ref_, ValueT const& value_) const + { + ref_.insert(ref_.begin(), value_.begin(), value_.end()); + } + + template + void act( + T& ref_, + IteratorT const& first_, + IteratorT const& last_ + ) const + { + ref_.insert(ref_.end(), first_, last_); + } +}; + +template +inline ref_value_actor +append_a(T& ref_) +{ + return ref_value_actor(ref_); +} + +template +inline ref_const_ref_actor +append_a( + T& ref_, + ValueT const& value_ +) +{ + return ref_const_ref_actor(ref_,value_); +} + +BOOST_SPIRIT_CLASSIC_NAMESPACE_END + +} +} + diff --git a/wrap/tests/testNamespaces.h b/wrap/tests/testNamespaces.h index f27a518c6..644fdb3ea 100644 --- a/wrap/tests/testNamespaces.h +++ b/wrap/tests/testNamespaces.h @@ -2,20 +2,24 @@ * This is a wrap header to verify permutations on namespaces */ +#include namespace ns1 { class ClassA { ClassA(); }; +#include class ClassB { ClassB(); }; }///\namespace ns1 +#include namespace ns2 { +#include class ClassA { ClassA(); static double afunction(); @@ -24,6 +28,7 @@ class ClassA { ns2::ns3::ClassB nsReturn(double q); }; +#include namespace ns3 { class ClassB { diff --git a/wrap/tests/testWrap.cpp b/wrap/tests/testWrap.cpp index 2ddc2925a..c3b2feafd 100644 --- a/wrap/tests/testWrap.cpp +++ b/wrap/tests/testWrap.cpp @@ -19,12 +19,14 @@ #include #include #include +#include #include #include #include using namespace std; +using namespace boost::assign; using namespace wrap; static bool enable_verbose = false; #ifdef TOPSRCDIR @@ -33,6 +35,8 @@ static string topdir = TOPSRCDIR; static string topdir = "TOPSRCDIR_NOT_CONFIGURED"; // If TOPSRCDIR is not defined, we error #endif +typedef vector strvec; + /* ************************************************************************* */ TEST( wrap, ArgumentList ) { ArgumentList args; @@ -117,8 +121,8 @@ TEST( wrap, parse ) { EXPECT_LONGS_EQUAL(19, testCls.methods.size()); EXPECT_LONGS_EQUAL( 0, testCls.static_methods.size()); EXPECT_LONGS_EQUAL( 0, testCls.namespaces.size()); - EXPECT_LONGS_EQUAL( 1, testCls.includes.size()); - EXPECT(assert_equal("folder/path/to/Test.h", testCls.includes.front())); + strvec exp_includes; exp_includes += "folder/path/to/Test.h"; + EXPECT(assert_equal(exp_includes, testCls.includes)); // function to parse: pair return_pair (Vector v, Matrix A) const; Method m2 = testCls.methods.front(); @@ -134,35 +138,60 @@ TEST( wrap, parse_namespaces ) { Module module(header_path.c_str(), "testNamespaces",enable_verbose); EXPECT_LONGS_EQUAL(6, module.classes.size()); - Class cls1 = module.classes.at(0); - EXPECT(assert_equal("ClassA", cls1.name)); - EXPECT_LONGS_EQUAL(1, cls1.namespaces.size()); - EXPECT(assert_equal("ns1", cls1.namespaces.front())); + { + Class cls = module.classes.at(0); + EXPECT(assert_equal("ClassA", cls.name)); + strvec exp_namespaces; exp_namespaces += "ns1"; + EXPECT(assert_equal(exp_namespaces, cls.namespaces)); + strvec exp_includes; exp_includes += "path/to/ns1.h", ""; + EXPECT(assert_equal(exp_includes, cls.includes)); + } - Class cls2 = module.classes.at(1); - EXPECT(assert_equal("ClassB", cls2.name)); - EXPECT_LONGS_EQUAL(1, cls2.namespaces.size()); - EXPECT(assert_equal("ns1", cls2.namespaces.front())); + { + Class cls = module.classes.at(1); + EXPECT(assert_equal("ClassB", cls.name)); + strvec exp_namespaces; exp_namespaces += "ns1"; + EXPECT(assert_equal(exp_namespaces, cls.namespaces)); + strvec exp_includes; exp_includes += "path/to/ns1.h", "path/to/ns1/ClassB.h"; + EXPECT(assert_equal(exp_includes, cls.includes)); + } - Class cls3 = module.classes.at(2); - EXPECT(assert_equal("ClassA", cls3.name)); - EXPECT_LONGS_EQUAL(1, cls3.namespaces.size()); - EXPECT(assert_equal("ns2", cls3.namespaces.front())); + { + Class cls = module.classes.at(2); + EXPECT(assert_equal("ClassA", cls.name)); + strvec exp_namespaces; exp_namespaces += "ns2"; + EXPECT(assert_equal(exp_namespaces, cls.namespaces)); + strvec exp_includes; exp_includes += "path/to/ns2.h", "path/to/ns2/ClassA.h"; + EXPECT(assert_equal(exp_includes, cls.includes)); + } - Class cls4 = module.classes.at(3); - EXPECT(assert_equal("ClassB", cls4.name)); - EXPECT_LONGS_EQUAL(2, cls4.namespaces.size()); - EXPECT(assert_equal("ns2", cls4.namespaces.front())); - EXPECT(assert_equal("ns3", cls4.namespaces.back())); + { + Class cls = module.classes.at(3); + EXPECT(assert_equal("ClassB", cls.name)); + strvec exp_namespaces; exp_namespaces += "ns2", "ns3"; + EXPECT(assert_equal(exp_namespaces, cls.namespaces)); + strvec exp_includes; exp_includes += "path/to/ns2.h", "path/to/ns3.h", ""; + EXPECT(assert_equal(exp_includes, cls.includes)); + } - Class cls5 = module.classes.at(4); - EXPECT(assert_equal("ClassC", cls5.name)); - EXPECT_LONGS_EQUAL(1, cls5.namespaces.size()); - EXPECT(assert_equal("ns2", cls5.namespaces.front())); + { + Class cls = module.classes.at(4); + EXPECT(assert_equal("ClassC", cls.name)); + strvec exp_namespaces; exp_namespaces += "ns2"; + EXPECT(assert_equal(exp_namespaces, cls.namespaces)); + strvec exp_includes; exp_includes += "path/to/ns2.h", ""; + EXPECT(assert_equal(exp_includes, cls.includes)); + } + + { + Class cls = module.classes.at(5); + EXPECT(assert_equal("ClassD", cls.name)); + strvec exp_namespaces; + EXPECT(assert_equal(exp_namespaces, cls.namespaces)); + strvec exp_includes; exp_includes += ""; + EXPECT(assert_equal(exp_includes, cls.includes)); + } - Class cls6 = module.classes.at(5); - EXPECT(assert_equal("ClassD", cls6.name)); - EXPECT_LONGS_EQUAL(0, cls6.namespaces.size()); } /* ************************************************************************* */ diff --git a/wrap/utilities.cpp b/wrap/utilities.cpp index ebf1ad020..97d18f579 100644 --- a/wrap/utilities.cpp +++ b/wrap/utilities.cpp @@ -51,6 +51,34 @@ bool assert_equal(const string& expected, const string& actual) { return false; } +/* ************************************************************************* */ +bool assert_equal(const vector& expected, const vector& actual) { + bool match = true; + if (expected.size() != actual.size()) + match = false; + vector::const_iterator + itExp = expected.begin(), + itAct = actual.begin(); + if(match) { + for (; itExp!=expected.end() && itAct!=actual.end(); ++itExp, ++itAct) { + if (*itExp != *itAct) { + match = false; + break; + } + } + } + if(!match) { + cout << "expected: " << endl; + BOOST_FOREACH(const vector::value_type& a, expected) { cout << "[" << a << "] "; } + cout << "\nactual: " << endl; + BOOST_FOREACH(const vector::value_type& a, actual) { cout << "[" << a << "] "; } + cout << endl; + return false; + } + return true; + +} + /* ************************************************************************* */ bool files_equal(const string& expected, const string& actual, bool skipheader) { try { @@ -97,8 +125,8 @@ void generateUsingNamespace(ofstream& ofs, const vector& using_namespace } /* ************************************************************************* */ -void generateIncludes(std::ofstream& ofs, const std::string& class_name, - const std::vector& includes) { +void generateIncludes(ofstream& ofs, const string& class_name, + const vector& includes) { ofs << "#include " << endl; if (includes.empty()) // add a default include ofs << "#include <" << class_name << ".h>" << endl; diff --git a/wrap/utilities.h b/wrap/utilities.h index 3837c2b95..82475ebac 100644 --- a/wrap/utilities.h +++ b/wrap/utilities.h @@ -79,6 +79,7 @@ bool files_equal(const std::string& expected, const std::string& actual, bool sk * Compare strings for unit tests */ bool assert_equal(const std::string& expected, const std::string& actual); +bool assert_equal(const std::vector& expected, const std::vector& actual); /** * emit a header at the top of generated files */