16#define DEBUG_TYPE "mustache"
26 return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
27 (V.getAsArray() && V.getAsArray()->empty());
37static Accessor splitMustacheString(
StringRef Str) {
45 Tokens.emplace_back(Str);
48 while (!Str.empty()) {
50 std::tie(Part, Str) = Str.
split(
".");
51 Tokens.emplace_back(Part.
trim());
68 void anchor()
override;
71void MustacheOutputStream::anchor() {}
80 void write_impl(
const char *
Ptr,
size_t Size)
override {
83 uint64_t current_pos()
const override {
return OS.
tell(); }
112 AccessorStr = AccessorStr.
substr(1);
125 switch (Identifier) {
169 : Ctx(Ctx), Ty(
Type::
Root), Parent(nullptr), ParentContext(nullptr) {}
173 ParentContext(nullptr) {}
177 : Ctx(Ctx), Ty(Ty), Parent(Parent), AccessorValue(
std::
move(Accessor)),
178 ParentContext(nullptr) {}
182 void setRawBody(std::string NewBody) { RawBody = std::move(NewBody); };
206 void renderUnescapeVariable(
const json::Value &CurrentCtx,
209 void renderInvertSection(
const json::Value &CurrentCtx,
214 size_t Indentation = 0;
219 std::vector<AstPtr> Children;
220 const Accessor AccessorValue;
226 return std::make_unique<ASTNode>(Ctx);
231 return std::make_unique<ASTNode>(Ctx,
T, std::move(
A), Parent);
236 return std::make_unique<ASTNode>(Ctx, std::move(Body), Parent);
255 size_t PrevIdx = Idx - 1;
259 const Token &PrevToken = Tokens[PrevIdx];
261 return !TokenBody.
ends_with(
"\n") && !(TokenBody.
empty() && Idx == 1);
268 if (Idx >= Tokens.
size() - 1)
271 size_t NextIdx = Idx + 1;
275 const Token &NextToken = Tokens[NextIdx];
294 Token &NextToken = Tokens[Idx + 1];
311 Token &PrevToken = Tokens[Idx - 1];
314 size_t Indentation = PrevTokenBody.
size() - Unindented.
size();
347 return "JSON_KIND_NULL";
349 return "JSON_KIND_BOOLEAN";
351 return "JSON_KIND_NUMBER";
353 return "JSON_KIND_STRING";
355 return "JSON_KIND_ARRAY";
357 return "JSON_KIND_OBJECT";
367 size_t NormalOpenPos =
Template.find(Open, StartPos);
368 size_t TripleOpenPos =
Template.find(TripleOpen, StartPos);
374 (NormalOpenPos ==
StringRef::npos || TripleOpenPos <= NormalOpenPos)) {
377 Template.find(TripleClose, TripleOpenPos + TripleOpen.
size());
382 Result.StartPosition = TripleOpenPos;
383 size_t ContentStart = TripleOpenPos + TripleOpen.
size();
384 Result.Content =
Template.substr(ContentStart, EndPos - ContentStart);
386 TripleOpenPos, (EndPos + TripleClose.
size()) - TripleOpenPos);
389 size_t EndPos =
Template.find(Close, NormalOpenPos + Open.
size());
394 Result.StartPosition = NormalOpenPos;
395 size_t ContentStart = NormalOpenPos + Open.
size();
396 Result.Content =
Template.substr(ContentStart, EndPos - ContentStart);
398 Template.substr(NormalOpenPos, (EndPos + Close.
size()) - NormalOpenPos);
404static std::optional<std::pair<StringRef, StringRef>>
409 Tokens.
emplace_back(
T.FullMatch.str(),
"&" +
T.Content.str(),
'&');
413 std::string RawBody =
T.FullMatch.str();
415 char Front = Interpolated.
empty() ?
' ' : Interpolated.
trim().
front();
422 DelimSpec = DelimSpec.
take_until([](
char C) {
return C ==
'='; });
423 DelimSpec = DelimSpec.
trim();
425 std::pair<StringRef, StringRef> Ret = DelimSpec.
split(
' ');
427 <<
", NewClose: " << Ret.second <<
"\n");
443 LLVM_DEBUG(
dbgs() <<
"[Tokenize Loop] Start:" << Start <<
", Open:'" << Open
444 <<
"', Close:'" << Close <<
"'\n");
450 LLVM_DEBUG(
dbgs() <<
" No more tags. Created final Text token: \""
451 <<
Template.substr(Start) <<
"\"\n");
456 if (
T.StartPosition > Start) {
462 std::tie(Open, Close) = *NewDelims;
466 Start =
T.StartPosition +
T.FullMatch.size();
483 size_t LastIdx = Tokens.
size() - 1;
484 for (
size_t Idx = 0, End = Tokens.
size(); Idx < End; ++Idx) {
485 Token &CurrentToken = Tokens[Idx];
490 if (!RequiresCleanUp)
503 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
506 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
517 : Escape(Escape), EscapeChars(Escape.keys().begin(), Escape.keys().end()),
518 WrappedStream(WrappedStream) {
526 while (Start <
Size) {
528 size_t Next =
Data.find_first_of(EscapeChars.str(), Start);
532 WrappedStream <<
Data.substr(Start);
538 WrappedStream <<
Data.substr(Start,
Next - Start);
541 WrappedStream << Escape[
Data[
Next]];
559 : Indentation(Indentation), WrappedStream(WrappedStream),
571 Indent.
resize(Indentation,
' ');
573 for (
char C :
Data) {
574 LLVM_DEBUG(
dbgs() <<
"[Indentation Stream] NeedsIndent:" << NeedsIndent
575 <<
", C:'" <<
C <<
"', Indentation:" << Indentation
577 if (NeedsIndent &&
C !=
'\n') {
578 WrappedStream << Indent;
582 if (
C ==
'\n' && !IsSuspended)
599 : Ctx(Ctx), TemplateStr(TemplateStr) {}
604 void parseMustache(
ASTNode *Parent);
616 size_t Start = CurrentPtr;
617 parseMustache(CurrentNode.get());
618 const size_t End = CurrentPtr - 1;
620 for (std::size_t
I = Start;
I < End;
I++)
621 RawBody += Tokens[
I].RawBody;
622 CurrentNode->setRawBody(std::move(RawBody));
623 Parent->
addChild(std::move(CurrentNode));
630 parseMustache(RootNode.get());
634void Parser::parseMustache(
ASTNode *Parent) {
636 while (CurrentPtr < Tokens.size()) {
637 Token CurrentToken = Tokens[CurrentPtr];
642 switch (CurrentToken.
getType()) {
646 Parent->
addChild(std::move(CurrentNode));
651 Parent->
addChild(std::move(CurrentNode));
657 Parent->
addChild(std::move(CurrentNode));
663 Parent->
addChild(std::move(CurrentNode));
686 switch (
Data.kind()) {
690 auto Num = *
Data.getAsNumber();
691 std::ostringstream SS;
697 auto Str = *
Data.getAsString();
703 auto Arr = *
Data.getAsArray();
717void ASTNode::renderRoot(
const json::Value &CurrentCtx,
719 renderChild(CurrentCtx, OS);
724void ASTNode::renderPartial(
const json::Value &CurrentCtx,
726 LLVM_DEBUG(
dbgs() <<
"[Render Partial] Accessor:" << AccessorValue[0]
727 <<
", Indentation:" << Indentation <<
"\n");
728 auto Partial = Ctx.Partials.find(AccessorValue[0]);
729 if (
Partial != Ctx.Partials.end())
730 renderPartial(CurrentCtx, OS,
Partial->getValue().get());
733void ASTNode::renderVariable(
const json::Value &CurrentCtx,
735 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
736 if (
Lambda != Ctx.Lambdas.end()) {
737 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
738 }
else if (
const json::Value *ContextPtr = findContext()) {
739 EscapeStringStream ES(OS, Ctx.Escapes);
744void ASTNode::renderUnescapeVariable(
const json::Value &CurrentCtx,
746 LLVM_DEBUG(
dbgs() <<
"[Render UnescapeVariable] Accessor:" << AccessorValue[0]
748 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
749 if (
Lambda != Ctx.Lambdas.end()) {
750 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
751 }
else if (
const json::Value *ContextPtr = findContext()) {
758void ASTNode::renderSection(
const json::Value &CurrentCtx,
760 auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
762 renderSectionLambdas(CurrentCtx, OS,
SectionLambda->getValue());
766 const json::Value *ContextPtr = findContext();
767 if (isContextFalsey(ContextPtr))
770 if (
const json::Array *Arr = ContextPtr->
getAsArray()) {
771 for (
const json::Value &V : *Arr)
775 renderChild(*ContextPtr, OS);
778void ASTNode::renderInvertSection(
const json::Value &CurrentCtx,
780 bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
781 const json::Value *ContextPtr = findContext();
782 if (isContextFalsey(ContextPtr) && !IsLambda) {
783 renderChild(CurrentCtx, OS);
788 if (Ty !=
Root && Ty !=
Text && AccessorValue.empty())
792 ParentContext = &
Data;
796 renderRoot(
Data, OS);
802 renderPartial(
Data, OS);
805 renderVariable(
Data, OS);
808 renderUnescapeVariable(
Data, OS);
811 renderSection(
Data, OS);
814 renderInvertSection(
Data, OS);
826 if (AccessorValue.empty())
828 if (AccessorValue[0] ==
".")
829 return ParentContext;
832 StringRef CurrentAccessor = AccessorValue[0];
833 ASTNode *CurrentParent = Parent;
835 while (!CurrentContext || !CurrentContext->
get(CurrentAccessor)) {
836 if (CurrentParent->Ty !=
Root) {
837 CurrentContext = CurrentParent->ParentContext->
getAsObject();
838 CurrentParent = CurrentParent->Parent;
844 for (
auto [Idx, Acc] :
enumerate(AccessorValue)) {
848 if (Idx < AccessorValue.size() - 1) {
859void ASTNode::renderChild(
const json::Value &Contexts,
861 for (
AstPtr &Child : Children)
862 Child->render(Contexts, OS);
865void ASTNode::renderPartial(
const json::Value &Contexts,
867 LLVM_DEBUG(
dbgs() <<
"[Render Partial Indentation] Indentation: " << Indentation <<
"\n");
868 AddIndentationStringStream IS(OS, Indentation);
872void ASTNode::renderLambdas(
const json::Value &Contexts,
874 json::Value LambdaResult =
L();
875 std::string LambdaStr;
876 raw_string_ostream Output(LambdaStr);
878 Parser
P(LambdaStr, Ctx);
881 EscapeStringStream ES(OS, Ctx.Escapes);
883 LambdaNode->render(Contexts, ES);
886 LambdaNode->render(Contexts, OS);
889void ASTNode::renderSectionLambdas(
const json::Value &Contexts,
891 json::Value
Return =
L(RawBody);
892 if (isFalsey(Return))
894 std::string LambdaStr;
895 raw_string_ostream Output(LambdaStr);
897 Parser
P(LambdaStr, Ctx);
899 LambdaNode->render(Contexts, OS);
904 Tree->render(
Data, MOS);
909 AstPtr PartialTree =
P.parse();
910 Ctx.Partials.insert(std::make_pair(Name, std::move(PartialTree)));
914 Ctx.Lambdas[Name] = L;
918 Ctx.SectionLambdas[Name] = L;
922 Ctx.Escapes = std::move(E);
929 const EscapeMap HtmlEntities = {{
'&',
"&"},
938 : Ctx(std::move(
Other.Ctx)), Tree(std::move(
Other.Tree)) {}
943 if (
this != &
Other) {
944 Ctx = std::move(
Other.Ctx);
945 Tree = std::move(
Other.Tree);
946 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)
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.
std::string str() const
str - Get the contents as an std::string.
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, std::string Body, ASTNode *Parent)
ASTNode(MustacheContext &Ctx)
void setIndentation(size_t NewIndentation)
ASTNode(MustacheContext &Ctx, Type Ty, Accessor Accessor, ASTNode *Parent)
void render(const llvm::json::Value &Data, MustacheOutputStream &OS)
void addChild(AstPtr Child)
void setRawBody(std::string NewBody)
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)
Template & operator=(const Template &)=delete
LLVM_ABI void registerLambda(std::string Name, Lambda Lambda)
LLVM_ABI Template(StringRef TemplateStr)
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
void setIndentation(size_t NewIndentation)
static Type getTokenType(char Identifier)
Token(std::string RawBody, std::string TokenBody, char Identifier)
Accessor 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 createTextNode(MustacheContext &Ctx, std::string Body, ASTNode *Parent)
static AstPtr createRootNode(MustacheContext &Ctx)
static const char * tagKindToString(Tag::Kind K)
std::unique_ptr< ASTNode > AstPtr
static SmallVector< Token > tokenize(StringRef Template)
void stripTokenBefore(SmallVectorImpl< Token > &Tokens, size_t Idx, Token &CurrentToken, Token::Type CurrentType)
static const char * jsonKindToString(json::Value::Kind K)
static AstPtr createNode(MustacheContext &Ctx, ASTNode::Type T, Accessor A, ASTNode *Parent)
std::function< llvm::json::Value(std::string)> SectionLambda
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)
DenseMap< char, std::string > EscapeMap
static std::optional< std::pair< StringRef, StringRef > > processTag(const Tag &T, SmallVectorImpl< Token > &Tokens)
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
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Implement std::hash so that hash_code can be used in STL containers.