16#define DEBUG_TYPE "mustache"
26 return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
27 (V.getAsArray() && V.getAsArray()->empty());
49 while (!Str.empty()) {
51 std::tie(Part, Str) = Str.
split(
'.');
60 std::copy(Tokens.
begin(), Tokens.
end(), ArenaTokens);
77 void anchor()
override;
80void MustacheOutputStream::anchor() {}
89 void write_impl(
const char *
Ptr,
size_t Size)
override {
92 uint64_t current_pos()
const override {
return OS.
tell(); }
121 AccessorStr = AccessorStr.
substr(1);
134 switch (Identifier) {
178 : Ctx(Ctx), Ty(
Type::
Root), Parent(nullptr), ParentContext(nullptr) {}
181 : Ctx(Ctx), Ty(
Type::
Text), Body(Body), Parent(Parent),
182 ParentContext(nullptr) {}
187 : Ctx(Ctx), Ty(Ty), Parent(Parent), AccessorValue(Accessor),
188 ParentContext(nullptr) {}
216 void renderUnescapeVariable(
const json::Value &CurrentCtx,
219 void renderInvertSection(
const json::Value &CurrentCtx,
224 size_t Indentation = 0;
245 return new (Ctx.Allocator.Allocate<
ASTNode>())
ASTNode(Ctx, Body, Parent);
264 size_t PrevIdx = Idx - 1;
268 const Token &PrevToken = Tokens[PrevIdx];
270 return !TokenBody.
ends_with(
"\n") && !(TokenBody.
empty() && Idx == 1);
277 if (Idx >= Tokens.
size() - 1)
280 size_t NextIdx = Idx + 1;
284 const Token &NextToken = Tokens[NextIdx];
303 Token &NextToken = Tokens[Idx + 1];
320 Token &PrevToken = Tokens[Idx - 1];
323 size_t Indentation = PrevTokenBody.
size() - Unindented.
size();
356 return "JSON_KIND_NULL";
358 return "JSON_KIND_BOOLEAN";
360 return "JSON_KIND_NUMBER";
362 return "JSON_KIND_STRING";
364 return "JSON_KIND_ARRAY";
366 return "JSON_KIND_OBJECT";
376 size_t NormalOpenPos =
Template.find(Open, StartPos);
377 size_t TripleOpenPos =
Template.find(TripleOpen, StartPos);
383 (NormalOpenPos ==
StringRef::npos || TripleOpenPos <= NormalOpenPos)) {
386 Template.find(TripleClose, TripleOpenPos + TripleOpen.
size());
391 Result.StartPosition = TripleOpenPos;
392 size_t ContentStart = TripleOpenPos + TripleOpen.
size();
393 Result.Content =
Template.substr(ContentStart, EndPos - ContentStart);
395 TripleOpenPos, (EndPos + TripleClose.
size()) - TripleOpenPos);
398 size_t EndPos =
Template.find(Close, NormalOpenPos + Open.
size());
403 Result.StartPosition = NormalOpenPos;
404 size_t ContentStart = NormalOpenPos + Open.
size();
405 Result.Content =
Template.substr(ContentStart, EndPos - ContentStart);
407 Template.substr(NormalOpenPos, (EndPos + Close.
size()) - NormalOpenPos);
413static std::optional<std::pair<StringRef, StringRef>>
418 Tokens.
emplace_back(
T.FullMatch, Ctx.Saver.save(
"&" +
T.Content),
'&', Ctx);
423 char Front = Interpolated.
empty() ?
' ' : Interpolated.
trim().
front();
430 DelimSpec = DelimSpec.
take_until([](
char C) {
return C ==
'='; });
431 DelimSpec = DelimSpec.
trim();
433 std::pair<StringRef, StringRef> Ret = DelimSpec.
split(
' ');
435 <<
", NewClose: " << Ret.second <<
"\n");
451 LLVM_DEBUG(
dbgs() <<
"[Tokenize Loop] Start:" << Start <<
", Open:'" << Open
452 <<
"', Close:'" << Close <<
"'\n");
462 if (
T.StartPosition > Start) {
468 std::tie(Open, Close) = *NewDelims;
472 Start =
T.StartPosition +
T.FullMatch.size();
489 size_t LastIdx = Tokens.
size() - 1;
490 for (
size_t Idx = 0, End = Tokens.
size(); Idx < End; ++Idx) {
491 Token &CurrentToken = Tokens[Idx];
496 if (!RequiresCleanUp)
509 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
512 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
523 : Escape(Escape), EscapeChars(Escape.keys().begin(), Escape.keys().end()),
524 WrappedStream(WrappedStream) {
532 while (Start <
Size) {
534 size_t Next =
Data.find_first_of(EscapeChars.str(), Start);
538 WrappedStream <<
Data.substr(Start);
544 WrappedStream <<
Data.substr(Start,
Next - Start);
547 WrappedStream << Escape[
Data[
Next]];
565 : Indentation(Indentation), WrappedStream(WrappedStream),
577 Indent.
resize(Indentation,
' ');
579 for (
char C :
Data) {
580 LLVM_DEBUG(
dbgs() <<
"[Indentation Stream] NeedsIndent:" << NeedsIndent
581 <<
", C:'" <<
C <<
"', Indentation:" << Indentation
583 if (NeedsIndent &&
C !=
'\n') {
584 WrappedStream << Indent;
588 if (
C ==
'\n' && !IsSuspended)
605 : Ctx(Ctx), TemplateStr(TemplateStr) {}
610 void parseMustache(
ASTNode *Parent);
622 size_t Start = CurrentPtr;
623 parseMustache(CurrentNode);
624 const size_t End = CurrentPtr - 1;
626 for (std::size_t
I = Start;
I < End;
I++)
627 RawBody += Tokens[
I].RawBody;
633 Tokens =
tokenize(TemplateStr, Ctx);
636 parseMustache(RootNode);
640void Parser::parseMustache(
ASTNode *Parent) {
642 while (CurrentPtr < Tokens.size()) {
643 Token CurrentToken = Tokens[CurrentPtr];
648 switch (CurrentToken.
getType()) {
690 switch (
Data.kind()) {
694 auto Num = *
Data.getAsNumber();
695 std::ostringstream SS;
701 OS << *
Data.getAsString();
706 auto Arr = *
Data.getAsArray();
720void ASTNode::renderRoot(
const json::Value &CurrentCtx,
722 renderChild(CurrentCtx, OS);
727void ASTNode::renderPartial(
const json::Value &CurrentCtx,
729 LLVM_DEBUG(
dbgs() <<
"[Render Partial] Accessor:" << AccessorValue[0]
730 <<
", Indentation:" << Indentation <<
"\n");
731 auto Partial = Ctx.Partials.find(AccessorValue[0]);
732 if (
Partial != Ctx.Partials.end())
733 renderPartial(CurrentCtx, OS,
Partial->getValue());
736void ASTNode::renderVariable(
const json::Value &CurrentCtx,
738 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
739 if (
Lambda != Ctx.Lambdas.end()) {
740 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
741 }
else if (
const json::Value *ContextPtr = findContext()) {
742 EscapeStringStream ES(OS, Ctx.Escapes);
747void ASTNode::renderUnescapeVariable(
const json::Value &CurrentCtx,
749 LLVM_DEBUG(
dbgs() <<
"[Render UnescapeVariable] Accessor:" << AccessorValue[0]
751 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
752 if (
Lambda != Ctx.Lambdas.end()) {
753 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
754 }
else if (
const json::Value *ContextPtr = findContext()) {
761void ASTNode::renderSection(
const json::Value &CurrentCtx,
763 auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
765 renderSectionLambdas(CurrentCtx, OS,
SectionLambda->getValue());
769 const json::Value *ContextPtr = findContext();
770 if (isContextFalsey(ContextPtr))
773 if (
const json::Array *Arr = ContextPtr->
getAsArray()) {
774 for (
const json::Value &V : *Arr)
778 renderChild(*ContextPtr, OS);
781void ASTNode::renderInvertSection(
const json::Value &CurrentCtx,
783 bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
784 const json::Value *ContextPtr = findContext();
785 if (isContextFalsey(ContextPtr) && !IsLambda) {
786 renderChild(CurrentCtx, OS);
791 if (Ty !=
Root && Ty !=
Text && AccessorValue.empty())
795 ParentContext = &
Data;
799 renderRoot(
Data, OS);
805 renderPartial(
Data, OS);
808 renderVariable(
Data, OS);
811 renderUnescapeVariable(
Data, OS);
814 renderSection(
Data, OS);
817 renderInvertSection(
Data, OS);
829 if (AccessorValue.empty())
831 if (AccessorValue[0] ==
".")
832 return ParentContext;
835 StringRef CurrentAccessor = AccessorValue[0];
836 ASTNode *CurrentParent = Parent;
838 while (!CurrentContext || !CurrentContext->
get(CurrentAccessor)) {
839 if (CurrentParent->Ty !=
Root) {
840 CurrentContext = CurrentParent->ParentContext->
getAsObject();
841 CurrentParent = CurrentParent->Parent;
847 for (
auto [Idx, Acc] :
enumerate(AccessorValue)) {
851 if (Idx < AccessorValue.size() - 1) {
862void ASTNode::renderChild(
const json::Value &Contexts,
864 for (
ASTNode &Child : Children)
865 Child.render(Contexts, OS);
868void ASTNode::renderPartial(
const json::Value &Contexts,
870 LLVM_DEBUG(
dbgs() <<
"[Render Partial Indentation] Indentation: " << Indentation <<
"\n");
871 AddIndentationStringStream IS(OS, Indentation);
875void ASTNode::renderLambdas(
const llvm::json::Value &Contexts,
877 json::Value LambdaResult =
L();
878 std::string LambdaStr;
879 raw_string_ostream Output(LambdaStr);
881 Parser
P(LambdaStr, Ctx);
884 EscapeStringStream ES(OS, Ctx.Escapes);
886 LambdaNode->
render(Contexts, ES);
889 LambdaNode->
render(Contexts, OS);
892void ASTNode::renderSectionLambdas(
const llvm::json::Value &Contexts,
894 json::Value
Return =
L(RawBody.str());
895 if (isFalsey(Return))
897 std::string LambdaStr;
898 raw_string_ostream Output(LambdaStr);
900 Parser
P(LambdaStr, Ctx);
902 LambdaNode->
render(Contexts, OS);
907 Tree->render(
Data, MOS);
911 StringRef SavedPartial = Ctx.Saver.save(Partial);
913 AstPtr PartialTree =
P.parse();
914 Ctx.Partials.insert(std::make_pair(Name, PartialTree));
918 Ctx.Lambdas[Name] = L;
922 Ctx.SectionLambdas[Name] = L;
926 Ctx.Escapes = std::move(E);
933 const EscapeMap HtmlEntities = {{
'&',
"&"},
943 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 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.
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.
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.
static constexpr size_t npos
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)
static std::optional< std::pair< StringRef, StringRef > > processTag(const Tag &T, SmallVectorImpl< Token > &Tokens, MustacheContext &Ctx)
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 Tag findNextTag(StringRef Template, size_t StartPos, StringRef Open, StringRef Close)
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 >