16#define DEBUG_TYPE "mustache"
26 return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
27 (V.getAsArray() && V.getAsArray()->empty());
38 size_t CurrentPos = 0;
39 while (CurrentPos < Str.size()) {
41 size_t DelimiterPos = Str.find(
'.', CurrentPos);
45 DelimiterPos = Str.size();
58 CurrentPos = DelimiterPos + 1;
74 splitAndTrim(Str, Tokens);
79 std::copy(Tokens.
begin(), Tokens.
end(), ArenaTokens);
96 void anchor()
override;
99void MustacheOutputStream::anchor() {}
108 void write_impl(
const char *
Ptr,
size_t Size)
override {
111 uint64_t current_pos()
const override {
return OS.
tell(); }
140 AccessorStr = AccessorStr.
substr(1);
153 switch (Identifier) {
197 : Ctx(Ctx), Ty(
Type::
Root), Parent(nullptr), ParentContext(nullptr) {}
200 : Ctx(Ctx), Ty(
Type::
Text), Body(Body), Parent(Parent),
201 ParentContext(nullptr) {}
206 : Ctx(Ctx), Ty(Ty), Parent(Parent), AccessorValue(Accessor),
207 ParentContext(nullptr) {}
235 void renderUnescapeVariable(
const json::Value &CurrentCtx,
238 void renderInvertSection(
const json::Value &CurrentCtx,
243 size_t Indentation = 0;
264 return new (Ctx.Allocator.Allocate<
ASTNode>())
ASTNode(Ctx, Body, Parent);
283 size_t PrevIdx = Idx - 1;
287 const Token &PrevToken = Tokens[PrevIdx];
289 return !TokenBody.
ends_with(
"\n") && !(TokenBody.
empty() && Idx == 1);
296 if (Idx >= Tokens.
size() - 1)
299 size_t NextIdx = Idx + 1;
303 const Token &NextToken = Tokens[NextIdx];
322 Token &NextToken = Tokens[Idx + 1];
339 Token &PrevToken = Tokens[Idx - 1];
342 size_t Indentation = PrevTokenBody.
size() - Unindented.
size();
375 return "JSON_KIND_NULL";
377 return "JSON_KIND_BOOLEAN";
379 return "JSON_KIND_NUMBER";
381 return "JSON_KIND_STRING";
383 return "JSON_KIND_ARRAY";
385 return "JSON_KIND_OBJECT";
397 size_t TextStart = 0;
410 TagOpen = TripleOpen;
411 TagClose = TripleClose;
423 if (Cursor > TextStart)
427 size_t EndPos =
Template.find(TagClose, Cursor + TagOpen.
size());
431 TextStart = Cursor =
Template.size();
436 size_t ContentStart = Cursor + TagOpen.
size();
439 Template.substr(Cursor, (EndPos + TagClose.
size()) - Cursor);
442 LLVM_DEBUG(
dbgs() <<
"[Tag] " << FullMatch <<
", Content: " << Content
445 Tokens.
emplace_back(FullMatch, Ctx.Saver.save(
"&" + Content),
'&', Ctx);
449 char Front = Interpolated.
empty() ?
' ' : Interpolated.
trim().
front();
450 Tokens.
emplace_back(FullMatch, Interpolated, Front, Ctx);
455 DelimSpec = DelimSpec.
take_until([](
char C) {
return C ==
'='; });
456 DelimSpec = DelimSpec.
trim();
458 auto [NewOpen, NewClose] = DelimSpec.
split(
' ');
460 <<
", NewClose: " << NewClose <<
"\n");
467 Cursor += FullMatch.
size();
476 size_t LastIdx = Tokens.
size() - 1;
477 for (
size_t Idx = 0, End = Tokens.
size(); Idx < End; ++Idx) {
478 Token &CurrentToken = Tokens[Idx];
486 if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
489 if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
500 : Escape(Escape), EscapeChars(Escape.keys().begin(), Escape.keys().end()),
501 WrappedStream(WrappedStream) {
509 while (Start <
Size) {
511 size_t Next =
Data.find_first_of(EscapeChars.str(), Start);
515 WrappedStream <<
Data.substr(Start);
521 WrappedStream <<
Data.substr(Start,
Next - Start);
524 WrappedStream << Escape[
Data[
Next]];
542 : Indentation(Indentation), WrappedStream(WrappedStream),
554 Indent.
resize(Indentation,
' ');
556 for (
char C :
Data) {
557 LLVM_DEBUG(
dbgs() <<
"[Indentation Stream] NeedsIndent:" << NeedsIndent
558 <<
", C:'" <<
C <<
"', Indentation:" << Indentation
560 if (NeedsIndent &&
C !=
'\n') {
561 WrappedStream << Indent;
565 if (
C ==
'\n' && !IsSuspended)
582 : Ctx(Ctx), TemplateStr(TemplateStr) {}
587 void parseMustache(
ASTNode *Parent);
599 size_t Start = CurrentPtr;
600 parseMustache(CurrentNode);
601 const size_t End = CurrentPtr - 1;
603 for (std::size_t
I = Start;
I < End;
I++)
604 RawBody += Tokens[
I].RawBody;
610 Tokens =
tokenize(TemplateStr, Ctx);
613 parseMustache(RootNode);
617void Parser::parseMustache(
ASTNode *Parent) {
619 while (CurrentPtr < Tokens.size()) {
620 Token CurrentToken = Tokens[CurrentPtr];
625 switch (CurrentToken.
getType()) {
667 switch (
Data.kind()) {
671 auto Num = *
Data.getAsNumber();
672 std::ostringstream SS;
678 OS << *
Data.getAsString();
683 auto Arr = *
Data.getAsArray();
697void ASTNode::renderRoot(
const json::Value &CurrentCtx,
699 renderChild(CurrentCtx, OS);
704void ASTNode::renderPartial(
const json::Value &CurrentCtx,
706 LLVM_DEBUG(
dbgs() <<
"[Render Partial] Accessor:" << AccessorValue[0]
707 <<
", Indentation:" << Indentation <<
"\n");
708 auto Partial = Ctx.Partials.find(AccessorValue[0]);
709 if (
Partial != Ctx.Partials.end())
710 renderPartial(CurrentCtx, OS,
Partial->getValue());
713void ASTNode::renderVariable(
const json::Value &CurrentCtx,
715 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
716 if (
Lambda != Ctx.Lambdas.end()) {
717 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
718 }
else if (
const json::Value *ContextPtr = findContext()) {
719 EscapeStringStream ES(OS, Ctx.Escapes);
724void ASTNode::renderUnescapeVariable(
const json::Value &CurrentCtx,
726 LLVM_DEBUG(
dbgs() <<
"[Render UnescapeVariable] Accessor:" << AccessorValue[0]
728 auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
729 if (
Lambda != Ctx.Lambdas.end()) {
730 renderLambdas(CurrentCtx, OS,
Lambda->getValue());
731 }
else if (
const json::Value *ContextPtr = findContext()) {
738void ASTNode::renderSection(
const json::Value &CurrentCtx,
740 auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
742 renderSectionLambdas(CurrentCtx, OS,
SectionLambda->getValue());
746 const json::Value *ContextPtr = findContext();
747 if (isContextFalsey(ContextPtr))
750 if (
const json::Array *Arr = ContextPtr->
getAsArray()) {
751 for (
const json::Value &V : *Arr)
755 renderChild(*ContextPtr, OS);
758void ASTNode::renderInvertSection(
const json::Value &CurrentCtx,
760 bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
761 const json::Value *ContextPtr = findContext();
762 if (isContextFalsey(ContextPtr) && !IsLambda) {
763 renderChild(CurrentCtx, OS);
768 if (Ty !=
Root && Ty !=
Text && AccessorValue.empty())
772 ParentContext = &
Data;
776 renderRoot(
Data, OS);
782 renderPartial(
Data, OS);
785 renderVariable(
Data, OS);
788 renderUnescapeVariable(
Data, OS);
791 renderSection(
Data, OS);
794 renderInvertSection(
Data, OS);
806 if (AccessorValue.empty())
808 if (AccessorValue[0] ==
".")
809 return ParentContext;
812 StringRef CurrentAccessor = AccessorValue[0];
813 ASTNode *CurrentParent = Parent;
815 while (!CurrentContext || !CurrentContext->
get(CurrentAccessor)) {
816 if (CurrentParent->Ty !=
Root) {
817 CurrentContext = CurrentParent->ParentContext->
getAsObject();
818 CurrentParent = CurrentParent->Parent;
824 for (
auto [Idx, Acc] :
enumerate(AccessorValue)) {
828 if (Idx < AccessorValue.size() - 1) {
839void ASTNode::renderChild(
const json::Value &Contexts,
841 for (
ASTNode &Child : Children)
842 Child.render(Contexts, OS);
845void ASTNode::renderPartial(
const json::Value &Contexts,
847 LLVM_DEBUG(
dbgs() <<
"[Render Partial Indentation] Indentation: " << Indentation <<
"\n");
848 AddIndentationStringStream IS(OS, Indentation);
852void ASTNode::renderLambdas(
const llvm::json::Value &Contexts,
854 json::Value LambdaResult =
L();
855 std::string LambdaStr;
856 raw_string_ostream Output(LambdaStr);
858 Parser
P(LambdaStr, Ctx);
861 EscapeStringStream ES(OS, Ctx.Escapes);
863 LambdaNode->
render(Contexts, ES);
866 LambdaNode->
render(Contexts, OS);
869void ASTNode::renderSectionLambdas(
const llvm::json::Value &Contexts,
871 json::Value
Return =
L(RawBody.str());
872 if (isFalsey(Return))
874 std::string LambdaStr;
875 raw_string_ostream Output(LambdaStr);
877 Parser
P(LambdaStr, Ctx);
879 LambdaNode->
render(Contexts, OS);
884 Tree->render(
Data, MOS);
888 StringRef SavedPartial = Ctx.Saver.save(Partial);
890 AstPtr PartialTree =
P.parse();
891 Ctx.Partials.insert(std::make_pair(Name, PartialTree));
895 Ctx.Lambdas[Name] = L;
899 Ctx.SectionLambdas[Name] = L;
903 Ctx.Escapes = std::move(E);
910 const EscapeMap HtmlEntities = {{
'&',
"&"},
920 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.
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 >