Parser now handles both namespace and class headers

release/4.3a0
Alex Cunningham 2011-12-15 19:39:11 +00:00
parent dbc6a8aeec
commit ea1f1e8b65
7 changed files with 244 additions and 101 deletions

View File

@ -16,10 +16,9 @@
#include "Module.h"
#include "utilities.h"
#include "pop_actor.h"
#include "spirit_actors.h"
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/spirit/include/classic_clear_actor.hpp>
#include <boost/foreach.hpp>
@ -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<string> namespaces, namespaces_return;
vector<string> 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
//----------------------------------------------------------------------------

View File

@ -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 <boost/spirit/include/classic_ref_actor.hpp>
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<typename T>
inline ref_actor<T,pop_action> pop_a(T& ref_)
{
return ref_actor<T,pop_action>(ref_);
}
BOOST_SPIRIT_CLASSIC_NAMESPACE_END
}}

123
wrap/spirit_actors.h Normal file
View File

@ -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 <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_ref_actor.hpp>
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<typename T>
inline ref_actor<T,pop_action> pop_a(T& ref_)
{
return ref_actor<T,pop_action>(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<typename T,typename IteratorT>
void act(
T& ref_,
IteratorT const& first_,
IteratorT const& last_
) const
{
ref_.insert(ref_.end(), first_, last_);
}
};
template<typename T>
inline ref_value_actor<T,append_action>
append_a(T& ref_)
{
return ref_value_actor<T,append_action>(ref_);
}
template<typename T,typename ValueT>
inline ref_const_ref_actor<T,ValueT,append_action>
append_a(
T& ref_,
ValueT const& value_
)
{
return ref_const_ref_actor<T,ValueT,append_action>(ref_,value_);
}
BOOST_SPIRIT_CLASSIC_NAMESPACE_END
}
}

View File

@ -2,20 +2,24 @@
* This is a wrap header to verify permutations on namespaces
*/
#include <path/to/ns1.h>
namespace ns1 {
class ClassA {
ClassA();
};
#include <path/to/ns1/ClassB.h>
class ClassB {
ClassB();
};
}///\namespace ns1
#include <path/to/ns2.h>
namespace ns2 {
#include <path/to/ns2/ClassA.h>
class ClassA {
ClassA();
static double afunction();
@ -24,6 +28,7 @@ class ClassA {
ns2::ns3::ClassB nsReturn(double q);
};
#include <path/to/ns3.h>
namespace ns3 {
class ClassB {

View File

@ -19,12 +19,14 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/assign/std/vector.hpp>
#include <CppUnitLite/TestHarness.h>
#include <wrap/utilities.h>
#include <wrap/Module.h>
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<string> 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<Vector,Matrix> 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());
}
/* ************************************************************************* */

View File

@ -51,6 +51,34 @@ bool assert_equal(const string& expected, const string& actual) {
return false;
}
/* ************************************************************************* */
bool assert_equal(const vector<string>& expected, const vector<string>& actual) {
bool match = true;
if (expected.size() != actual.size())
match = false;
vector<string>::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<string>::value_type& a, expected) { cout << "[" << a << "] "; }
cout << "\nactual: " << endl;
BOOST_FOREACH(const vector<string>::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<string>& using_namespace
}
/* ************************************************************************* */
void generateIncludes(std::ofstream& ofs, const std::string& class_name,
const std::vector<std::string>& includes) {
void generateIncludes(ofstream& ofs, const string& class_name,
const vector<string>& includes) {
ofs << "#include <wrap/matlab.h>" << endl;
if (includes.empty()) // add a default include
ofs << "#include <" << class_name << ".h>" << endl;

View File

@ -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<std::string>& expected, const std::vector<std::string>& actual);
/**
* emit a header at the top of generated files
*/