aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/Mustache.cpp
blob: 6c2ed6c84c6cf8e1c9045302556c5b9179e2d2a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
//===-- Mustache.cpp ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Mustache.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"
#include <sstream>

using namespace llvm;
using namespace llvm::mustache;

namespace {

using Accessor = SmallVector<std::string>;

static bool isFalsey(const json::Value &V) {
  return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
         (V.getAsArray() && V.getAsArray()->empty());
}

static bool isContextFalsey(const json::Value *V) {
  // A missing context (represented by a nullptr) is defined as falsey.
  if (!V)
    return true;
  return isFalsey(*V);
}

static Accessor splitMustacheString(StringRef Str) {
  // We split the mustache string into an accessor.
  // For example:
  //    "a.b.c" would be split into {"a", "b", "c"}
  // We make an exception for a single dot which
  // refers to the current context.
  Accessor Tokens;
  if (Str == ".") {
    Tokens.emplace_back(Str);
    return Tokens;
  }
  while (!Str.empty()) {
    StringRef Part;
    std::tie(Part, Str) = Str.split(".");
    Tokens.emplace_back(Part.trim());
  }
  return Tokens;
}
} // namespace

namespace llvm::mustache {

class Token {
public:
  enum class Type {
    Text,
    Variable,
    Partial,
    SectionOpen,
    SectionClose,
    InvertSectionOpen,
    UnescapeVariable,
    Comment,
  };

  Token(std::string Str)
      : TokenType(Type::Text), RawBody(std::move(Str)), TokenBody(RawBody),
        AccessorValue({}), Indentation(0) {};

  Token(std::string RawBody, std::string TokenBody, char Identifier)
      : RawBody(std::move(RawBody)), TokenBody(std::move(TokenBody)),
        Indentation(0) {
    TokenType = getTokenType(Identifier);
    if (TokenType == Type::Comment)
      return;
    StringRef AccessorStr(this->TokenBody);
    if (TokenType != Type::Variable)
      AccessorStr = AccessorStr.substr(1);
    AccessorValue = splitMustacheString(StringRef(AccessorStr).trim());
  }

  Accessor getAccessor() const { return AccessorValue; }

  Type getType() const { return TokenType; }

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

  size_t getIndentation() const { return Indentation; }

  static Type getTokenType(char Identifier) {
    switch (Identifier) {
    case '#':
      return Type::SectionOpen;
    case '/':
      return Type::SectionClose;
    case '^':
      return Type::InvertSectionOpen;
    case '!':
      return Type::Comment;
    case '>':
      return Type::Partial;
    case '&':
      return Type::UnescapeVariable;
    default:
      return Type::Variable;
    }
  }

  Type TokenType;
  // RawBody is the original string that was tokenized.
  std::string RawBody;
  // TokenBody is the original string with the identifier removed.
  std::string TokenBody;
  Accessor AccessorValue;
  size_t Indentation;
};

using EscapeMap = DenseMap<char, std::string>;

class ASTNode {
public:
  enum Type {
    Root,
    Text,
    Partial,
    Variable,
    UnescapeVariable,
    Section,
    InvertSection,
  };

  ASTNode(llvm::StringMap<AstPtr> &Partials, llvm::StringMap<Lambda> &Lambdas,
          llvm::StringMap<SectionLambda> &SectionLambdas, EscapeMap &Escapes)
      : Partials(Partials), Lambdas(Lambdas), SectionLambdas(SectionLambdas),
        Escapes(Escapes), Ty(Type::Root), Parent(nullptr),
        ParentContext(nullptr) {}

  ASTNode(std::string Body, ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
          llvm::StringMap<Lambda> &Lambdas,
          llvm::StringMap<SectionLambda> &SectionLambdas, EscapeMap &Escapes)
      : Partials(Partials), Lambdas(Lambdas), SectionLambdas(SectionLambdas),
        Escapes(Escapes), Ty(Type::Text), Body(std::move(Body)), Parent(Parent),
        ParentContext(nullptr) {}

  // Constructor for Section/InvertSection/Variable/UnescapeVariable Nodes
  ASTNode(Type Ty, Accessor Accessor, ASTNode *Parent,
          llvm::StringMap<AstPtr> &Partials, llvm::StringMap<Lambda> &Lambdas,
          llvm::StringMap<SectionLambda> &SectionLambdas, EscapeMap &Escapes)
      : Partials(Partials), Lambdas(Lambdas), SectionLambdas(SectionLambdas),
        Escapes(Escapes), Ty(Ty), Parent(Parent),
        AccessorValue(std::move(Accessor)), ParentContext(nullptr) {}

  void addChild(AstPtr Child) { Children.emplace_back(std::move(Child)); };

  void setRawBody(std::string NewBody) { RawBody = std::move(NewBody); };

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

  void render(const llvm::json::Value &Data, llvm::raw_ostream &OS);

private:
  void renderLambdas(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
                     Lambda &L);

  void renderSectionLambdas(const llvm::json::Value &Contexts,
                            llvm::raw_ostream &OS, SectionLambda &L);

  void renderPartial(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
                     ASTNode *Partial);

  void renderChild(const llvm::json::Value &Context, llvm::raw_ostream &OS);

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

  StringMap<AstPtr> &Partials;
  StringMap<Lambda> &Lambdas;
  StringMap<SectionLambda> &SectionLambdas;
  EscapeMap &Escapes;
  Type Ty;
  size_t Indentation = 0;
  std::string RawBody;
  std::string Body;
  ASTNode *Parent;
  // TODO: switch implementation to SmallVector<T>
  std::vector<AstPtr> Children;
  const Accessor AccessorValue;
  const llvm::json::Value *ParentContext;
};

// A wrapper for arena allocator for ASTNodes
AstPtr createRootNode(llvm::StringMap<AstPtr> &Partials,
                      llvm::StringMap<Lambda> &Lambdas,
                      llvm::StringMap<SectionLambda> &SectionLambdas,
                      EscapeMap &Escapes) {
  return std::make_unique<ASTNode>(Partials, Lambdas, SectionLambdas, Escapes);
}

AstPtr createNode(ASTNode::Type T, Accessor A, ASTNode *Parent,
                  llvm::StringMap<AstPtr> &Partials,
                  llvm::StringMap<Lambda> &Lambdas,
                  llvm::StringMap<SectionLambda> &SectionLambdas,
                  EscapeMap &Escapes) {
  return std::make_unique<ASTNode>(T, std::move(A), Parent, Partials, Lambdas,
                                   SectionLambdas, Escapes);
}

AstPtr createTextNode(std::string Body, ASTNode *Parent,
                      llvm::StringMap<AstPtr> &Partials,
                      llvm::StringMap<Lambda> &Lambdas,
                      llvm::StringMap<SectionLambda> &SectionLambdas,
                      EscapeMap &Escapes) {
  return std::make_unique<ASTNode>(std::move(Body), Parent, Partials, Lambdas,
                                   SectionLambdas, Escapes);
}

// Function to check if there is meaningful text behind.
// We determine if a token has meaningful text behind
// if the right of previous token contains anything that is
// not a newline.
// For example:
//  "Stuff {{#Section}}" (returns true)
//   vs
//  "{{#Section}} \n" (returns false)
// We make an exception for when previous token is empty
// and the current token is the second token.
// For example:
//  "{{#Section}}"
bool hasTextBehind(size_t Idx, const ArrayRef<Token> &Tokens) {
  if (Idx == 0)
    return true;

  size_t PrevIdx = Idx - 1;
  if (Tokens[PrevIdx].getType() != Token::Type::Text)
    return true;

  const Token &PrevToken = Tokens[PrevIdx];
  StringRef TokenBody = StringRef(PrevToken.RawBody).rtrim(" \r\t\v");
  return !TokenBody.ends_with("\n") && !(TokenBody.empty() && Idx == 1);
}

// Function to check if there's no meaningful text ahead.
// We determine if a token has text ahead if the left of previous
// token does not start with a newline.
bool hasTextAhead(size_t Idx, const ArrayRef<Token> &Tokens) {
  if (Idx >= Tokens.size() - 1)
    return true;

  size_t NextIdx = Idx + 1;
  if (Tokens[NextIdx].getType() != Token::Type::Text)
    return true;

  const Token &NextToken = Tokens[NextIdx];
  StringRef TokenBody = StringRef(NextToken.RawBody).ltrim(" ");
  return !TokenBody.starts_with("\r\n") && !TokenBody.starts_with("\n");
}

bool requiresCleanUp(Token::Type T) {
  // We must clean up all the tokens that could contain child nodes.
  return T == Token::Type::SectionOpen || T == Token::Type::InvertSectionOpen ||
         T == Token::Type::SectionClose || T == Token::Type::Comment ||
         T == Token::Type::Partial;
}

// Adjust next token body if there is no text ahead.
// For example:
// The template string
//  "{{! Comment }} \nLine 2"
// would be considered as no text ahead and should be rendered as
//  " Line 2"
void stripTokenAhead(SmallVectorImpl<Token> &Tokens, size_t Idx) {
  Token &NextToken = Tokens[Idx + 1];
  StringRef NextTokenBody = NextToken.TokenBody;
  // Cut off the leading newline which could be \n or \r\n.
  if (NextTokenBody.starts_with("\r\n"))
    NextToken.TokenBody = NextTokenBody.substr(2).str();
  else if (NextTokenBody.starts_with("\n"))
    NextToken.TokenBody = NextTokenBody.substr(1).str();
}

// Adjust previous token body if there no text behind.
// For example:
//  The template string
//  " \t{{#section}}A{{/section}}"
// would be considered as having no text ahead and would be render as
//  "A"
// The exception for this is partial tag which requires us to
// keep track of the indentation once it's rendered.
void stripTokenBefore(SmallVectorImpl<Token> &Tokens, size_t Idx,
                      Token &CurrentToken, Token::Type CurrentType) {
  Token &PrevToken = Tokens[Idx - 1];
  StringRef PrevTokenBody = PrevToken.TokenBody;
  StringRef Unindented = PrevTokenBody.rtrim(" \r\t\v");
  size_t Indentation = PrevTokenBody.size() - Unindented.size();
  if (CurrentType != Token::Type::Partial)
    PrevToken.TokenBody = Unindented.str();
  CurrentToken.setIndentation(Indentation);
}

// Simple tokenizer that splits the template into tokens.
// The mustache spec allows {{{ }}} to unescape variables,
// but we don't support that here. An unescape variable
// is represented only by {{& variable}}.
SmallVector<Token> tokenize(StringRef Template) {
  SmallVector<Token> Tokens;
  StringLiteral Open("{{");
  StringLiteral Close("}}");
  size_t Start = 0;
  size_t DelimiterStart = Template.find(Open);
  if (DelimiterStart == StringRef::npos) {
    Tokens.emplace_back(Template.str());
    return Tokens;
  }
  while (DelimiterStart != StringRef::npos) {
    if (DelimiterStart != Start)
      Tokens.emplace_back(Template.substr(Start, DelimiterStart - Start).str());
    size_t DelimiterEnd = Template.find(Close, DelimiterStart);
    if (DelimiterEnd == StringRef::npos)
      break;

    // Extract the Interpolated variable without delimiters.
    size_t InterpolatedStart = DelimiterStart + Open.size();
    size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
    std::string Interpolated =
        Template.substr(InterpolatedStart, InterpolatedEnd).str();
    std::string RawBody = Open.str() + Interpolated + Close.str();
    Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
    Start = DelimiterEnd + Close.size();
    DelimiterStart = Template.find(Open, Start);
  }

  if (Start < Template.size())
    Tokens.emplace_back(Template.substr(Start).str());

  // Fix up white spaces for:
  //   - open sections
  //   - inverted sections
  //   - close sections
  //   - comments
  //
  // This loop attempts to find standalone tokens and tries to trim out
  // the surrounding whitespace.
  // For example:
  // if you have the template string
  //  {{#section}} \n Example \n{{/section}}
  // The output should would be
  // For example:
  //  \n Example \n
  size_t LastIdx = Tokens.size() - 1;
  for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
    Token &CurrentToken = Tokens[Idx];
    Token::Type CurrentType = CurrentToken.getType();
    // Check if token type requires cleanup.
    bool RequiresCleanUp = requiresCleanUp(CurrentType);

    if (!RequiresCleanUp)
      continue;

    // We adjust the token body if there's no text behind or ahead.
    // A token is considered to have no text ahead if the right of the previous
    // token is a newline followed by spaces.
    // A token is considered to have no text behind if the left of the next
    // token is spaces followed by a newline.
    // eg.
    //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
    bool HasTextBehind = hasTextBehind(Idx, Tokens);
    bool HasTextAhead = hasTextAhead(Idx, Tokens);

    if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
      stripTokenAhead(Tokens, Idx);

    if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
      stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
  }
  return Tokens;
}

// Custom stream to escape strings.
class EscapeStringStream : public raw_ostream {
public:
  explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
                              EscapeMap &Escape)
      : Escape(Escape), WrappedStream(WrappedStream) {
    SetUnbuffered();
  }

protected:
  void write_impl(const char *Ptr, size_t Size) override {
    llvm::StringRef Data(Ptr, Size);
    for (char C : Data) {
      auto It = Escape.find(C);
      if (It != Escape.end())
        WrappedStream << It->getSecond();
      else
        WrappedStream << C;
    }
  }

  uint64_t current_pos() const override { return WrappedStream.tell(); }

private:
  EscapeMap &Escape;
  llvm::raw_ostream &WrappedStream;
};

// Custom stream to add indentation used to for rendering partials.
class AddIndentationStringStream : public raw_ostream {
public:
  explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
                                      size_t Indentation)
      : Indentation(Indentation), WrappedStream(WrappedStream) {
    SetUnbuffered();
  }

protected:
  void write_impl(const char *Ptr, size_t Size) override {
    llvm::StringRef Data(Ptr, Size);
    SmallString<0> Indent;
    Indent.resize(Indentation, ' ');
    for (char C : Data) {
      WrappedStream << C;
      if (C == '\n')
        WrappedStream << Indent;
    }
  }

  uint64_t current_pos() const override { return WrappedStream.tell(); }

private:
  size_t Indentation;
  llvm::raw_ostream &WrappedStream;
};

class Parser {
public:
  Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}

  AstPtr parse(llvm::StringMap<AstPtr> &Partials,
               llvm::StringMap<Lambda> &Lambdas,
               llvm::StringMap<SectionLambda> &SectionLambdas,
               EscapeMap &Escapes);

private:
  void parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
                     llvm::StringMap<Lambda> &Lambdas,
                     llvm::StringMap<SectionLambda> &SectionLambdas,
                     EscapeMap &Escapes);

  SmallVector<Token> Tokens;
  size_t CurrentPtr;
  StringRef TemplateStr;
};

AstPtr Parser::parse(llvm::StringMap<AstPtr> &Partials,
                     llvm::StringMap<Lambda> &Lambdas,
                     llvm::StringMap<SectionLambda> &SectionLambdas,
                     EscapeMap &Escapes) {
  Tokens = tokenize(TemplateStr);
  CurrentPtr = 0;
  AstPtr RootNode = createRootNode(Partials, Lambdas, SectionLambdas, Escapes);
  parseMustache(RootNode.get(), Partials, Lambdas, SectionLambdas, Escapes);
  return RootNode;
}

void Parser::parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
                           llvm::StringMap<Lambda> &Lambdas,
                           llvm::StringMap<SectionLambda> &SectionLambdas,
                           EscapeMap &Escapes) {

  while (CurrentPtr < Tokens.size()) {
    Token CurrentToken = Tokens[CurrentPtr];
    CurrentPtr++;
    Accessor A = CurrentToken.getAccessor();
    AstPtr CurrentNode;

    switch (CurrentToken.getType()) {
    case Token::Type::Text: {
      CurrentNode = createTextNode(std::move(CurrentToken.TokenBody), Parent,
                                   Partials, Lambdas, SectionLambdas, Escapes);
      Parent->addChild(std::move(CurrentNode));
      break;
    }
    case Token::Type::Variable: {
      CurrentNode = createNode(ASTNode::Variable, std::move(A), Parent,
                               Partials, Lambdas, SectionLambdas, Escapes);
      Parent->addChild(std::move(CurrentNode));
      break;
    }
    case Token::Type::UnescapeVariable: {
      CurrentNode = createNode(ASTNode::UnescapeVariable, std::move(A), Parent,
                               Partials, Lambdas, SectionLambdas, Escapes);
      Parent->addChild(std::move(CurrentNode));
      break;
    }
    case Token::Type::Partial: {
      CurrentNode = createNode(ASTNode::Partial, std::move(A), Parent, Partials,
                               Lambdas, SectionLambdas, Escapes);
      CurrentNode->setIndentation(CurrentToken.getIndentation());
      Parent->addChild(std::move(CurrentNode));
      break;
    }
    case Token::Type::SectionOpen: {
      CurrentNode = createNode(ASTNode::Section, A, Parent, Partials, Lambdas,
                               SectionLambdas, Escapes);
      size_t Start = CurrentPtr;
      parseMustache(CurrentNode.get(), Partials, Lambdas, SectionLambdas,
                    Escapes);
      const size_t End = CurrentPtr - 1;
      std::string RawBody;
      for (std::size_t I = Start; I < End; I++)
        RawBody += Tokens[I].RawBody;
      CurrentNode->setRawBody(std::move(RawBody));
      Parent->addChild(std::move(CurrentNode));
      break;
    }
    case Token::Type::InvertSectionOpen: {
      CurrentNode = createNode(ASTNode::InvertSection, A, Parent, Partials,
                               Lambdas, SectionLambdas, Escapes);
      size_t Start = CurrentPtr;
      parseMustache(CurrentNode.get(), Partials, Lambdas, SectionLambdas,
                    Escapes);
      const size_t End = CurrentPtr - 1;
      std::string RawBody;
      for (size_t Idx = Start; Idx < End; Idx++)
        RawBody += Tokens[Idx].RawBody;
      CurrentNode->setRawBody(std::move(RawBody));
      Parent->addChild(std::move(CurrentNode));
      break;
    }
    case Token::Type::Comment:
      break;
    case Token::Type::SectionClose:
      return;
    }
  }
}
void toMustacheString(const json::Value &Data, raw_ostream &OS) {
  switch (Data.kind()) {
  case json::Value::Null:
    return;
  case json::Value::Number: {
    auto Num = *Data.getAsNumber();
    std::ostringstream SS;
    SS << Num;
    OS << SS.str();
    return;
  }
  case json::Value::String: {
    auto Str = *Data.getAsString();
    OS << Str.str();
    return;
  }

  case json::Value::Array: {
    auto Arr = *Data.getAsArray();
    if (Arr.empty())
      return;
    [[fallthrough]];
  }
  case json::Value::Object:
  case json::Value::Boolean: {
    llvm::json::OStream JOS(OS, 2);
    JOS.value(Data);
    break;
  }
  }
}

void ASTNode::render(const json::Value &CurrentCtx, raw_ostream &OS) {
  // Set the parent context to the incoming context so that we
  // can walk up the context tree correctly in findContext().
  ParentContext = &CurrentCtx;
  const json::Value *ContextPtr = Ty == Root ? ParentContext : findContext();

  switch (Ty) {
  case Root:
    renderChild(CurrentCtx, OS);
    return;
  case Text:
    OS << Body;
    return;
  case Partial: {
    auto Partial = Partials.find(AccessorValue[0]);
    if (Partial != Partials.end())
      renderPartial(CurrentCtx, OS, Partial->getValue().get());
    return;
  }
  case Variable: {
    auto Lambda = Lambdas.find(AccessorValue[0]);
    if (Lambda != Lambdas.end()) {
      renderLambdas(CurrentCtx, OS, Lambda->getValue());
    } else if (ContextPtr) {
      EscapeStringStream ES(OS, Escapes);
      toMustacheString(*ContextPtr, ES);
    }
    return;
  }
  case UnescapeVariable: {
    auto Lambda = Lambdas.find(AccessorValue[0]);
    if (Lambda != Lambdas.end()) {
      renderLambdas(CurrentCtx, OS, Lambda->getValue());
    } else if (ContextPtr) {
      toMustacheString(*ContextPtr, OS);
    }
    return;
  }
  case Section: {
    auto SectionLambda = SectionLambdas.find(AccessorValue[0]);
    bool IsLambda = SectionLambda != SectionLambdas.end();

    if (IsLambda) {
      renderSectionLambdas(CurrentCtx, OS, SectionLambda->getValue());
      return;
    }

    if (isContextFalsey(ContextPtr))
      return;

    if (const json::Array *Arr = ContextPtr->getAsArray()) {
      for (const json::Value &V : *Arr)
        renderChild(V, OS);
      return;
    }
    renderChild(*ContextPtr, OS);
    return;
  }
  case InvertSection: {
    bool IsLambda = SectionLambdas.contains(AccessorValue[0]);
    if (isContextFalsey(ContextPtr) && !IsLambda) {
      // The context for the children remains unchanged from the parent's, so
      // we pass this node's original incoming context.
      renderChild(CurrentCtx, OS);
    }
    return;
  }
  }
  llvm_unreachable("Invalid ASTNode type");
}

const json::Value *ASTNode::findContext() {
  // The mustache spec allows for dot notation to access nested values
  // a single dot refers to the current context.
  // We attempt to find the JSON context in the current node, if it is not
  // found, then we traverse the parent nodes to find the context until we
  // reach the root node or the context is found.
  if (AccessorValue.empty())
    return nullptr;
  if (AccessorValue[0] == ".")
    return ParentContext;

  const json::Object *CurrentContext = ParentContext->getAsObject();
  StringRef CurrentAccessor = AccessorValue[0];
  ASTNode *CurrentParent = Parent;

  while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
    if (CurrentParent->Ty != Root) {
      CurrentContext = CurrentParent->ParentContext->getAsObject();
      CurrentParent = CurrentParent->Parent;
      continue;
    }
    return nullptr;
  }
  const json::Value *Context = nullptr;
  for (auto [Idx, Acc] : enumerate(AccessorValue)) {
    const json::Value *CurrentValue = CurrentContext->get(Acc);
    if (!CurrentValue)
      return nullptr;
    if (Idx < AccessorValue.size() - 1) {
      CurrentContext = CurrentValue->getAsObject();
      if (!CurrentContext)
        return nullptr;
    } else {
      Context = CurrentValue;
    }
  }
  return Context;
}

void ASTNode::renderChild(const json::Value &Contexts, llvm::raw_ostream &OS) {
  for (AstPtr &Child : Children)
    Child->render(Contexts, OS);
}

void ASTNode::renderPartial(const json::Value &Contexts, llvm::raw_ostream &OS,
                            ASTNode *Partial) {
  AddIndentationStringStream IS(OS, Indentation);
  Partial->render(Contexts, IS);
}

void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
                            Lambda &L) {
  json::Value LambdaResult = L();
  std::string LambdaStr;
  raw_string_ostream Output(LambdaStr);
  toMustacheString(LambdaResult, Output);
  Parser P = Parser(LambdaStr);
  AstPtr LambdaNode = P.parse(Partials, Lambdas, SectionLambdas, Escapes);

  EscapeStringStream ES(OS, Escapes);
  if (Ty == Variable) {
    LambdaNode->render(Contexts, ES);
    return;
  }
  LambdaNode->render(Contexts, OS);
}

void ASTNode::renderSectionLambdas(const json::Value &Contexts,
                                   llvm::raw_ostream &OS, SectionLambda &L) {
  json::Value Return = L(RawBody);
  if (isFalsey(Return))
    return;
  std::string LambdaStr;
  raw_string_ostream Output(LambdaStr);
  toMustacheString(Return, Output);
  Parser P = Parser(LambdaStr);
  AstPtr LambdaNode = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
  LambdaNode->render(Contexts, OS);
}

void Template::render(const json::Value &Data, llvm::raw_ostream &OS) {
  Tree->render(Data, OS);
}

void Template::registerPartial(std::string Name, std::string Partial) {
  Parser P = Parser(Partial);
  AstPtr PartialTree = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
  Partials.insert(std::make_pair(Name, std::move(PartialTree)));
}

void Template::registerLambda(std::string Name, Lambda L) { Lambdas[Name] = L; }

void Template::registerLambda(std::string Name, SectionLambda L) {
  SectionLambdas[Name] = L;
}

void Template::overrideEscapeCharacters(EscapeMap E) { Escapes = std::move(E); }

Template::Template(StringRef TemplateStr) {
  Parser P = Parser(TemplateStr);
  Tree = P.parse(Partials, Lambdas, SectionLambdas, Escapes);
  // The default behavior is to escape html entities.
  const EscapeMap HtmlEntities = {{'&', "&amp;"},
                                  {'<', "&lt;"},
                                  {'>', "&gt;"},
                                  {'"', "&quot;"},
                                  {'\'', "&#39;"}};
  overrideEscapeCharacters(HtmlEntities);
}

Template::Template(Template &&Other) noexcept
    : Partials(std::move(Other.Partials)), Lambdas(std::move(Other.Lambdas)),
      SectionLambdas(std::move(Other.SectionLambdas)),
      Escapes(std::move(Other.Escapes)), Tree(std::move(Other.Tree)) {}

Template::~Template() = default;

Template &Template::operator=(Template &&Other) noexcept {
  if (this != &Other) {
    Partials = std::move(Other.Partials);
    Lambdas = std::move(Other.Lambdas);
    SectionLambdas = std::move(Other.SectionLambdas);
    Escapes = std::move(Other.Escapes);
    Tree = std::move(Other.Tree);
    Other.Tree = nullptr;
  }
  return *this;
}
} // namespace llvm::mustache