configor_parser.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // Copyright (c) 2018-2020 configor - Nomango
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. #pragma once
  21. #include "configor_encoding.hpp"
  22. #include "configor_stream.hpp"
  23. #include "configor_token.hpp"
  24. #include "configor_value.hpp"
  25. #include <initializer_list> // std::initializer_list
  26. #include <istream> // std::basic_istream
  27. #include <locale> // std::locale
  28. namespace configor
  29. {
  30. namespace detail
  31. {
  32. template <typename _ValTy, typename _SourceCharTy>
  33. class basic_parser
  34. {
  35. public:
  36. using value_type = _ValTy;
  37. using source_char_type = _SourceCharTy;
  38. using target_char_type = typename value_type::char_type;
  39. basic_parser(std::basic_istream<source_char_type>& is)
  40. : is_(is.rdbuf())
  41. , err_handler_(nullptr)
  42. , source_decoder_(nullptr)
  43. , target_encoder_(nullptr)
  44. {
  45. is_.unsetf(std::ios_base::skipws);
  46. is_.imbue(std::locale(std::locale::classic(), is.getloc(), std::locale::collate | std::locale::ctype));
  47. }
  48. virtual void parse(value_type& c)
  49. {
  50. try
  51. {
  52. do_parse(c, token_type::uninitialized);
  53. if (scan() != token_type::end_of_input)
  54. fail(token_type::end_of_input);
  55. }
  56. catch (...)
  57. {
  58. if (err_handler_)
  59. err_handler_->handle(std::current_exception());
  60. else
  61. throw;
  62. }
  63. }
  64. inline void set_error_handler(configor::error_handler* eh)
  65. {
  66. err_handler_ = eh;
  67. }
  68. template <template <class> class _Encoding>
  69. inline void set_source_encoding()
  70. {
  71. source_decoder_ = _Encoding<source_char_type>::decode;
  72. }
  73. template <template <class> class _Encoding>
  74. inline void set_target_encoding()
  75. {
  76. target_encoder_ = _Encoding<target_char_type>::encode;
  77. }
  78. protected:
  79. virtual token_type scan() = 0;
  80. virtual void get_integer(typename value_type::integer_type& out) = 0;
  81. virtual void get_float(typename value_type::float_type& out) = 0;
  82. virtual void get_string(typename value_type::string_type& out) = 0;
  83. virtual void do_parse(value_type& c, token_type last_token, bool read_next = true)
  84. {
  85. using string_type = typename _ValTy::string_type;
  86. token_type token = last_token;
  87. if (read_next)
  88. {
  89. token = scan();
  90. }
  91. switch (token)
  92. {
  93. case token_type::literal_true:
  94. c = true;
  95. break;
  96. case token_type::literal_false:
  97. c = false;
  98. break;
  99. case token_type::literal_null:
  100. c = value_constant::null;
  101. break;
  102. case token_type::value_string:
  103. c = value_constant::string;
  104. get_string(*c.data().string);
  105. break;
  106. case token_type::value_integer:
  107. c = value_constant::integer;
  108. get_integer(c.data().integer);
  109. break;
  110. case token_type::value_float:
  111. c = value_constant::floating;
  112. get_float(c.data().floating);
  113. break;
  114. case token_type::begin_array:
  115. c = value_constant::array;
  116. while (true)
  117. {
  118. token = scan();
  119. bool is_end = false;
  120. switch (token)
  121. {
  122. case token_type::literal_true:
  123. case token_type::literal_false:
  124. case token_type::literal_null:
  125. case token_type::value_string:
  126. case token_type::value_integer:
  127. case token_type::value_float:
  128. case token_type::begin_array:
  129. case token_type::begin_object:
  130. break;
  131. default:
  132. is_end = true;
  133. break;
  134. }
  135. if (is_end)
  136. break;
  137. c.data().vector->push_back(_ValTy());
  138. do_parse(c.data().vector->back(), token, false);
  139. // read ','
  140. token = scan();
  141. if (token != token_type::value_separator)
  142. break;
  143. }
  144. if (token != token_type::end_array)
  145. fail(token, token_type::end_array);
  146. break;
  147. case token_type::begin_object:
  148. c = value_constant::object;
  149. while (true)
  150. {
  151. token = scan();
  152. if (token != token_type::value_string)
  153. break;
  154. string_type key{};
  155. get_string(key);
  156. token = scan();
  157. if (token != token_type::name_separator)
  158. break;
  159. _ValTy object;
  160. do_parse(object, token);
  161. c.data().object->emplace(key, object);
  162. // read ','
  163. token = scan();
  164. if (token != token_type::value_separator)
  165. break;
  166. }
  167. if (token != token_type::end_object)
  168. fail(token, token_type::end_object);
  169. break;
  170. default:
  171. fail(token);
  172. break;
  173. }
  174. }
  175. void fail(token_type actual_token, const std::string& msg = "unexpected token")
  176. {
  177. detail::fast_ostringstream ss;
  178. ss << msg << " '" << to_string(actual_token) << "'";
  179. throw configor_deserialization_error(ss.str());
  180. }
  181. void fail(token_type actual_token, token_type expected_token, const std::string& msg = "unexpected token")
  182. {
  183. fail(actual_token, msg + ", expect '" + to_string(expected_token) + "', but got");
  184. }
  185. protected:
  186. std::basic_istream<source_char_type> is_;
  187. error_handler* err_handler_;
  188. encoding::decoder<source_char_type> source_decoder_;
  189. encoding::encoder<target_char_type> target_encoder_;
  190. };
  191. //
  192. // parsable
  193. //
  194. template <typename _Args, template <class, class> class _ParserTy, template <class> class _DefaultEncoding>
  195. class parsable
  196. {
  197. public:
  198. using value_type = basic_value<_Args>;
  199. template <typename _SourceCharTy>
  200. using parser_type = _ParserTy<value_type, _SourceCharTy>;
  201. template <typename _SourceCharTy>
  202. using parser_option = typename parser_type<_SourceCharTy>::option;
  203. // parse from stream
  204. template <typename _SourceCharTy>
  205. static void parse(value_type& c, std::basic_istream<_SourceCharTy>& is,
  206. std::initializer_list<parser_option<_SourceCharTy>> options = {})
  207. {
  208. parser_type<_SourceCharTy> p{ is };
  209. p.template set_source_encoding<_DefaultEncoding>();
  210. p.template set_target_encoding<_DefaultEncoding>();
  211. p.prepare(options);
  212. p.parse(c);
  213. }
  214. template <typename _SourceCharTy>
  215. static value_type parse(std::basic_istream<_SourceCharTy>& is,
  216. std::initializer_list<parser_option<_SourceCharTy>> options = {})
  217. {
  218. value_type c;
  219. parse(c, is, options);
  220. return c;
  221. }
  222. // parse from string
  223. template <typename _SourceCharTy>
  224. static void parse(value_type& c, const typename _Args::template string_type<_SourceCharTy>& str,
  225. std::initializer_list<parser_option<_SourceCharTy>> options = {})
  226. {
  227. detail::fast_string_istreambuf<_SourceCharTy> buf{ str };
  228. std::basic_istream<_SourceCharTy> is{ &buf };
  229. parse(c, is, options);
  230. }
  231. template <typename _SourceCharTy>
  232. static value_type parse(const typename _Args::template string_type<_SourceCharTy>& str,
  233. std::initializer_list<parser_option<_SourceCharTy>> options = {})
  234. {
  235. detail::fast_string_istreambuf<_SourceCharTy> buf{ str };
  236. std::basic_istream<_SourceCharTy> is{ &buf };
  237. return parse(is, options);
  238. }
  239. // parse from c-style string
  240. template <typename _SourceCharTy>
  241. static void parse(value_type& c, const _SourceCharTy* str,
  242. std::initializer_list<parser_option<_SourceCharTy>> options = {})
  243. {
  244. detail::fast_buffer_istreambuf<_SourceCharTy> buf{ str };
  245. std::basic_istream<_SourceCharTy> is{ &buf };
  246. parse(c, is, options);
  247. }
  248. template <typename _SourceCharTy>
  249. static value_type parse(const _SourceCharTy* str, std::initializer_list<parser_option<_SourceCharTy>> options = {})
  250. {
  251. detail::fast_buffer_istreambuf<_SourceCharTy> buf{ str };
  252. std::basic_istream<_SourceCharTy> is{ &buf };
  253. return parse(is, options);
  254. }
  255. // parse from c-style file
  256. template <typename _SourceCharTy = typename value_type::char_type>
  257. static void parse(value_type& c, std::FILE* file, std::initializer_list<parser_option<_SourceCharTy>> options = {})
  258. {
  259. detail::fast_cfile_istreambuf<_SourceCharTy> buf{ file };
  260. std::basic_istream<_SourceCharTy> is{ &buf };
  261. parse(c, is, options);
  262. }
  263. template <typename _SourceCharTy = typename value_type::char_type>
  264. static value_type parse(std::FILE* file, std::initializer_list<parser_option<_SourceCharTy>> options = {})
  265. {
  266. detail::fast_cfile_istreambuf<_SourceCharTy> buf{ file };
  267. std::basic_istream<_SourceCharTy> is{ &buf };
  268. return parse(is, options);
  269. }
  270. };
  271. } // namespace detail
  272. } // namespace configor