160 body_(std::move(body))
167 std::string tag_name(
const Action& action)
const
169 return body_.substr(action.start, action.end - action.start);
171 auto find_context(
const std::string& name,
const std::vector<const context*>& stack,
bool shouldUseOnlyFirstStackValue =
false)
const -> std::pair<bool, const context&>
175 return {
true, *stack.back()};
180 int dotPosition = name.find(
".");
181 if (dotPosition ==
static_cast<int>(name.npos))
183 for (
auto it = stack.rbegin(); it != stack.rend(); ++it)
185 if ((*it)->t() == json::type::Object)
187 if ((*it)->count(name))
188 return {
true, (**it)[name]};
194 std::vector<int> dotPositions;
195 dotPositions.push_back(-1);
196 while (dotPosition !=
static_cast<int>(name.npos))
198 dotPositions.push_back(dotPosition);
199 dotPosition = name.find(
".", dotPosition + 1);
201 dotPositions.push_back(name.size());
202 std::vector<std::string> names;
203 names.reserve(dotPositions.size() - 1);
204 for (
int i = 1; i < static_cast<int>(dotPositions.size()); i++)
205 names.emplace_back(name.substr(dotPositions[i - 1] + 1, dotPositions[i] - dotPositions[i - 1] - 1));
207 for (
auto it = stack.rbegin(); it != stack.rend(); ++it)
211 for (
auto jt = names.begin(); jt != names.end(); ++jt)
213 if (view->t() == json::type::Object &&
216 view = &(*view)[*jt];
220 if (shouldUseOnlyFirstStackValue)
222 return {
false, empty_str};
229 return {
true, *view};
233 return {
false, empty_str};
236 void escape(
const std::string& in, std::string& out)
const
238 out.reserve(out.size() + in.size());
239 for (
auto it = in.begin(); it != in.end(); ++it)
243 case '&': out +=
"&";
break;
244 case '<': out +=
"<";
break;
245 case '>': out +=
">";
break;
246 case '"': out +=
""";
break;
247 case '\'': out +=
"'";
break;
248 case '/': out +=
"/";
break;
249 case '`': out +=
"`";
break;
250 case '=': out +=
"=";
break;
251 default: out += *it;
break;
256 bool isTagInsideObjectBlock(
const int& current,
const std::vector<const context*>& stack)
const
259 for (
int i = current; i > 0; --i)
261 auto& action = actions_[i - 1];
263 if (action.t == ActionType::OpenBlock)
265 if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object)
271 else if (action.t == ActionType::CloseBlock)
280 void render_internal(
int actionBegin,
int actionEnd, std::vector<const context*>& stack, std::string& out,
int indent)
const
282 int current = actionBegin;
285 out.insert(out.size(), indent,
' ');
287 while (current < actionEnd)
289 auto& fragment = fragments_[current];
290 auto& action = actions_[current];
291 render_fragment(fragment, indent, out);
294 case ActionType::Ignore:
297 case ActionType::Partial:
299 std::string partial_name = tag_name(action);
300 auto partial_templ =
load(partial_name);
301 int partial_indent = action.pos;
302 partial_templ.render_internal(0, partial_templ.fragments_.size() - 1, stack, out, partial_indent ? indent + partial_indent : 0);
305 case ActionType::UnescapeTag:
306 case ActionType::Tag:
308 bool shouldUseOnlyFirstStackValue =
false;
309 if (isTagInsideObjectBlock(current, stack))
311 shouldUseOnlyFirstStackValue =
true;
313 auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
314 auto& ctx = optional_ctx.second;
317 case json::type::False:
318 case json::type::True:
319 case json::type::Number:
322 case json::type::String:
323 if (action.t == ActionType::Tag)
328 case json::type::Function:
330 std::string execute_result = ctx.execute();
331 while (execute_result.find(
"{{") != std::string::npos)
337 if (action.t == ActionType::Tag)
338 escape(execute_result, out);
340 out += execute_result;
344 throw std::runtime_error(
"not implemented tag type" + utility::lexical_cast<std::string>(
static_cast<int>(ctx.t())));
348 case ActionType::ElseBlock:
351 auto optional_ctx = find_context(tag_name(action), stack);
352 if (!optional_ctx.first)
354 stack.emplace_back(&nullContext);
358 auto& ctx = optional_ctx.second;
361 case json::type::List:
362 if (ctx.l && !ctx.l->empty())
363 current = action.pos;
365 stack.emplace_back(&nullContext);
367 case json::type::False:
368 case json::type::Null:
369 stack.emplace_back(&nullContext);
372 current = action.pos;
377 case ActionType::OpenBlock:
379 auto optional_ctx = find_context(tag_name(action), stack);
380 if (!optional_ctx.first)
382 current = action.pos;
386 auto& ctx = optional_ctx.second;
389 case json::type::List:
391 for (
auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
393 stack.push_back(&*it);
394 render_internal(current + 1, action.pos, stack, out, indent);
397 current = action.pos;
399 case json::type::Number:
400 case json::type::String:
401 case json::type::Object:
402 case json::type::True:
403 stack.push_back(&ctx);
405 case json::type::False:
406 case json::type::Null:
407 current = action.pos;
410 throw std::runtime_error(
"{{#: not implemented context type: " + utility::lexical_cast<std::string>(
static_cast<int>(ctx.t())));
415 case ActionType::CloseBlock:
419 throw std::runtime_error(
"not implemented " + utility::lexical_cast<std::string>(
static_cast<int>(action.t)));
423 auto& fragment = fragments_[actionEnd];
424 render_fragment(fragment, indent, out);
426 void render_fragment(
const std::pair<int, int> fragment,
int indent, std::string& out)
const
430 for (
int i = fragment.first; i < fragment.second; i++)
433 if (body_[i] ==
'\n' && i + 1 !=
static_cast<int>(body_.size()))
434 out.insert(out.size(), indent,
' ');
438 out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
446 std::vector<const context*> stack;
447 stack.emplace_back(&empty_ctx);
450 render_internal(0, fragments_.size() - 1, stack, ret, 0);
457 std::vector<const context*> stack;
458 stack.emplace_back(&ctx);
461 render_internal(0, fragments_.size() - 1, stack, ret, 0);
475 std::vector<const context*> stack;
476 stack.emplace_back(&empty_ctx);
479 render_internal(0, fragments_.size() - 1, stack, ret, 0);
486 std::vector<const context*> stack;
487 stack.emplace_back(&ctx);
490 render_internal(0, fragments_.size() - 1, stack, ret, 0);
497 std::string tag_open =
"{{";
498 std::string tag_close =
"}}";
500 std::vector<int> blockPositions;
505 size_t idx = body_.find(tag_open, current);
506 if (idx == body_.npos)
508 fragments_.emplace_back(
static_cast<int>(current),
static_cast<int>(body_.size()));
509 actions_.emplace_back(
'!', ActionType::Ignore, 0, 0);
512 fragments_.emplace_back(
static_cast<int>(current),
static_cast<int>(idx));
514 idx += tag_open.size();
515 size_t endIdx = body_.find(tag_close, idx);
518 throw invalid_template_exception(
"empty tag is not allowed");
520 if (endIdx == body_.npos)
523 throw invalid_template_exception(
"not matched opening tag");
525 current = endIdx + tag_close.size();
526 char tag_char = body_[idx];
531 while (body_[idx] ==
' ')
533 while (body_[endIdx - 1] ==
' ')
535 blockPositions.emplace_back(
static_cast<int>(actions_.size()));
536 actions_.emplace_back(tag_char, ActionType::OpenBlock, idx, endIdx);
540 while (body_[idx] ==
' ')
542 while (body_[endIdx - 1] ==
' ')
545 if (blockPositions.empty())
547 throw invalid_template_exception(
548 std::string(
"unexpected closing tag: ")
549 + body_.substr(idx, endIdx - idx)
552 auto& matched = actions_[blockPositions.back()];
553 if (body_.compare(idx, endIdx - idx,
554 body_, matched.start, matched.end - matched.start) != 0)
556 throw invalid_template_exception(
557 std::string(
"not matched {{")
560 + body_.substr(matched.start, matched.end - matched.start) +
", "
561 + body_.substr(idx, endIdx - idx)
564 matched.pos =
static_cast<int>(actions_.size());
565 matched.has_end_match =
true;
567 actions_.emplace_back(tag_char, ActionType::CloseBlock, idx, endIdx, blockPositions.back());
568 blockPositions.pop_back();
572 while (body_[idx] ==
' ')
574 while (body_[endIdx - 1] ==
' ')
576 blockPositions.emplace_back(
static_cast<int>(actions_.size()));
577 actions_.emplace_back(tag_char, ActionType::ElseBlock, idx, endIdx);
581 actions_.emplace_back(tag_char, ActionType::Ignore, idx + 1, endIdx);
585 while (body_[idx] ==
' ')
587 while (body_[endIdx - 1] ==
' ')
589 actions_.emplace_back(tag_char, ActionType::Partial, idx, endIdx);
592 if (tag_open !=
"{{" || tag_close !=
"}}")
593 throw invalid_template_exception(
"cannot use triple mustache when delimiter changed");
596 if (body_[endIdx + 2] !=
'}')
598 throw invalid_template_exception(
"{{{: }}} not matched");
600 while (body_[idx] ==
' ')
602 while (body_[endIdx - 1] ==
' ')
604 actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
609 while (body_[idx] ==
' ')
611 while (body_[endIdx - 1] ==
' ')
613 actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
618 actions_.emplace_back(tag_char, ActionType::Ignore, idx, endIdx);
620 if (body_[endIdx] !=
'=')
621 throw invalid_template_exception(
"{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
623 while (body_[idx] ==
' ')
625 while (body_[endIdx] ==
' ')
629 bool succeeded =
false;
630 for (
size_t i = idx; i < endIdx; i++)
634 tag_open = body_.substr(idx, i - idx);
635 while (body_[i] ==
' ')
637 tag_close = body_.substr(i, endIdx - i);
638 if (tag_open.empty())
639 throw invalid_template_exception(
"{{=: empty open tag");
640 if (tag_close.empty())
641 throw invalid_template_exception(
"{{=: empty close tag");
643 if (tag_close.find(
" ") != tag_close.npos)
644 throw invalid_template_exception(
"{{=: invalid open/close tag: " + tag_open +
" " + tag_close);
650 throw invalid_template_exception(
"{{=: cannot find space between new open/close tags");
655 while (body_[idx] ==
' ')
657 while (body_[endIdx - 1] ==
' ')
659 actions_.emplace_back(tag_char, ActionType::Tag, idx, endIdx);
665 for (
int i = 0; i < static_cast<int>(actions_.size()); i++)
667 if (actions_[i].missing_end_pair())
669 throw invalid_template_exception(
670 std::string(
"open tag has no matching end tag {{")
671 + actions_[i].tag_char
673 + body_.substr(actions_[i].start, actions_[i].end - actions_[i].start)
679 for (
int i =
static_cast<int>(actions_.size()) - 2; i >= 0; i--)
681 if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
683 auto& fragment_before = fragments_[i];
684 auto& fragment_after = fragments_[i + 1];
685 bool is_last_action = i ==
static_cast<int>(actions_.size()) - 2;
686 bool all_space_before =
true;
688 for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
692 all_space_before =
false;
696 if (all_space_before && i > 0)
698 if (!all_space_before && body_[j] !=
'\n')
700 bool all_space_after =
true;
701 for (k = fragment_after.first; k < static_cast<int>(body_.size()) && k < fragment_after.second; k++)
705 all_space_after =
false;
709 if (all_space_after && !is_last_action)
711 if (!all_space_after &&
715 k + 1 <
static_cast<int>(body_.size()) &&
716 body_[k + 1] ==
'\n')))
718 if (actions_[i].t == ActionType::Partial)
720 actions_[i].pos = fragment_before.second - j - 1;
722 fragment_before.second = j + 1;
723 if (!all_space_after)
725 if (body_[k] ==
'\n')
729 fragment_after.first = k;
734 std::vector<std::pair<int, int>> fragments_;
735 std::vector<Action> actions_;