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.

419 lines
9.9 KiB

  1. #ifdef _MSC_VER
  2. #include "stdafx.h"
  3. #endif
  4. #include "cpptempl.h"
  5. #include <sstream>
  6. #include <boost/algorithm/string.hpp>
  7. #include <boost/lexical_cast.hpp>
  8. namespace cpptempl
  9. {
  10. //////////////////////////////////////////////////////////////////////////
  11. // Data classes
  12. //////////////////////////////////////////////////////////////////////////
  13. // data_map
  14. data_ptr& data_map::operator [](const std::wstring& key) {
  15. return data[key];
  16. }
  17. data_ptr& data_map::operator [](const std::string& key) {
  18. return data[utf8_to_wide(key)];
  19. }
  20. bool data_map::empty() {
  21. return data.empty();
  22. }
  23. bool data_map::has(const wstring& key) {
  24. return data.find(key) != data.end();
  25. }
  26. // data_ptr
  27. template<>
  28. inline void data_ptr::operator = (const data_ptr& data) {
  29. ptr = data.ptr;
  30. }
  31. template<>
  32. void data_ptr::operator = (const std::string& data) {
  33. ptr.reset(new DataValue(utf8_to_wide(data)));
  34. }
  35. template<>
  36. void data_ptr::operator = (const std::wstring& data) {
  37. ptr.reset(new DataValue(data));
  38. }
  39. template<>
  40. void data_ptr::operator = (const data_map& data) {
  41. ptr.reset(new DataMap(data));
  42. }
  43. void data_ptr::push_back(const data_ptr& data) {
  44. if (!ptr) {
  45. ptr.reset(new DataList(data_list()));
  46. }
  47. data_list& list = ptr->getlist();
  48. list.push_back(data);
  49. }
  50. // base data
  51. wstring Data::getvalue()
  52. {
  53. throw TemplateException("Data item is not a value") ;
  54. }
  55. data_list& Data::getlist()
  56. {
  57. throw TemplateException("Data item is not a list") ;
  58. }
  59. data_map& Data::getmap()
  60. {
  61. throw TemplateException("Data item is not a dictionary") ;
  62. }
  63. // data value
  64. wstring DataValue::getvalue()
  65. {
  66. return m_value ;
  67. }
  68. bool DataValue::empty()
  69. {
  70. return m_value.empty();
  71. }
  72. // data list
  73. data_list& DataList::getlist()
  74. {
  75. return m_items ;
  76. }
  77. bool DataList::empty()
  78. {
  79. return m_items.empty();
  80. }
  81. // data map
  82. data_map& DataMap:: getmap()
  83. {
  84. return m_items ;
  85. }
  86. bool DataMap::empty()
  87. {
  88. return m_items.empty();
  89. }
  90. //////////////////////////////////////////////////////////////////////////
  91. // parse_val
  92. //////////////////////////////////////////////////////////////////////////
  93. data_ptr parse_val(wstring key, data_map &data)
  94. {
  95. // quoted string
  96. if (key[0] == L'\"')
  97. {
  98. return make_data(boost::trim_copy_if(key, boost::is_any_of(L"\""))) ;
  99. }
  100. // check for dotted notation, i.e [foo.bar]
  101. size_t index = key.find(L".") ;
  102. if (index == wstring::npos)
  103. {
  104. if (!data.has(key))
  105. {
  106. return make_data(L"{$" + key + L"}") ;
  107. }
  108. return data[key] ;
  109. }
  110. wstring sub_key = key.substr(0, index) ;
  111. if (!data.has(sub_key))
  112. {
  113. return make_data(L"{$" + key + L"}") ;
  114. }
  115. data_ptr item = data[sub_key] ;
  116. return parse_val(key.substr(index+1), item->getmap()) ;
  117. }
  118. //////////////////////////////////////////////////////////////////////////
  119. // Token classes
  120. //////////////////////////////////////////////////////////////////////////
  121. // defaults, overridden by subclasses with children
  122. void Token::set_children( token_vector & )
  123. {
  124. throw TemplateException("This token type cannot have children") ;
  125. }
  126. token_vector & Token::get_children()
  127. {
  128. throw TemplateException("This token type cannot have children") ;
  129. }
  130. // TokenText
  131. TokenType TokenText::gettype()
  132. {
  133. return TOKEN_TYPE_TEXT ;
  134. }
  135. void TokenText::gettext( std::wostream &stream, data_map & )
  136. {
  137. stream << m_text ;
  138. }
  139. // TokenVar
  140. TokenType TokenVar::gettype()
  141. {
  142. return TOKEN_TYPE_VAR ;
  143. }
  144. void TokenVar::gettext( std::wostream &stream, data_map &data )
  145. {
  146. stream << parse_val(m_key, data)->getvalue() ;
  147. }
  148. // TokenFor
  149. TokenFor::TokenFor(wstring expr)
  150. {
  151. std::vector<wstring> elements ;
  152. boost::split(elements, expr, boost::is_space()) ;
  153. if (elements.size() != 4u)
  154. {
  155. throw TemplateException("Invalid syntax in for statement") ;
  156. }
  157. m_val = elements[1] ;
  158. m_key = elements[3] ;
  159. }
  160. TokenType TokenFor::gettype()
  161. {
  162. return TOKEN_TYPE_FOR ;
  163. }
  164. void TokenFor::gettext( std::wostream &stream, data_map &data )
  165. {
  166. data_ptr value = parse_val(m_key, data) ;
  167. data_list &items = value->getlist() ;
  168. for (size_t i = 0 ; i < items.size() ; ++i)
  169. {
  170. data_map loop ;
  171. loop[L"index"] = make_data(boost::lexical_cast<wstring>(i+1)) ;
  172. loop[L"index0"] = make_data(boost::lexical_cast<wstring>(i)) ;
  173. data[L"loop"] = make_data(loop);
  174. data[m_val] = items[i] ;
  175. for(size_t j = 0 ; j < m_children.size() ; ++j)
  176. {
  177. m_children[j]->gettext(stream, data) ;
  178. }
  179. }
  180. }
  181. void TokenFor::set_children( token_vector &children )
  182. {
  183. m_children.assign(children.begin(), children.end()) ;
  184. }
  185. token_vector & TokenFor::get_children()
  186. {
  187. return m_children;
  188. }
  189. // TokenIf
  190. TokenType TokenIf::gettype()
  191. {
  192. return TOKEN_TYPE_IF ;
  193. }
  194. void TokenIf::gettext( std::wostream &stream, data_map &data )
  195. {
  196. if (is_true(m_expr, data))
  197. {
  198. for(size_t j = 0 ; j < m_children.size() ; ++j)
  199. {
  200. m_children[j]->gettext(stream, data) ;
  201. }
  202. }
  203. }
  204. bool TokenIf::is_true( wstring expr, data_map &data )
  205. {
  206. std::vector<wstring> elements ;
  207. boost::split(elements, expr, boost::is_space()) ;
  208. if (elements[1] == L"not")
  209. {
  210. return parse_val(elements[2], data)->empty() ;
  211. }
  212. if (elements.size() == 2)
  213. {
  214. return ! parse_val(elements[1], data)->empty() ;
  215. }
  216. data_ptr lhs = parse_val(elements[1], data) ;
  217. data_ptr rhs = parse_val(elements[3], data) ;
  218. if (elements[2] == L"==")
  219. {
  220. return lhs->getvalue() == rhs->getvalue() ;
  221. }
  222. return lhs->getvalue() != rhs->getvalue() ;
  223. }
  224. void TokenIf::set_children( token_vector &children )
  225. {
  226. m_children.assign(children.begin(), children.end()) ;
  227. }
  228. token_vector & TokenIf::get_children()
  229. {
  230. return m_children;
  231. }
  232. // TokenEnd
  233. TokenType TokenEnd::gettype()
  234. {
  235. return m_type == L"endfor" ? TOKEN_TYPE_ENDFOR : TOKEN_TYPE_ENDIF ;
  236. }
  237. void TokenEnd::gettext( std::wostream &, data_map &)
  238. {
  239. throw TemplateException("End-of-control statements have no associated text") ;
  240. }
  241. // gettext
  242. // generic helper for getting text from tokens.
  243. wstring gettext(token_ptr token, data_map &data)
  244. {
  245. std::wostringstream stream ;
  246. token->gettext(stream, data) ;
  247. return stream.str() ;
  248. }
  249. //////////////////////////////////////////////////////////////////////////
  250. // parse_tree
  251. // recursively parses list of tokens into a tree
  252. //////////////////////////////////////////////////////////////////////////
  253. void parse_tree(token_vector &tokens, token_vector &tree, TokenType until)
  254. {
  255. while(! tokens.empty())
  256. {
  257. // 'pops' first item off list
  258. token_ptr token = tokens[0] ;
  259. tokens.erase(tokens.begin()) ;
  260. if (token->gettype() == TOKEN_TYPE_FOR)
  261. {
  262. token_vector children ;
  263. parse_tree(tokens, children, TOKEN_TYPE_ENDFOR) ;
  264. token->set_children(children) ;
  265. }
  266. else if (token->gettype() == TOKEN_TYPE_IF)
  267. {
  268. token_vector children ;
  269. parse_tree(tokens, children, TOKEN_TYPE_ENDIF) ;
  270. token->set_children(children) ;
  271. }
  272. else if (token->gettype() == until)
  273. {
  274. return ;
  275. }
  276. tree.push_back(token) ;
  277. }
  278. }
  279. //////////////////////////////////////////////////////////////////////////
  280. // tokenize
  281. // parses a template into tokens (text, for, if, variable)
  282. //////////////////////////////////////////////////////////////////////////
  283. token_vector & tokenize(wstring text, token_vector &tokens)
  284. {
  285. while(! text.empty())
  286. {
  287. size_t pos = text.find(L"{") ;
  288. if (pos == wstring::npos)
  289. {
  290. if (! text.empty())
  291. {
  292. tokens.push_back(token_ptr(new TokenText(text))) ;
  293. }
  294. return tokens ;
  295. }
  296. wstring pre_text = text.substr(0, pos) ;
  297. if (! pre_text.empty())
  298. {
  299. tokens.push_back(token_ptr(new TokenText(pre_text))) ;
  300. }
  301. text = text.substr(pos+1) ;
  302. if (text.empty())
  303. {
  304. tokens.push_back(token_ptr(new TokenText(L"{"))) ;
  305. return tokens ;
  306. }
  307. // variable
  308. if (text[0] == L'$')
  309. {
  310. pos = text.find(L"}") ;
  311. if (pos != wstring::npos)
  312. {
  313. tokens.push_back(token_ptr (new TokenVar(text.substr(1, pos-1)))) ;
  314. text = text.substr(pos+1) ;
  315. }
  316. }
  317. // control statement
  318. else if (text[0] == L'%')
  319. {
  320. pos = text.find(L"}") ;
  321. if (pos != wstring::npos)
  322. {
  323. wstring expression = boost::trim_copy(text.substr(1, pos-2)) ;
  324. text = text.substr(pos+1) ;
  325. if (boost::starts_with(expression, L"for"))
  326. {
  327. tokens.push_back(token_ptr (new TokenFor(expression))) ;
  328. }
  329. else if (boost::starts_with(expression, L"if"))
  330. {
  331. tokens.push_back(token_ptr (new TokenIf(expression))) ;
  332. }
  333. else
  334. {
  335. tokens.push_back(token_ptr (new TokenEnd(boost::trim_copy(expression)))) ;
  336. }
  337. }
  338. }
  339. else
  340. {
  341. tokens.push_back(token_ptr(new TokenText(L"{"))) ;
  342. }
  343. }
  344. return tokens ;
  345. }
  346. /************************************************************************
  347. * parse
  348. *
  349. * 1. tokenizes template
  350. * 2. parses tokens into tree
  351. * 3. resolves template
  352. * 4. returns converted text
  353. ************************************************************************/
  354. wstring parse(wstring templ_text, data_map &data)
  355. {
  356. std::wostringstream stream ;
  357. parse(stream, templ_text, data) ;
  358. return stream.str() ;
  359. }
  360. std::string parse(std::string templ_text, data_map &data)
  361. {
  362. return wide_to_utf8(parse(utf8_to_wide(templ_text), data));
  363. }
  364. void parse(std::wostream &stream, wstring templ_text, data_map &data)
  365. {
  366. token_vector tokens ;
  367. tokenize(templ_text, tokens) ;
  368. token_vector tree ;
  369. parse_tree(tokens, tree) ;
  370. for (size_t i = 0 ; i < tree.size() ; ++i)
  371. {
  372. // Recursively calls gettext on each node in the tree.
  373. // gettext returns the appropriate text for that node.
  374. // for text, itself;
  375. // for variable, substitution;
  376. // for control statement, recursively gets kids
  377. tree[i]->gettext(stream, data) ;
  378. }
  379. }
  380. }