Skip to content

Commit a5faef5

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 cbc8872 commit a5faef5

File tree

3 files changed

+263
-206
lines changed

3 files changed

+263
-206
lines changed

llvm/lib/Support/Mustache.cpp

Lines changed: 106 additions & 41 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,29 @@ 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,
190+
void renderLambdas(const llvm::json::Value &Contexts, MustacheOutputStream &OS,
164191
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,
196+
void renderPartial(const llvm::json::Value &Contexts, MustacheOutputStream &OS,
170197
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, MustacheOutputStream &OS);
208+
void renderSection(const json::Value &CurrentCtx, MustacheOutputStream &OS);
209+
void renderInvertSection(const json::Value &CurrentCtx, MustacheOutputStream &OS);
183210

184211
MustacheContext &Ctx;
185212
Type Ty;
@@ -393,7 +420,7 @@ static SmallVector<Token> tokenize(StringRef Template) {
393420
if (T.TagKind == Tag::Kind::None) {
394421
// No more tags, the rest is text.
395422
Tokens.emplace_back(Template.substr(Start).str());
396-
LLVM_DEBUG(dbgs() << " No more tags. Created final Text token: \""
423+
LLVM_DEBUG(dbgs() << " No more tags. Created final Text token: \""
397424
<< Template.substr(Start) << "\"\n");
398425
break;
399426
}
@@ -458,9 +485,9 @@ static SmallVector<Token> tokenize(StringRef Template) {
458485
}
459486

460487
// Custom stream to escape strings.
461-
class EscapeStringStream : public raw_ostream {
488+
class EscapeStringStream : public MustacheOutputStream {
462489
public:
463-
explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
490+
explicit EscapeStringStream(raw_ostream &WrappedStream,
464491
EscapeMap &Escape)
465492
: Escape(Escape), WrappedStream(WrappedStream) {
466493
SetUnbuffered();
@@ -482,32 +509,38 @@ class EscapeStringStream : public raw_ostream {
482509

483510
private:
484511
EscapeMap &Escape;
485-
llvm::raw_ostream &WrappedStream;
512+
raw_ostream &WrappedStream;
486513
};
487514

488515
// Custom stream to add indentation used to for rendering partials.
489-
class AddIndentationStringStream : public raw_ostream {
516+
class AddIndentationStringStream : public MustacheOutputStream {
490517
public:
491-
explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
518+
explicit AddIndentationStringStream(raw_ostream &WrappedStream,
492519
size_t Indentation)
493520
: Indentation(Indentation), WrappedStream(WrappedStream),
494-
NeedsIndent(true) {
521+
NeedsIndent(true), IsSuspended(false) {
495522
SetUnbuffered();
496523
}
497524

525+
void suspendIndentation() override { IsSuspended = true; }
526+
void resumeIndentation() override { IsSuspended = false; }
527+
498528
protected:
499529
void write_impl(const char *Ptr, size_t Size) override {
500530
llvm::StringRef Data(Ptr, Size);
501531
SmallString<0> Indent;
502532
Indent.resize(Indentation, ' ');
503533

504534
for (char C : Data) {
535+
LLVM_DEBUG(dbgs() << "IndentationStream: NeedsIndent=" << NeedsIndent
536+
<< ", C='" << C << "', Indentation=" << Indentation
537+
<< "\n");
505538
if (NeedsIndent && C != '\n') {
506539
WrappedStream << Indent;
507540
NeedsIndent = false;
508541
}
509542
WrappedStream << C;
510-
if (C == '\n')
543+
if (C == '\n' && !IsSuspended)
511544
NeedsIndent = true;
512545
}
513546
}
@@ -516,8 +549,9 @@ class AddIndentationStringStream : public raw_ostream {
516549

517550
private:
518551
size_t Indentation;
519-
llvm::raw_ostream &WrappedStream;
552+
raw_ostream &WrappedStream;
520553
bool NeedsIndent;
554+
bool IsSuspended;
521555
};
522556

523557
class Parser {
@@ -607,6 +641,7 @@ void Parser::parseMustache(ASTNode *Parent) {
607641
}
608642
}
609643
static void toMustacheString(const json::Value &Data, raw_ostream &OS) {
644+
LLVM_DEBUG(dbgs() << "toMustacheString: kind=" << (int)Data.kind() << "\n");
610645
switch (Data.kind()) {
611646
case json::Value::Null:
612647
return;
@@ -619,6 +654,7 @@ static void toMustacheString(const json::Value &Data, raw_ostream &OS) {
619654
}
620655
case json::Value::String: {
621656
auto Str = *Data.getAsString();
657+
LLVM_DEBUG(dbgs() << " --> writing string: \"" << Str << "\"\n");
622658
OS << Str.str();
623659
return;
624660
}
@@ -638,19 +674,21 @@ static void toMustacheString(const json::Value &Data, raw_ostream &OS) {
638674
}
639675
}
640676

641-
void ASTNode::renderRoot(const json::Value &CurrentCtx, raw_ostream &OS) {
677+
void ASTNode::renderRoot(const json::Value &CurrentCtx, MustacheOutputStream &OS) {
642678
renderChild(CurrentCtx, OS);
643679
}
644680

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

647-
void ASTNode::renderPartial(const json::Value &CurrentCtx, raw_ostream &OS) {
683+
void ASTNode::renderPartial(const json::Value &CurrentCtx, MustacheOutputStream &OS) {
684+
LLVM_DEBUG(dbgs() << "renderPartial: Accessor=" << AccessorValue[0]
685+
<< ", Indentation=" << Indentation << "\n");
648686
auto Partial = Ctx.Partials.find(AccessorValue[0]);
649687
if (Partial != Ctx.Partials.end())
650688
renderPartial(CurrentCtx, OS, Partial->getValue().get());
651689
}
652690

653-
void ASTNode::renderVariable(const json::Value &CurrentCtx, raw_ostream &OS) {
691+
void ASTNode::renderVariable(const json::Value &CurrentCtx, MustacheOutputStream &OS) {
654692
auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
655693
if (Lambda != Ctx.Lambdas.end()) {
656694
renderLambdas(CurrentCtx, OS, Lambda->getValue());
@@ -661,16 +699,22 @@ void ASTNode::renderVariable(const json::Value &CurrentCtx, raw_ostream &OS) {
661699
}
662700

663701
void ASTNode::renderUnescapeVariable(const json::Value &CurrentCtx,
664-
raw_ostream &OS) {
702+
MustacheOutputStream &OS) {
703+
LLVM_DEBUG(dbgs() << "renderUnescapeVariable: Accessor=" << AccessorValue[0]
704+
<< "\n");
665705
auto Lambda = Ctx.Lambdas.find(AccessorValue[0]);
666706
if (Lambda != Ctx.Lambdas.end()) {
667707
renderLambdas(CurrentCtx, OS, Lambda->getValue());
668708
} else if (const json::Value *ContextPtr = findContext()) {
709+
LLVM_DEBUG(dbgs() << " --> Found context value, writing to stream.\n");
710+
OS.suspendIndentation();
669711
toMustacheString(*ContextPtr, OS);
712+
OS.resumeIndentation();
670713
}
671714
}
672715

673-
void ASTNode::renderSection(const json::Value &CurrentCtx, raw_ostream &OS) {
716+
void ASTNode::renderSection(const json::Value &CurrentCtx,
717+
MustacheOutputStream &OS) {
674718
auto SectionLambda = Ctx.SectionLambdas.find(AccessorValue[0]);
675719
if (SectionLambda != Ctx.SectionLambdas.end()) {
676720
renderSectionLambdas(CurrentCtx, OS, SectionLambda->getValue());
@@ -690,48 +734,50 @@ void ASTNode::renderSection(const json::Value &CurrentCtx, raw_ostream &OS) {
690734
}
691735

692736
void ASTNode::renderInvertSection(const json::Value &CurrentCtx,
693-
raw_ostream &OS) {
737+
MustacheOutputStream &OS) {
694738
bool IsLambda = Ctx.SectionLambdas.contains(AccessorValue[0]);
695739
const json::Value *ContextPtr = findContext();
696740
if (isContextFalsey(ContextPtr) && !IsLambda) {
697741
renderChild(CurrentCtx, OS);
698742
}
699743
}
700744

701-
void ASTNode::render(const json::Value &CurrentCtx, raw_ostream &OS) {
745+
void ASTNode::render(const llvm::json::Value &Data, MustacheOutputStream &OS) {
702746
if (Ty != Root && Ty != Text && AccessorValue.empty())
703747
return;
704748
// Set the parent context to the incoming context so that we
705749
// can walk up the context tree correctly in findContext().
706-
ParentContext = &CurrentCtx;
750+
ParentContext = &Data;
707751

708752
switch (Ty) {
709753
case Root:
710-
renderRoot(CurrentCtx, OS);
754+
renderRoot(Data, OS);
711755
return;
712756
case Text:
713757
renderText(OS);
714758
return;
715759
case Partial:
716-
renderPartial(CurrentCtx, OS);
760+
renderPartial(Data, OS);
717761
return;
718762
case Variable:
719-
renderVariable(CurrentCtx, OS);
763+
renderVariable(Data, OS);
720764
return;
721765
case UnescapeVariable:
722-
renderUnescapeVariable(CurrentCtx, OS);
766+
renderUnescapeVariable(Data, OS);
723767
return;
724768
case Section:
725-
renderSection(CurrentCtx, OS);
769+
renderSection(Data, OS);
726770
return;
727771
case InvertSection:
728-
renderInvertSection(CurrentCtx, OS);
772+
renderInvertSection(Data, OS);
729773
return;
730774
}
731775
llvm_unreachable("Invalid ASTNode type");
732776
}
733777

734778
const json::Value *ASTNode::findContext() {
779+
LLVM_DEBUG(dbgs() << "findContext: AccessorValue[0]=" << AccessorValue[0]
780+
<< "\n");
735781
// The mustache spec allows for dot notation to access nested values
736782
// a single dot refers to the current context.
737783
// We attempt to find the JSON context in the current node, if it is not
@@ -746,12 +792,24 @@ const json::Value *ASTNode::findContext() {
746792
StringRef CurrentAccessor = AccessorValue[0];
747793
ASTNode *CurrentParent = Parent;
748794

795+
LLVM_DEBUG(dbgs() << "findContext: ParentContext: ";
796+
if (ParentContext) ParentContext->print(dbgs());
797+
else dbgs() << "nullptr";
798+
dbgs() << "\n");
799+
749800
while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
801+
LLVM_DEBUG(dbgs() << "findContext: climbing parent\n");
750802
if (CurrentParent->Ty != Root) {
751803
CurrentContext = CurrentParent->ParentContext->getAsObject();
752804
CurrentParent = CurrentParent->Parent;
805+
LLVM_DEBUG(dbgs() << "findContext: new ParentContext: ";
806+
if (CurrentParent->ParentContext)
807+
CurrentParent->ParentContext->print(dbgs());
808+
else dbgs() << "nullptr";
809+
dbgs() << "\n");
753810
continue;
754811
}
812+
LLVM_DEBUG(dbgs() << "findContext: reached root, not found\n");
755813
return nullptr;
756814
}
757815
const json::Value *Context = nullptr;
@@ -767,21 +825,27 @@ const json::Value *ASTNode::findContext() {
767825
Context = CurrentValue;
768826
}
769827
}
828+
LLVM_DEBUG(dbgs() << "findContext: found value: ";
829+
if (Context) Context->print(dbgs());
830+
else dbgs() << "nullptr";
831+
dbgs() << "\n");
770832
return Context;
771833
}
772834

773-
void ASTNode::renderChild(const json::Value &Contexts, llvm::raw_ostream &OS) {
835+
void ASTNode::renderChild(const json::Value &Contexts, MustacheOutputStream &OS) {
774836
for (AstPtr &Child : Children)
775837
Child->render(Contexts, OS);
776838
}
777839

778-
void ASTNode::renderPartial(const json::Value &Contexts, llvm::raw_ostream &OS,
840+
void ASTNode::renderPartial(const json::Value &Contexts, MustacheOutputStream &OS,
779841
ASTNode *Partial) {
842+
LLVM_DEBUG(dbgs() << "renderPartial (helper): Indentation=" << Indentation
843+
<< "\n");
780844
AddIndentationStringStream IS(OS, Indentation);
781845
Partial->render(Contexts, IS);
782846
}
783847

784-
void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
848+
void ASTNode::renderLambdas(const json::Value &Contexts, MustacheOutputStream &OS,
785849
Lambda &L) {
786850
json::Value LambdaResult = L();
787851
std::string LambdaStr;
@@ -799,7 +863,7 @@ void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
799863
}
800864

801865
void ASTNode::renderSectionLambdas(const json::Value &Contexts,
802-
llvm::raw_ostream &OS, SectionLambda &L) {
866+
MustacheOutputStream &OS, SectionLambda &L) {
803867
json::Value Return = L(RawBody);
804868
if (isFalsey(Return))
805869
return;
@@ -812,7 +876,8 @@ void ASTNode::renderSectionLambdas(const json::Value &Contexts,
812876
}
813877

814878
void Template::render(const json::Value &Data, llvm::raw_ostream &OS) {
815-
Tree->render(Data, OS);
879+
RawMustacheOutputStream MOS(OS);
880+
Tree->render(Data, MOS);
816881
}
817882

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

0 commit comments

Comments
 (0)