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.

411 lines
9.8 KiB

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