Repository: alecthomas/chroma
Pull request #983 (2024-07-11 12:39:13)

Add JSONata Lexer
Add lexer for [JSONata](https://docs.jsonata.org/overview.html) including test data.


Patch:
diff --git a/bin/.esbuild-0.21.5.pkg b/bin/.esbuild-0.23.0.pkg
similarity index 100%
rename from bin/.esbuild-0.21.5.pkg
rename to bin/.esbuild-0.23.0.pkg
diff --git a/bin/.go-1.22.4.pkg b/bin/.go-1.22.5.pkg
similarity index 100%
rename from bin/.go-1.22.4.pkg
rename to bin/.go-1.22.5.pkg
diff --git a/bin/.watchexec-2.1.1.pkg b/bin/.watchexec-2.1.2.pkg
similarity index 100%
rename from bin/.watchexec-2.1.1.pkg
rename to bin/.watchexec-2.1.2.pkg
diff --git a/bin/esbuild b/bin/esbuild
index b201c3b..be3d9ce 120000
--- a/bin/esbuild
+++ b/bin/esbuild
@@ -1,1 +1,1 @@
-.esbuild-0.21.5.pkg
\ No newline at end of file
+.esbuild-0.23.0.pkg
\ No newline at end of file
diff --git a/bin/go b/bin/go
index 27ef99e..5c26cb9 120000
--- a/bin/go
+++ b/bin/go
@@ -1,1 +1,1 @@
-.go-1.22.4.pkg
\ No newline at end of file
+.go-1.22.5.pkg
\ No newline at end of file
diff --git a/bin/gofmt b/bin/gofmt
index 27ef99e..5c26cb9 120000
--- a/bin/gofmt
+++ b/bin/gofmt
@@ -1,1 +1,1 @@
-.go-1.22.4.pkg
\ No newline at end of file
+.go-1.22.5.pkg
\ No newline at end of file
diff --git a/bin/watchexec b/bin/watchexec
index 17387d0..b813380 120000
--- a/bin/watchexec
+++ b/bin/watchexec
@@ -1,1 +1,1 @@
-.watchexec-2.1.1.pkg
\ No newline at end of file
+.watchexec-2.1.2.pkg
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 109fc73..ba327f8 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.19
 require (
 	github.com/alecthomas/assert/v2 v2.10.0
 	github.com/alecthomas/repr v0.4.0
-	github.com/dlclark/regexp2 v1.11.0
+	github.com/dlclark/regexp2 v1.11.2
 )
 
 require github.com/hexops/gotextdiff v1.0.3 // indirect
diff --git a/go.sum b/go.sum
index 2333d1c..2f79b48 100644
--- a/go.sum
+++ b/go.sum
@@ -20,5 +20,7 @@ github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq
 github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
 github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68=
+github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
diff --git a/lexers/embedded/jsonata.xml b/lexers/embedded/jsonata.xml
new file mode 100644
index 0000000..c0eafab
--- /dev/null
+++ b/lexers/embedded/jsonata.xml
@@ -0,0 +1,83 @@
+<lexer>
+  <config>
+    <name>JSONata</name>
+    <alias>jsonata</alias>
+    <filename>*.jsonata</filename>
+    <dot_all>true</dot_all>
+  </config>
+  <rules>
+    <state name="root">
+      <rule pattern="/\*.*?\*/">
+        <token type="CommentMultiline"/>
+      </rule>
+      <rule pattern="[{}()\[\]:;,\.=]">
+        <token type="Punctuation"/>
+      </rule>
+       <rule pattern="\.\."> // Spread operator
+        <token type="Operator"/>
+      </rule> 
+      <rule pattern="\^(?=\()"> // Sort operator
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="\*\*|\*(?=\.)|\*"> // Descendant | Wildcard | Multiplication
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="\/(?!\*)"> // Division
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="[&lt;&gt;!]=?"> // Comparison operators
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="~>">
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="\b(and|or|in)\b">
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="[%@#&amp;?]|\+(?!\d)|\-(?!\d)">
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="\$[a-zA-Z0-9_]*(?![\w\(])">
+        <token type="NameVariable"/>
+      </rule>
+      <rule pattern="\$\w*(?=\()">
+        <token type="NameFunction"/>
+      </rule>
+      <rule pattern="\s+">
+        <token type="Text"/>
+      </rule>
+      <rule pattern="(true|false)\b">
+        <token type="KeywordConstant"/>
+      </rule>
+      <rule pattern="\b(function)\b">
+        <token type="Keyword"/>
+      </rule>
+      <rule pattern="(\+|-)?(0|[1-9]\d*)(\.\d+[eE](\+|-)?\d+|[eE](\+|-)?\d+|\.\d+)">
+        <token type="LiteralNumberFloat"/>
+      </rule> 
+      <rule pattern="(\+|-)?(0|[1-9]\d*)">
+        <token type="LiteralNumberInteger"/>
+      </rule>
+      <!-- NOTE: This expression matches all object keys (NameTags), which are essentially strings with double quotes
+      that should only be captured on the left side of a colon (:) within a JSON-like object.
+      Therefore, this expression must preceed the one for all LiteralStringDouble -->
+      <rule pattern="&#34;(\\.|[^\\&#34;\r\n])*&#34;(?=\s*:)">
+        <token type="NameTag"/>
+      </rule>
+      <rule pattern="&#34;(\\\\|\\&#34;|[^&#34;])*&#34;">
+        <token type="LiteralStringDouble"/>
+      </rule>
+      <rule pattern="&#39;(\\|\\&#39;|[^&#39;])*&#39;">
+        <token type="LiteralStringSingle"/>
+      </rule>
+      <rule pattern="`.*`">
+        <token type="LiteralStringBacktick"/>
+      </rule>
+      <!-- NOTE: This expression matches everything remaining, which should be only JSONata names.
+      Therefore, it has been left as last intentionally -->
+      <rule pattern="[a-zA-Z0-9_]*"> 
+        <token type="Name"/>
+      </rule>
+    </state>
+  </rules>
+</lexer>
\ No newline at end of file


Test patch:
diff --git a/lexers/testdata/jsonata.actual b/lexers/testdata/jsonata.actual
new file mode 100644
index 0000000..4a5eeff
--- /dev/null
+++ b/lexers/testdata/jsonata.actual
@@ -0,0 +1,38 @@
+(
+  $.a.b.c;
+
+  /* Example json input */
+  $jsonInput := {
+    "firstName": "JSONata",
+    "lastName": "User",
+    "age": 25,
+    "skills": ["programming", "cooking"],
+    "over 18": true,
+    "coordinates": { "latitude": 51.509865, "longitude": -0.118092 }
+  };
+  
+  /* Example function definition */
+  $getGreeting := function($firstName, $lastName) {
+    "Hello, " & $firstName & " " & $lastName
+  };
+
+  $isAdult := $jsonInput.`over 18`;
+  
+  /* Example json transformation */
+  $jsonInput.{
+    "fullName": $join([firstName, lastName], " "),
+    "greeting": $getGreeting(firstName, lastName),
+    "skills": $append(skills, "jsonata"),
+    "isAdult": $isAdult
+  }
+
+  $spreadOp = [1..5];
+  $sortOperator := Account.Order^(>Price, <Quantity);
+  $descendants := **.Postcode;
+  $math:= 10 / 5 * 2 + 4 - 2 % 2;
+  $conditions := 20 > 10 and 10 > 20 ? 20 = 20 or 20 != 10 : 10 in [1,10,20];
+  $chainOperator := " trim " ~> $trim();
+  $sum := +10 + -5 - +3 + $x / $y;
+  $multiplyVars := $a*$b * $c;
+  $wildcard := $.wild.*.card;
+)
\ No newline at end of file
diff --git a/lexers/testdata/jsonata.expected b/lexers/testdata/jsonata.expected
new file mode 100644
index 0000000..345e0ba
--- /dev/null
+++ b/lexers/testdata/jsonata.expected
@@ -0,0 +1,335 @@
+[
+  {"type":"Punctuation","value":"("},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"a"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"b"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"c"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n\n  "},
+  {"type":"CommentMultiline","value":"/* Example json input */"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$jsonInput"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"firstName\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\"JSONata\""},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"lastName\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\"User\""},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"age\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"25"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"skills\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"["},
+  {"type":"LiteralStringDouble","value":"\"programming\""},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\"cooking\""},
+  {"type":"Punctuation","value":"],"},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"over 18\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"true"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"coordinates\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"NameTag","value":"\"latitude\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberFloat","value":"51.509865"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"NameTag","value":"\"longitude\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberFloat","value":"-0.118092"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n  "},
+  {"type":"Punctuation","value":"};"},
+  {"type":"Text","value":"\n  \n  "},
+  {"type":"CommentMultiline","value":"/* Example function definition */"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$getGreeting"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"function"},
+  {"type":"Punctuation","value":"("},
+  {"type":"NameVariable","value":"$firstName"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$lastName"},
+  {"type":"Punctuation","value":")"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":"\n    "},
+  {"type":"LiteralStringDouble","value":"\"Hello, \""},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"\u0026"},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$firstName"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"\u0026"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\" \""},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"\u0026"},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$lastName"},
+  {"type":"Text","value":"\n  "},
+  {"type":"Punctuation","value":"};"},
+  {"type":"Text","value":"\n\n  "},
+  {"type":"NameVariable","value":"$isAdult"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$jsonInput"},
+  {"type":"Punctuation","value":"."},
+  {"type":"LiteralStringBacktick","value":"`over 18`"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n  \n  "},
+  {"type":"CommentMultiline","value":"/* Example json transformation */"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$jsonInput"},
+  {"type":"Punctuation","value":".{"},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"fullName\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"NameFunction","value":"$join"},
+  {"type":"Punctuation","value":"(["},
+  {"type":"Name","value":"firstName"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"Name","value":"lastName"},
+  {"type":"Punctuation","value":"],"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\" \""},
+  {"type":"Punctuation","value":"),"},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"greeting\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"NameFunction","value":"$getGreeting"},
+  {"type":"Punctuation","value":"("},
+  {"type":"Name","value":"firstName"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"Name","value":"lastName"},
+  {"type":"Punctuation","value":"),"},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"skills\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"NameFunction","value":"$append"},
+  {"type":"Punctuation","value":"("},
+  {"type":"Name","value":"skills"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\"jsonata\""},
+  {"type":"Punctuation","value":"),"},
+  {"type":"Text","value":"\n    "},
+  {"type":"NameTag","value":"\"isAdult\""},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$isAdult"},
+  {"type":"Text","value":"\n  "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n  "},
+  {"type":"NameVariable","value":"$spreadOp"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"="},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"["},
+  {"type":"LiteralNumberInteger","value":"1"},
+  {"type":"Punctuation","value":".."},
+  {"type":"LiteralNumberInteger","value":"5"},
+  {"type":"Punctuation","value":"];"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$sortOperator"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"Name","value":"Account"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"Order"},
+  {"type":"Operator","value":"^"},
+  {"type":"Punctuation","value":"("},
+  {"type":"Operator","value":"\u003e"},
+  {"type":"Name","value":"Price"},
+  {"type":"Punctuation","value":","},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"\u003c"},
+  {"type":"Name","value":"Quantity"},
+  {"type":"Punctuation","value":");"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$descendants"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"**"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"Postcode"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$math"},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"10"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"/"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"5"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"*"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"2"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"+"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"4"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"-"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"2"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"2"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$conditions"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"20"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"10"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"and"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"10"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"20"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"?"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"20"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"="},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"20"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"or"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"20"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"!="},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"10"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"10"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"in"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"["},
+  {"type":"LiteralNumberInteger","value":"1"},
+  {"type":"Punctuation","value":","},
+  {"type":"LiteralNumberInteger","value":"10"},
+  {"type":"Punctuation","value":","},
+  {"type":"LiteralNumberInteger","value":"20"},
+  {"type":"Punctuation","value":"];"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$chainOperator"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"LiteralStringDouble","value":"\" trim \""},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"~\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"NameFunction","value":"$trim"},
+  {"type":"Punctuation","value":"();"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$sum"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"+10"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"+"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"-5"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"-"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"+3"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"+"},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$x"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"/"},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$y"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$multiplyVars"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$a"},
+  {"type":"Operator","value":"*"},
+  {"type":"NameVariable","value":"$b"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"*"},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$c"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n  "},
+  {"type":"NameVariable","value":"$wildcard"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":":="},
+  {"type":"Text","value":" "},
+  {"type":"NameVariable","value":"$"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"wild"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Operator","value":"*"},
+  {"type":"Punctuation","value":"."},
+  {"type":"Name","value":"card"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":")"}
+]