15#define DEBUG_TYPE "mustache"
25 return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
26 (V.getAsArray() && V.getAsArray()->empty());
37 size_t CurrentPos = 0;
38 while (CurrentPos < Str.size()) {
40 size_t DelimiterPos = Str.find(
'.', CurrentPos);
44 DelimiterPos = Str.size();
57 CurrentPos = DelimiterPos + 1;
73 splitAndTrim(Str, Tokens);
78 std::copy(Tokens.
begin(), Tokens.
end(), ArenaTokens);
95 void anchor()
override;
98void MustacheOutputStream::anchor() {}
107 void write_impl(
const char *
Ptr,
size_t Size)
override {
110 uint64_t current_pos()
const override {
return OS.
tell(); }
139 AccessorStr = AccessorStr.
substr(1);
152 switch (Identifier) {
196 : Ctx(Ctx), Ty(
Type::
Root), Parent(nullptr), ParentContext(nullptr) {}
199 : Ctx(Ctx), Ty(
Type::
Text), Body(Body), Parent(Parent),
200 ParentContext(nullptr) {}
205 : Ctx(Ctx), Ty(Ty), Parent(Parent), AccessorValue(Accessor),
206 ParentContext(nullptr) {}
234 void renderUnescapeVariable(
const json::Value &CurrentCtx,
237 void renderInvertSection(
const json::Value &CurrentCtx,
242 size_t Indentation = 0;
263 return new (Ctx.Allocator.Allocate<
ASTNode>())
ASTNode(Ctx, Body, Parent);
282 size_t PrevIdx = Idx - 1;
286 const Token &PrevToken = Tokens[PrevIdx];
288 return !TokenBody.
ends_with(
"\n") && !(TokenBody.
empty() && Idx == 1);
295 if (Idx >= Tokens.
size() - 1)
298 size_t NextIdx = Idx + 1;
302 const Token &NextToken = Tokens[NextIdx];
321 Token &NextToken = Tokens[Idx + 1];
338 Token &PrevToken = Tokens[Idx - 1];
341 size_t Indentation = PrevTokenBody.
size() - Unindented.
size();
374 return "JSON_KIND_NULL";
376 return "JSON_KIND_BOOLEAN";
378 return "JSON_KIND_NUMBER";
380 return "JSON_KIND_STRING";
382 return "JSON_KIND_ARRAY";
384 return "JSON_KIND_OBJECT";
396 size_t TextStart = 0;
409 TagOpen = TripleOpen;
410 TagClose = TripleClose;
422 if (Cursor > TextStart)
426 size_t EndPos =
Template.find(TagClose, Cursor + TagOpen.
size());
430 TextStart = Cursor =
Template.size();
435 size_t ContentStart = Cursor + TagOpen.
size();
438 Template.substr(Cursor, (EndPos + TagClose.
size()) - Cursor);
441 LLVM_DEBUG(
dbgs() <<
"[Tag] " << FullMatch <<
", Content: " << Content
444 Tokens.
emplace_back(FullMatch, Ctx.Saver.save(
"&" + Content),
'&', Ctx);
448 char Front = Interpolated.
empty() ?
' ' : Interpolated.
trim().
front();
449 Tokens.
emplace_back(FullMatch, Interpolated, Front, Ctx);
454 DelimSpec = DelimSpec.
take_until([](
char C) {
return C ==
'='; });
455 DelimSpec = DelimSpec.
trim();
457 auto [NewOpen, NewClose] = DelimSpec.
split(
' ');
459 <<
", NewClose: " << NewClose <<
"\n");
466 Cursor += FullMatch.
size();
475 size_t LastIdx = Tokens.
size() - 1;
476 for (
size_t Idx = 0, End = Tokens.
size(); Idx < End; ++Idx) {
477 Token &CurrentToken = Tokens[Idx];
485 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
488 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
499 : Escape(Escape), EscapeChars(Escape.keys().begin(), Escape.keys().end()),
500 WrappedStream(WrappedStream) {
508 while (Start <
Size) {
510 size_t Next =
Data.find_first_of(EscapeChars.str(), Start);
514 WrappedStream <<
Data.substr(Start);
520 WrappedStream <<
Data.substr(Start,
Next - Start);
523 WrappedStream << Escape[
Data[
Next]];
541 : Indentation(Indentation), WrappedStream(WrappedStream),
553 Indent.
resize(Indentation,
' ');
555 for (
char C :
Data) {
556 LLVM_DEBUG(
dbgs() <<
"[Indentation Stream] NeedsIndent:" << NeedsIndent
557 <<
", C:'" <<
C <<
"', Indentation:" << Indentation
559 if (NeedsIndent &&
C !=
'\n') {
560 WrappedStream << Indent;
564 if (
C ==
'\n' && !IsSuspended)
581 : Ctx(Ctx), TemplateStr(TemplateStr) {}
586 void parseMustache(
ASTNode *Parent);
598 size_t Start = CurrentPtr;
599 parseMustache(CurrentNode);
600 const size_t End = CurrentPtr - 1;
602 size_t RawBodySize = 0;
603 for (
size_t I = Start;
I < End; ++
I)
604 RawBodySize += Tokens[
I].RawBody.size();
608 for (std::size_t
I = Start;
I < End; ++
I)
609 RawBody += Tokens[
I].RawBody;
616 Tokens =
tokenize(TemplateStr, Ctx);
619 parseMustache(RootNode);
623void Parser::parseMustache(
ASTNode *Parent) {
625 while (CurrentPtr < Tokens.size()) {
626 Token CurrentToken = Tokens[CurrentPtr];
631 switch (CurrentToken.
getType()) {
673 switch (
Data.kind()) {
677 auto Num = *
Data.getAsNumber();
678 std::ostringstream SS;
684 OS << *
Data.getAsString();
689 auto Arr = *
Data.getAsArray();
703void ASTNode::renderRoot(
const json::Value &CurrentCtx,
705 renderChild(CurrentCtx, OS);
710void ASTNode::renderPartial(
const json::Value &CurrentCtx,
712 LLVM_DEBUG(
dbgs() <<
"[Render Partial] Accessor:" << AccessorValue[0]
713 <<
", Indentation:" << Indentation <<
"\n");
714 auto Partial = Ctx.Partials.find(AccessorValue[0]);
715 if (
Partial != Ctx.Partials.end())
716 renderPartial(CurrentCtx, OS,
Partial->getValue());
719void ASTNode::renderVariable(
const json::Value &CurrentCtx,
721 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
722 if (
Lambda != Ctx.Lambdas.end()) {
723 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
724 }
else if (
const json::Value *ContextPtr = findContext()) {
725 EscapeStringStream ES(OS, Ctx.Escapes);
730void ASTNode::renderUnescapeVariable(
const json::Value &CurrentCtx,
732 LLVM_DEBUG(
dbgs() <<
"[Render UnescapeVariable] Accessor:" << AccessorValue[0]
734 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
735 if (
Lambda != Ctx.Lambdas.end()) {
736 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
737 }
else if (
const json::Value *ContextPtr = findContext()) {
744void ASTNode::renderSection(
const json::Value &CurrentCtx,
746 auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
748 renderSectionLambdas(CurrentCtx, OS,
SectionLambda->getValue());
752 const json::Value *ContextPtr = findContext();
753 if (isContextFalsey(ContextPtr))
756 if (
const json::Array *Arr = ContextPtr->
getAsArray()) {
757 for (
const json::Value &V : *Arr)
761 renderChild(*ContextPtr, OS);
764void ASTNode::renderInvertSection(
const json::Value &CurrentCtx,
766 bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
767 const json::Value *ContextPtr = findContext();
768 if (isContextFalsey(ContextPtr) && !IsLambda) {
769 renderChild(CurrentCtx, OS);
774 if (Ty !=
Root && Ty !=
Text && AccessorValue.empty())
778 ParentContext = &
Data;
782 renderRoot(
Data, OS);
788 renderPartial(
Data, OS);
791 renderVariable(
Data, OS);
794 renderUnescapeVariable(
Data, OS);
797 renderSection(
Data, OS);
800 renderInvertSection(
Data, OS);
812 if (AccessorValue.empty())
814 if (AccessorValue[0] ==
".")
815 return ParentContext;
818 StringRef CurrentAccessor = AccessorValue[0];
819 ASTNode *CurrentParent = Parent;
821 while (!CurrentContext || !CurrentContext->
get(CurrentAccessor)) {
822 if (CurrentParent->Ty !=
Root) {
823 CurrentContext = CurrentParent->ParentContext->
getAsObject();
824 CurrentParent = CurrentParent->Parent;
830 for (
auto [Idx, Acc] :
enumerate(AccessorValue)) {
834 if (Idx < AccessorValue.size() - 1) {
845void ASTNode::renderChild(
const json::Value &Contexts,
847 for (
ASTNode &Child : Children)
848 Child.render(Contexts, OS);
851void ASTNode::renderPartial(
const json::Value &Contexts,
853 LLVM_DEBUG(
dbgs() <<
"[Render Partial Indentation] Indentation: " << Indentation <<
"\n");
854 AddIndentationStringStream IS(OS, Indentation);
858void ASTNode::renderLambdas(
const llvm::json::Value &Contexts,
860 json::Value LambdaResult =
L();
861 std::string LambdaStr;
862 raw_string_ostream Output(LambdaStr);
864 Parser
P(LambdaStr, Ctx);
867 EscapeStringStream ES(OS, Ctx.Escapes);
869 LambdaNode->
render(Contexts, ES);
872 LambdaNode->
render(Contexts, OS);
875void ASTNode::renderSectionLambdas(
const llvm::json::Value &Contexts,
877 json::Value
Return =
L(RawBody.str());
878 if (isFalsey(Return))
880 std::string LambdaStr;
881 raw_string_ostream Output(LambdaStr);
883 Parser
P(LambdaStr, Ctx);
885 LambdaNode->
render(Contexts, OS);
890 Tree->render(
Data, MOS);
894 StringRef SavedPartial = Ctx.Saver.save(Partial);
896 AstPtr PartialTree =
P.parse();
897 Ctx.Partials.insert(std::make_pair(Name, PartialTree));
901 Ctx.Lambdas[Name] = L;
905 Ctx.SectionLambdas[Name] = L;
909 Ctx.Escapes = std::move(E);
916 const EscapeMap HtmlEntities = {{
'&',
"&"},
926 Other.Tree =
nullptr;
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file defines the SmallVector class.
static SymbolRef::Type getType(const Symbol *Sym)
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
size - 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...
StringRef - 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
empty - 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
size - Get the string size.
char front() const
front - 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.
@ C
The default llvm calling convention, compatible with C.
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.
FunctionAddr VTableAddr uintptr_t uintptr_t Data
FunctionAddr VTableAddr Next
ArrayRef(const T &OneElt) -> ArrayRef< T >