You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
9.1 KiB

  1. /*
  2. cpptempl
  3. =================
  4. This is a template engine for C++.
  5. Syntax
  6. =================
  7. Variables: {$variable_name}
  8. Loops: {% for person in people %}Name: {$person.name}{% endfor %}
  9. If: {% for person.name == "Bob" %}Full name: Robert{% endif %}
  10. Copyright
  11. ==================
  12. Author: Ryan Ginstrom
  13. MIT License
  14. Usage
  15. =======================
  16. wstring text = L"{% if item %}{$item}{% endif %}\n"
  17. L"{% if thing %}{$thing}{% endif %}" ;
  18. cpptempl::data_map data ;
  19. data[L"item"] = cpptempl::make_data(L"aaa") ;
  20. data[L"thing"] = cpptempl::make_data(L"bbb") ;
  21. wstring result = cpptempl::parse(text, data) ;
  22. Handy Functions
  23. ========================
  24. make_data() : Feed it a string, data_map, or data_list to create a data entry.
  25. Example:
  26. data_map person ;
  27. person[L"name"] = make_data(L"Bob") ;
  28. person[L"occupation"] = make_data(L"Plumber") ;
  29. data_map data ;
  30. data[L"person"] = make_data(person) ;
  31. wstring result = parse(templ_text, data) ;
  32. */
  33. #pragma once
  34. #ifdef _WIN32
  35. #pragma warning( disable : 4996 ) // 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
  36. #pragma warning( disable : 4512 ) // 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
  37. #endif
  38. #include <string>
  39. #include <vector>
  40. #include <map>
  41. #include <boost/shared_ptr.hpp>
  42. #ifndef _MSC_VER
  43. #include <boost/locale.hpp>
  44. #include <utf8.h>
  45. #else
  46. #include <boost/scoped_array.hpp>
  47. #include "windows.h"
  48. #include "winnls.h" // unicode-multibyte conversion
  49. #endif
  50. #include <boost/unordered_map.hpp>
  51. #include <boost/lexical_cast.hpp>
  52. #include <iostream>
  53. namespace cpptempl
  54. {
  55. using std::wstring ;
  56. // various typedefs
  57. class data_ptr;
  58. typedef std::vector<data_ptr> data_list ;
  59. class data_map {
  60. public:
  61. data_ptr& operator [](const std::wstring& key);
  62. data_ptr& operator [](const std::string& key);
  63. bool empty();
  64. bool has(const wstring& key);
  65. private:
  66. boost::unordered_map<wstring, data_ptr> data;
  67. };
  68. // data classes
  69. class Data ;
  70. class DataValue ;
  71. class DataList ;
  72. class DataMap ;
  73. class data_ptr {
  74. public:
  75. data_ptr() {}
  76. template<typename T> data_ptr(const T& data) {
  77. this->operator =(data);
  78. }
  79. data_ptr(DataValue* data) : ptr(data) {}
  80. data_ptr(DataList* data) : ptr(data) {}
  81. data_ptr(DataMap* data) : ptr(data) {}
  82. data_ptr(const data_ptr& data) {
  83. ptr = data.ptr;
  84. }
  85. template<typename T> void operator = (const T& data);
  86. void push_back(const data_ptr& data);
  87. virtual ~data_ptr() {}
  88. Data* operator ->() {
  89. return ptr.get();
  90. }
  91. private:
  92. boost::shared_ptr<Data> ptr;
  93. };
  94. template<> inline void data_ptr::operator = (const data_ptr& data);
  95. template<> void data_ptr::operator = (const std::string& data);
  96. template<> void data_ptr::operator = (const std::wstring& data);
  97. template<> void data_ptr::operator = (const data_map& data);
  98. template<typename T>
  99. void data_ptr::operator = (const T& data) {
  100. #ifndef _MSC_VER
  101. std::wstring data_str = boost::lexical_cast<std::wstring>(data);
  102. #else
  103. #endif
  104. this->operator =(data_str);
  105. }
  106. // convenience functions for recoding utf8 string to wstring and back
  107. inline std::wstring utf8_to_wide(const std::string& text) {
  108. #ifndef _MSC_VER
  109. std::wstring result;
  110. if (sizeof(wchar_t) == 2) {
  111. utf8::utf8to16(text.begin(), text.end(), std::back_inserter(result));
  112. } else {
  113. assert(sizeof(wchar_t) == 4);
  114. utf8::utf8to32(text.begin(), text.end(), std::back_inserter(result));
  115. }
  116. return result;
  117. //return boost::locale::conv::to_utf<wchar_t>(text, "UTF-8");
  118. #else
  119. // Calculate the required length of the buffer
  120. const size_t len_needed = ::MultiByteToWideChar(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , NULL, 0 );
  121. boost::scoped_array<wchar_t> buff(new wchar_t[len_needed+1]) ;
  122. const size_t num_copied = ::MultiByteToWideChar(CP_UTF8, 0, text.c_str(), text.size(), buff.get(), len_needed+1) ;
  123. return std::wstring(buff.get(), num_copied) ;
  124. #endif
  125. }
  126. inline std::string wide_to_utf8(const std::wstring& text) {
  127. #ifndef _MSC_VER
  128. std::string result;
  129. if (sizeof(wchar_t) == 2) {
  130. utf8::utf16to8(text.begin(), text.end(), std::back_inserter(result));
  131. } else {
  132. assert(sizeof(wchar_t) == 4);
  133. utf8::utf32to8(text.begin(), text.end(), std::back_inserter(result));
  134. }
  135. return result;
  136. //return boost::locale::conv::from_utf<>(text, "UTF-8");
  137. #else
  138. const size_t len_needed = ::WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , NULL, 0, NULL, NULL) ;
  139. boost::scoped_array<char> buff(new char[len_needed+1]) ;
  140. const size_t num_copied = ::WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , buff.get(), len_needed+1, NULL, NULL) ;
  141. return std::string(buff.get(), num_copied) ;
  142. #endif
  143. }
  144. // token classes
  145. class Token ;
  146. typedef boost::shared_ptr<Token> token_ptr ;
  147. typedef std::vector<token_ptr> token_vector ;
  148. // Custom exception class for library errors
  149. class TemplateException : public std::exception
  150. {
  151. public:
  152. TemplateException(std::string reason) : m_reason(reason){}
  153. ~TemplateException() throw() {}
  154. const char* what() const throw() {
  155. return m_reason.c_str();
  156. }
  157. private:
  158. std::string m_reason;
  159. };
  160. // Data types used in templates
  161. class Data
  162. {
  163. public:
  164. virtual ~Data() {
  165. // Intentionally left empty.
  166. }
  167. virtual bool empty() = 0 ;
  168. virtual wstring getvalue();
  169. virtual data_list& getlist();
  170. virtual data_map& getmap() ;
  171. };
  172. class DataValue : public Data
  173. {
  174. wstring m_value ;
  175. public:
  176. DataValue(wstring value) : m_value(value){}
  177. wstring getvalue();
  178. bool empty();
  179. };
  180. class DataList : public Data
  181. {
  182. data_list m_items ;
  183. public:
  184. DataList(const data_list &items) : m_items(items){}
  185. data_list& getlist() ;
  186. bool empty();
  187. };
  188. class DataMap : public Data
  189. {
  190. data_map m_items ;
  191. public:
  192. DataMap(const data_map &items) : m_items(items){}
  193. data_map& getmap();
  194. bool empty();
  195. };
  196. // convenience functions for making data objects
  197. inline data_ptr make_data(wstring val)
  198. {
  199. return data_ptr(new DataValue(val)) ;
  200. }
  201. inline data_ptr make_data(data_list &val)
  202. {
  203. return data_ptr(new DataList(val)) ;
  204. }
  205. inline data_ptr make_data(data_map &val)
  206. {
  207. return data_ptr(new DataMap(val)) ;
  208. }
  209. // get a data value from a data map
  210. // e.g. foo.bar => data["foo"]["bar"]
  211. data_ptr parse_val(wstring key, data_map &data) ;
  212. typedef enum
  213. {
  214. TOKEN_TYPE_NONE,
  215. TOKEN_TYPE_TEXT,
  216. TOKEN_TYPE_VAR,
  217. TOKEN_TYPE_IF,
  218. TOKEN_TYPE_FOR,
  219. TOKEN_TYPE_ENDIF,
  220. TOKEN_TYPE_ENDFOR,
  221. } TokenType;
  222. // Template tokens
  223. // base class for all token types
  224. class Token
  225. {
  226. public:
  227. virtual TokenType gettype() = 0 ;
  228. virtual void gettext(std::wostream &stream, data_map &data) = 0 ;
  229. virtual void set_children(token_vector &children);
  230. virtual token_vector & get_children();
  231. };
  232. // normal text
  233. class TokenText : public Token
  234. {
  235. wstring m_text ;
  236. public:
  237. TokenText(wstring text) : m_text(text){}
  238. TokenType gettype();
  239. void gettext(std::wostream &stream, data_map &data);
  240. };
  241. // variable
  242. class TokenVar : public Token
  243. {
  244. wstring m_key ;
  245. public:
  246. TokenVar(wstring key) : m_key(key){}
  247. TokenType gettype();
  248. void gettext(std::wostream &stream, data_map &data);
  249. };
  250. // for block
  251. class TokenFor : public Token
  252. {
  253. public:
  254. wstring m_key ;
  255. wstring m_val ;
  256. token_vector m_children ;
  257. TokenFor(wstring expr);
  258. TokenType gettype();
  259. void gettext(std::wostream &stream, data_map &data);
  260. void set_children(token_vector &children);
  261. token_vector &get_children();
  262. };
  263. // if block
  264. class TokenIf : public Token
  265. {
  266. public:
  267. wstring m_expr ;
  268. token_vector m_children ;
  269. TokenIf(wstring expr) : m_expr(expr){}
  270. TokenType gettype();
  271. void gettext(std::wostream &stream, data_map &data);
  272. bool is_true(wstring expr, data_map &data);
  273. void set_children(token_vector &children);
  274. token_vector &get_children();
  275. };
  276. // end of block
  277. class TokenEnd : public Token // end of control block
  278. {
  279. wstring m_type ;
  280. public:
  281. TokenEnd(wstring text) : m_type(text){}
  282. TokenType gettype();
  283. void gettext(std::wostream &stream, data_map &data);
  284. };
  285. wstring gettext(token_ptr token, data_map &data) ;
  286. void parse_tree(token_vector &tokens, token_vector &tree, TokenType until=TOKEN_TYPE_NONE) ;
  287. token_vector & tokenize(wstring text, token_vector &tokens) ;
  288. // The big daddy. Pass in the template and data,
  289. // and get out a completed doc.
  290. void parse(std::wostream &stream, wstring templ_text, data_map &data) ;
  291. wstring parse(wstring templ_text, data_map &data);
  292. std::string parse(std::string templ_text, data_map &data);
  293. }