From e7f9486aa16c73cd9f1ca165da3f5fc0f1f2a08d Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Wed, 23 Jan 2013 01:09:57 +0100 Subject: [PATCH] Nasal String wrapper and allow adding methods to string objects. - Add nasal::String for wrapping Nasal string data and accessing string methods (which eg. could be exposed to Nasal) - Allow adding functions from C/C++ which are callable on Nasal strings. --- simgear/nasal/code.c | 9 +- simgear/nasal/cppbind/CMakeLists.txt | 2 + simgear/nasal/cppbind/NasalString.cxx | 180 ++++++++++++++++++++ simgear/nasal/cppbind/NasalString.hxx | 80 +++++++++ simgear/nasal/cppbind/cppbind_test.cxx | 22 +++ simgear/nasal/cppbind/from_nasal.cxx | 10 ++ simgear/nasal/cppbind/from_nasal_detail.hxx | 6 + simgear/nasal/nasal.h | 4 + simgear/nasal/string.c | 24 +++ 9 files changed, 334 insertions(+), 3 deletions(-) create mode 100644 simgear/nasal/cppbind/NasalString.cxx create mode 100644 simgear/nasal/cppbind/NasalString.hxx diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 08d710f8..511f490f 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -441,14 +441,17 @@ static const char* getMember_r(naContext ctx, naRef obj, naRef field, naRef* out naRef p; struct VecRec* pv; if(--count < 0) return "too many parents"; - if(!IS_HASH(obj) && !IS_GHOST(obj)) return "non-objects have no members"; - + if (IS_GHOST(obj)) { if (ghostGetMember(ctx, obj, field, out)) return ""; if(!ghostGetMember(ctx, obj, globals->parentsRef, &p)) return 0; - } else { + } else if (IS_HASH(obj)) { if(naHash_get(obj, field, out)) return ""; if(!naHash_get(obj, globals->parentsRef, &p)) return 0; + } else if (IS_STR(obj) ) { + return getMember_r(ctx, getStringMethods(ctx), field, out, count); + } else { + return "non-objects have no members"; } if(!IS_VEC(p)) return "object \"parents\" field not vector"; diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 636116eb..62df0058 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -3,6 +3,7 @@ include (SimGearComponent) set(HEADERS Ghost.hxx NasalHash.hxx + NasalString.hxx from_nasal_detail.hxx from_nasal.hxx nasal_traits.hxx @@ -11,6 +12,7 @@ set(HEADERS set(SOURCES NasalHash.cxx + NasalString.cxx from_nasal.cxx to_nasal.cxx ) diff --git a/simgear/nasal/cppbind/NasalString.cxx b/simgear/nasal/cppbind/NasalString.cxx new file mode 100644 index 00000000..41388b3d --- /dev/null +++ b/simgear/nasal/cppbind/NasalString.cxx @@ -0,0 +1,180 @@ +// Wrapper class for Nasal strings +// +// Copyright (C) 2013 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include "NasalString.hxx" +#include "to_nasal.hxx" + +#include +#include +#include +#include +#include + +namespace nasal +{ + + /** + * Predicate (eg. for std::find_if) returning true if no character of the + * stored string given by the range [begin, end) matches. + */ + struct no_match: + public std::unary_function + { + no_match(const char* begin, const char* end): + _begin(begin), + _end(end) + {} + + bool operator()(const char c) const + { + return std::find(_begin, _end, c) == _end; + } + + private: + const char* _begin; + const char* _end; + }; + +//template +//Iterator +//rfind_first_of( Iterator rbegin_src, Iterator rend_src, +// Iterator begin_find, Iterator end_find ) +//{ +// for(; rbegin_src != rend_src; --rbegin_src) +// { +// for(Iterator chr = begin_find; chr != end_find; ++chr) +// { +// if( *rbegin_src == *chr ) +// return rbegin_src; +// } +// } +// return rend_src; +//} + + + const size_t String::npos = static_cast(-1); + + //---------------------------------------------------------------------------- + String::String(naContext c, const char* str): + _str( to_nasal(c, str) ) + { + assert( naIsString(_str) ); + } + + //---------------------------------------------------------------------------- + String::String(naRef str): + _str(str) + { + assert( naIsString(_str) ); + } + + //---------------------------------------------------------------------------- + const char* String::c_str() const + { + return naStr_data(_str); + } + + //---------------------------------------------------------------------------- + const char* String::begin() const + { + return c_str(); + } + + //---------------------------------------------------------------------------- + const char* String::end() const + { + return c_str() + size(); + } + + //---------------------------------------------------------------------------- + size_t String::size() const + { + return naStr_len(_str); + } + + //---------------------------------------------------------------------------- + size_t String::length() const + { + return size(); + } + + //---------------------------------------------------------------------------- + bool String::empty() const + { + return size() == 0; + } + + //---------------------------------------------------------------------------- + int String::compare(size_t pos, size_t len, const String& rhs) const + { + if( pos >= size() ) + throw std::out_of_range("nasal::String::compare: pos"); + + return memcmp( begin() + pos, + rhs.begin(), + std::min(rhs.size(), std::min(size() - pos, len)) ); + } + + //---------------------------------------------------------------------------- + bool String::starts_with(const String& rhs) const + { + return rhs.size() <= size() && compare(0, npos, rhs) == 0; + } + + //---------------------------------------------------------------------------- + size_t String::find(const char c, size_t pos) const + { + if( pos >= size() ) + return npos; + + const char* result = std::find(begin() + pos, end(), c); + + return result != end() ? result - begin() : npos; + } + + //---------------------------------------------------------------------------- + size_t String::find_first_of(const String& chr, size_t pos) const + { + if( pos >= size() ) + return npos; + + const char* result = std::find_first_of( begin() + pos, end(), + chr.begin(), chr.end() ); + + return result != end() ? result - begin() : npos; + } + + //---------------------------------------------------------------------------- + size_t String::find_first_not_of(const String& chr, size_t pos) const + { + if( pos >= size() ) + return npos; + + const char* result = std::find_if( begin() + pos, end(), + no_match(chr.begin(), chr.end()) ); + + return result != end() ? result - begin() : npos; + } + + //---------------------------------------------------------------------------- + const naRef String::get_naRef() const + { + return _str; + } + +} // namespace nasal diff --git a/simgear/nasal/cppbind/NasalString.hxx b/simgear/nasal/cppbind/NasalString.hxx new file mode 100644 index 00000000..d30dfebd --- /dev/null +++ b/simgear/nasal/cppbind/NasalString.hxx @@ -0,0 +1,80 @@ +///@file Wrapper class for Nasal strings +// +// Copyright (C) 2013 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_NASAL_STRING_HXX_ +#define SG_NASAL_STRING_HXX_ + +#include "from_nasal.hxx" +#include "to_nasal.hxx" + +namespace nasal +{ + + /** + * A Nasal String + */ + class String + { + public: + + static const size_t npos; + + /** + * Create a new Nasal String + * + * @param c Nasal context for creating the string + * @param str String data + */ + String(naContext c, const char* str); + + /** + * Initialize from an existing Nasal String + * + * @param str Existing Nasal String + */ + String(naRef string); + + const char* c_str() const; + const char* begin() const; + const char* end() const; + + size_t size() const; + size_t length() const; + bool empty() const; + + int compare(size_t pos, size_t len, const String& rhs) const; + bool starts_with(const String& rhs) const; + + size_t find(const char c, size_t pos = 0) const; + size_t find_first_of(const String& chr, size_t pos = 0) const; + size_t find_first_not_of(const String& chr, size_t pos = 0) const; + + /** + * Get Nasal representation of String + */ + const naRef get_naRef() const; + + protected: + + naRef _str; + + }; + +} // namespace nasal + +#endif /* SG_NASAL_STRING_HXX_ */ diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx index 416d4a0e..422f3d70 100644 --- a/simgear/nasal/cppbind/cppbind_test.cxx +++ b/simgear/nasal/cppbind/cppbind_test.cxx @@ -2,6 +2,7 @@ #include "Ghost.hxx" #include "NasalHash.hxx" +#include "NasalString.hxx" #include @@ -111,6 +112,27 @@ int main(int argc, char* argv[]) Hash mod = hash.createHash("mod"); mod.set("parent", hash); + String string( to_nasal(c, "Test") ); + VERIFY( from_nasal(c, string.get_naRef()) == "Test" ); + VERIFY( string.c_str() == std::string("Test") ); + VERIFY( string.starts_with(string) ); + VERIFY( string.starts_with(String(c, "T")) ); + VERIFY( string.starts_with(String(c, "Te")) ); + VERIFY( string.starts_with(String(c, "Tes")) ); + VERIFY( string.starts_with(String(c, "Test")) ); + VERIFY( !string.starts_with(String(c, "Test1")) ); + VERIFY( !string.starts_with(String(c, "bb")) ); + VERIFY( !string.starts_with(String(c, "bbasdasdafasd")) ); + VERIFY( string.find('e') == 1 ); + VERIFY( string.find('9') == String::npos ); + VERIFY( string.find_first_of(String(c, "st")) == 2 ); + VERIFY( string.find_first_of(String(c, "st"), 3) == 3 ); + VERIFY( string.find_first_of(String(c, "xyz")) == String::npos ); + VERIFY( string.find_first_not_of(String(c, "Tst")) == 1 ); + VERIFY( string.find_first_not_of(String(c, "Tse"), 2) == 3 ); + VERIFY( string.find_first_not_of(String(c, "abc")) == 0 ); + VERIFY( string.find_first_not_of(String(c, "abc"), 20) == String::npos ); + Ghost::init("BasePtr") .method<&Base::member>("member") .member("str", &Base::getString, &Base::setString); diff --git a/simgear/nasal/cppbind/from_nasal.cxx b/simgear/nasal/cppbind/from_nasal.cxx index 0c3fba95..2eb1e1f4 100644 --- a/simgear/nasal/cppbind/from_nasal.cxx +++ b/simgear/nasal/cppbind/from_nasal.cxx @@ -18,6 +18,7 @@ #include "from_nasal_detail.hxx" #include "NasalHash.hxx" +#include "NasalString.hxx" #include @@ -70,4 +71,13 @@ namespace nasal return Hash(ref, c); } + //---------------------------------------------------------------------------- + String from_nasal_helper(naContext c, naRef ref, String*) + { + if( !naIsString(ref) ) + throw bad_nasal_cast("Not a string"); + + return String(ref); + } + } // namespace nasal diff --git a/simgear/nasal/cppbind/from_nasal_detail.hxx b/simgear/nasal/cppbind/from_nasal_detail.hxx index aeec6e20..7eb0c966 100644 --- a/simgear/nasal/cppbind/from_nasal_detail.hxx +++ b/simgear/nasal/cppbind/from_nasal_detail.hxx @@ -36,6 +36,7 @@ class SGPath; namespace nasal { class Hash; + class String; /** * Thrown when converting a type from/to Nasal has failed @@ -87,6 +88,11 @@ namespace nasal */ Hash from_nasal_helper(naContext c, naRef ref, Hash*); + /** + * Convert a Nasal string to a nasal::String + */ + String from_nasal_helper(naContext c, naRef ref, String*); + /** * Convert a Nasal number to a C++ numeric type */ diff --git a/simgear/nasal/nasal.h b/simgear/nasal/nasal.h index bf48cdc2..7fc54203 100644 --- a/simgear/nasal/nasal.h +++ b/simgear/nasal/nasal.h @@ -119,6 +119,9 @@ naRef naInit_readline(naContext c); naRef naInit_gtk(naContext ctx); naRef naInit_cairo(naContext ctx); +// Returns a hash which can be used to add methods callable on strings +naRef naInit_string(naContext c); + // Context stack inspection, frame zero is the "top" int naStackDepth(naContext ctx); int naGetLine(naContext ctx, int frame); @@ -159,6 +162,7 @@ naRef naStr_fromdata(naRef dst, const char* data, int len); naRef naStr_concat(naRef dest, naRef s1, naRef s2); naRef naStr_substr(naRef dest, naRef str, int start, int len); naRef naInternSymbol(naRef sym); +naRef getStringMethods(naContext c); // Vector utilities: int naVec_size(naRef v); diff --git a/simgear/nasal/string.c b/simgear/nasal/string.c index 4855054c..ce8b5ac2 100644 --- a/simgear/nasal/string.c +++ b/simgear/nasal/string.c @@ -320,3 +320,27 @@ static int fromnum(double val, unsigned char* s) *ptr = 0; return ptr - s; } + + +//------------------------------------------------------------------------------ +static naRef string_methods; +static int init = 0; // As we can't use naNil() for static initialization we + // need a separate variable for saving whether we have + // already initialized. + +//------------------------------------------------------------------------------ +naRef naInit_string(naContext c) +{ + string_methods = naNewHash(c); + init = 1; + return string_methods; +} + +//------------------------------------------------------------------------------ +naRef getStringMethods(naContext c) +{ + if( !init ) + return naNil(); + + return string_methods; +}