77// ===----------------------------------------------------------------------===//
88#include  " llvm/Support/Mustache.h" 
99#include  " llvm/ADT/SmallVector.h" 
10+ #include  " llvm/Support/Debug.h" 
1011#include  " llvm/Support/raw_ostream.h" 
12+ #include  < cctype> 
1113#include  < sstream> 
1214
15+ #define  DEBUG_TYPE  " mustache" 
16+ 
1317using  namespace  llvm ; 
1418using  namespace  llvm ::mustache; 
1519
@@ -62,6 +66,7 @@ class Token {
6266    InvertSectionOpen,
6367    UnescapeVariable,
6468    Comment,
69+     SetDelimiter,
6570  };
6671
6772  Token (std::string Str)
@@ -102,6 +107,8 @@ class Token {
102107      return  Type::Partial;
103108    case  ' &' 
104109      return  Type::UnescapeVariable;
110+     case  ' =' 
111+       return  Type::SetDelimiter;
105112    default :
106113      return  Type::Variable;
107114    }
@@ -189,27 +196,27 @@ class ASTNode {
189196};
190197
191198//  A wrapper for arena allocator for ASTNodes
192- AstPtr createRootNode (llvm::StringMap<AstPtr> &Partials,
193-                       llvm::StringMap<Lambda> &Lambdas,
194-                       llvm::StringMap<SectionLambda> &SectionLambdas,
195-                       EscapeMap &Escapes) {
199+ static   AstPtr createRootNode (llvm::StringMap<AstPtr> &Partials,
200+                               llvm::StringMap<Lambda> &Lambdas,
201+                               llvm::StringMap<SectionLambda> &SectionLambdas,
202+                               EscapeMap &Escapes) {
196203  return  std::make_unique<ASTNode>(Partials, Lambdas, SectionLambdas, Escapes);
197204}
198205
199- AstPtr createNode (ASTNode::Type T, Accessor A, ASTNode *Parent,
200-                   llvm::StringMap<AstPtr> &Partials,
201-                   llvm::StringMap<Lambda> &Lambdas,
202-                   llvm::StringMap<SectionLambda> &SectionLambdas,
203-                   EscapeMap &Escapes) {
206+ static   AstPtr createNode (ASTNode::Type T, Accessor A, ASTNode *Parent,
207+                           llvm::StringMap<AstPtr> &Partials,
208+                           llvm::StringMap<Lambda> &Lambdas,
209+                           llvm::StringMap<SectionLambda> &SectionLambdas,
210+                           EscapeMap &Escapes) {
204211  return  std::make_unique<ASTNode>(T, std::move (A), Parent, Partials, Lambdas,
205212                                   SectionLambdas, Escapes);
206213}
207214
208- AstPtr createTextNode (std::string Body, ASTNode *Parent,
209-                       llvm::StringMap<AstPtr> &Partials,
210-                       llvm::StringMap<Lambda> &Lambdas,
211-                       llvm::StringMap<SectionLambda> &SectionLambdas,
212-                       EscapeMap &Escapes) {
215+ static   AstPtr createTextNode (std::string Body, ASTNode *Parent,
216+                               llvm::StringMap<AstPtr> &Partials,
217+                               llvm::StringMap<Lambda> &Lambdas,
218+                               llvm::StringMap<SectionLambda> &SectionLambdas,
219+                               EscapeMap &Escapes) {
213220  return  std::make_unique<ASTNode>(std::move (Body), Parent, Partials, Lambdas,
214221                                   SectionLambdas, Escapes);
215222}
@@ -226,7 +233,7 @@ AstPtr createTextNode(std::string Body, ASTNode *Parent,
226233//  and the current token is the second token.
227234//  For example:
228235//   "{{#Section}}"
229- bool  hasTextBehind (size_t  Idx, const  ArrayRef<Token> &Tokens) {
236+ static   bool  hasTextBehind (size_t  Idx, const  ArrayRef<Token> &Tokens) {
230237  if  (Idx == 0 )
231238    return  true ;
232239
@@ -242,7 +249,7 @@ bool hasTextBehind(size_t Idx, const ArrayRef<Token> &Tokens) {
242249//  Function to check if there's no meaningful text ahead.
243250//  We determine if a token has text ahead if the left of previous
244251//  token does not start with a newline.
245- bool  hasTextAhead (size_t  Idx, const  ArrayRef<Token> &Tokens) {
252+ static   bool  hasTextAhead (size_t  Idx, const  ArrayRef<Token> &Tokens) {
246253  if  (Idx >= Tokens.size () - 1 )
247254    return  true ;
248255
@@ -255,11 +262,11 @@ bool hasTextAhead(size_t Idx, const ArrayRef<Token> &Tokens) {
255262  return  !TokenBody.starts_with (" \r\n " starts_with (" \n " 
256263}
257264
258- bool  requiresCleanUp (Token::Type T) {
265+ static   bool  requiresCleanUp (Token::Type T) {
259266  //  We must clean up all the tokens that could contain child nodes.
260267  return  T == Token::Type::SectionOpen || T == Token::Type::InvertSectionOpen ||
261268         T == Token::Type::SectionClose || T == Token::Type::Comment ||
262-          T == Token::Type::Partial;
269+          T == Token::Type::Partial || T == Token::Type::SetDelimiter ;
263270}
264271
265272//  Adjust next token body if there is no text ahead.
@@ -268,7 +275,7 @@ bool requiresCleanUp(Token::Type T) {
268275//   "{{! Comment }} \nLine 2"
269276//  would be considered as no text ahead and should be rendered as
270277//   " Line 2"
271- void  stripTokenAhead (SmallVectorImpl<Token> &Tokens, size_t  Idx) {
278+ static   void  stripTokenAhead (SmallVectorImpl<Token> &Tokens, size_t  Idx) {
272279  Token &NextToken = Tokens[Idx + 1 ];
273280  StringRef NextTokenBody = NextToken.TokenBody ;
274281  //  Cut off the leading newline which could be \n or \r\n.
@@ -294,57 +301,128 @@ void stripTokenBefore(SmallVectorImpl<Token> &Tokens, size_t Idx,
294301  CurrentToken.setIndentation (Indentation);
295302}
296303
304+ struct  Tag  {
305+   enum  class  Kind  {
306+     None,
307+     Normal, //  {{...}}
308+     Triple, //  {{{...}}}
309+   };
310+ 
311+   Kind TagKind = Kind::None;
312+   StringRef Content;   //  The content between the delimiters.
313+   StringRef FullMatch; //  The entire tag, including delimiters.
314+   size_t  StartPosition = StringRef::npos;
315+ };
316+ 
317+ static  Tag findNextTag (StringRef Template, size_t  StartPos, StringRef Open,
318+                        StringRef Close) {
319+   const  StringLiteral TripleOpen (" {{{" 
320+   const  StringLiteral TripleClose (" }}}" 
321+ 
322+   size_t  NormalOpenPos = Template.find (Open, StartPos);
323+   size_t  TripleOpenPos = Template.find (TripleOpen, StartPos);
324+ 
325+   Tag Result;
326+ 
327+   //  Determine which tag comes first.
328+   if  (TripleOpenPos != StringRef::npos &&
329+       (NormalOpenPos == StringRef::npos || TripleOpenPos <= NormalOpenPos)) {
330+     //  Found a triple mustache tag.
331+     size_t  EndPos =
332+         Template.find (TripleClose, TripleOpenPos + TripleOpen.size ());
333+     if  (EndPos == StringRef::npos)
334+       return  Result; //  No closing tag found.
335+ 
336+     Result.TagKind  = Tag::Kind::Triple;
337+     Result.StartPosition  = TripleOpenPos;
338+     size_t  ContentStart = TripleOpenPos + TripleOpen.size ();
339+     Result.Content  = Template.substr (ContentStart, EndPos - ContentStart);
340+     Result.FullMatch  = Template.substr (
341+         TripleOpenPos, (EndPos + TripleClose.size ()) - TripleOpenPos);
342+   } else  if  (NormalOpenPos != StringRef::npos) {
343+     //  Found a normal mustache tag.
344+     size_t  EndPos = Template.find (Close, NormalOpenPos + Open.size ());
345+     if  (EndPos == StringRef::npos)
346+       return  Result; //  No closing tag found.
347+ 
348+     Result.TagKind  = Tag::Kind::Normal;
349+     Result.StartPosition  = NormalOpenPos;
350+     size_t  ContentStart = NormalOpenPos + Open.size ();
351+     Result.Content  = Template.substr (ContentStart, EndPos - ContentStart);
352+     Result.FullMatch  =
353+         Template.substr (NormalOpenPos, (EndPos + Close.size ()) - NormalOpenPos);
354+   }
355+ 
356+   return  Result;
357+ }
358+ 
359+ static  void  processTag (const  Tag &T, SmallVectorImpl<Token> &Tokens,
360+                        SmallString<8 > &Open, SmallString<8 > &Close) {
361+   LLVM_DEBUG (dbgs () << "   Found tag: \" " FullMatch  << " \" , Content: \" " 
362+                     << T.Content  << " \"\n " 
363+   if  (T.TagKind  == Tag::Kind::Triple) {
364+     Tokens.emplace_back (T.FullMatch .str (), " &" Content .str (), ' &' 
365+     LLVM_DEBUG (dbgs () << "   Created UnescapeVariable token.\n " 
366+     return ;
367+   }
368+   StringRef Interpolated = T.Content ;
369+   std::string RawBody = T.FullMatch .str ();
370+   if  (!Interpolated.trim ().starts_with (" =" 
371+     char  Front = Interpolated.empty () ? '  ' trim ().front ();
372+     Tokens.emplace_back (RawBody, Interpolated.str (), Front);
373+     LLVM_DEBUG (dbgs () << "   Created tag token of type '" " '\n " 
374+     return ;
375+   }
376+   Tokens.emplace_back (RawBody, Interpolated.str (), ' =' 
377+   StringRef DelimSpec = Interpolated.trim ();
378+   DelimSpec = DelimSpec.drop_front (1 );
379+   DelimSpec = DelimSpec.take_until ([](char  C) { return  C == ' =' 
380+   DelimSpec = DelimSpec.trim ();
381+ 
382+   auto  [NewOpen, NewClose] = DelimSpec.split ('  ' 
383+   Open = NewOpen;
384+   Close = NewClose;
385+ 
386+   LLVM_DEBUG (dbgs () << "   Found Set Delimiter tag. NewOpen='" 
387+                     << " ', NewClose='" " '\n " 
388+ }
389+ 
297390//  Simple tokenizer that splits the template into tokens.
298391//  The mustache spec allows {{{ }}} to unescape variables,
299392//  but we don't support that here. An unescape variable
300393//  is represented only by {{& variable}}.
301- SmallVector<Token> tokenize (StringRef Template) {
394+ static  SmallVector<Token> tokenize (StringRef Template) {
395+   LLVM_DEBUG (dbgs () << " Tokenizing template: \" " " \"\n " 
302396  SmallVector<Token> Tokens;
303-   StringLiteral Open (" {{" 
304-   StringLiteral Close (" }}" 
305-   StringLiteral TripleOpen (" {{{" 
306-   StringLiteral TripleClose (" }}}" 
397+   SmallString<8 > Open (" {{" 
398+   SmallString<8 > Close (" }}" 
307399  size_t  Start = 0 ;
308-   size_t  DelimiterStart = Template.find (Open);
309-   if  (DelimiterStart == StringRef::npos) {
310-     Tokens.emplace_back (Template.str ());
311-     return  Tokens;
312-   }
313-   while  (DelimiterStart != StringRef::npos) {
314-     if  (DelimiterStart != Start)
315-       Tokens.emplace_back (Template.substr (Start, DelimiterStart - Start).str ());
316- 
317-     if  (Template.substr (DelimiterStart).starts_with (TripleOpen)) {
318-       size_t  DelimiterEnd = Template.find (TripleClose, DelimiterStart);
319-       if  (DelimiterEnd == StringRef::npos)
320-         break ;
321-       size_t  BodyStart = DelimiterStart + TripleOpen.size ();
322-       std::string Body =
323-           Template.substr (BodyStart, DelimiterEnd - BodyStart).str ();
324-       std::string RawBody =
325-           Template.substr (DelimiterStart, DelimiterEnd - DelimiterStart + 3 )
326-               .str ();
327-       Tokens.emplace_back (RawBody, " &" ' &' 
328-       Start = DelimiterEnd + TripleClose.size ();
329-     } else  {
330-       size_t  DelimiterEnd = Template.find (Close, DelimiterStart);
331-       if  (DelimiterEnd == StringRef::npos)
332-         break ;
333- 
334-       //  Extract the Interpolated variable without delimiters.
335-       size_t  InterpolatedStart = DelimiterStart + Open.size ();
336-       size_t  InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size ();
337-       std::string Interpolated =
338-           Template.substr (InterpolatedStart, InterpolatedEnd).str ();
339-       std::string RawBody = Open.str () + Interpolated + Close.str ();
340-       Tokens.emplace_back (RawBody, Interpolated, Interpolated[0 ]);
341-       Start = DelimiterEnd + Close.size ();
400+ 
401+   while  (Start < Template.size ()) {
402+     LLVM_DEBUG (dbgs () << " Loop start. Start=" " , Open='" 
403+                       << " ', Close='" " '\n " 
404+     Tag T = findNextTag (Template, Start, Open, Close);
405+ 
406+     if  (T.TagKind  == Tag::Kind::None) {
407+       //  No more tags, the rest is text.
408+       Tokens.emplace_back (Template.substr (Start).str ());
409+       LLVM_DEBUG (dbgs () << "   No more tags. Created final Text token: \" " 
410+                         << Template.substr (Start) << " \"\n " 
411+       break ;
412+     }
413+ 
414+     //  Add the text before the tag.
415+     if  (T.StartPosition  > Start) {
416+       StringRef Text = Template.substr (Start, T.StartPosition  - Start);
417+       Tokens.emplace_back (Text.str ());
418+       LLVM_DEBUG (dbgs () << "   Created Text token: \" " " \"\n " 
342419    }
343-     DelimiterStart = Template.find (Open, Start);
344-   }
345420
346-   if  (Start < Template.size ())
347-     Tokens.emplace_back (Template.substr (Start).str ());
421+     processTag (T, Tokens, Open, Close);
422+ 
423+     //  Move past the tag.
424+     Start = T.StartPosition  + T.FullMatch .size ();
425+   }
348426
349427  //  Fix up white spaces for:
350428  //    - open sections
@@ -386,6 +464,7 @@ SmallVector<Token> tokenize(StringRef Template) {
386464    if  ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
387465      stripTokenBefore (Tokens, Idx, CurrentToken, CurrentType);
388466  }
467+   LLVM_DEBUG (dbgs () << " Tokenizing finished.\n " 
389468  return  Tokens;
390469}
391470
@@ -563,13 +642,14 @@ void Parser::parseMustache(ASTNode *Parent, llvm::StringMap<AstPtr> &Partials,
563642      break ;
564643    }
565644    case  Token::Type::Comment:
645+     case  Token::Type::SetDelimiter:
566646      break ;
567647    case  Token::Type::SectionClose:
568648      return ;
569649    }
570650  }
571651}
572- void  toMustacheString (const  json::Value &Data, raw_ostream &OS) {
652+ static   void  toMustacheString (const  json::Value &Data, raw_ostream &OS) {
573653  switch  (Data.kind ()) {
574654  case  json::Value::Null:
575655    return ;
@@ -602,6 +682,8 @@ void toMustacheString(const json::Value &Data, raw_ostream &OS) {
602682}
603683
604684void  ASTNode::render (const  json::Value &CurrentCtx, raw_ostream &OS) {
685+   if  (Ty != Root && Ty != Text && AccessorValue.empty ())
686+     return ;
605687  //  Set the parent context to the incoming context so that we
606688  //  can walk up the context tree correctly in findContext().
607689  ParentContext = &CurrentCtx;
@@ -801,3 +883,5 @@ Template &Template::operator=(Template &&Other) noexcept {
801883  return  *this ;
802884}
803885} //  namespace llvm::mustache
886+ 
887+ #undef  DEBUG_TYPE
0 commit comments