From da21737bbeaebdacb563cc738430b5fcc7f7173e Mon Sep 17 00:00:00 2001 From: TQ Hirsch <thequux@upstandinghackers.com> Date: Thu, 16 Jan 2014 09:38:55 -0500 Subject: [PATCH] Added start at C++ bindings --- src/bindings/cpp/SConscript | 9 ++ src/bindings/cpp/cpp_tests.cpp | 23 ++++ src/bindings/cpp/hammer/hammer.cpp | 1 + src/bindings/cpp/hammer/hammer.hpp | 160 ++++++++++++++++++++++++ src/bindings/cpp/hammer/hammer_test.hpp | 46 +++++++ 5 files changed, 239 insertions(+) create mode 100644 src/bindings/cpp/SConscript create mode 100644 src/bindings/cpp/cpp_tests.cpp create mode 100644 src/bindings/cpp/hammer/hammer.cpp create mode 100644 src/bindings/cpp/hammer/hammer.hpp create mode 100644 src/bindings/cpp/hammer/hammer_test.hpp diff --git a/src/bindings/cpp/SConscript b/src/bindings/cpp/SConscript new file mode 100644 index 00000000..448c3929 --- /dev/null +++ b/src/bindings/cpp/SConscript @@ -0,0 +1,9 @@ +# -*- python -*- +Import("env libhammer_shared") + +cppenv = env.Clone() +cppenv.Append(INCPATH=["."]) + + +libhammerxx = env.SharedLibrary("hammer++", libhammer_shared + ["hammer.cpp"]) + diff --git a/src/bindings/cpp/cpp_tests.cpp b/src/bindings/cpp/cpp_tests.cpp new file mode 100644 index 00000000..b8eda68f --- /dev/null +++ b/src/bindings/cpp/cpp_tests.cpp @@ -0,0 +1,23 @@ +#include <gtest/gtest.h> +#include <hammer/hammer.hpp> +#include <hammer/hammer_test.hpp> + +namespace { + using namespace ::hammer; + TEST(ParserTypes, Token) { + Parser p = Token("95\xA2"); + EXPECT_TRUE(ParsesTo(p, "95\xA2", "<39.35.a2>")); + EXPECT_TRUE(ParseFails(p, "95")); + } + + TEST(ParserTypes, Ch) { + Parser p = Ch(0xA2); + EXPECT_TRUE(ParsesTo(p, "\xA2", "u0xa2")); + EXPECT_TRUE(ParseFails(p, "\xA3")); + } +}; + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/bindings/cpp/hammer/hammer.cpp b/src/bindings/cpp/hammer/hammer.cpp new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/bindings/cpp/hammer/hammer.cpp @@ -0,0 +1 @@ + diff --git a/src/bindings/cpp/hammer/hammer.hpp b/src/bindings/cpp/hammer/hammer.hpp new file mode 100644 index 00000000..61622eea --- /dev/null +++ b/src/bindings/cpp/hammer/hammer.hpp @@ -0,0 +1,160 @@ +#ifndef HAMMER_HAMMER__HPP +#define HAMMER_HAMMER__HPP + +#include <hammer/hammer.h> +#include <string> +#include <stdint.h> + +namespace hammer { + class Parser { + + }; + + class ParsedToken { + // This object can suddenly become invalid if the underlying parse + // tree is destroyed. + + // This object should serve as a very thin wrapper around an HParsedToken*. + // In particular sizeof(ParsedToken) should== sizeof(HParsedToken*) + // This means that we only get one member variable and no virtual functions. + protected: + HParsedToken *token; + + public: + + ParsedToken(HParsedToken *inner) : token(inner) {} + ParsedToken(const ParsedToken &other) : token(other.token) {} + + inline TokenType getType() { + return type; + } + + void* getUser() { + return token->user; + } + // TODO: add accessors. + + + std::string asUnambiguous() { + char* buf = h_write_result_unamb(token); + std::string s = std::string(buf); + free(buf); + return s; + } + }; + + class ParseResult { + protected: + HParseResult *_result; + public: + + ParsedToken getAST() { + return ParsedToken(_result); + } + inline string asUnambiguous() { + return getAST().asUnambiguous(); + } + + bool operator bool() { + return _result != NULL; + } + bool operator !() { + return _result == NULL; + } + + ~ParseResult() { + h_parse_result_free(_result); + _result = NULL; + } + }; + + inline Parser token(const std::string &str) { + std::string *str_clone = new std::string(str); + return Parser(h_token(str_clone->data(), str. + } + Parser token(const uint8_t *buf, size_t len); + + Parser ch(char ch); + + Parser ch_range(uint8_t lower, uint8_t upper); + + Parser int64(); + Parser int32(); + Parser int16(); + Parser int8(); + + Parser uint64(); + Parser uint32(); + Parser uint16(); + Parser uint8(); + + Parser int_range(Parser p, int64_t lower, int64_t upper); + + Parser bits(size_t len, bool sign); + + Parser whitespace(Parser p); + + Parser left(Parser p, Parser q); + + Parser right(Parser p, Parser q); + + Parser middle(Parser p, Parser q, Parser r); + + // TODO: Define Action + //Parser action(Parser p, Action a); + + Parser in(string charset); + Parser in(const uint8_t *charset, size_t length); + Parser in(std::set<uint8_t> charset); + + Parser not_in(string charset); + Parser not_in(const uint8_t *charset, size_t length); + Parser not_in(std::set<uint8_t> charset); + + Parser end(); + + Parser nothing(); + + // TODO: figure out varargs + //Parser sequence(); + // + //Parser choice(); + + Parser butnot(Parser p1, Parser p2); + + Parser difference(Parser p1, Parser p2); + + Parser xor_(Parser p1, Parser p2); + + Parser many(Parser p); + + Parser many1(Parser p); + + Parser repeat_n(Parser p, size_t n); + + Parser optional(Parser p); + + Parser ignore(Parser p); + + Parser sepBy(Parser p, Parser sep); + + Parser sepBy1(Parser p, Parser sep); + + Parser epsilon(); + + Parser length_value(Parser length, Parser value); + + // Was attr_bool in the old C bindings. + // TODO: Figure out closure + //Parser validate(Parser p, Predicate pred); + + Parser and_(Parser p); + + Parser not_(Parser p); + + IndirectParser indirect(); + static inline void bind_indirect(IndirectParser &indirect, Parser p) { + indirect.bind(p); + } +} +#endif diff --git a/src/bindings/cpp/hammer/hammer_test.hpp b/src/bindings/cpp/hammer/hammer_test.hpp new file mode 100644 index 00000000..aa9b9940 --- /dev/null +++ b/src/bindings/cpp/hammer/hammer_test.hpp @@ -0,0 +1,46 @@ +#ifndef HAMMER_HAMMER_TEST__HPP +#define HAMMER_HAMMER_TEST__HPP +#include <string> + +#include <gtest/gtest.h> +#include <hammer/hammer.hpp> + +static ::testing::AssertionResult ParseFails(hammer::Parser parser, + const string &input) { + hammer::ParseResult result = parser.Parse(input); + if (result) { + return ::testing::AssertionFailure() << "Parse succeeded with " << result.AsUnambiguous() << "; expected failure"; + } else { + return ::testing::AssertionSuccess(); + } +} + +static ::testing::AssertionResult ParsesOK(hammer::Parser parser, + const string &input) { + hammer::ParseResult result = parser.Parse(input); + if (!result) { + return ::testing::AssertionFailure() << "Parse failed; expected success"; + } else { + return ::testing::AssertionSuccess(); + } +} + +static ::testing::AssertionResult ParsesTo(hammer::Parser parser, + const string &input, + const string &expected_result) { + hammer::ParseResult result = parser.Parse(input); + if (!result) { + return ::testing::AssertionFailure() << "Parse failed; expected success"; + } else if (result.AsUnambiguous() != expected_result) { + return ::testing::AssertionFailure() + << "Parse succeeded with wrong result: got " + << result.AsUnambiguous() + << "; expected " + << expected_result; + } else { + return ::testing::AssertionSuccess(); + } +} + + +#endif // defined(HAMMER_HAMMER_TEST__HPP) -- GitLab