From e1316604bda7360458ae1e43d54631c14b40e2b5 Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Wed, 9 Nov 2011 17:56:03 +0800 Subject: [PATCH] Json basic test passing --- test/json_test.cpp | 100 +++++++++++++++++++++++++ test/scalar_tests.cpp | 6 +- test/test_files/basic_json_test.json | 14 ++++ yaml/ast.hpp | 26 ++++--- yaml/detail/ast_impl.hpp | 108 +++++++++++++++++++++++++++ yaml/parser/error_handler.hpp | 56 ++++++++++++++ yaml/parser/flow.hpp | 41 ++++++++++ yaml/parser/flow_def.hpp | 73 ++++++++++++++++++ yaml/parser/scalar.hpp | 15 +++- yaml/parser/scalar_def.hpp | 22 ++++-- 10 files changed, 440 insertions(+), 21 deletions(-) create mode 100644 test/json_test.cpp create mode 100644 test/test_files/basic_json_test.json create mode 100644 yaml/detail/ast_impl.hpp create mode 100644 yaml/parser/error_handler.hpp create mode 100644 yaml/parser/flow.hpp create mode 100644 yaml/parser/flow_def.hpp diff --git a/test/json_test.cpp b/test/json_test.cpp new file mode 100644 index 00000000..6e4d7fd3 --- /dev/null +++ b/test/json_test.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2010, 2011 Object Modeling Designs + */ + +#include "../yaml/parser/flow_def.hpp" +#include "../yaml/parser/scalar_def.hpp" + +#include +#include + +#include +#include + +namespace +{ + template + bool parse( + std::basic_istream& is, + omd::ast::value_t& result, + std::string const& source_file = "") + { + // no white space skipping in the stream! + is.unsetf(std::ios::skipws); + + typedef + boost::spirit::basic_istream_iterator + stream_iterator_type; + stream_iterator_type sfirst(is); + stream_iterator_type slast; + + typedef boost::spirit::line_pos_iterator + iterator_type; + iterator_type first(sfirst); + iterator_type last(slast); + + omd::parser::flow p(source_file); + omd::parser::white_space ws; + + using boost::spirit::qi::phrase_parse; + return phrase_parse(first, last, p, ws, result); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename = NULL; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + // Ignore the BOM marking the beginning of a UTF-8 file in Windows + char c = in.peek(); + if (c == '\xef') + { + char s[3]; + in >> s[0] >> s[1] >> s[2]; + s[3] = '\0'; + if (s != std::string("\xef\xbb\xbf")) + { + std::cerr << "Error: Unexpected characters from input file: " + << filename << std::endl; + return 1; + } + } + + using omd::ast::value_t; + namespace qi = boost::spirit::qi; + + value_t result; + if (parse(in, result, filename)) + { + std::cout << "success: "; + //~ println(std::cout, result); + std::cout << std::endl; + } + else + { + std::cout << "parse error" << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/test/scalar_tests.cpp b/test/scalar_tests.cpp index 7f302d46..a90cc871 100644 --- a/test/scalar_tests.cpp +++ b/test/scalar_tests.cpp @@ -1,4 +1,8 @@ -#include "../yaml/parser/scalar_def.hpp" +/** + * Copyright (C) 2010, 2011 Object Modeling Designs + */ + +#include "../yaml/parser/scalar_def.hpp" #include namespace diff --git a/test/test_files/basic_json_test.json b/test/test_files/basic_json_test.json new file mode 100644 index 00000000..b431e1f8 --- /dev/null +++ b/test/test_files/basic_json_test.json @@ -0,0 +1,14 @@ +[ + 123.45, + true, + false, + 0xFF, + 077, + "this is a unicode \u20AC string", + "Τη γλώσσα μου έδωσαν ελληνική", + 12345, + [ + 92, + ["another string", "apple", "Sîne"] + ] +] diff --git a/yaml/ast.hpp b/yaml/ast.hpp index 48d4bef1..279b2153 100644 --- a/yaml/ast.hpp +++ b/yaml/ast.hpp @@ -3,8 +3,8 @@ * consultomd.com * */ -#ifndef OMD_JSON_VALUE_HPP -#define OMD_JSON_VALUE_HPP +#ifndef OMD_AST_VALUE_HPP +#define OMD_AST_VALUE_HPP #include #include @@ -20,14 +20,14 @@ namespace omd { namespace ast typedef int int_t; typedef bool bool_t; struct null_t - { - // nulls always compare - bool operator==(const null_t&) const{ return true; } - bool operator!=(const null_t&) const{ return false; } - }; + {}; + + // nulls always compare + inline bool operator==(null_t a, null_t b) { return true; } + inline bool operator!=(null_t a, null_t b) { return false; } struct value_t; - typedef std::map object_t; + typedef std::map object_t; typedef std::vector array_t; struct value_t @@ -37,7 +37,8 @@ namespace omd { namespace ast string_t, double_t, int_t, - object_t + object_t, + array_t > { value_t(string_t const& val) : base_type(val) {} @@ -45,12 +46,19 @@ namespace omd { namespace ast value_t(int_t val) : base_type(val) {} value_t(bool_t val) : base_type(val) {} value_t(null_t val = null_t()) : base_type(val) {} + value_t(object_t const& val) : base_type(val) {} + value_t(array_t const& val) : base_type(val) {} value_t(value_t const& rhs) : base_type(rhs.get()) {} }; + bool operator==(value_t const& a, value_t const& b); + bool operator!=(value_t const& a, value_t const& b); + bool operator<(value_t const& a, value_t const& b); + // --------------------------------------------------- }} +#include "detail/ast_impl.hpp" #endif diff --git a/yaml/detail/ast_impl.hpp b/yaml/detail/ast_impl.hpp new file mode 100644 index 00000000..ea136f5c --- /dev/null +++ b/yaml/detail/ast_impl.hpp @@ -0,0 +1,108 @@ +/** + * Copyright (C) 2010, 2011 Michael Caisse, Object Modeling Designs + * consultomd.com + * + */ +#ifndef OMD_AST_VALUE_IMPL_HPP +#define OMD_AST_VALUE_IMPL_HPP + +#include "../ast.hpp" +#include + +namespace omd { namespace ast +{ + namespace detail + { + struct value_equal + { + typedef bool result_type; + + template + bool operator()(A const& a, B const& b) const + { + BOOST_ASSERT(false); // this should not happen. We cannot compare different types + return false; + } + + template + bool operator()(T const& a, T const& b) const + { + return a == b; + } + + bool operator()(object_t const& a, object_t const& b) + { + if (a.size() != b.size()) + return false; + object_t::const_iterator ii = b.begin(); + for (object_t::const_iterator i = a.begin(); i != a.end(); ++i) + { + if (*i != *ii++) + return false; + } + return true; + } + + bool operator()(array_t const& a, array_t const& b) + { + if (a.size() != b.size()) + return false; + array_t::const_iterator ii = b.begin(); + for (array_t::const_iterator i = a.begin(); i != a.end(); ++i) + { + if (*i != *ii++) + return false; + } + return true; + } + }; + } + + inline bool operator==(value_t const& a, value_t const& b) + { + return boost::apply_visitor(detail::value_equal(), a.get(), b.get()); + } + + inline bool operator!=(value_t const& a, value_t const& b) + { + return !(a == b); + } + + namespace detail + { + struct value_compare + { + typedef bool result_type; + + template + bool operator()(A const& a, B const& b) const + { + BOOST_ASSERT(false); // this should not happen. We cannot compare different types + return false; + } + + template + bool operator()(T const& a, T const& b) const + { + return a < b; + } + + bool operator()(object_t const& a, object_t const& b) + { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + bool operator()(array_t const& a, array_t const& b) + { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + }; + } + + inline bool operator<(value_t const& a, value_t const& b) + { + return boost::apply_visitor(detail::value_compare(), a.get(), b.get()); + } +}} + +#endif diff --git a/yaml/parser/error_handler.hpp b/yaml/parser/error_handler.hpp new file mode 100644 index 00000000..3c96ae26 --- /dev/null +++ b/yaml/parser/error_handler.hpp @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2010, 2011 Object Modeling Designs + * Copyright (c) 2010 Joel de Guzman + */ + +#if !defined(OMD_COMMON_ERROR_HANDLER_HPP) +#define OMD_COMMON_ERROR_HANDLER_HPP + +#include +#include +#include +#include +#include + +namespace omd { namespace parser +{ + template + struct error_handler + { + template + struct result { typedef void type; }; + + std::string source_file; + error_handler(std::string const& source_file = "") + : source_file(source_file) {} + + void operator()( + Iterator first, Iterator last, + Iterator err_pos, boost::spirit::info const& what) const + { + Iterator eol = err_pos; + int line = boost::spirit::get_line(err_pos); + + if (source_file != "") + std::cerr << source_file; + + if (line != -1) + std::cerr << '(' << line << ')'; + + std::cerr << " : Error! Expecting " << what; + + std::cerr << " got:\""; + for (Iterator i = err_pos; i != last; ++i) + { + Iterator::value_type c = *i; + if (c == '\r' || c == '\n') + break; + std::cerr << c; + } + + std::cerr << "\"" << std::endl; + } + }; +}} + +#endif diff --git a/yaml/parser/flow.hpp b/yaml/parser/flow.hpp new file mode 100644 index 00000000..793f78e7 --- /dev/null +++ b/yaml/parser/flow.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2010, 2011 Object Modeling Designs + */ + +#if !defined(OMD_PARSER_FLOW_HPP) +#define OMD_PARSER_FLOW_HPP + +#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS + +#include +#include "scalar.hpp" + +namespace omd { namespace parser +{ + template + struct white_space : qi::grammar + { + white_space(); + qi::rule start; + }; + + template + struct flow : qi::grammar > + { + flow(std::string const& source_file = ""); + + typedef std::pair element_t; + typedef white_space white_space; + + qi::rule value; + qi::rule object; + qi::rule< Iterator, element_t(), white_space > member_pair; + qi::rule array; + scalar scalar_value; + + typedef omd::parser::error_handler error_handler_t; + boost::phoenix::function const error_handler; + }; +}} + +#endif diff --git a/yaml/parser/flow_def.hpp b/yaml/parser/flow_def.hpp new file mode 100644 index 00000000..7d924b29 --- /dev/null +++ b/yaml/parser/flow_def.hpp @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2010, 2011 Object Modeling Designs + */ + +#if !defined(OMD_PARSER_FLOW_DEF_HPP) +#define OMD_PARSER_FLOW_DEF_HPP + +#include "flow.hpp" + +#include + +namespace omd { namespace parser +{ + template + white_space::white_space() + : white_space::base_type(start) + { + qi::char_type char_; + qi::eol_type eol; + qi::space_type space; + + start = + space // tab/space/cr/lf + | '#' >> *(char_ - eol) >> eol // comments + ; + } + + template + flow::flow(std::string const& source_file) + : flow::base_type(value), + scalar_value(source_file), + error_handler(error_handler_t(source_file)) + { + value = + scalar_value + | object + | array + ; + + object = + '{' + > -(member_pair % ',') + > '}' + ; + + member_pair = + scalar_value + >> ':' + >> value + ; + + array = + '[' + > -(value % ',') + > ']' + ; + + BOOST_SPIRIT_DEBUG_NODES( + (value) + (object) + (member_pair) + (array) + ); + + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + qi::on_error(value, error_handler(_1, _2, _3, _4)); + } +}} + +#endif diff --git a/yaml/parser/scalar.hpp b/yaml/parser/scalar.hpp index e03974d2..710d60c3 100644 --- a/yaml/parser/scalar.hpp +++ b/yaml/parser/scalar.hpp @@ -1,16 +1,20 @@ /** * Copyright (C) 2010, 2011 Object Modeling Designs + * Copyright (c) 2010 Joel de Guzman */ -#if !defined(OMD_PARSER_STRING) -#define OMD_PARSER_STRING +#if !defined(OMD_PARSER_SCALAR_HPP) +#define OMD_PARSER_SCALAR_HPP #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS #include #include -#include "../ast.hpp" // our AST +#include + +#include "../ast.hpp" // our AST +#include "error_handler.hpp" // Our Error Handler namespace omd { namespace parser { @@ -32,13 +36,16 @@ namespace omd { namespace parser template struct scalar : qi::grammar { - scalar(); + scalar(std::string const& source_file = ""); qi::rule value; unicode_string string_value; qi::rule integer_value; qi::symbols bool_value; qi::rule null_value; + + typedef omd::parser::error_handler error_handler_t; + boost::phoenix::function const error_handler; }; }} diff --git a/yaml/parser/scalar_def.hpp b/yaml/parser/scalar_def.hpp index fa4b7572..09cdb1cb 100644 --- a/yaml/parser/scalar_def.hpp +++ b/yaml/parser/scalar_def.hpp @@ -1,11 +1,13 @@ /** * Copyright (C) 2010, 2011 Object Modeling Designs + * Copyright (c) 2010 Joel de Guzman */ -#if !defined(OMD_COMMON_STRING_DEF) -#define OMD_COMMON_STRING_DEF +#if !defined(OMD_COMMON_SCALAR_DEF_HPP) +#define OMD_COMMON_SCALAR_DEF_HPP #include "scalar.hpp" + #include #include #include @@ -99,12 +101,12 @@ namespace omd { namespace parser } template - scalar::scalar() - : scalar::base_type(value) + scalar::scalar(std::string const& source_file) + : scalar::base_type(value), + error_handler(error_handler_t(source_file)) { qi::_val_type _val; qi::lit_type lit; - qi::lexeme_type lexeme; qi::char_type char_; qi::hex_type hex; qi::oct_type oct; @@ -123,8 +125,8 @@ namespace omd { namespace parser ; integer_value = - lexeme[no_case["0x"] > hex] - | lexeme['0' >> oct] + no_case["0x"] > hex + | '0' >> oct | int_ ; @@ -148,6 +150,12 @@ namespace omd { namespace parser (bool_value) (null_value) ); + + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + qi::on_error(value, error_handler(_1, _2, _3, _4)); } }}