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:
56 enum class Type {
57 Text,
59 Partial,
64 Comment,
65 };
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:
123 enum Type {
131 };
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"
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 size_t Start = 0;
309 size_t DelimiterStart = Template.find(Open);
310 if (DelimiterStart == StringRef::npos) {
311 Tokens.emplace_back(Template.str());
312 return Tokens;
313 }
314 while (DelimiterStart != StringRef::npos) {
315 if (DelimiterStart != Start)
316 Tokens.emplace_back(Template.substr(Start, DelimiterStart - Start).str());
317 size_t DelimiterEnd = Template.find(Close, DelimiterStart);
318 if (DelimiterEnd == StringRef::npos)
319 break;
320
321 // Extract the Interpolated variable without delimiters.
322 size_t InterpolatedStart = DelimiterStart + Open.size();
323 size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
324 std::string Interpolated =
325 Template.substr(InterpolatedStart, InterpolatedEnd).str();
326 std::string RawBody = Open.str() + Interpolated + Close.str();
327 Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
328 Start = DelimiterEnd + Close.size();
329 DelimiterStart = Template.find(Open, Start);
330 }
331
332 if (Start < Template.size())
333 Tokens.emplace_back(Template.substr(Start).str());
334
335 // Fix up white spaces for:
336 // - open sections
337 // - inverted sections
338 // - close sections
339 // - comments
340 //
341 // This loop attempts to find standalone tokens and tries to trim out
342 // the surrounding whitespace.
343 // For example:
344 // if you have the template string
345 // {{#section}} \n Example \n{{/section}}
346 // The output should would be
347 // For example:
348 // \n Example \n
349 size_t LastIdx = Tokens.size() - 1;
350 for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
351 Token &CurrentToken = Tokens[Idx];
352 Token::Type CurrentType = CurrentToken.getType();
353 // Check if token type requires cleanup.
354 bool RequiresCleanUp = requiresCleanUp(CurrentType);
355
356 if (!RequiresCleanUp)
357 continue;
358
359 // We adjust the token body if there's no text behind or ahead.
360 // A token is considered to have no text ahead if the right of the previous
361 // token is a newline followed by spaces.
362 // A token is considered to have no text behind if the left of the next
363 // token is spaces followed by a newline.
364 // eg.
365 // "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
366 bool HasTextBehind = hasTextBehind(Idx, Tokens);
367 bool HasTextAhead = hasTextAhead(Idx, Tokens);
368
369 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
370 stripTokenAhead(Tokens, Idx);
371
372 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
373 stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
374 }
375 return Tokens;
376}
377
378// Custom stream to escape strings.
380public:
381 explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
382 EscapeMap &Escape)
383 : Escape(Escape), WrappedStream(WrappedStream) {
385 }
386
387protected:
388 void write_impl(const char *Ptr, size_t Size) override {
390 for (char C : Data) {
391 auto It = Escape.find(C);
392 if (It != Escape.end())
393 WrappedStream << It->getSecond();
394 else
395 WrappedStream << C;
396 }
397 }
398
399 uint64_t current_pos() const override { return WrappedStream.tell(); }
400
401private:
402 EscapeMap &Escape;
403 llvm::raw_ostream &WrappedStream;
404};
405
406// Custom stream to add indentation used to for rendering partials.
408public:
410 size_t Indentation)
411 : Indentation(Indentation), WrappedStream(WrappedStream) {
413 }
414
415protected:
416 void write_impl(const char *Ptr, size_t Size) override {
418 SmallString<0> Indent;
419 Indent.resize(Indentation, ' ');
420 for (char C : Data) {
421 WrappedStream << C;
422 if (C == '\n')
423 WrappedStream << Indent;
424 }
425 }
426
427 uint64_t current_pos() const override { return WrappedStream.tell(); }
428
429private:
430 size_t Indentation;
431 llvm::raw_ostream &WrappedStream;
432};
433
434class Parser {
435public:
436 Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}
437
440 llvm::StringMap<SectionLambda> &SectionLambdas,
441 EscapeMap &Escapes);
442
443private:
444 void parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
446 llvm::StringMap<SectionLambda> &SectionLambdas,
447 EscapeMap &Escapes);
448
449 SmallVector<Token> Tokens;
450 size_t CurrentPtr;
451 StringRef TemplateStr;
452};
453
456 llvm::StringMap<SectionLambda> &SectionLambdas,
457 EscapeMap &Escapes) {
458 Tokens = tokenize(TemplateStr);
459 CurrentPtr = 0;
460 AstPtr RootNode = createRootNode(Partials, Lambdas, SectionLambdas, Escapes);
461 parseMustache(RootNode.get(), Partials, Lambdas, SectionLambdas, Escapes);
462 return RootNode;
463}
464
465void Parser::parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
467 llvm::StringMap<SectionLambda> &SectionLambdas,
468 EscapeMap &Escapes) {
469
470 while (CurrentPtr < Tokens.size()) {
471 Token CurrentToken = Tokens[CurrentPtr];
472 CurrentPtr++;
473 Accessor A = CurrentToken.getAccessor();
474 AstPtr CurrentNode;
475
476 switch (CurrentToken.getType()) {
477 case Token::Type::Text: {
478 CurrentNode = createTextNode(std::move(CurrentToken.TokenBody), Parent,
479 Partials, Lambdas, SectionLambdas, Escapes);
480 Parent->addChild(std::move(CurrentNode));
481 break;
482 }
484 CurrentNode = createNode(ASTNode::Variable, std::move(A), Parent,
485 Partials, Lambdas, SectionLambdas, Escapes);
486 Parent->addChild(std::move(CurrentNode));
487 break;
488 }
490 CurrentNode = createNode(ASTNode::UnescapeVariable, std::move(A), Parent,
491 Partials, Lambdas, SectionLambdas, Escapes);
492 Parent->addChild(std::move(CurrentNode));
493 break;
494 }
496 CurrentNode = createNode(ASTNode::Partial, std::move(A), Parent, Partials,
497 Lambdas, SectionLambdas, Escapes);
498 CurrentNode->setIndentation(CurrentToken.getIndentation());
499 Parent->addChild(std::move(CurrentNode));
500 break;
501 }
503 CurrentNode = createNode(ASTNode::Section, A, Parent, Partials, Lambdas,
504 SectionLambdas, Escapes);
505 size_t Start = CurrentPtr;
506 parseMustache(CurrentNode.get(), Partials, Lambdas, SectionLambdas,
507 Escapes);
508 const size_t End = CurrentPtr - 1;
509 std::string RawBody;
510 for (std::size_t I = Start; I < End; I++)
511 RawBody += Tokens[I].RawBody;
512 CurrentNode->setRawBody(std::move(RawBody));
513 Parent->addChild(std::move(CurrentNode));
514 break;
515 }
517 CurrentNode = createNode(ASTNode::InvertSection, A, Parent, Partials,
518 Lambdas, SectionLambdas, Escapes);
519 size_t Start = CurrentPtr;
520 parseMustache(CurrentNode.get(), Partials, Lambdas, SectionLambdas,
521 Escapes);
522 const size_t End = CurrentPtr - 1;
523 std::string RawBody;
524 for (size_t Idx = Start; Idx < End; Idx++)
525 RawBody += Tokens[Idx].RawBody;
526 CurrentNode->setRawBody(std::move(RawBody));
527 Parent->addChild(std::move(CurrentNode));
528 break;
529 }
531 break;
533 return;
534 }
535 }
536}
538 switch (Data.kind()) {
540 return;
541 case json::Value::Number: {
542 auto Num = *Data.getAsNumber();
543 std::ostringstream SS;
544 SS << Num;
545 OS << SS.str();
546 return;
547 }
548 case json::Value::String: {
549 auto Str = *Data.getAsString();
550 OS << Str.str();
551 return;
552 }
553
554 case json::Value::Array: {
555 auto Arr = *Data.getAsArray();
556 if (Arr.empty())
557 return;
558 [[fallthrough]];
559 }
562 llvm::json::OStream JOS(OS, 2);
563 JOS.value(Data);
564 break;
565 }
566 }
567}
568
569void ASTNode::render(const json::Value &CurrentCtx, raw_ostream &OS) {
570 // Set the parent context to the incoming context so that we
571 // can walk up the context tree correctly in findContext().
572 ParentContext = &CurrentCtx;
573 const json::Value *ContextPtr = Ty == Root ? ParentContext : findContext();
574
575 switch (Ty) {
576 case Root:
577 renderChild(CurrentCtx, OS);
578 return;
579 case Text:
580 OS << Body;
581 return;
582 case Partial: {
583 auto Partial = Partials.find(AccessorValue[0]);
584 if (Partial != Partials.end())
585 renderPartial(CurrentCtx, OS, Partial->getValue().get());
586 return;
587 }
588 case Variable: {
589 auto Lambda = Lambdas.find(AccessorValue[0]);
590 if (Lambda != Lambdas.end()) {
591 renderLambdas(CurrentCtx, OS, Lambda->getValue());
592 } else if (ContextPtr) {
593 EscapeStringStream ES(OS, Escapes);
594 toMustacheString(*ContextPtr, ES);
595 }
596 return;
597 }
598 case UnescapeVariable: {
599 auto Lambda = Lambdas.find(AccessorValue[0]);
600 if (Lambda != Lambdas.end()) {
601 renderLambdas(CurrentCtx, OS, Lambda->getValue());
602 } else if (ContextPtr) {
603 toMustacheString(*ContextPtr, OS);
604 }
605 return;
606 }
607 case Section: {
608 auto SectionLambda = SectionLambdas.find(AccessorValue[0]);
609 bool IsLambda = SectionLambda != SectionLambdas.end();
610
611 if (IsLambda) {
612 renderSectionLambdas(CurrentCtx, OS, SectionLambda->getValue());
613 return;
614 }
615
616 if (isContextFalsey(ContextPtr))
617 return;
618
619 if (const json::Array *Arr = ContextPtr->getAsArray()) {
620 for (const json::Value &V : *Arr)
621 renderChild(V, OS);
622 return;
623 }
624 renderChild(*ContextPtr, OS);
625 return;
626 }
627 case InvertSection: {
628 bool IsLambda = SectionLambdas.contains(AccessorValue[0]);
629 if (isContextFalsey(ContextPtr) && !IsLambda) {
630 // The context for the children remains unchanged from the parent's, so
631 // we pass this node's original incoming context.
632 renderChild(CurrentCtx, OS);
633 }
634 return;
635 }
636 }
637 llvm_unreachable("Invalid ASTNode type");
638}
639
640const json::Value *ASTNode::findContext() {
641 // The mustache spec allows for dot notation to access nested values
642 // a single dot refers to the current context.
643 // We attempt to find the JSON context in the current node, if it is not
644 // found, then we traverse the parent nodes to find the context until we
645 // reach the root node or the context is found.
646 if (AccessorValue.empty())
647 return nullptr;
648 if (AccessorValue[0] == ".")
649 return ParentContext;
650
651 const json::Object *CurrentContext = ParentContext->getAsObject();
652 StringRef CurrentAccessor = AccessorValue[0];
653 ASTNode *CurrentParent = Parent;
654
655 while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
656 if (CurrentParent->Ty != Root) {
657 CurrentContext = CurrentParent->ParentContext->getAsObject();
658 CurrentParent = CurrentParent->Parent;
659 continue;
660 }
661 return nullptr;
662 }
663 const json::Value *Context = nullptr;
664 for (auto [Idx, Acc] : enumerate(AccessorValue)) {
665 const json::Value *CurrentValue = CurrentContext->get(Acc);
666 if (!CurrentValue)
667 return nullptr;
668 if (Idx < AccessorValue.size() - 1) {
669 CurrentContext = CurrentValue->getAsObject();
670 if (!CurrentContext)
671 return nullptr;
672 } else {
673 Context = CurrentValue;
674 }
675 }
676 return Context;
677}
678
679void ASTNode::renderChild(const json::Value &Contexts, llvm::raw_ostream &OS) {
680 for (AstPtr &Child : Children)
681 Child->render(Contexts, OS);
682}
683
684void ASTNode::renderPartial(const json::Value &Contexts, llvm::raw_ostream &OS,
685 ASTNode *Partial) {
686 AddIndentationStringStream IS(OS, Indentation);
687 Partial->render(Contexts, IS);
688}
689
690void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
691 Lambda &L) {
692 json::Value LambdaResult = L();
693 std::string LambdaStr;
694 raw_string_ostream Output(LambdaStr);
695 toMustacheString(LambdaResult, Output);
696 Parser P = Parser(LambdaStr);
697 AstPtr LambdaNode = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
698
699 EscapeStringStream ES(OS, Escapes);
700 if (Ty == Variable) {
701 LambdaNode->render(Contexts, ES);
702 return;
703 }
704 LambdaNode->render(Contexts, OS);
705}
706
707void ASTNode::renderSectionLambdas(const json::Value &Contexts,
709 json::Value Return = L(RawBody);
710 if (isFalsey(Return))
711 return;
712 std::string LambdaStr;
713 raw_string_ostream Output(LambdaStr);
714 toMustacheString(Return, Output);
715 Parser P = Parser(LambdaStr);
716 AstPtr LambdaNode = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
717 LambdaNode->render(Contexts, OS);
718}
719
721 Tree->render(Data, OS);
722}
723
724void Template::registerPartial(std::string Name, std::string Partial) {
725 Parser P = Parser(Partial);
726 AstPtr PartialTree = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
727 Partials.insert(std::make_pair(Name, std::move(PartialTree)));
728}
729
730void Template::registerLambda(std::string Name, Lambda L) { Lambdas[Name] = L; }
731
733 SectionLambdas[Name] = L;
734}
735
736void Template::overrideEscapeCharacters(EscapeMap E) { Escapes = std::move(E); }
737
739 Parser P = Parser(TemplateStr);
740 Tree = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
741 // The default behavior is to escape html entities.
742 const EscapeMap HtmlEntities = {{'&', "&amp;"},
743 {'<', "&lt;"},
744 {'>', "&gt;"},
745 {'"', "&quot;"},
746 {'\'', "&#39;"}};
747 overrideEscapeCharacters(HtmlEntities);
748}
749
751 : Partials(std::move(Other.Partials)), Lambdas(std::move(Other.Lambdas)),
752 SectionLambdas(std::move(Other.SectionLambdas)),
753 Escapes(std::move(Other.Escapes)), Tree(std::move(Other.Tree)) {}
754
755Template::~Template() = default;
756
758 if (this != &Other) {
759 Partials = std::move(Other.Partials);
760 Lambdas = std::move(Other.Lambdas);
761 SectionLambdas = std::move(Other.SectionLambdas);
762 Escapes = std::move(Other.Escapes);
763 Tree = std::move(Other.Tree);
764 Other.Tree = nullptr;
765 }
766 return *this;
767}
768} // namespace llvm::mustache
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
std::string Name
uint64_t Size
bool End
Definition: ELF_riscv.cpp:480
#define I(x, y, z)
Definition: MD5.cpp:58
#define P(N)
raw_pwrite_stream & OS
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
iterator find(const_arg_type_t< KeyT > Val)
Definition: DenseMap.h:177
iterator end()
Definition: DenseMap.h:87
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
size_t size() const
Definition: SmallVector.h:79
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:574
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:938
void resize(size_type N)
Definition: SmallVector.h:639
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
A wrapper around a string literal that serves as a proxy for constructing global tables of StringRefs...
Definition: StringRef.h:862
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:133
iterator end()
Definition: StringMap.h:224
iterator find(StringRef Key)
Definition: StringMap.h:237
bool contains(StringRef Key) const
contains - Return true if the element is in the map, false otherwise.
Definition: StringMap.h:277
bool insert(MapEntryTy *KeyValue)
insert - Insert the specified key/value pair into the map.
Definition: StringMap.h:312
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:233
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:581
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:269
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:151
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:154
StringRef ltrim(char Char) const
Return string with consecutive Char characters starting from the the left removed.
Definition: StringRef.h:800
StringRef rtrim(char Char) const
Return string with consecutive Char characters starting from the right removed.
Definition: StringRef.h:812
StringRef trim(char Char) const
Return string with consecutive Char characters starting from the left and right removed.
Definition: StringRef.h:824
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition: StringRef.h:281
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:164
json::OStream allows writing well-formed JSON without materializing all structures as json::Value ahe...
Definition: JSON.h:996
LLVM_ABI void value(const Value &V)
Emit a self-contained value (number, string, vector<string> etc).
Definition: JSON.cpp:756
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:288
friend class Object
Definition: JSON.h:491
@ Number
Number values can store both int64s and doubles at full precision, depending on what they were constr...
Definition: JSON.h:295
friend class Array
Definition: JSON.h:490
const json::Object * getAsObject() const
Definition: JSON.h:462
const json::Array * getAsArray() const
Definition: JSON.h:468
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:569
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:427
AddIndentationStringStream(llvm::raw_ostream &WrappedStream, size_t Indentation)
Definition: Mustache.cpp:409
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:416
uint64_t current_pos() const override
Return the current position within the stream, not counting the bytes currently in the buffer.
Definition: Mustache.cpp:399
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:388
EscapeStringStream(llvm::raw_ostream &WrappedStream, EscapeMap &Escape)
Definition: Mustache.cpp:381
AstPtr parse(llvm::StringMap< AstPtr > &Partials, llvm::StringMap< Lambda > &Lambdas, llvm::StringMap< SectionLambda > &SectionLambdas, EscapeMap &Escapes)
Definition: Mustache.cpp:454
Parser(StringRef TemplateStr)
Definition: Mustache.cpp:436
LLVM_ABI void registerPartial(std::string Name, std::string Partial)
Definition: Mustache.cpp:724
Template & operator=(const Template &)=delete
LLVM_ABI void registerLambda(std::string Name, Lambda Lambda)
Definition: Mustache.cpp:730
LLVM_ABI Template(StringRef TemplateStr)
Definition: Mustache.cpp:738
LLVM_ABI void render(const llvm::json::Value &Data, llvm::raw_ostream &OS)
Definition: Mustache.cpp:720
LLVM_ABI void overrideEscapeCharacters(DenseMap< char, std::string > Escapes)
Definition: Mustache.cpp:736
std::string RawBody
Definition: Mustache.cpp:112
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
uint64_t tell() const
tell - Return the current offset with the file.
Definition: raw_ostream.h:148
void SetUnbuffered()
Set the stream to be unbuffered.
Definition: raw_ostream.h:186
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:662
#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
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
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:537
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
std::function< llvm::json::Value()> Lambda
Definition: Mustache.h:83
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
std::function< llvm::json::Value(std::string)> SectionLambda
Definition: Mustache.h:84
SmallVector< Token > tokenize(StringRef Template)
Definition: Mustache.cpp:304
bool hasTextBehind(size_t Idx, const ArrayRef< Token > &Tokens)
Definition: Mustache.cpp:229
std::unique_ptr< ASTNode > AstPtr
Definition: Mustache.h:87
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
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:2491
@ Other
Any other memory.
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:1886
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:856
Definition: regcomp.c:186