14#define DEBUG_TYPE "mustache"
24 return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
25 (V.getAsArray() && V.getAsArray()->empty());
36 size_t CurrentPos = 0;
37 while (CurrentPos < Str.size()) {
39 size_t DelimiterPos = Str.find(
'.', CurrentPos);
43 DelimiterPos = Str.size();
56 CurrentPos = DelimiterPos + 1;
72 splitAndTrim(Str, Tokens);
94 void anchor()
override;
97void MustacheOutputStream::anchor() {}
106 void write_impl(
const char *Ptr,
size_t Size)
override {
109 uint64_t current_pos()
const override {
return OS.
tell(); }
138 AccessorStr = AccessorStr.
substr(1);
151 switch (Identifier) {
195 : Ctx(Ctx), Ty(
Type::
Root), Parent(nullptr), ParentContext(nullptr) {}
198 : Ctx(Ctx), Ty(
Type::
Text), Body(Body), Parent(Parent),
199 ParentContext(nullptr) {}
204 : Ctx(Ctx), Ty(Ty), Parent(Parent), AccessorValue(Accessor),
205 ParentContext(nullptr) {}
233 void renderUnescapeVariable(
const json::Value &CurrentCtx,
236 void renderInvertSection(
const json::Value &CurrentCtx,
241 size_t Indentation = 0;
262 return new (Ctx.Allocator.Allocate<
ASTNode>())
ASTNode(Ctx, Body, Parent);
281 size_t PrevIdx = Idx - 1;
285 const Token &PrevToken = Tokens[PrevIdx];
287 return !TokenBody.
ends_with(
"\n") && !(TokenBody.
empty() && Idx == 1);
294 if (Idx >= Tokens.
size() - 1)
297 size_t NextIdx = Idx + 1;
301 const Token &NextToken = Tokens[NextIdx];
320 Token &NextToken = Tokens[Idx + 1];
337 Token &PrevToken = Tokens[Idx - 1];
340 size_t Indentation = PrevTokenBody.
size() - Unindented.
size();
373 return "JSON_KIND_NULL";
375 return "JSON_KIND_BOOLEAN";
377 return "JSON_KIND_NUMBER";
379 return "JSON_KIND_STRING";
381 return "JSON_KIND_ARRAY";
383 return "JSON_KIND_OBJECT";
395 size_t TextStart = 0;
408 TagOpen = TripleOpen;
409 TagClose = TripleClose;
421 if (Cursor > TextStart)
425 size_t EndPos =
Template.find(TagClose, Cursor + TagOpen.
size());
429 TextStart = Cursor =
Template.size();
434 size_t ContentStart = Cursor + TagOpen.
size();
437 Template.substr(Cursor, (EndPos + TagClose.
size()) - Cursor);
440 LLVM_DEBUG(
dbgs() <<
"[Tag] " << FullMatch <<
", Content: " << Content
443 Tokens.
emplace_back(FullMatch, Ctx.Saver.save(
"&" + Content),
'&', Ctx);
447 char Front = Interpolated.
empty() ?
' ' : Interpolated.
trim().
front();
448 Tokens.
emplace_back(FullMatch, Interpolated, Front, Ctx);
453 DelimSpec = DelimSpec.
take_until([](
char C) {
return C ==
'='; });
454 DelimSpec = DelimSpec.
trim();
456 auto [NewOpen, NewClose] = DelimSpec.
split(
' ');
458 <<
", NewClose: " << NewClose <<
"\n");
465 Cursor += FullMatch.
size();
474 size_t LastIdx = Tokens.
size() - 1;
475 for (
size_t Idx = 0, End = Tokens.
size(); Idx < End; ++Idx) {
476 Token &CurrentToken = Tokens[Idx];
484 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
487 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
498 : Escape(Escape), EscapeChars(Escape.keys().begin(), Escape.keys().end()),
499 WrappedStream(WrappedStream) {
507 while (Start <
Size) {
509 size_t Next =
Data.find_first_of(EscapeChars.str(), Start);
513 WrappedStream <<
Data.substr(Start);
519 WrappedStream <<
Data.substr(Start,
Next - Start);
522 WrappedStream << Escape[
Data[
Next]];
527 uint64_t
current_pos()
const override {
return WrappedStream.tell(); }
540 : Indentation(Indentation), WrappedStream(WrappedStream),
552 Indent.
resize(Indentation,
' ');
554 for (
char C :
Data) {
555 LLVM_DEBUG(
dbgs() <<
"[Indentation Stream] NeedsIndent:" << NeedsIndent
556 <<
", C:'" <<
C <<
"', Indentation:" << Indentation
558 if (NeedsIndent &&
C !=
'\n') {
559 WrappedStream << Indent;
563 if (
C ==
'\n' && !IsSuspended)
568 uint64_t
current_pos()
const override {
return WrappedStream.tell(); }
580 : Ctx(Ctx), TemplateStr(TemplateStr) {}
585 void parseMustache(
ASTNode *Parent);
597 size_t Start = CurrentPtr;
598 parseMustache(CurrentNode);
599 const size_t End = CurrentPtr - 1;
601 size_t RawBodySize = 0;
602 for (
size_t I = Start;
I < End; ++
I)
603 RawBodySize += Tokens[
I].RawBody.size();
607 for (std::size_t
I = Start;
I < End; ++
I)
608 RawBody += Tokens[
I].RawBody;
615 Tokens =
tokenize(TemplateStr, Ctx);
618 parseMustache(RootNode);
622void Parser::parseMustache(
ASTNode *Parent) {
624 while (CurrentPtr < Tokens.size()) {
625 Token CurrentToken = Tokens[CurrentPtr];
630 switch (CurrentToken.
getType()) {
672 switch (
Data.kind()) {
676 auto Num = *
Data.getAsNumber();
677 std::ostringstream SS;
683 OS << *
Data.getAsString();
688 auto Arr = *
Data.getAsArray();
702void ASTNode::renderRoot(
const json::Value &CurrentCtx,
704 renderChild(CurrentCtx, OS);
709void ASTNode::renderPartial(
const json::Value &CurrentCtx,
711 LLVM_DEBUG(
dbgs() <<
"[Render Partial] Accessor:" << AccessorValue[0]
712 <<
", Indentation:" << Indentation <<
"\n");
713 auto Partial = Ctx.Partials.find(AccessorValue[0]);
714 if (
Partial != Ctx.Partials.end())
715 renderPartial(CurrentCtx, OS,
Partial->getValue());
718void ASTNode::renderVariable(
const json::Value &CurrentCtx,
720 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
721 if (
Lambda != Ctx.Lambdas.end()) {
722 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
723 }
else if (
const json::Value *ContextPtr = findContext()) {
724 EscapeStringStream ES(OS, Ctx.Escapes);
729void ASTNode::renderUnescapeVariable(
const json::Value &CurrentCtx,
731 LLVM_DEBUG(
dbgs() <<
"[Render UnescapeVariable] Accessor:" << AccessorValue[0]
733 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
734 if (
Lambda != Ctx.Lambdas.end()) {
735 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
736 }
else if (
const json::Value *ContextPtr = findContext()) {
743void ASTNode::renderSection(
const json::Value &CurrentCtx,
745 auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
747 renderSectionLambdas(CurrentCtx, OS,
SectionLambda->getValue());
751 const json::Value *ContextPtr = findContext();
752 if (isContextFalsey(ContextPtr))
755 if (
const json::Array *Arr = ContextPtr->
getAsArray()) {
756 for (
const json::Value &V : *Arr)
760 renderChild(*ContextPtr, OS);
763void ASTNode::renderInvertSection(
const json::Value &CurrentCtx,
765 bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
766 const json::Value *ContextPtr = findContext();
767 if (isContextFalsey(ContextPtr) && !IsLambda) {
768 renderChild(CurrentCtx, OS);
773 if (Ty !=
Root && Ty !=
Text && AccessorValue.empty())
777 ParentContext = &
Data;
781 renderRoot(
Data, OS);
787 renderPartial(
Data, OS);
790 renderVariable(
Data, OS);
793 renderUnescapeVariable(
Data, OS);
796 renderSection(
Data, OS);
799 renderInvertSection(
Data, OS);
811 if (AccessorValue.empty())
813 if (AccessorValue[0] ==
".")
814 return ParentContext;
817 StringRef CurrentAccessor = AccessorValue[0];
818 ASTNode *CurrentParent = Parent;
820 while (!CurrentContext || !CurrentContext->
get(CurrentAccessor)) {
821 if (CurrentParent->Ty !=
Root) {
822 CurrentContext = CurrentParent->ParentContext->
getAsObject();
823 CurrentParent = CurrentParent->Parent;
829 for (
auto [Idx, Acc] :
enumerate(AccessorValue)) {
833 if (Idx < AccessorValue.size() - 1) {
844void ASTNode::renderChild(
const json::Value &Contexts,
846 for (
ASTNode &Child : Children)
847 Child.render(Contexts, OS);
850void ASTNode::renderPartial(
const json::Value &Contexts,
852 LLVM_DEBUG(
dbgs() <<
"[Render Partial Indentation] Indentation: " << Indentation <<
"\n");
853 AddIndentationStringStream
IS(OS, Indentation);
857void ASTNode::renderLambdas(
const llvm::json::Value &Contexts,
859 json::Value LambdaResult =
L();
860 std::string LambdaStr;
861 raw_string_ostream Output(LambdaStr);
863 Parser
P(LambdaStr, Ctx);
866 EscapeStringStream ES(OS, Ctx.Escapes);
868 LambdaNode->
render(Contexts, ES);
871 LambdaNode->
render(Contexts, OS);
874void ASTNode::renderSectionLambdas(
const llvm::json::Value &Contexts,
876 json::Value
Return =
L(RawBody.str());
877 if (isFalsey(Return))
879 std::string LambdaStr;
880 raw_string_ostream Output(LambdaStr);
882 Parser
P(LambdaStr, Ctx);
884 LambdaNode->
render(Contexts, OS);
889 Tree->render(
Data, MOS);
893 StringRef SavedPartial = Ctx.Saver.save(Partial);
895 AstPtr PartialTree =
P.parse();
896 Ctx.Partials.insert(std::make_pair(Name, PartialTree));
900 Ctx.Lambdas[Name] = std::move(L);
904 Ctx.SectionLambdas[Name] = std::move(L);
908 Ctx.Escapes = std::move(E);
915 const EscapeMap HtmlEntities = {{
'&',
"&"},
925 Other.Tree =
nullptr;
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file defines the SmallVector class.
static SymbolRef::Type getType(const Symbol *Sym)
Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
Get the array size.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void push_back(const T &Elt)
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...
Represent a constant reference to a string, i.e.
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
LLVM_ABI size_t find_last_not_of(char C, size_t From=npos) const
Find the last character in the string that is not C, or npos if not found.
static constexpr size_t npos
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
constexpr bool empty() const
Check if the string is empty.
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
StringRef slice(size_t Start, size_t End) const
Return a reference to the substring from [Start, End).
constexpr size_t size() const
Get the string size.
char front() const
Get the first character in the string.
StringRef ltrim(char Char) const
Return string with consecutive Char characters starting from the the left removed.
StringRef rtrim(char Char) const
Return string with consecutive Char characters starting from the right removed.
StringRef take_until(function_ref< bool(char)> F) const
Return the longest prefix of 'this' such that no character in the prefix satisfies the given predicat...
StringRef trim(char Char) const
Return string with consecutive Char characters starting from the left and right removed.
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
LLVM_ABI size_t find_first_not_of(char C, size_t From=0) const
Find the first character in the string that is not C or npos if not found.
The instances of the Type class are immutable: once they are created, they are never changed.
json::OStream allows writing well-formed JSON without materializing all structures as json::Value ahe...
LLVM_ABI void value(const Value &V)
Emit a self-contained value (number, string, vector<string> etc).
An Object is a JSON object, which maps strings to heterogenous JSON values.
LLVM_ABI Value * get(StringRef K)
A Value is an JSON value of unknown type.
@ Number
Number values can store both int64s and doubles at full precision, depending on what they were constr...
const json::Object * getAsObject() const
const json::Array * getAsArray() const
ASTNode(MustacheContext &Ctx, Type Ty, ArrayRef< StringRef > Accessor, ASTNode *Parent)
ASTNode(MustacheContext &Ctx)
void setIndentation(size_t NewIndentation)
ASTNode(MustacheContext &Ctx, StringRef Body, ASTNode *Parent)
void setRawBody(StringRef NewBody)
void render(const llvm::json::Value &Data, MustacheOutputStream &OS)
void addChild(AstPtr Child)
void resumeIndentation() override
AddIndentationStringStream(raw_ostream &WrappedStream, size_t Indentation)
uint64_t current_pos() const override
Return the current position within the stream, not counting the bytes currently in the buffer.
void write_impl(const char *Ptr, size_t Size) override
The is the piece of the class that is implemented by subclasses.
void suspendIndentation() override
uint64_t current_pos() const override
Return the current position within the stream, not counting the bytes currently in the buffer.
void write_impl(const char *Ptr, size_t Size) override
The is the piece of the class that is implemented by subclasses.
EscapeStringStream(llvm::raw_ostream &WrappedStream, EscapeMap &Escape)
MustacheOutputStream()=default
virtual void suspendIndentation()
~MustacheOutputStream() override=default
virtual void resumeIndentation()
Parser(StringRef TemplateStr, MustacheContext &Ctx)
RawMustacheOutputStream(raw_ostream &OS)
LLVM_ABI void registerPartial(std::string Name, std::string Partial)
LLVM_ABI void registerLambda(std::string Name, Lambda Lambda)
LLVM_ABI Template(StringRef TemplateStr, MustacheContext &Ctx)
LLVM_ABI void render(const llvm::json::Value &Data, llvm::raw_ostream &OS)
LLVM_ABI void overrideEscapeCharacters(DenseMap< char, std::string > Escapes)
size_t getIndentation() const
Token(StringRef RawBody, StringRef TokenBody, char Identifier, MustacheContext &Ctx)
void setIndentation(size_t NewIndentation)
static Type getTokenType(char Identifier)
ArrayRef< StringRef > AccessorValue
ArrayRef< StringRef > getAccessor() const
This class implements an extremely fast bulk output stream that can only output to a stream.
raw_ostream(bool unbuffered=false, OStreamKind K=OStreamKind::OK_OStream)
uint64_t tell() const
tell - Return the current offset with the file.
raw_ostream & write(unsigned char C)
void SetUnbuffered()
Set the stream to be unbuffered.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
static bool hasTextAhead(size_t Idx, const ArrayRef< Token > &Tokens)
static AstPtr createRootNode(MustacheContext &Ctx)
static const char * tagKindToString(Tag::Kind K)
void stripTokenBefore(SmallVectorImpl< Token > &Tokens, size_t Idx, Token &CurrentToken, Token::Type CurrentType)
iplist< ASTNode > ASTNodeList
static AstPtr createTextNode(MustacheContext &Ctx, StringRef Body, ASTNode *Parent)
static const char * jsonKindToString(json::Value::Kind K)
std::function< llvm::json::Value(std::string)> SectionLambda
static AstPtr createNode(MustacheContext &Ctx, ASTNode::Type T, ArrayRef< StringRef > A, ASTNode *Parent)
static void stripTokenAhead(SmallVectorImpl< Token > &Tokens, size_t Idx)
std::function< llvm::json::Value()> Lambda
static bool hasTextBehind(size_t Idx, const ArrayRef< Token > &Tokens)
static bool requiresCleanUp(Token::Type T)
static SmallVector< Token > tokenize(StringRef Template, MustacheContext &Ctx)
DenseMap< char, std::string > EscapeMap
static void toMustacheString(const json::Value &Data, raw_ostream &OS)
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,...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
ArrayRef(const T &OneElt) -> ArrayRef< T >
OutputIt copy(R &&Range, OutputIt Out)
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next