Skip to content

Commit d5312d1

Browse files
committed
[llvm][mustache] Fix failing StandaloneIndentation test
When rendering partials, we need to use an indentation stream, but when part of the partial is a unescaped sequence, we cannot indent those. To address this, we build a common MustacheStream interface for all the output streams to use. This allows us to furhter customize the AddIndentationStream implementation and opt it out of indenting the UnescapeSequence.
1 parent a31a1ef commit d5312d1

File tree

3 files changed

+265
-206
lines changed

3 files changed

+265
-206
lines changed

llvm/lib/Support/Mustache.cpp

Lines changed: 112 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,33 @@ static Accessor splitMustacheString(StringRef Str) {
5757

5858
namespace llvm::mustache {
5959

60+
class MustacheOutputStream : public raw_ostream {
61+
public:
62+
MustacheOutputStream() = default;
63+
~MustacheOutputStream() override = default;
64+
65+
virtual void suspendIndentation() {}
66+
virtual void resumeIndentation() {}
67+
68+
private:
69+
void anchor() override;
70+
};
71+
72+
void MustacheOutputStream::anchor() {}
73+
74+
class RawMustacheOutputStream : public MustacheOutputStream {
75+
public:
76+
RawMustacheOutputStream(raw_ostream &OS) : OS(OS) { SetUnbuffered(); }
77+
78+
private:
79+
raw_ostream &OS;
80+
81+
void write_impl(const char *Ptr, size_t Size) override {
82+
OS.write(Ptr, Size);
83+
}
84+
uint64_t current_pos() const override { return OS.tell(); }
85+
};
86+
6087
class Token {
6188
public:
6289
enum class Type {
@@ -157,29 +184,31 @@ class ASTNode {
157184

158185
void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
159186

160-
void render(const llvm::json::Value &Data, llvm::raw_ostream &OS);
187+
void render(const llvm::json::Value &Data, MustacheOutputStream &OS);
161188

162189
private:
163-
void renderLambdas(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
164-
Lambda &L);
190+
void renderLambdas(const llvm::json::Value &Contexts,
191+
MustacheOutputStream &OS, Lambda &L);
165192

166193
void renderSectionLambdas(const llvm::json::Value &Contexts,
167-
llvm::raw_ostream &OS, SectionLambda &L);
194+
MustacheOutputStream &OS, SectionLambda &L);
168195

169-
void renderPartial(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
170-
ASTNode *Partial);
196+
void renderPartial(const llvm::json::Value &Contexts,
197+
MustacheOutputStream &OS, ASTNode *Partial);
171198

172-
void renderChild(const llvm::json::Value &Context, llvm::raw_ostream &OS);
199+
void renderChild(const llvm::json::Value &Context, MustacheOutputStream &OS);
173200

174201
const llvm::json::Value *findContext();
175202

176-
void renderRoot(const json::Value &CurrentCtx, raw_ostream &OS);
177-
void renderText(raw_ostream &OS);
178-
void renderPartial(const json::Value &CurrentCtx, raw_ostream &OS);
179-
void renderVariable(const json::Value &CurrentCtx, raw_ostream &OS);
180-
void renderUnescapeVariable(const json::Value &CurrentCtx, raw_ostream &OS);
181-
void renderSection(const json::Value &CurrentCtx, raw_ostream &OS);
182-
void renderInvertSection(const json::Value &CurrentCtx, raw_ostream &OS);
203+
void renderRoot(const json::Value &CurrentCtx, MustacheOutputStream &OS);
204+
void renderText(MustacheOutputStream &OS);
205+
void renderPartial(const json::Value &CurrentCtx, MustacheOutputStream &OS);
206+
void renderVariable(const json::Value &CurrentCtx, MustacheOutputStream &OS);
207+
void renderUnescapeVariable(const json::Value &CurrentCtx,
208+
MustacheOutputStream &OS);
209+
void renderSection(const json::Value &CurrentCtx, MustacheOutputStream &OS);
210+
void renderInvertSection(const json::Value &CurrentCtx,
211+
MustacheOutputStream &OS);
183212

184213
MustacheContext &Ctx;
185214
Type Ty;
@@ -458,10 +487,9 @@ static SmallVector<Token> tokenize(StringRef Template) {
458487
}
459488

460489
// Custom stream to escape strings.
461-
class EscapeStringStream : public raw_ostream {
490+
class EscapeStringStream : public MustacheOutputStream {
462491
public:
463-
explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
464-
EscapeMap &Escape)
492+
explicit EscapeStringStream(raw_ostream &WrappedStream, EscapeMap &Escape)
465493
: Escape(Escape), WrappedStream(WrappedStream) {
466494
SetUnbuffered();
467495
}
@@ -482,32 +510,38 @@ class EscapeStringStream : public raw_ostream {
482510

483511
private:
484512
EscapeMap &Escape;
485-
llvm::raw_ostream &WrappedStream;
513+
raw_ostream &WrappedStream;
486514
};
487515

488516
// Custom stream to add indentation used to for rendering partials.
489-
class AddIndentationStringStream : public raw_ostream {
517+
class AddIndentationStringStream : public MustacheOutputStream {
490518
public:
491-
explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
519+
explicit AddIndentationStringStream(raw_ostream &WrappedStream,
492520
size_t Indentation)
493521
: Indentation(Indentation), WrappedStream(WrappedStream),
494-
NeedsIndent(true) {
522+
NeedsIndent(true), IsSuspended(false) {
495523
SetUnbuffered();
496524
}
497525

526+
void suspendIndentation() override { IsSuspended = true; }
527+
void resumeIndentation() override { IsSuspended = false; }
528+
498529
protected:
499530
void write_impl(const char *Ptr, size_t Size) override {
500531
llvm::StringRef Data(Ptr, Size);
501532
SmallString<0> Indent;
502533
Indent.resize(Indentation, ' ');
503534

504535
for (char C : Data) {
536+
LLVM_DEBUG(dbgs() << "IndentationStream: NeedsIndent=" << NeedsIndent
537+
<< ", C='" << C << "', Indentation=" << Indentation
538+
<< "\n");
505539
if (NeedsIndent && C != '\n') {
506540
WrappedStream << Indent;
507541
NeedsIndent = false;
508542
}
509543
WrappedStream << C;
510-
if (C == '\n')
544+
if (C == '\n' && !IsSuspended)
511545
NeedsIndent = true;
512546
}
513547
}
@@ -516,8 +550,9 @@ class AddIndentationStringStream : public raw_ostream {
516550

517551
private:
518552
size_t Indentation;
519-
llvm::raw_ostream &WrappedStream;
553+
raw_ostream &WrappedStream;
520554
bool NeedsIndent;
555+
bool IsSuspended;
521556
};
522557

523558
class Parser {
@@ -607,6 +642,7 @@ void Parser::parseMustache(ASTNode *Parent) {
607642
}
608643
}
609644
static void toMustacheString(const json::Value &Data, raw_ostream &OS) {
645+
LLVM_DEBUG(dbgs() << "toMustacheString: kind=" << (int)Data.kind() << "\n");
610646
switch (Data.kind()) {
611647
case json::Value::Null:
612648
return;
@@ -619,6 +655,7 @@ static void toMustacheString(const json::Value &Data, raw_ostream &OS) {
619655
}
620656
case json::Value::String: {
621657
auto Str = *Data.getAsString();
658+
LLVM_DEBUG(dbgs() << " --> writing string: \"" << Str << "\"\n");
622659
OS << Str.str();
623660
return;
624661
}
@@ -638,19 +675,24 @@ static void toMustacheString(const json::Value &Data, raw_ostream &OS) {
638675
}
639676
}
640677

641-
void ASTNode::renderRoot(const json::Value &CurrentCtx, raw_ostream &OS) {
678+
void ASTNode::renderRoot(const json::Value &CurrentCtx,
679+
MustacheOutputStream &OS) {
642680
renderChild(CurrentCtx, OS);
643681
}
644682

645-
void ASTNode::renderText(raw_ostream &OS) { OS << Body; }
683+
void ASTNode::renderText(MustacheOutputStream &OS) { OS << Body; }
646684

647-
void ASTNode::renderPartial(const json::Value &CurrentCtx, raw_ostream &OS) {
685+
void ASTNode::renderPartial(const json::Value &CurrentCtx,
686+
MustacheOutputStream &OS) {
687+
LLVM_DEBUG(dbgs() << "renderPartial: Accessor=" << AccessorValue[0]
688+
<< ", Indentation=" << Indentation << "\n");
648689
auto Partial = Ctx.Partials.find(AccessorValue[0]);
649690
if (Partial != Ctx.Partials.end())
650691
renderPartial(CurrentCtx, OS, Partial->getValue().get());
651692
}
652693

653-
void ASTNode::renderVariable(const json::Value &CurrentCtx, raw_ostream &OS) {
694+
void ASTNode::renderVariable(const json::Value &CurrentCtx,
695+
MustacheOutputStream &OS) {
654696
auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
655697
if (Lambda != Ctx.Lambdas.end()) {
656698
renderLambdas(CurrentCtx, OS, Lambda->getValue());
@@ -661,16 +703,22 @@ void ASTNode::renderVariable(const json::Value &CurrentCtx, raw_ostream &OS) {
661703
}
662704

663705
void ASTNode::renderUnescapeVariable(const json::Value &CurrentCtx,
664-
raw_ostream &OS) {
706+
MustacheOutputStream &OS) {
707+
LLVM_DEBUG(dbgs() << "renderUnescapeVariable: Accessor=" << AccessorValue[0]
708+
<< "\n");
665709
auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
666710
if (Lambda != Ctx.Lambdas.end()) {
667711
renderLambdas(CurrentCtx, OS, Lambda->getValue());
668712
} else if (const json::Value *ContextPtr = findContext()) {
713+
LLVM_DEBUG(dbgs() << " --> Found context value, writing to stream.\n");
714+
OS.suspendIndentation();
669715
toMustacheString(*ContextPtr, OS);
716+
OS.resumeIndentation();
670717
}
671718
}
672719

673-
void ASTNode::renderSection(const json::Value &CurrentCtx, raw_ostream &OS) {
720+
void ASTNode::renderSection(const json::Value &CurrentCtx,
721+
MustacheOutputStream &OS) {
674722
auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
675723
if (SectionLambda != Ctx.SectionLambdas.end()) {
676724
renderSectionLambdas(CurrentCtx, OS, SectionLambda->getValue());
@@ -690,48 +738,50 @@ void ASTNode::renderSection(const json::Value &CurrentCtx, raw_ostream &OS) {
690738
}
691739

692740
void ASTNode::renderInvertSection(const json::Value &CurrentCtx,
693-
raw_ostream &OS) {
741+
MustacheOutputStream &OS) {
694742
bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
695743
const json::Value *ContextPtr = findContext();
696744
if (isContextFalsey(ContextPtr) && !IsLambda) {
697745
renderChild(CurrentCtx, OS);
698746
}
699747
}
700748

701-
void ASTNode::render(const json::Value &CurrentCtx, raw_ostream &OS) {
749+
void ASTNode::render(const llvm::json::Value &Data, MustacheOutputStream &OS) {
702750
if (Ty != Root && Ty != Text && AccessorValue.empty())
703751
return;
704752
// Set the parent context to the incoming context so that we
705753
// can walk up the context tree correctly in findContext().
706-
ParentContext = &CurrentCtx;
754+
ParentContext = &Data;
707755

708756
switch (Ty) {
709757
case Root:
710-
renderRoot(CurrentCtx, OS);
758+
renderRoot(Data, OS);
711759
return;
712760
case Text:
713761
renderText(OS);
714762
return;
715763
case Partial:
716-
renderPartial(CurrentCtx, OS);
764+
renderPartial(Data, OS);
717765
return;
718766
case Variable:
719-
renderVariable(CurrentCtx, OS);
767+
renderVariable(Data, OS);
720768
return;
721769
case UnescapeVariable:
722-
renderUnescapeVariable(CurrentCtx, OS);
770+
renderUnescapeVariable(Data, OS);
723771
return;
724772
case Section:
725-
renderSection(CurrentCtx, OS);
773+
renderSection(Data, OS);
726774
return;
727775
case InvertSection:
728-
renderInvertSection(CurrentCtx, OS);
776+
renderInvertSection(Data, OS);
729777
return;
730778
}
731779
llvm_unreachable("Invalid ASTNode type");
732780
}
733781

734782
const json::Value *ASTNode::findContext() {
783+
LLVM_DEBUG(dbgs() << "findContext: AccessorValue[0]=" << AccessorValue[0]
784+
<< "\n");
735785
// The mustache spec allows for dot notation to access nested values
736786
// a single dot refers to the current context.
737787
// We attempt to find the JSON context in the current node, if it is not
@@ -746,12 +796,22 @@ const json::Value *ASTNode::findContext() {
746796
StringRef CurrentAccessor = AccessorValue[0];
747797
ASTNode *CurrentParent = Parent;
748798

799+
LLVM_DEBUG(dbgs() << "findContext: ParentContext: ";
800+
if (ParentContext) ParentContext->print(dbgs());
801+
else dbgs() << "nullptr"; dbgs() << "\n");
802+
749803
while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
804+
LLVM_DEBUG(dbgs() << "findContext: climbing parent\n");
750805
if (CurrentParent->Ty != Root) {
751806
CurrentContext = CurrentParent->ParentContext->getAsObject();
752807
CurrentParent = CurrentParent->Parent;
808+
LLVM_DEBUG(dbgs() << "findContext: new ParentContext: ";
809+
if (CurrentParent->ParentContext)
810+
CurrentParent->ParentContext->print(dbgs());
811+
else dbgs() << "nullptr"; dbgs() << "\n");
753812
continue;
754813
}
814+
LLVM_DEBUG(dbgs() << "findContext: reached root, not found\n");
755815
return nullptr;
756816
}
757817
const json::Value *Context = nullptr;
@@ -767,22 +827,28 @@ const json::Value *ASTNode::findContext() {
767827
Context = CurrentValue;
768828
}
769829
}
830+
LLVM_DEBUG(dbgs() << "findContext: found value: ";
831+
if (Context) Context->print(dbgs()); else dbgs() << "nullptr";
832+
dbgs() << "\n");
770833
return Context;
771834
}
772835

773-
void ASTNode::renderChild(const json::Value &Contexts, llvm::raw_ostream &OS) {
836+
void ASTNode::renderChild(const json::Value &Contexts,
837+
MustacheOutputStream &OS) {
774838
for (AstPtr &Child : Children)
775839
Child->render(Contexts, OS);
776840
}
777841

778-
void ASTNode::renderPartial(const json::Value &Contexts, llvm::raw_ostream &OS,
779-
ASTNode *Partial) {
842+
void ASTNode::renderPartial(const json::Value &Contexts,
843+
MustacheOutputStream &OS, ASTNode *Partial) {
844+
LLVM_DEBUG(dbgs() << "renderPartial (helper): Indentation=" << Indentation
845+
<< "\n");
780846
AddIndentationStringStream IS(OS, Indentation);
781847
Partial->render(Contexts, IS);
782848
}
783849

784-
void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
785-
Lambda &L) {
850+
void ASTNode::renderLambdas(const json::Value &Contexts,
851+
MustacheOutputStream &OS, Lambda &L) {
786852
json::Value LambdaResult = L();
787853
std::string LambdaStr;
788854
raw_string_ostream Output(LambdaStr);
@@ -799,7 +865,7 @@ void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
799865
}
800866

801867
void ASTNode::renderSectionLambdas(const json::Value &Contexts,
802-
llvm::raw_ostream &OS, SectionLambda &L) {
868+
MustacheOutputStream &OS, SectionLambda &L) {
803869
json::Value Return = L(RawBody);
804870
if (isFalsey(Return))
805871
return;
@@ -812,7 +878,8 @@ void ASTNode::renderSectionLambdas(const json::Value &Contexts,
812878
}
813879

814880
void Template::render(const json::Value &Data, llvm::raw_ostream &OS) {
815-
Tree->render(Data, OS);
881+
RawMustacheOutputStream MOS(OS);
882+
Tree->render(Data, MOS);
816883
}
817884

818885
void Template::registerPartial(std::string Name, std::string Partial) {

0 commit comments

Comments
 (0)