Line data Source code
1 : #pragma GCC system_header // ignore warnings
2 :
3 : /*
4 : * Boost Software License - Version 1.0
5 : *
6 : * Mustache v4.0
7 : * Copyright 2015-2018 Kevin Wojniak
8 : *
9 : * Permission is hereby granted, free of charge, to any person or organization
10 : * obtaining a copy of the software and accompanying documentation covered by
11 : * this license (the "Software") to use, reproduce, display, distribute,
12 : * execute, and transmit the Software, and to prepare derivative works of the
13 : * Software, and to permit third-parties to whom the Software is furnished to
14 : * do so, all subject to the following:
15 : *
16 : * The copyright notices in the Software and this entire statement, including
17 : * the above license grant, this restriction and the following disclaimer,
18 : * must be included in all copies of the Software, in whole or in part, and
19 : * all derivative works of the Software, unless such copies or derivative
20 : * works are solely in the form of machine-executable object code generated by
21 : * a source language processor.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
26 : * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
27 : * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
28 : * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : */
31 :
32 : #ifndef KAINJOW_MUSTACHE_HPP
33 : #define KAINJOW_MUSTACHE_HPP
34 :
35 : #include <cassert>
36 : #include <cctype>
37 : #include <functional>
38 : #include <iostream>
39 : #include <memory>
40 : #include <sstream>
41 : #include <unordered_map>
42 : #include <vector>
43 :
44 : namespace kainjow
45 : {
46 : namespace mustache
47 : {
48 :
49 : template <typename string_type>
50 10516 : string_type trim (const string_type & s)
51 : {
52 10516 : auto it = s.begin ();
53 47406 : while (it != s.end () && std::isspace (*it))
54 : {
55 5286 : it++;
56 : }
57 : auto rit = s.rbegin ();
58 52596 : while (rit.base () != it && std::isspace (*rit))
59 : {
60 10524 : rit++;
61 : }
62 42064 : return { it, rit.base () };
63 : }
64 :
65 : template <typename string_type>
66 0 : string_type html_escape (const string_type & s)
67 : {
68 0 : string_type ret;
69 0 : ret.reserve (s.size () * 2);
70 0 : for (const auto ch : s)
71 : {
72 0 : switch (ch)
73 : {
74 : case '&':
75 0 : ret.append ({ '&', 'a', 'm', 'p', ';' });
76 : break;
77 : case '<':
78 0 : ret.append ({ '&', 'l', 't', ';' });
79 : break;
80 : case '>':
81 0 : ret.append ({ '&', 'g', 't', ';' });
82 : break;
83 : case '\"':
84 0 : ret.append ({ '&', 'q', 'u', 'o', 't', ';' });
85 : break;
86 : case '\'':
87 0 : ret.append ({ '&', 'a', 'p', 'o', 's', ';' });
88 : break;
89 : default:
90 0 : ret.append (1, ch);
91 : break;
92 : }
93 : }
94 0 : return ret;
95 : }
96 :
97 : template <typename string_type>
98 27 : std::vector<string_type> split (const string_type & s, typename string_type::value_type delim)
99 : {
100 27 : std::vector<string_type> elems;
101 54 : std::basic_stringstream<typename string_type::value_type> ss (s);
102 27 : string_type item;
103 60 : while (std::getline (ss, item, delim))
104 : {
105 3 : elems.push_back (item);
106 : }
107 27 : return elems;
108 : }
109 :
110 : template <typename string_type>
111 : class basic_renderer
112 : {
113 : public:
114 : using type1 = std::function<string_type (const string_type &)>;
115 : using type2 = std::function<string_type (const string_type &, bool escaped)>;
116 :
117 : string_type operator() (const string_type & text) const
118 : {
119 : return type1_ (text);
120 : }
121 :
122 : string_type operator() (const string_type & text, bool escaped) const
123 : {
124 : return type2_ (text, escaped);
125 : }
126 :
127 : private:
128 0 : basic_renderer (const type1 & t1, const type2 & t2) : type1_ (t1), type2_ (t2)
129 : {
130 : }
131 :
132 : const type1 & type1_;
133 : const type2 & type2_;
134 :
135 : template <typename StringType>
136 : friend class basic_mustache;
137 : };
138 :
139 : template <typename string_type>
140 0 : class basic_lambda_t
141 : {
142 : public:
143 : using type1 = std::function<string_type (const string_type &)>;
144 : using type2 = std::function<string_type (const string_type &, const basic_renderer<string_type> & render)>;
145 :
146 : basic_lambda_t (const type1 & t) : type1_ (new type1 (t))
147 : {
148 : }
149 : basic_lambda_t (const type2 & t) : type2_ (new type2 (t))
150 : {
151 : }
152 :
153 : bool is_type1 () const
154 : {
155 : return static_cast<bool> (type1_);
156 : }
157 : bool is_type2 () const
158 : {
159 : return static_cast<bool> (type2_);
160 : }
161 :
162 : const type1 & type1_value () const
163 : {
164 0 : return *type1_;
165 : }
166 : const type2 & type2_value () const
167 : {
168 0 : return *type2_;
169 : }
170 :
171 : // Copying
172 0 : basic_lambda_t (const basic_lambda_t & l)
173 0 : {
174 0 : if (l.type1_)
175 : {
176 0 : type1_.reset (new type1 (*l.type1_));
177 : }
178 0 : else if (l.type2_)
179 : {
180 0 : type2_.reset (new type2 (*l.type2_));
181 : }
182 0 : }
183 :
184 : string_type operator() (const string_type & text) const
185 : {
186 : return (*type1_) (text);
187 : }
188 :
189 : string_type operator() (const string_type & text, const basic_renderer<string_type> & render) const
190 : {
191 : return (*type2_) (text, render);
192 : }
193 :
194 : private:
195 : std::unique_ptr<type1> type1_;
196 : std::unique_ptr<type2> type2_;
197 : };
198 :
199 : template <typename string_type>
200 : class basic_data;
201 : template <typename string_type>
202 : using basic_object = std::unordered_map<string_type, basic_data<string_type>>;
203 : template <typename string_type>
204 : using basic_list = std::vector<basic_data<string_type>>;
205 : template <typename string_type>
206 : using basic_partial = std::function<string_type ()>;
207 : template <typename string_type>
208 : using basic_lambda = typename basic_lambda_t<string_type>::type1;
209 : template <typename string_type>
210 : using basic_lambda2 = typename basic_lambda_t<string_type>::type2;
211 :
212 : template <typename string_type>
213 39381 : class basic_data
214 : {
215 : public:
216 : enum class type
217 : {
218 : object,
219 : string,
220 : list,
221 : bool_true,
222 : bool_false,
223 : partial,
224 : lambda,
225 : lambda2,
226 : invalid,
227 : };
228 :
229 : // Construction
230 612 : basic_data () : basic_data (type::object)
231 : {
232 : }
233 6804 : basic_data (const string_type & string) : type_{ type::string }
234 : {
235 3402 : str_.reset (new string_type (string));
236 1134 : }
237 264 : basic_data (const typename string_type::value_type * string) : type_{ type::string }
238 : {
239 176 : str_.reset (new string_type (string));
240 44 : }
241 1356 : basic_data (const basic_object<string_type> & obj) : type_{ type::object }
242 : {
243 678 : obj_.reset (new basic_object<string_type> (obj));
244 226 : }
245 966 : basic_data (const basic_list<string_type> & l) : type_{ type::list }
246 : {
247 322 : list_.reset (new basic_list<string_type> (l));
248 161 : }
249 3672 : basic_data (type t) : type_{ t }
250 : {
251 612 : switch (type_)
252 : {
253 : case type::object:
254 1224 : obj_.reset (new basic_object<string_type>);
255 : break;
256 : case type::string:
257 0 : str_.reset (new string_type);
258 : break;
259 : case type::list:
260 0 : list_.reset (new basic_list<string_type>);
261 : break;
262 : default:
263 : break;
264 : }
265 612 : }
266 : basic_data (const string_type & name, const basic_data & var) : basic_data{}
267 : {
268 : set (name, var);
269 : }
270 1872 : basic_data (const basic_partial<string_type> & p) : type_{ type::partial }
271 : {
272 624 : partial_.reset (new basic_partial<string_type> (p));
273 312 : }
274 : basic_data (const basic_lambda<string_type> & l) : type_{ type::lambda }
275 : {
276 : lambda_.reset (new basic_lambda_t<string_type> (l));
277 : }
278 : basic_data (const basic_lambda2<string_type> & l) : type_{ type::lambda2 }
279 : {
280 : lambda_.reset (new basic_lambda_t<string_type> (l));
281 : }
282 : basic_data (const basic_lambda_t<string_type> & l)
283 : {
284 : if (l.is_type1 ())
285 : {
286 : type_ = type::lambda;
287 : }
288 : else if (l.is_type2 ())
289 : {
290 : type_ = type::lambda2;
291 : }
292 : lambda_.reset (new basic_lambda_t<string_type> (l));
293 : }
294 2820 : basic_data (bool b) : type_{ b ? type::bool_true : type::bool_false }
295 : {
296 : }
297 :
298 : // Copying
299 60852 : basic_data (const basic_data & dat) : type_ (dat.type_)
300 : {
301 20284 : if (dat.obj_)
302 : {
303 2116 : obj_.reset (new basic_object<string_type> (*dat.obj_));
304 : }
305 18168 : else if (dat.str_)
306 : {
307 18273 : str_.reset (new string_type (*dat.str_));
308 : }
309 5986 : else if (dat.list_)
310 : {
311 814 : list_.reset (new basic_list<string_type> (*dat.list_));
312 : }
313 5172 : else if (dat.partial_)
314 : {
315 0 : partial_.reset (new basic_partial<string_type> (*dat.partial_));
316 : }
317 5172 : else if (dat.lambda_)
318 : {
319 0 : lambda_.reset (new basic_lambda_t<string_type> (*dat.lambda_));
320 : }
321 10142 : }
322 :
323 : // Move
324 156 : basic_data (basic_data && dat) : type_{ dat.type_ }
325 : {
326 52 : if (dat.obj_)
327 : {
328 26 : obj_ = std::move (dat.obj_);
329 : }
330 0 : else if (dat.str_)
331 : {
332 0 : str_ = std::move (dat.str_);
333 : }
334 0 : else if (dat.list_)
335 : {
336 0 : list_ = std::move (dat.list_);
337 : }
338 0 : else if (dat.partial_)
339 : {
340 0 : partial_ = std::move (dat.partial_);
341 : }
342 0 : else if (dat.lambda_)
343 : {
344 0 : lambda_ = std::move (dat.lambda_);
345 : }
346 26 : dat.type_ = type::invalid;
347 26 : }
348 652 : basic_data & operator= (basic_data && dat)
349 : {
350 652 : if (this != &dat)
351 : {
352 1304 : obj_.reset ();
353 1304 : str_.reset ();
354 1304 : list_.reset ();
355 1304 : partial_.reset ();
356 1304 : lambda_.reset ();
357 1304 : if (dat.obj_)
358 : {
359 24 : obj_ = std::move (dat.obj_);
360 : }
361 1256 : else if (dat.str_)
362 : {
363 166 : str_ = std::move (dat.str_);
364 : }
365 924 : else if (dat.list_)
366 : {
367 94 : list_ = std::move (dat.list_);
368 : }
369 736 : else if (dat.partial_)
370 : {
371 312 : partial_ = std::move (dat.partial_);
372 : }
373 112 : else if (dat.lambda_)
374 : {
375 0 : lambda_ = std::move (dat.lambda_);
376 : }
377 652 : type_ = dat.type_;
378 652 : dat.type_ = type::invalid;
379 : }
380 652 : return *this;
381 : }
382 :
383 : // Type info
384 : bool is_object () const
385 : {
386 : return type_ == type::object;
387 : }
388 : bool is_string () const
389 : {
390 : return type_ == type::string;
391 : }
392 : bool is_list () const
393 : {
394 : return type_ == type::list;
395 : }
396 : bool is_bool () const
397 : {
398 : return is_true () || is_false ();
399 : }
400 : bool is_true () const
401 : {
402 : return type_ == type::bool_true;
403 : }
404 : bool is_false () const
405 : {
406 : return type_ == type::bool_false;
407 : }
408 : bool is_partial () const
409 : {
410 : return type_ == type::partial;
411 : }
412 : bool is_lambda () const
413 : {
414 : return type_ == type::lambda;
415 : }
416 : bool is_lambda2 () const
417 : {
418 : return type_ == type::lambda2;
419 : }
420 : bool is_invalid () const
421 : {
422 : return type_ == type::invalid;
423 : }
424 :
425 : // Object data
426 : bool is_empty_object () const
427 : {
428 : return is_object () && obj_->empty ();
429 : }
430 : bool is_non_empty_object () const
431 : {
432 : return is_object () && !obj_->empty ();
433 : }
434 : void set (const string_type & name, const basic_data & var)
435 : {
436 : if (is_object ())
437 : {
438 : auto it = obj_->find (name);
439 : if (it != obj_->end ())
440 : {
441 : obj_->erase (it);
442 : }
443 : obj_->insert (std::pair<string_type, basic_data>{ name, var });
444 : }
445 : }
446 : const basic_data * get (const string_type & name) const
447 : {
448 4653 : if (!is_object ())
449 : {
450 : return nullptr;
451 : }
452 10785 : const auto & it = obj_->find (name);
453 10785 : if (it == obj_->end ())
454 : {
455 : return nullptr;
456 : }
457 2309 : return &it->second;
458 : }
459 :
460 : // List data
461 : void push_back (const basic_data & var)
462 : {
463 : if (is_list ())
464 : {
465 : list_->push_back (var);
466 : }
467 : }
468 : const basic_list<string_type> & list_value () const
469 : {
470 212 : return *list_;
471 : }
472 : bool is_empty_list () const
473 : {
474 849 : return is_list () && list_->empty ();
475 : }
476 : bool is_non_empty_list () const
477 : {
478 567 : return is_list () && !list_->empty ();
479 : }
480 : basic_data & operator<< (const basic_data & data)
481 : {
482 : push_back (data);
483 : return *this;
484 : }
485 :
486 : // String data
487 : const string_type & string_value () const
488 : {
489 3188 : return *str_;
490 : }
491 :
492 : basic_data & operator[] (const string_type & key)
493 : {
494 1014 : return (*obj_)[key];
495 : }
496 :
497 : const basic_partial<string_type> & partial_value () const
498 : {
499 142 : return (*partial_);
500 : }
501 :
502 : const basic_lambda<string_type> & lambda_value () const
503 : {
504 0 : return lambda_->type1_value ();
505 : }
506 :
507 : const basic_lambda2<string_type> & lambda2_value () const
508 : {
509 0 : return lambda_->type2_value ();
510 : }
511 :
512 : private:
513 : type type_;
514 : std::unique_ptr<basic_object<string_type>> obj_;
515 : std::unique_ptr<string_type> str_;
516 : std::unique_ptr<basic_list<string_type>> list_;
517 : std::unique_ptr<basic_partial<string_type>> partial_;
518 : std::unique_ptr<basic_lambda_t<string_type>> lambda_;
519 : };
520 :
521 : template <typename string_type>
522 3786 : class delimiter_set
523 : {
524 : public:
525 : string_type begin;
526 : string_type end;
527 357 : delimiter_set () : begin (default_begin), end (default_end)
528 : {
529 119 : }
530 436 : bool is_default () const
531 : {
532 436 : return begin == default_begin && end == default_end;
533 : }
534 : static const string_type default_begin;
535 : static const string_type default_end;
536 : };
537 :
538 : template <typename string_type>
539 14328 : const string_type delimiter_set<string_type>::default_begin (2, '{');
540 : template <typename string_type>
541 14328 : const string_type delimiter_set<string_type>::default_end (2, '}');
542 :
543 : template <typename string_type>
544 : class basic_context
545 : {
546 : public:
547 : virtual void push (const basic_data<string_type> * data) = 0;
548 : virtual void pop () = 0;
549 :
550 : virtual const basic_data<string_type> * get (const string_type & name) const = 0;
551 : virtual const basic_data<string_type> * get_partial (const string_type & name) const = 0;
552 : };
553 :
554 : template <typename string_type>
555 238 : class context : public basic_context<string_type>
556 : {
557 : public:
558 24 : context (const basic_data<string_type> * data)
559 48 : {
560 24 : push (data);
561 24 : }
562 :
563 95 : context ()
564 190 : {
565 : }
566 :
567 500 : virtual void push (const basic_data<string_type> * data) override
568 : {
569 1572 : items_.insert (items_.begin (), data);
570 500 : }
571 :
572 500 : virtual void pop () override
573 : {
574 2000 : items_.erase (items_.begin ());
575 500 : }
576 :
577 2734 : virtual const basic_data<string_type> * get (const string_type & name) const override
578 : {
579 : // process {{.}} name
580 2736 : if (name.size () == 1 && name.at (0) == '.')
581 : {
582 4 : return items_.front ();
583 : }
584 2732 : if (name.find ('.') == string_type::npos)
585 : {
586 : // process normal name without having to split which is slower
587 13258 : for (const auto & item : items_)
588 : {
589 9136 : const auto var = item->get (name);
590 4568 : if (var)
591 : {
592 : return var;
593 : }
594 : }
595 : return nullptr;
596 : }
597 : // process x.y-like name
598 0 : const auto names = split (name, '.');
599 0 : for (const auto & item : items_)
600 : {
601 0 : auto var = item;
602 0 : for (const auto & n : names)
603 : {
604 0 : var = var->get (n);
605 0 : if (!var)
606 : {
607 : break;
608 : }
609 : }
610 0 : if (var)
611 : {
612 : return var;
613 : }
614 : }
615 : return nullptr;
616 : }
617 :
618 71 : virtual const basic_data<string_type> * get_partial (const string_type & name) const override
619 : {
620 298 : for (const auto & item : items_)
621 : {
622 170 : const auto var = item->get (name);
623 85 : if (var)
624 : {
625 : return var;
626 : }
627 : }
628 : return nullptr;
629 : }
630 :
631 : context (const context &) = delete;
632 : context & operator= (const context &) = delete;
633 :
634 : private:
635 : std::vector<const basic_data<string_type> *> items_;
636 : };
637 :
638 : template <typename string_type>
639 476 : class line_buffer_state
640 : {
641 : public:
642 : string_type data;
643 : bool contained_section_tag = false;
644 :
645 : bool is_empty_or_contains_only_whitespace () const
646 : {
647 5577 : for (const auto ch : data)
648 : {
649 : // don't look at newlines
650 1049 : if (ch != ' ' && ch != '\t')
651 : {
652 : return false;
653 : }
654 : }
655 : return true;
656 : }
657 :
658 : void clear ()
659 : {
660 14574 : data.clear ();
661 7287 : contained_section_tag = false;
662 : }
663 : };
664 :
665 : template <typename string_type>
666 238 : class context_internal
667 : {
668 : public:
669 : basic_context<string_type> & ctx;
670 : delimiter_set<string_type> delim_set;
671 : line_buffer_state<string_type> line_buffer;
672 :
673 238 : context_internal (basic_context<string_type> & a_ctx) : ctx (a_ctx)
674 : {
675 : }
676 : };
677 :
678 : enum class tag_type
679 : {
680 : text,
681 : variable,
682 : unescaped_variable,
683 : section_begin,
684 : section_end,
685 : section_begin_inverted,
686 : comment,
687 : partial,
688 : set_delimiter,
689 : };
690 :
691 : template <typename string_type>
692 2147352 : class mstch_tag /* gcc doesn't allow "tag tag;" so rename the class :( */
693 : {
694 : public:
695 : string_type name;
696 : tag_type type = tag_type::text;
697 : std::shared_ptr<string_type> section_text;
698 : std::shared_ptr<delimiter_set<string_type>> delim_set;
699 : bool is_section_begin () const
700 : {
701 82336 : return type == tag_type::section_begin || type == tag_type::section_begin_inverted;
702 : }
703 : bool is_section_end () const
704 : {
705 : return type == tag_type::section_end;
706 : }
707 : };
708 :
709 : template <typename string_type>
710 : class context_pusher
711 : {
712 : public:
713 500 : context_pusher (context_internal<string_type> & ctx, const basic_data<string_type> * data) : ctx_ (ctx)
714 : {
715 500 : ctx.ctx.push (data);
716 : }
717 : ~context_pusher ()
718 : {
719 500 : ctx_.ctx.pop ();
720 500 : }
721 : context_pusher (const context_pusher &) = delete;
722 : context_pusher & operator= (const context_pusher &) = delete;
723 :
724 : private:
725 : context_internal<string_type> & ctx_;
726 : };
727 :
728 : template <typename string_type>
729 1142350 : class component
730 : {
731 : private:
732 : using string_size_type = typename string_type::size_type;
733 :
734 : public:
735 : string_type text;
736 : mstch_tag<string_type> tag;
737 : std::vector<component> children;
738 : string_size_type position = string_type::npos;
739 :
740 : enum class walk_control
741 : {
742 : walk, // "continue" is reserved :/
743 : stop,
744 : skip,
745 : };
746 : using walk_callback = std::function<walk_control (component &)>;
747 :
748 5722 : component ()
749 22888 : {
750 : }
751 289348 : component (const string_type & t, string_size_type p) : text (t), position (p)
752 : {
753 72337 : }
754 :
755 : bool is_text () const
756 : {
757 : return tag.type == tag_type::text;
758 : }
759 :
760 56743 : bool is_newline () const
761 : {
762 116698 : return is_text () && ((text.size () == 2 && text[0] == '\r' && text[1] == '\n') ||
763 165663 : (text.size () == 1 && (text[0] == '\n' || text[0] == '\r')));
764 : }
765 :
766 : bool is_non_newline_whitespace () const
767 : {
768 : return is_text () && !is_newline () && text.size () == 1 && (text[0] == ' ' || text[0] == '\t');
769 : }
770 :
771 : void walk_children (const walk_callback & callback)
772 : {
773 95653 : for (auto & child : children)
774 : {
775 92097 : if (child.walk (callback) != walk_control::walk)
776 : {
777 : break;
778 : }
779 : }
780 : }
781 :
782 : private:
783 136734 : walk_control walk (const walk_callback & callback)
784 : {
785 136734 : walk_control control{ callback (*this) };
786 136734 : if (control == walk_control::stop)
787 : {
788 : return control;
789 : }
790 136734 : else if (control == walk_control::skip)
791 : {
792 : return walk_control::walk;
793 : }
794 586885 : for (auto & child : children)
795 : {
796 44637 : control = child.walk (callback);
797 44637 : if (control == walk_control::stop)
798 : {
799 : return control;
800 : }
801 : }
802 : return control;
803 : }
804 : };
805 :
806 : template <typename string_type>
807 : class parser
808 : {
809 : public:
810 : parser (const string_type & input, context_internal<string_type> & ctx, component<string_type> & root_component,
811 : string_type & error_message)
812 : {
813 95 : parse (input, ctx, root_component, error_message);
814 : }
815 :
816 : private:
817 95 : void parse (const string_type & input, context_internal<string_type> & ctx, component<string_type> & root_component,
818 : string_type & error_message) const
819 : {
820 : using string_size_type = typename string_type::size_type;
821 : using streamstring = std::basic_ostringstream<typename string_type::value_type>;
822 :
823 475 : const string_type brace_delimiter_end_unescaped (3, '}');
824 95 : const string_size_type input_size{ input.size () };
825 :
826 95 : bool current_delimiter_is_brace{ ctx.delim_set.is_default () };
827 :
828 380 : std::vector<component<string_type> *> sections{ &root_component };
829 190 : std::vector<string_size_type> section_starts;
830 190 : string_type current_text;
831 95 : string_size_type current_text_position = -1;
832 :
833 95 : current_text.reserve (input_size);
834 :
835 164999 : const auto process_current_text = [¤t_text, ¤t_text_position, §ions]() {
836 49079 : if (!current_text.empty ())
837 : {
838 57960 : const component<string_type> comp{ current_text, current_text_position };
839 28980 : sections.back ()->children.push_back (comp);
840 28980 : current_text.clear ();
841 28980 : current_text_position = -1;
842 : }
843 49174 : };
844 :
845 : const std::vector<string_type> whitespace{
846 : string_type (1, '\r') + string_type (1, '\n'),
847 : string_type (1, '\n'),
848 : string_type (1, '\r'),
849 : string_type (1, ' '),
850 : string_type (1, '\t'),
851 2375 : };
852 :
853 95 : for (string_size_type input_position = 0; input_position != input_size;)
854 : {
855 210328 : bool parse_tag = false;
856 :
857 210328 : if (input.compare (input_position, ctx.delim_set.begin.size (), ctx.delim_set.begin) == 0)
858 : {
859 5627 : process_current_text ();
860 :
861 : // Tag start delimiter
862 : parse_tag = true;
863 : }
864 : else
865 : {
866 204701 : bool parsed_whitespace = false;
867 1741620 : for (const auto & whitespace_text : whitespace)
868 : {
869 966173 : if (input.compare (input_position, whitespace_text.size (), whitespace_text) == 0)
870 : {
871 43357 : process_current_text ();
872 :
873 86714 : const component<string_type> comp{ whitespace_text, input_position };
874 43357 : sections.back ()->children.push_back (comp);
875 43357 : input_position += whitespace_text.size ();
876 :
877 43357 : parsed_whitespace = true;
878 : break;
879 : }
880 : }
881 :
882 204701 : if (!parsed_whitespace)
883 : {
884 161344 : if (current_text.empty ())
885 : {
886 28980 : current_text_position = input_position;
887 : }
888 484032 : current_text.append (1, input[input_position]);
889 161344 : input_position++;
890 : }
891 : }
892 :
893 210328 : if (!parse_tag)
894 : {
895 204701 : continue;
896 : }
897 :
898 : // Find the next tag start delimiter
899 5627 : const string_size_type tag_location_start = input_position;
900 :
901 : // Find the next tag end delimiter
902 5627 : string_size_type tag_contents_location{ tag_location_start + ctx.delim_set.begin.size () };
903 6647 : const bool tag_is_unescaped_var{ current_delimiter_is_brace && tag_location_start != (input_size - 2) &&
904 7667 : input.at (tag_contents_location) == ctx.delim_set.begin.at (0) };
905 5627 : const string_type & current_tag_delimiter_end{ tag_is_unescaped_var ? brace_delimiter_end_unescaped :
906 5627 : ctx.delim_set.end };
907 5627 : const auto current_tag_delimiter_end_size = current_tag_delimiter_end.size ();
908 5627 : if (tag_is_unescaped_var)
909 : {
910 498 : ++tag_contents_location;
911 : }
912 5627 : const string_size_type tag_location_end{ input.find (current_tag_delimiter_end, tag_contents_location) };
913 5627 : if (tag_location_end == string_type::npos)
914 : {
915 0 : streamstring ss;
916 0 : ss << "Unclosed tag at " << tag_location_start;
917 0 : error_message.assign (ss.str ());
918 : return;
919 : }
920 :
921 : // Parse tag
922 : const string_type tag_contents{ trim (
923 16881 : string_type{ input, tag_contents_location, tag_location_end - tag_contents_location }) };
924 11254 : component<string_type> comp;
925 11254 : if (!tag_contents.empty () && tag_contents[0] == '=')
926 : {
927 341 : if (!parse_set_delimiter_tag (tag_contents, ctx.delim_set))
928 : {
929 0 : streamstring ss;
930 0 : ss << "Invalid set delimiter tag at " << tag_location_start;
931 0 : error_message.assign (ss.str ());
932 : return;
933 : }
934 341 : current_delimiter_is_brace = ctx.delim_set.is_default ();
935 341 : comp.tag.type = tag_type::set_delimiter;
936 341 : comp.tag.delim_set.reset (new delimiter_set<string_type> (ctx.delim_set));
937 : }
938 5627 : if (comp.tag.type != tag_type::set_delimiter)
939 : {
940 5286 : parse_tag_contents (tag_is_unescaped_var, tag_contents, comp.tag);
941 : }
942 5627 : comp.position = tag_location_start;
943 5627 : sections.back ()->children.push_back (comp);
944 :
945 : // Start next search after this tag
946 5627 : input_position = tag_location_end + current_tag_delimiter_end_size;
947 :
948 : // Push or pop sections
949 11254 : if (comp.tag.is_section_begin ())
950 : {
951 3765 : sections.push_back (§ions.back ()->children.back ());
952 1255 : section_starts.push_back (input_position);
953 : }
954 4372 : else if (comp.tag.is_section_end ())
955 : {
956 1255 : if (sections.size () == 1)
957 : {
958 0 : streamstring ss;
959 0 : ss << "Unopened section \"" << comp.tag.name << "\" at " << comp.position;
960 0 : error_message.assign (ss.str ());
961 : return;
962 : }
963 3765 : sections.back ()->tag.section_text.reset (new string_type (
964 1255 : input.substr (section_starts.back (), tag_location_start - section_starts.back ())));
965 1255 : sections.pop_back ();
966 1255 : section_starts.pop_back ();
967 : }
968 : }
969 :
970 95 : process_current_text ();
971 :
972 : // Check for sections without an ending tag
973 380 : root_component.walk_children ([&error_message](component<string_type> & comp) ->
974 76709 : typename component<string_type>::walk_control {
975 153418 : if (!comp.tag.is_section_begin ())
976 : {
977 : return component<string_type>::walk_control::walk;
978 : }
979 5020 : if (comp.children.empty () || !comp.children.back ().tag.is_section_end () ||
980 3765 : comp.children.back ().tag.name != comp.tag.name)
981 : {
982 0 : streamstring ss;
983 0 : ss << "Unclosed section \"" << comp.tag.name << "\" at " << comp.position;
984 0 : error_message.assign (ss.str ());
985 : return component<string_type>::walk_control::stop;
986 : }
987 1255 : comp.children.pop_back (); // remove now useless end section component
988 1255 : return component<string_type>::walk_control::walk;
989 : });
990 95 : if (!error_message.empty ())
991 : {
992 : return;
993 : }
994 : }
995 :
996 682 : bool is_set_delimiter_valid (const string_type & delimiter) const
997 : {
998 : // "Custom delimiters may not contain whitespace or the equals sign."
999 4520 : for (const auto ch : delimiter)
1000 : {
1001 1792 : if (ch == '=' || std::isspace (ch))
1002 : {
1003 : return false;
1004 : }
1005 : }
1006 : return true;
1007 : }
1008 :
1009 341 : bool parse_set_delimiter_tag (const string_type & contents, delimiter_set<string_type> & delimiter_set) const
1010 : {
1011 : // Smallest legal tag is "=X X="
1012 341 : if (contents.size () < 5)
1013 : {
1014 : return false;
1015 : }
1016 341 : if (contents.back () != '=')
1017 : {
1018 : return false;
1019 : }
1020 682 : const auto contents_substr = trim (contents.substr (1, contents.size () - 2));
1021 341 : const auto spacepos = contents_substr.find (' ');
1022 341 : if (spacepos == string_type::npos)
1023 : {
1024 : return false;
1025 : }
1026 341 : const auto nonspace = contents_substr.find_first_not_of (' ', spacepos + 1);
1027 : assert (nonspace != string_type::npos);
1028 341 : const string_type begin = contents_substr.substr (0, spacepos);
1029 682 : const string_type end = contents_substr.substr (nonspace, contents_substr.size () - nonspace);
1030 341 : if (!is_set_delimiter_valid (begin) || !is_set_delimiter_valid (end))
1031 : {
1032 : return false;
1033 : }
1034 682 : delimiter_set.begin = begin;
1035 341 : delimiter_set.end = end;
1036 : return true;
1037 : }
1038 :
1039 5286 : void parse_tag_contents (bool is_unescaped_var, const string_type & contents, mstch_tag<string_type> & tag) const
1040 : {
1041 5286 : if (is_unescaped_var)
1042 : {
1043 498 : tag.type = tag_type::unescaped_variable;
1044 498 : tag.name = contents;
1045 : }
1046 4788 : else if (contents.empty ())
1047 : {
1048 0 : tag.type = tag_type::variable;
1049 0 : tag.name.clear ();
1050 : }
1051 : else
1052 : {
1053 4788 : switch (contents.at (0))
1054 : {
1055 : case '#':
1056 872 : tag.type = tag_type::section_begin;
1057 872 : break;
1058 : case '^':
1059 383 : tag.type = tag_type::section_begin_inverted;
1060 383 : break;
1061 : case '/':
1062 1255 : tag.type = tag_type::section_end;
1063 1255 : break;
1064 : case '>':
1065 96 : tag.type = tag_type::partial;
1066 96 : break;
1067 : case '&':
1068 1894 : tag.type = tag_type::unescaped_variable;
1069 1894 : break;
1070 : case '!':
1071 48 : tag.type = tag_type::comment;
1072 48 : break;
1073 : default:
1074 240 : tag.type = tag_type::variable;
1075 240 : break;
1076 : }
1077 4788 : if (tag.type == tag_type::variable)
1078 : {
1079 240 : tag.name = contents;
1080 : }
1081 : else
1082 : {
1083 9096 : string_type name{ contents };
1084 13644 : name.erase (name.begin ());
1085 9096 : tag.name = trim (name);
1086 : }
1087 : }
1088 5286 : }
1089 : };
1090 :
1091 : template <typename StringType>
1092 285 : class basic_mustache
1093 : {
1094 : public:
1095 : using string_type = StringType;
1096 :
1097 190 : basic_mustache (const string_type & input) : basic_mustache ()
1098 : {
1099 190 : context<string_type> ctx;
1100 190 : context_internal<string_type> context{ ctx };
1101 190 : parser<string_type> parser{ input, context, root_component_, error_message_ };
1102 95 : }
1103 :
1104 : bool is_valid () const
1105 : {
1106 522 : return error_message_.empty ();
1107 : }
1108 :
1109 : const string_type & error_message () const
1110 : {
1111 0 : return error_message_;
1112 : }
1113 :
1114 : using escape_handler = std::function<string_type (const string_type &)>;
1115 : void set_custom_escape (const escape_handler & escape_fn)
1116 : {
1117 95 : escape_ = escape_fn;
1118 : }
1119 :
1120 : template <typename stream_type>
1121 24 : stream_type & render (const basic_data<string_type> & data, stream_type & stream)
1122 : {
1123 24850 : render (data, [&stream](const string_type & str) { stream << str; });
1124 24 : return stream;
1125 : }
1126 :
1127 : string_type render (const basic_data<string_type> & data)
1128 : {
1129 : std::basic_ostringstream<typename string_type::value_type> ss;
1130 : return render (data, ss).str ();
1131 : }
1132 :
1133 : template <typename stream_type>
1134 : stream_type & render (basic_context<string_type> & ctx, stream_type & stream)
1135 : {
1136 : context_internal<string_type> context{ ctx };
1137 : render ([&stream](const string_type & str) { stream << str; }, context);
1138 : return stream;
1139 : }
1140 :
1141 : string_type render (basic_context<string_type> & ctx)
1142 : {
1143 : std::basic_ostringstream<typename string_type::value_type> ss;
1144 : return render (ctx, ss).str ();
1145 : }
1146 :
1147 : using render_handler = std::function<void(const string_type &)>;
1148 24 : void render (const basic_data<string_type> & data, const render_handler & handler)
1149 : {
1150 24 : if (!is_valid ())
1151 : {
1152 0 : return;
1153 : }
1154 48 : context<string_type> ctx{ &data };
1155 48 : context_internal<string_type> context{ ctx };
1156 24 : render (handler, context);
1157 : }
1158 :
1159 : private:
1160 : using string_size_type = typename string_type::size_type;
1161 :
1162 380 : basic_mustache () : escape_ (html_escape<string_type>)
1163 : {
1164 : }
1165 :
1166 0 : basic_mustache (const string_type & input, context_internal<string_type> & ctx) : basic_mustache ()
1167 : {
1168 0 : parser<string_type> parser{ input, ctx, root_component_, error_message_ };
1169 0 : }
1170 :
1171 0 : string_type render (context_internal<string_type> & ctx)
1172 : {
1173 0 : std::basic_ostringstream<typename string_type::value_type> ss;
1174 0 : render ([&ss](const string_type & str) { ss << str; }, ctx);
1175 0 : return ss.str ();
1176 : }
1177 :
1178 95 : void render (const render_handler & handler, context_internal<string_type> & ctx)
1179 : {
1180 285 : root_component_.walk_children (
1181 : [&handler, &ctx, this](component<string_type> & comp) ->
1182 32072 : typename component<string_type>::walk_control { return render_component (handler, ctx, comp); });
1183 : // process the last line
1184 95 : render_current_line (handler, ctx, nullptr);
1185 95 : }
1186 :
1187 7287 : void render_current_line (const render_handler & handler, context_internal<string_type> & ctx,
1188 : const component<string_type> * comp) const
1189 : {
1190 : // We're at the end of a line, so check the line buffer state to see
1191 : // if the line had tags in it, and also if the line is now empty or
1192 : // contains whitespace only. if this situation is true, skip the line.
1193 7287 : bool output = true;
1194 8448 : if (ctx.line_buffer.contained_section_tag && ctx.line_buffer.is_empty_or_contains_only_whitespace ())
1195 : {
1196 1045 : output = false;
1197 : }
1198 7287 : if (output)
1199 : {
1200 12484 : handler (ctx.line_buffer.data);
1201 6242 : if (comp)
1202 : {
1203 6147 : handler (comp->text);
1204 : }
1205 : }
1206 14574 : ctx.line_buffer.clear ();
1207 7287 : }
1208 :
1209 : void render_result (context_internal<string_type> & ctx, const string_type & text) const
1210 : {
1211 102226 : ctx.line_buffer.data.append (text);
1212 : }
1213 :
1214 60025 : typename component<string_type>::walk_control render_component (const render_handler & handler, context_internal<string_type> & ctx,
1215 : component<string_type> & comp)
1216 : {
1217 60025 : if (comp.is_text ())
1218 : {
1219 56743 : if (comp.is_newline ())
1220 : {
1221 7192 : render_current_line (handler, ctx, &comp);
1222 : }
1223 : else
1224 : {
1225 49551 : render_result (ctx, comp.text);
1226 : }
1227 : return component<string_type>::walk_control::walk;
1228 : }
1229 :
1230 3282 : const mstch_tag<string_type> & tag{ comp.tag };
1231 3282 : const basic_data<string_type> * var = nullptr;
1232 3282 : switch (tag.type)
1233 : {
1234 : case tag_type::variable:
1235 : case tag_type::unescaped_variable:
1236 1562 : if ((var = ctx.ctx.get (tag.name)) != nullptr)
1237 : {
1238 1562 : if (!render_variable (handler, var, ctx, tag.type == tag_type::variable))
1239 : {
1240 : return component<string_type>::walk_control::stop;
1241 : }
1242 : }
1243 : break;
1244 : case tag_type::section_begin:
1245 822 : if ((var = ctx.ctx.get (tag.name)) != nullptr)
1246 : {
1247 527 : if (var->is_lambda () || var->is_lambda2 ())
1248 : {
1249 0 : if (!render_lambda (handler, var, ctx, render_lambda_escape::optional, *comp.tag.section_text,
1250 : true))
1251 : {
1252 : return component<string_type>::walk_control::stop;
1253 : }
1254 : }
1255 902 : else if (!var->is_false () && !var->is_empty_list ())
1256 : {
1257 284 : render_section (handler, ctx, comp, var);
1258 : }
1259 : }
1260 : return component<string_type>::walk_control::skip;
1261 : case tag_type::section_begin_inverted:
1262 430 : if ((var = ctx.ctx.get (tag.name)) == nullptr || var->is_false () || var->is_empty_list ())
1263 : {
1264 270 : render_section (handler, ctx, comp, var);
1265 : }
1266 : return component<string_type>::walk_control::skip;
1267 : case tag_type::partial:
1268 71 : if ((var = ctx.ctx.get_partial (tag.name)) != nullptr && (var->is_partial () || var->is_string ()))
1269 : {
1270 284 : const auto & partial_result = var->is_partial () ? var->partial_value () () : var->string_value ();
1271 142 : basic_mustache tmpl{ partial_result };
1272 142 : tmpl.set_custom_escape (escape_);
1273 71 : if (!tmpl.is_valid ())
1274 : {
1275 0 : error_message_ = tmpl.error_message ();
1276 : }
1277 : else
1278 : {
1279 71 : tmpl.render (handler, ctx);
1280 71 : if (!tmpl.is_valid ())
1281 : {
1282 0 : error_message_ = tmpl.error_message ();
1283 : }
1284 : }
1285 71 : if (!tmpl.is_valid ())
1286 : {
1287 0 : return component<string_type>::walk_control::stop;
1288 : }
1289 : }
1290 : break;
1291 : case tag_type::set_delimiter:
1292 461 : ctx.delim_set = *comp.tag.delim_set;
1293 : break;
1294 : default:
1295 : break;
1296 : }
1297 :
1298 : return component<string_type>::walk_control::walk;
1299 : }
1300 :
1301 : enum class render_lambda_escape
1302 : {
1303 : escape,
1304 : unescape,
1305 : optional,
1306 : };
1307 :
1308 0 : bool render_lambda (const render_handler & handler, const basic_data<string_type> * var, context_internal<string_type> & ctx,
1309 : render_lambda_escape escape, const string_type & text, bool parse_with_same_context)
1310 : {
1311 : const typename basic_renderer<string_type>::type2 render2 = [this, &ctx, parse_with_same_context,
1312 0 : escape](const string_type & text, bool escaped) {
1313 0 : const auto process_template = [this, &ctx, escape, escaped](basic_mustache & tmpl) -> string_type {
1314 0 : if (!tmpl.is_valid ())
1315 : {
1316 0 : error_message_ = tmpl.error_message ();
1317 : return {};
1318 : }
1319 0 : const string_type str{ tmpl.render (ctx) };
1320 0 : if (!tmpl.is_valid ())
1321 : {
1322 0 : error_message_ = tmpl.error_message ();
1323 : return {};
1324 : }
1325 0 : bool do_escape = false;
1326 0 : switch (escape)
1327 : {
1328 : case render_lambda_escape::escape:
1329 0 : do_escape = true;
1330 0 : break;
1331 : case render_lambda_escape::unescape:
1332 : do_escape = false;
1333 : break;
1334 : case render_lambda_escape::optional:
1335 0 : do_escape = escaped;
1336 0 : break;
1337 : }
1338 0 : return do_escape ? escape_ (str) : str;
1339 0 : };
1340 0 : if (parse_with_same_context)
1341 : {
1342 0 : basic_mustache tmpl{ text, ctx };
1343 0 : tmpl.set_custom_escape (escape_);
1344 0 : return process_template (tmpl);
1345 : }
1346 0 : basic_mustache tmpl{ text };
1347 0 : tmpl.set_custom_escape (escape_);
1348 0 : return process_template (tmpl);
1349 0 : };
1350 0 : const typename basic_renderer<string_type>::type1 render = [&render2](const string_type & text) {
1351 : return render2 (text, false);
1352 0 : };
1353 0 : if (var->is_lambda2 ())
1354 : {
1355 0 : const basic_renderer<string_type> renderer{ render, render2 };
1356 0 : render_result (ctx, var->lambda2_value () (text, renderer));
1357 : }
1358 : else
1359 : {
1360 0 : render_current_line (handler, ctx, nullptr);
1361 0 : render_result (ctx, render (var->lambda_value () (text)));
1362 : }
1363 0 : return error_message_.empty ();
1364 : }
1365 :
1366 1562 : bool render_variable (const render_handler & handler, const basic_data<string_type> * var, context_internal<string_type> & ctx,
1367 : bool escaped)
1368 : {
1369 1562 : if (var->is_string ())
1370 : {
1371 1562 : const auto & varstr = var->string_value ();
1372 6248 : render_result (ctx, escaped ? escape_ (varstr) : varstr);
1373 : }
1374 0 : else if (var->is_lambda ())
1375 : {
1376 0 : const render_lambda_escape escape_opt = escaped ? render_lambda_escape::escape : render_lambda_escape::unescape;
1377 0 : return render_lambda (handler, var, ctx, escape_opt, {}, false);
1378 : }
1379 0 : else if (var->is_lambda2 ())
1380 : {
1381 : using streamstring = std::basic_ostringstream<typename string_type::value_type>;
1382 0 : streamstring ss;
1383 0 : ss << "Lambda with render argument is not allowed for regular variables";
1384 0 : error_message_ = ss.str ();
1385 : return false;
1386 : }
1387 : return true;
1388 : }
1389 :
1390 554 : void render_section (const render_handler & handler, context_internal<string_type> & ctx, component<string_type> & incomp,
1391 : const basic_data<string_type> * var)
1392 : {
1393 : const auto callback = [&handler, &ctx, this](component<string_type> & comp) -> typename component<string_type>::walk_control
1394 27953 : {
1395 27953 : return render_component (handler, ctx, comp);
1396 554 : };
1397 909 : if (var && var->is_non_empty_list ())
1398 : {
1399 675 : for (const auto & item : var->list_value ())
1400 : {
1401 : // account for the section begin tag
1402 251 : ctx.line_buffer.contained_section_tag = true;
1403 :
1404 502 : const context_pusher<string_type> ctxpusher{ ctx, &item };
1405 753 : incomp.walk_children (callback);
1406 :
1407 : // ctx may have been cleared. account for the section end tag
1408 251 : ctx.line_buffer.contained_section_tag = true;
1409 : }
1410 : }
1411 448 : else if (var)
1412 : {
1413 : // account for the section begin tag
1414 249 : ctx.line_buffer.contained_section_tag = true;
1415 :
1416 498 : const context_pusher<string_type> ctxpusher{ ctx, var };
1417 747 : incomp.walk_children (callback);
1418 :
1419 : // ctx may have been cleared. account for the section end tag
1420 249 : ctx.line_buffer.contained_section_tag = true;
1421 : }
1422 : else
1423 : {
1424 : // account for the section begin tag
1425 199 : ctx.line_buffer.contained_section_tag = true;
1426 :
1427 597 : incomp.walk_children (callback);
1428 :
1429 : // ctx may have been cleared. account for the section end tag
1430 199 : ctx.line_buffer.contained_section_tag = true;
1431 : }
1432 554 : }
1433 :
1434 : private:
1435 : string_type error_message_;
1436 : component<string_type> root_component_;
1437 : escape_handler escape_;
1438 : };
1439 :
1440 : using mustache = basic_mustache<std::string>;
1441 : using data = basic_data<mustache::string_type>;
1442 : using object = basic_object<mustache::string_type>;
1443 : using list = basic_list<mustache::string_type>;
1444 : using partial = basic_partial<mustache::string_type>;
1445 : using renderer = basic_renderer<mustache::string_type>;
1446 : using lambda = basic_lambda<mustache::string_type>;
1447 : using lambda2 = basic_lambda2<mustache::string_type>;
1448 : using lambda_t = basic_lambda_t<mustache::string_type>;
1449 :
1450 : using mustachew = basic_mustache<std::wstring>;
1451 : using dataw = basic_data<mustachew::string_type>;
1452 :
1453 : } // namespace mustache
1454 : } // namespace kainjow
1455 :
1456 : #endif // KAINJOW_MUSTACHE_HPP
|