Skip to content

Conversation

ilovepi
Copy link
Contributor

@ilovepi ilovepi commented Sep 16, 2025

We extend the logic in tokenize() to treat the {{{}}} delimiters
to treat it like other unescaped HTML. We do this by updating the
tokenizer to treat the new tokes the same way we do for the {{&variable}}
syntax, which avoid the need to change the parser.

We also update the llvm-test-mustache-spec tool to no longer mark Triple
Mustache as XFAIL.

Copy link
Contributor Author

ilovepi commented Sep 16, 2025

@llvmbot
Copy link
Member

llvmbot commented Sep 16, 2025

@llvm/pr-subscribers-llvm-support

Author: Paul Kirth (ilovepi)

Changes

We extend the logic in tokenize() to treat the {{{}}} delimiters
to treat it like other unescaped HTML. We do this by updating the
tokenizer to treat the new tokes the same way we do for the {{&variable}}
syntax, which avoid the need to change the parser.

We also update the llvm-test-mustache-spec tool to no longer mark Triple
Mustache as XFAIL.


Full diff: https://github.com/llvm/llvm-project/pull/159183.diff

3 Files Affected:

  • (modified) llvm/lib/Support/Mustache.cpp (+28-11)
  • (modified) llvm/unittests/Support/MustacheTest.cpp (+10-10)
  • (modified) llvm/utils/llvm-test-mustache-spec/llvm-test-mustache-spec.cpp (-14)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 6c2ed6c84c6cf..be9cbfd46982f 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -305,6 +305,8 @@ SmallVector<Token> tokenize(StringRef Template) {
   SmallVector<Token> Tokens;
   StringLiteral Open("{{");
   StringLiteral Close("}}");
+  StringLiteral TripleOpen("{{{");
+  StringLiteral TripleClose("}}}");
   size_t Start = 0;
   size_t DelimiterStart = Template.find(Open);
   if (DelimiterStart == StringRef::npos) {
@@ -314,18 +316,33 @@ SmallVector<Token> tokenize(StringRef Template) {
   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();
+    if (Template.substr(DelimiterStart).starts_with(TripleOpen)) {
+      size_t DelimiterEnd = Template.find(TripleClose, DelimiterStart);
+      if (DelimiterEnd == StringRef::npos)
+        break;
+      size_t BodyStart = DelimiterStart + TripleOpen.size();
+      std::string Body =
+          Template.substr(BodyStart, DelimiterEnd - BodyStart).str();
+      std::string RawBody =
+          Template.substr(DelimiterStart, DelimiterEnd - DelimiterStart + 3)
+              .str();
+      Tokens.emplace_back(RawBody, "&" + Body, '&');
+      Start = DelimiterEnd + TripleClose.size();
+    } else {
+      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);
   }
 
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 87586cdcef5af..08613f4b66962 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -1235,7 +1235,7 @@ TEST(MustacheTripleMustache, Basic) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("Hello, <b>World</b>!", Out);
+  EXPECT_EQ("Hello, <b>World</b>!", Out);
 }
 
 TEST(MustacheTripleMustache, IntegerInterpolation) {
@@ -1244,7 +1244,7 @@ TEST(MustacheTripleMustache, IntegerInterpolation) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("85 miles an hour!", Out);
+  EXPECT_EQ("85 miles an hour!", Out);
 }
 
 TEST(MustacheTripleMustache, DecimalInterpolation) {
@@ -1253,7 +1253,7 @@ TEST(MustacheTripleMustache, DecimalInterpolation) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("1.21 jiggawatts!", Out);
+  EXPECT_EQ("1.21 jiggawatts!", Out);
 }
 
 TEST(MustacheTripleMustache, NullInterpolation) {
@@ -1262,7 +1262,7 @@ TEST(MustacheTripleMustache, NullInterpolation) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("I () be seen!", Out);
+  EXPECT_EQ("I () be seen!", Out);
 }
 
 TEST(MustacheTripleMustache, ContextMissInterpolation) {
@@ -1271,7 +1271,7 @@ TEST(MustacheTripleMustache, ContextMissInterpolation) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("I () be seen!", Out);
+  EXPECT_EQ("I () be seen!", Out);
 }
 
 TEST(MustacheTripleMustache, DottedNames) {
@@ -1280,7 +1280,7 @@ TEST(MustacheTripleMustache, DottedNames) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("<b>Joe</b>", Out);
+  EXPECT_EQ("<b>Joe</b>", Out);
 }
 
 TEST(MustacheTripleMustache, ImplicitIterator) {
@@ -1289,7 +1289,7 @@ TEST(MustacheTripleMustache, ImplicitIterator) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("(<a>)(<b>)", Out);
+  EXPECT_EQ("(<a>)(<b>)", Out);
 }
 
 TEST(MustacheTripleMustache, SurroundingWhitespace) {
@@ -1298,7 +1298,7 @@ TEST(MustacheTripleMustache, SurroundingWhitespace) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("| --- |", Out);
+  EXPECT_EQ("| --- |", Out);
 }
 
 TEST(MustacheTripleMustache, Standalone) {
@@ -1307,7 +1307,7 @@ TEST(MustacheTripleMustache, Standalone) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("  ---\n", Out);
+  EXPECT_EQ("  ---\n", Out);
 }
 
 TEST(MustacheTripleMustache, WithPadding) {
@@ -1316,6 +1316,6 @@ TEST(MustacheTripleMustache, WithPadding) {
   std::string Out;
   raw_string_ostream OS(Out);
   T.render(D, OS);
-  EXPECT_NE("|---|", Out);
+  EXPECT_EQ("|---|", Out);
 }
 
diff --git a/llvm/utils/llvm-test-mustache-spec/llvm-test-mustache-spec.cpp b/llvm/utils/llvm-test-mustache-spec/llvm-test-mustache-spec.cpp
index 1f566e13f070a..ea1395b2646f6 100644
--- a/llvm/utils/llvm-test-mustache-spec/llvm-test-mustache-spec.cpp
+++ b/llvm/utils/llvm-test-mustache-spec/llvm-test-mustache-spec.cpp
@@ -128,21 +128,7 @@ static const StringMap<StringSet<>> XFailTestNames = {{
          "Section - Multiple Calls",
 
      }},
-    {"interpolation.json",
-     {
-         "Triple Mustache",
-         "Triple Mustache Integer Interpolation",
-         "Triple Mustache Decimal Interpolation",
-         "Triple Mustache Null Interpolation",
-         "Triple Mustache Context Miss Interpolation",
-         "Dotted Names - Triple Mustache Interpolation",
-         "Implicit Iterators - Triple Mustache",
-         "Triple Mustache - Surrounding Whitespace",
-         "Triple Mustache - Standalone",
-         "Triple Mustache With Padding",
-     }},
     {"partials.json", {"Standalone Indentation"}},
-    {"sections.json", {"Implicit Iterator - Triple mustache"}},
 }};
 
 struct TestData {

@ilovepi ilovepi force-pushed the users/ilovepi/triple-mustache branch from fbd9cd6 to cd974ac Compare September 22, 2025 17:07
@ilovepi ilovepi force-pushed the users/ilovepi/triple-muststache-impl branch from edd7cff to 30e8489 Compare September 22, 2025 17:07
Base automatically changed from users/ilovepi/triple-mustache to main September 22, 2025 17:49
@ilovepi ilovepi enabled auto-merge (squash) September 22, 2025 17:54
We extend the logic in tokenize() to treat the `{{{}}}` delimiters
to treat it like other unescaped HTML. We do this by updating the
tokenizer to treat the new tokes the same way we do for the `{{&variable}}`
syntax, which avoid the need to change the parser.

We also update the llvm-test-mustache-spec tool to no longer mark Triple
Mustache as XFAIL.
@ilovepi ilovepi force-pushed the users/ilovepi/triple-muststache-impl branch from 30e8489 to 20788dd Compare September 22, 2025 17:56
@ilovepi ilovepi disabled auto-merge September 22, 2025 17:56
Copy link
Contributor Author

ilovepi commented Sep 24, 2025

Merge activity

  • Sep 24, 8:42 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Sep 24, 8:44 PM UTC: @ilovepi merged this pull request with Graphite.

@ilovepi ilovepi merged commit 1c6e896 into main Sep 24, 2025
9 checks passed
@ilovepi ilovepi deleted the users/ilovepi/triple-muststache-impl branch September 24, 2025 20:44
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
We extend the logic in tokenize() to treat the `{{{}}}` delimiters
to treat it like other unescaped HTML. We do this by updating the
tokenizer to treat the new tokes the same way we do for the `{{&variable}}`
syntax, which avoid the need to change the parser.

We also update the llvm-test-mustache-spec tool to no longer mark Triple
Mustache as XFAIL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants