LLVM 22.0.0git
Mustache.cpp
Go to the documentation of this file.
1//===-- Mustache.cpp ------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
11#include <sstream>
12
13using namespace llvm;
14using namespace llvm::mustache;
15
16namespace {
17
18using Accessor = SmallVector<std::string>;
19
20static bool isFalsey(const json::Value &V) {
21 return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
22 (V.getAsArray() && V.getAsArray()->empty());
23}
24
25static bool isContextFalsey(const json::Value *V) {
26 // A missing context (represented by a nullptr) is defined as falsey.
27 if (!V)
28 return true;
29 return isFalsey(*V);
30}
31
32static Accessor splitMustacheString(StringRef Str) {
33 // We split the mustache string into an accessor.
34 // For example:
35 // "a.b.c" would be split into {"a", "b", "c"}
36 // We make an exception for a single dot which
37 // refers to the current context.
38 Accessor Tokens;
39 if (Str == ".") {
40 Tokens.emplace_back(Str);
41 return Tokens;
42 }
43 while (!Str.empty()) {
44 StringRef Part;
45 std::tie(Part, Str) = Str.split(".");
46 Tokens.emplace_back(Part.trim());
47 }
48 return Tokens;
49}
50} // namespace
51
52namespace llvm::mustache {
53
54class Token {
55public:
66
67 Token(std::string Str)
69 AccessorValue({}), Indentation(0) {};
70
71 Token(std::string RawBody, std::string TokenBody, char Identifier)
73 Indentation(0) {
74 TokenType = getTokenType(Identifier);
76 return;
77 StringRef AccessorStr(this->TokenBody);
79 AccessorStr = AccessorStr.substr(1);
80 AccessorValue = splitMustacheString(StringRef(AccessorStr).trim());
81 }
82
83 Accessor getAccessor() const { return AccessorValue; }
84
85 Type getType() const { return TokenType; }
86
87 void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; }
88
89 size_t getIndentation() const { return Indentation; }
90
91 static Type getTokenType(char Identifier) {
92 switch (Identifier) {
93 case '#':
94 return Type::SectionOpen;
95 case '/':
96 return Type::SectionClose;
97 case '^':
99 case '!':
100 return Type::Comment;
101 case '>':
102 return Type::Partial;
103 case '&':
105 default:
106 return Type::Variable;
107 }
108 }
109
111 // RawBody is the original string that was tokenized.
112 std::string RawBody;
113 // TokenBody is the original string with the identifier removed.
114 std::string TokenBody;
117};
118
120
121class ASTNode {
122public:
132
134 llvm::StringMap<SectionLambda> &SectionLambdas, EscapeMap &Escapes)
135 : Partials(Partials), Lambdas(Lambdas), SectionLambdas(SectionLambdas),
136 Escapes(Escapes), Ty(Type::Root), Parent(nullptr),
137 ParentContext(nullptr) {}
138
139 ASTNode(std::string Body, ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
141 llvm::StringMap<SectionLambda> &SectionLambdas, EscapeMap &Escapes)
142 : Partials(Partials), Lambdas(Lambdas), SectionLambdas(SectionLambdas),
143 Escapes(Escapes), Ty(Type::Text), Body(std::move(Body)), Parent(Parent),
144 ParentContext(nullptr) {}
145
146 // Constructor for Section/InvertSection/Variable/UnescapeVariable Nodes
147 ASTNode(Type Ty, Accessor Accessor, ASTNode *Parent,
149 llvm::StringMap<SectionLambda> &SectionLambdas, EscapeMap &Escapes)
150 : Partials(Partials), Lambdas(Lambdas), SectionLambdas(SectionLambdas),
151 Escapes(Escapes), Ty(Ty), Parent(Parent),
152 AccessorValue(std::move(Accessor)), ParentContext(nullptr) {}
153
154 void addChild(AstPtr Child) { Children.emplace_back(std::move(Child)); };
155
156 void setRawBody(std::string NewBody) { RawBody = std::move(NewBody); };
157
158 void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
159
161
162private:
163 void renderLambdas(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
164 Lambda &L);
165
166 void renderSectionLambdas(const llvm::json::Value &Contexts,
168
169 void renderPartial(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
171
172 void renderChild(const llvm::json::Value &Context, llvm::raw_ostream &OS);
173
174 const llvm::json::Value *findContext();
175
176 StringMap<AstPtr> &Partials;
177 StringMap<Lambda> &Lambdas;
178 StringMap<SectionLambda> &SectionLambdas;
179 EscapeMap &Escapes;
180 Type Ty;
181 size_t Indentation = 0;
182 std::string RawBody;
183 std::string Body;
184 ASTNode *Parent;
185 // TODO: switch implementation to SmallVector<T>
186 std::vector<AstPtr> Children;
187 const Accessor AccessorValue;
188 const llvm::json::Value *ParentContext;
189};
190
191// A wrapper for arena allocator for ASTNodes
194 llvm::StringMap<SectionLambda> &SectionLambdas,
195 EscapeMap &Escapes) {
196 return std::make_unique<ASTNode>(Partials, Lambdas, SectionLambdas, Escapes);
197}
198
200 llvm::StringMap<AstPtr> &Partials,
202 llvm::StringMap<SectionLambda> &SectionLambdas,
203 EscapeMap &Escapes) {
204 return std::make_unique<ASTNode>(T, std::move(A), Parent, Partials, Lambdas,
205 SectionLambdas, Escapes);
206}
207
208AstPtr createTextNode(std::string Body, ASTNode *Parent,
209 llvm::StringMap<AstPtr> &Partials,
211 llvm::StringMap<SectionLambda> &SectionLambdas,
212 EscapeMap &Escapes) {
213 return std::make_unique<ASTNode>(std::move(Body), Parent, Partials, Lambdas,
214 SectionLambdas, Escapes);
215}
216
217// Function to check if there is meaningful text behind.
218// We determine if a token has meaningful text behind
219// if the right of previous token contains anything that is
220// not a newline.
221// For example:
222// "Stuff {{#Section}}" (returns true)
223// vs
224// "{{#Section}} \n" (returns false)
225// We make an exception for when previous token is empty
226// and the current token is the second token.
227// For example:
228// "{{#Section}}"
229bool hasTextBehind(size_t Idx, const ArrayRef<Token> &Tokens) {
230 if (Idx == 0)
231 return true;
232
233 size_t PrevIdx = Idx - 1;
234 if (Tokens[PrevIdx].getType() != Token::Type::Text)
235 return true;
236
237 const Token &PrevToken = Tokens[PrevIdx];
238 StringRef TokenBody = StringRef(PrevToken.RawBody).rtrim(" \r\t\v");
239 return !TokenBody.ends_with("\n") && !(TokenBody.empty() && Idx == 1);
240}
241
242// Function to check if there's no meaningful text ahead.
243// We determine if a token has text ahead if the left of previous
244// token does not start with a newline.
245bool hasTextAhead(size_t Idx, const ArrayRef<Token> &Tokens) {
246 if (Idx >= Tokens.size() - 1)
247 return true;
248
249 size_t NextIdx = Idx + 1;
250 if (Tokens[NextIdx].getType() != Token::Type::Text)
251 return true;
252
253 const Token &NextToken = Tokens[NextIdx];
254 StringRef TokenBody = StringRef(NextToken.RawBody).ltrim(" ");
255 return !TokenBody.starts_with("\r\n") && !TokenBody.starts_with("\n");
256}
257
259 // We must clean up all the tokens that could contain child nodes.
263}
264
265// Adjust next token body if there is no text ahead.
266// For example:
267// The template string
268// "{{! Comment }} \nLine 2"
269// would be considered as no text ahead and should be rendered as
270// " Line 2"
271void stripTokenAhead(SmallVectorImpl<Token> &Tokens, size_t Idx) {
272 Token &NextToken = Tokens[Idx + 1];
273 StringRef NextTokenBody = NextToken.TokenBody;
274 // Cut off the leading newline which could be \n or \r\n.
275 if (NextTokenBody.starts_with("\r\n"))
276 NextToken.TokenBody = NextTokenBody.substr(2).str();
277 else if (NextTokenBody.starts_with("\n"))
278 NextToken.TokenBody = NextTokenBody.substr(1).str();
279}
280
281// Adjust previous token body if there no text behind.
282// For example:
283// The template string
284// " \t{{#section}}A{{/section}}"
285// would be considered as having no text ahead and would be render as
286// "A"
287// The exception for this is partial tag which requires us to
288// keep track of the indentation once it's rendered.
290 Token &CurrentToken, Token::Type CurrentType) {
291 Token &PrevToken = Tokens[Idx - 1];
292 StringRef PrevTokenBody = PrevToken.TokenBody;
293 StringRef Unindented = PrevTokenBody.rtrim(" \r\t\v");
294 size_t Indentation = PrevTokenBody.size() - Unindented.size();
295 if (CurrentType != Token::Type::Partial)
296 PrevToken.TokenBody = Unindented.str();
297 CurrentToken.setIndentation(Indentation);
298}
299
300// Simple tokenizer that splits the template into tokens.
301// The mustache spec allows {{{ }}} to unescape variables,
302// but we don't support that here. An unescape variable
303// is represented only by {{& variable}}.
305 SmallVector<Token> Tokens;
306 StringLiteral Open("{{");
307 StringLiteral Close("}}");
308 StringLiteral TripleOpen("{{{");
309 StringLiteral TripleClose("}}}");
310 size_t Start = 0;
311 size_t DelimiterStart = Template.find(Open);
312 if (DelimiterStart == StringRef::npos) {
313 Tokens.emplace_back(Template.str());
314 return Tokens;
315 }
316 while (DelimiterStart != StringRef::npos) {
317 if (DelimiterStart != Start)
318 Tokens.emplace_back(Template.substr(Start, DelimiterStart - Start).str());
319
320 if (Template.substr(DelimiterStart).starts_with(TripleOpen)) {
321 size_t DelimiterEnd = Template.find(TripleClose, DelimiterStart);
322 if (DelimiterEnd == StringRef::npos)
323 break;
324 size_t BodyStart = DelimiterStart + TripleOpen.size();
325 std::string Body =
326 Template.substr(BodyStart, DelimiterEnd - BodyStart).str();
327 std::string RawBody =
328 Template.substr(DelimiterStart, DelimiterEnd - DelimiterStart + 3)
329 .str();
330 Tokens.emplace_back(RawBody, "&" + Body, '&');
331 Start = DelimiterEnd + TripleClose.size();
332 } else {
333 size_t DelimiterEnd = Template.find(Close, DelimiterStart);
334 if (DelimiterEnd == StringRef::npos)
335 break;
336
337 // Extract the Interpolated variable without delimiters.
338 size_t InterpolatedStart = DelimiterStart + Open.size();
339 size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
340 std::string Interpolated =
341 Template.substr(InterpolatedStart, InterpolatedEnd).str();
342 std::string RawBody = Open.str() + Interpolated + Close.str();
343 Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
344 Start = DelimiterEnd + Close.size();
345 }
346 DelimiterStart = Template.find(Open, Start);
347 }
348
349 if (Start < Template.size())
350 Tokens.emplace_back(Template.substr(Start).str());
351
352 // Fix up white spaces for:
353 // - open sections
354 // - inverted sections
355 // - close sections
356 // - comments
357 //
358 // This loop attempts to find standalone tokens and tries to trim out
359 // the surrounding whitespace.
360 // For example:
361 // if you have the template string
362 // {{#section}} \n Example \n{{/section}}
363 // The output should would be
364 // For example:
365 // \n Example \n
366 size_t LastIdx = Tokens.size() - 1;
367 for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
368 Token &CurrentToken = Tokens[Idx];
369 Token::Type CurrentType = CurrentToken.getType();
370 // Check if token type requires cleanup.
371 bool RequiresCleanUp = requiresCleanUp(CurrentType);
372
373 if (!RequiresCleanUp)
374 continue;
375
376 // We adjust the token body if there's no text behind or ahead.
377 // A token is considered to have no text ahead if the right of the previous
378 // token is a newline followed by spaces.
379 // A token is considered to have no text behind if the left of the next
380 // token is spaces followed by a newline.
381 // eg.
382 // "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
383 bool HasTextBehind = hasTextBehind(Idx, Tokens);
384 bool HasTextAhead = hasTextAhead(Idx, Tokens);
385
386 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
387 stripTokenAhead(Tokens, Idx);
388
389 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
390 stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
391 }
392 return Tokens;
393}
394
395// Custom stream to escape strings.
397public:
398 explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
399 EscapeMap &Escape)
400 : Escape(Escape), WrappedStream(WrappedStream) {
402 }
403
404protected:
405 void write_impl(const char *Ptr, size_t Size) override {
407 for (char C : Data) {
408 auto It = Escape.find(C);
409 if (It != Escape.end())
410 WrappedStream << It->getSecond();
411 else
412 WrappedStream << C;
413 }
414 }
415
416 uint64_t current_pos() const override { return WrappedStream.tell(); }
417
418private:
419 EscapeMap &Escape;
420 llvm::raw_ostream &WrappedStream;
421};
422
423// Custom stream to add indentation used to for rendering partials.
425public:
427 size_t Indentation)
428 : Indentation(Indentation), WrappedStream(WrappedStream) {
430 }
431
432protected:
433 void write_impl(const char *Ptr, size_t Size) override {
435 SmallString<0> Indent;
436 Indent.resize(Indentation, ' ');
437 for (char C : Data) {
438 WrappedStream << C;
439 if (C == '\n')
440 WrappedStream << Indent;
441 }
442 }
443
444 uint64_t current_pos() const override { return WrappedStream.tell(); }
445
446private:
447 size_t Indentation;
448 llvm::raw_ostream &WrappedStream;
449};
450
451class Parser {
452public:
453 Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}
454
457 llvm::StringMap<SectionLambda> &SectionLambdas,
458 EscapeMap &Escapes);
459
460private:
461 void parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
463 llvm::StringMap<SectionLambda> &SectionLambdas,
464 EscapeMap &Escapes);
465
466 SmallVector<Token> Tokens;
467 size_t CurrentPtr;
468 StringRef TemplateStr;
469};
470
473 llvm::StringMap<SectionLambda> &SectionLambdas,
474 EscapeMap &Escapes) {
475 Tokens = tokenize(TemplateStr);
476 CurrentPtr = 0;
477 AstPtr RootNode = createRootNode(Partials, Lambdas, SectionLambdas, Escapes);
478 parseMustache(RootNode.get(), Partials, Lambdas, SectionLambdas, Escapes);
479 return RootNode;
480}
481
482void Parser::parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
484 llvm::StringMap<SectionLambda> &SectionLambdas,
485 EscapeMap &Escapes) {
486
487 while (CurrentPtr < Tokens.size()) {
488 Token CurrentToken = Tokens[CurrentPtr];
489 CurrentPtr++;
490 Accessor A = CurrentToken.getAccessor();
491 AstPtr CurrentNode;
492
493 switch (CurrentToken.getType()) {
494 case Token::Type::Text: {
495 CurrentNode = createTextNode(std::move(CurrentToken.TokenBody), Parent,
496 Partials, Lambdas, SectionLambdas, Escapes);
497 Parent->addChild(std::move(CurrentNode));
498 break;
499 }
501 CurrentNode = createNode(ASTNode::Variable, std::move(A), Parent,
502 Partials, Lambdas, SectionLambdas, Escapes);
503 Parent->addChild(std::move(CurrentNode));
504 break;
505 }
507 CurrentNode = createNode(ASTNode::UnescapeVariable, std::move(A), Parent,
508 Partials, Lambdas, SectionLambdas, Escapes);
509 Parent->addChild(std::move(CurrentNode));
510 break;
511 }
513 CurrentNode = createNode(ASTNode::Partial, std::move(A), Parent, Partials,
514 Lambdas, SectionLambdas, Escapes);
515 CurrentNode->setIndentation(CurrentToken.getIndentation());
516 Parent->addChild(std::move(CurrentNode));
517 break;
518 }
520 CurrentNode = createNode(ASTNode::Section, A, Parent, Partials, Lambdas,
521 SectionLambdas, Escapes);
522 size_t Start = CurrentPtr;
523 parseMustache(CurrentNode.get(), Partials, Lambdas, SectionLambdas,
524 Escapes);
525 const size_t End = CurrentPtr - 1;
526 std::string RawBody;
527 for (std::size_t I = Start; I < End; I++)
528 RawBody += Tokens[I].RawBody;
529 CurrentNode->setRawBody(std::move(RawBody));
530 Parent->addChild(std::move(CurrentNode));
531 break;
532 }
534 CurrentNode = createNode(ASTNode::InvertSection, A, Parent, Partials,
535 Lambdas, SectionLambdas, Escapes);
536 size_t Start = CurrentPtr;
537 parseMustache(CurrentNode.get(), Partials, Lambdas, SectionLambdas,
538 Escapes);
539 const size_t End = CurrentPtr - 1;
540 std::string RawBody;
541 for (size_t Idx = Start; Idx < End; Idx++)
542 RawBody += Tokens[Idx].RawBody;
543 CurrentNode->setRawBody(std::move(RawBody));
544 Parent->addChild(std::move(CurrentNode));
545 break;
546 }
548 break;
550 return;
551 }
552 }
553}
555 switch (Data.kind()) {
557 return;
558 case json::Value::Number: {
559 auto Num = *Data.getAsNumber();
560 std::ostringstream SS;
561 SS << Num;
562 OS << SS.str();
563 return;
564 }
565 case json::Value::String: {
566 auto Str = *Data.getAsString();
567 OS << Str.str();
568 return;
569 }
570
571 case json::Value::Array: {
572 auto Arr = *Data.getAsArray();
573 if (Arr.empty())
574 return;
575 [[fallthrough]];
576 }
579 llvm::json::OStream JOS(OS, 2);
580 JOS.value(Data);
581 break;
582 }
583 }
584}
585
586void ASTNode::render(const json::Value &CurrentCtx, raw_ostream &OS) {
587 // Set the parent context to the incoming context so that we
588 // can walk up the context tree correctly in findContext().
589 ParentContext = &CurrentCtx;
590 const json::Value *ContextPtr = Ty == Root ? ParentContext : findContext();
591
592 switch (Ty) {
593 case Root:
594 renderChild(CurrentCtx, OS);
595 return;
596 case Text:
597 OS << Body;
598 return;
599 case Partial: {
600 auto Partial = Partials.find(AccessorValue[0]);
601 if (Partial != Partials.end())
602 renderPartial(CurrentCtx, OS, Partial->getValue().get());
603 return;
604 }
605 case Variable: {
606 auto Lambda = Lambdas.find(AccessorValue[0]);
607 if (Lambda != Lambdas.end()) {
608 renderLambdas(CurrentCtx, OS, Lambda->getValue());
609 } else if (ContextPtr) {
610 EscapeStringStream ES(OS, Escapes);
611 toMustacheString(*ContextPtr, ES);
612 }
613 return;
614 }
615 case UnescapeVariable: {
616 auto Lambda = Lambdas.find(AccessorValue[0]);
617 if (Lambda != Lambdas.end()) {
618 renderLambdas(CurrentCtx, OS, Lambda->getValue());
619 } else if (ContextPtr) {
620 toMustacheString(*ContextPtr, OS);
621 }
622 return;
623 }
624 case Section: {
625 auto SectionLambda = SectionLambdas.find(AccessorValue[0]);
626 bool IsLambda = SectionLambda != SectionLambdas.end();
627
628 if (IsLambda) {
629 renderSectionLambdas(CurrentCtx, OS, SectionLambda->getValue());
630 return;
631 }
632
633 if (isContextFalsey(ContextPtr))
634 return;
635
636 if (const json::Array *Arr = ContextPtr->getAsArray()) {
637 for (const json::Value &V : *Arr)
638 renderChild(V, OS);
639 return;
640 }
641 renderChild(*ContextPtr, OS);
642 return;
643 }
644 case InvertSection: {
645 bool IsLambda = SectionLambdas.contains(AccessorValue[0]);
646 if (isContextFalsey(ContextPtr) && !IsLambda) {
647 // The context for the children remains unchanged from the parent's, so
648 // we pass this node's original incoming context.
649 renderChild(CurrentCtx, OS);
650 }
651 return;
652 }
653 }
654 llvm_unreachable("Invalid ASTNode type");
655}
656
657const json::Value *ASTNode::findContext() {
658 // The mustache spec allows for dot notation to access nested values
659 // a single dot refers to the current context.
660 // We attempt to find the JSON context in the current node, if it is not
661 // found, then we traverse the parent nodes to find the context until we
662 // reach the root node or the context is found.
663 if (AccessorValue.empty())
664 return nullptr;
665 if (AccessorValue[0] == ".")
666 return ParentContext;
667
668 const json::Object *CurrentContext = ParentContext->getAsObject();
669 StringRef CurrentAccessor = AccessorValue[0];
670 ASTNode *CurrentParent = Parent;
671
672 while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
673 if (CurrentParent->Ty != Root) {
674 CurrentContext = CurrentParent->ParentContext->getAsObject();
675 CurrentParent = CurrentParent->Parent;
676 continue;
677 }
678 return nullptr;
679 }
680 const json::Value *Context = nullptr;
681 for (auto [Idx, Acc] : enumerate(AccessorValue)) {
682 const json::Value *CurrentValue = CurrentContext->get(Acc);
683 if (!CurrentValue)
684 return nullptr;
685 if (Idx < AccessorValue.size() - 1) {
686 CurrentContext = CurrentValue->getAsObject();
687 if (!CurrentContext)
688 return nullptr;
689 } else {
690 Context = CurrentValue;
691 }
692 }
693 return Context;
694}
695
696void ASTNode::renderChild(const json::Value &Contexts, llvm::raw_ostream &OS) {
697 for (AstPtr &Child : Children)
698 Child->render(Contexts, OS);
699}
700
701void ASTNode::renderPartial(const json::Value &Contexts, llvm::raw_ostream &OS,
702 ASTNode *Partial) {
703 AddIndentationStringStream IS(OS, Indentation);
704 Partial->render(Contexts, IS);
705}
706
707void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
708 Lambda &L) {
709 json::Value LambdaResult = L();
710 std::string LambdaStr;
711 raw_string_ostream Output(LambdaStr);
712 toMustacheString(LambdaResult, Output);
713 Parser P = Parser(LambdaStr);
714 AstPtr LambdaNode = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
715
716 EscapeStringStream ES(OS, Escapes);
717 if (Ty == Variable) {
718 LambdaNode->render(Contexts, ES);
719 return;
720 }
721 LambdaNode->render(Contexts, OS);
722}
723
724void ASTNode::renderSectionLambdas(const json::Value &Contexts,
725 llvm::raw_ostream &OS, SectionLambda &L) {
726 json::Value Return = L(RawBody);
727 if (isFalsey(Return))
728 return;
729 std::string LambdaStr;
730 raw_string_ostream Output(LambdaStr);
731 toMustacheString(Return, Output);
732 Parser P = Parser(LambdaStr);
733 AstPtr LambdaNode = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
734 LambdaNode->render(Contexts, OS);
735}
736
738 Tree->render(Data, OS);
739}
740
741void Template::registerPartial(std::string Name, std::string Partial) {
742 Parser P = Parser(Partial);
743 AstPtr PartialTree = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
744 Partials.insert(std::make_pair(Name, std::move(PartialTree)));
745}
746
747void Template::registerLambda(std::string Name, Lambda L) { Lambdas[Name] = L; }
748
749void Template::registerLambda(std::string Name, SectionLambda L) {
750 SectionLambdas[Name] = L;
751}
752
753void Template::overrideEscapeCharacters(EscapeMap E) { Escapes = std::move(E); }
754
756 Parser P = Parser(TemplateStr);
757 Tree = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
758 // The default behavior is to escape html entities.
759 const EscapeMap HtmlEntities = {{'&', "&amp;"},
760 {'<', "&lt;"},
761 {'>', "&gt;"},
762 {'"', "&quot;"},
763 {'\'', "&#39;"}};
764 overrideEscapeCharacters(HtmlEntities);
765}
766
768 : Partials(std::move(Other.Partials)), Lambdas(std::move(Other.Lambdas)),
769 SectionLambdas(std::move(Other.SectionLambdas)),
770 Escapes(std::move(Other.Escapes)), Tree(std::move(Other.Tree)) {}
771
772Template::~Template() = default;
773
775 if (this != &Other) {
776 Partials = std::move(Other.Partials);
777 Lambdas = std::move(Other.Lambdas);
778 SectionLambdas = std::move(Other.SectionLambdas);
779 Escapes = std::move(Other.Escapes);
780 Tree = std::move(Other.Tree);
781 Other.Tree = nullptr;
782 }
783 return *this;
784}
785} // namespace llvm::mustache
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
#define I(x, y, z)
Definition MD5.cpp:58
#define T
#define P(N)
This file defines the SmallVector class.
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:41
size_t size() const
size - Get the array size.
Definition ArrayRef.h:147
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void resize(size_type N)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
A wrapper around a string literal that serves as a proxy for constructing global tables of StringRefs...
Definition StringRef.h:854
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:133
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition StringRef.h:702
std::string str() const
str - Get the contents as an std::string.
Definition StringRef.h:225
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition StringRef.h:573
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:261
constexpr bool empty() const
empty - Check if the string is empty.
Definition StringRef.h:143
constexpr size_t size() const
size - Get the string size.
Definition StringRef.h:146
StringRef ltrim(char Char) const
Return string with consecutive Char characters starting from the the left removed.
Definition StringRef.h:792
StringRef rtrim(char Char) const
Return string with consecutive Char characters starting from the right removed.
Definition StringRef.h:804
StringRef trim(char Char) const
Return string with consecutive Char characters starting from the left and right removed.
Definition StringRef.h:816
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:273
static constexpr size_t npos
Definition StringRef.h:57
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
An Array is a JSON array, which contains heterogeneous JSON values.
Definition JSON.h:166
json::OStream allows writing well-formed JSON without materializing all structures as json::Value ahe...
Definition JSON.h:998
LLVM_ABI void value(const Value &V)
Emit a self-contained value (number, string, vector<string> etc).
Definition JSON.cpp:747
An Object is a JSON object, which maps strings to heterogenous JSON values.
Definition JSON.h:98
LLVM_ABI Value * get(StringRef K)
Definition JSON.cpp:30
A Value is an JSON value of unknown type.
Definition JSON.h:290
friend class Object
Definition JSON.h:493
@ Number
Number values can store both int64s and doubles at full precision, depending on what they were constr...
Definition JSON.h:297
friend class Array
Definition JSON.h:492
const json::Object * getAsObject() const
Definition JSON.h:464
const json::Array * getAsArray() const
Definition JSON.h:470
ASTNode(Type Ty, Accessor Accessor, ASTNode *Parent, llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:147
void render(const llvm::json::Value &Data, llvm::raw_ostream &OS)
Definition Mustache.cpp:586
void setIndentation(size_t NewIndentation)
Definition Mustache.cpp:158
ASTNode(std::string Body, ASTNode *Parent, llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:139
void addChild(AstPtr Child)
Definition Mustache.cpp:154
void setRawBody(std::string NewBody)
Definition Mustache.cpp:156
ASTNode(llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:133
uint64_t current_pos() const override
Return the current position within the stream, not counting the bytes currently in the buffer.
Definition Mustache.cpp:444
AddIndentationStringStream(llvm::raw_ostream &WrappedStream, size_t Indentation)
Definition Mustache.cpp:426
void write_impl(const char *Ptr, size_t Size) override
The is the piece of the class that is implemented by subclasses.
Definition Mustache.cpp:433
uint64_t current_pos() const override
Return the current position within the stream, not counting the bytes currently in the buffer.
Definition Mustache.cpp:416
void write_impl(const char *Ptr, size_t Size) override
The is the piece of the class that is implemented by subclasses.
Definition Mustache.cpp:405
EscapeStringStream(llvm::raw_ostream &WrappedStream, EscapeMap &Escape)
Definition Mustache.cpp:398
AstPtr parse(llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:471
Parser(StringRef TemplateStr)
Definition Mustache.cpp:453
LLVM_ABI void registerPartial(std::string Name, std::string Partial)
Definition Mustache.cpp:741
Template & operator=(const Template &)=delete
LLVM_ABI void registerLambda(std::string Name, Lambda Lambda)
Definition Mustache.cpp:747
LLVM_ABI Template(StringRef TemplateStr)
Definition Mustache.cpp:755
LLVM_ABI void render(const llvm::json::Value &Data, llvm::raw_ostream &OS)
Definition Mustache.cpp:737
LLVM_ABI void overrideEscapeCharacters(DenseMap< char, std::string > Escapes)
Definition Mustache.cpp:753
Token(std::string Str)
Definition Mustache.cpp:67
Type getType() const
Definition Mustache.cpp:85
size_t getIndentation() const
Definition Mustache.cpp:89
void setIndentation(size_t NewIndentation)
Definition Mustache.cpp:87
static Type getTokenType(char Identifier)
Definition Mustache.cpp:91
Token(std::string RawBody, std::string TokenBody, char Identifier)
Definition Mustache.cpp:71
Accessor getAccessor() const
Definition Mustache.cpp:83
std::string TokenBody
Definition Mustache.cpp:114
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
raw_ostream(bool unbuffered=false, OStreamKind K=OStreamKind::OK_OStream)
void SetUnbuffered()
Set the stream to be unbuffered.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
AstPtr createRootNode(llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:192
std::unique_ptr< ASTNode > AstPtr
Definition Mustache.h:87
void stripTokenAhead(SmallVectorImpl< Token > &Tokens, size_t Idx)
Definition Mustache.cpp:271
bool hasTextAhead(size_t Idx, const ArrayRef< Token > &Tokens)
Definition Mustache.cpp:245
DenseMap< char, std::string > EscapeMap
Definition Mustache.cpp:119
void stripTokenBefore(SmallVectorImpl< Token > &Tokens, size_t Idx, Token &CurrentToken, Token::Type CurrentType)
Definition Mustache.cpp:289
void toMustacheString(const json::Value &Data, raw_ostream &OS)
Definition Mustache.cpp:554
std::function< llvm::json::Value(std::string)> SectionLambda
Definition Mustache.h:84
std::function< llvm::json::Value()> Lambda
Definition Mustache.h:83
AstPtr createNode(ASTNode::Type T, Accessor A, ASTNode *Parent, llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:199
bool requiresCleanUp(Token::Type T)
Definition Mustache.cpp:258
AstPtr createTextNode(std::string Body, ASTNode *Parent, llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition Mustache.cpp:208
SmallVector< Token > tokenize(StringRef Template)
Definition Mustache.cpp:304
bool hasTextBehind(size_t Idx, const ArrayRef< Token > &Tokens)
Definition Mustache.cpp:229
This is an optimization pass for GlobalISel generic memory operations.
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2452
@ Other
Any other memory.
Definition ModRef.h:68
FunctionAddr VTableAddr uintptr_t uintptr_t Data
Definition InstrProf.h:189
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1847
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:851