@platformos/liquid-html-parser 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,751 @@
1
+ module.exports = String.raw`Helpers {
2
+ Node = TextNode*
3
+ TextNode = AnyExceptPlus<openControl>
4
+ openControl = end
5
+
6
+ empty = /* nothing */
7
+ anyExcept<lit> = (~ lit any)
8
+ anyExceptStar<lit> = (~ lit any)*
9
+ anyExceptPlus<lit> = (~ lit any)+
10
+ AnyExcept<lit> = (~ lit any)
11
+ AnyExceptPlus<lit> = (~ lit any)+
12
+ AnyExceptStar<lit> = (~ lit any)*
13
+ identifierCharacter = alnum | "_" | "-"
14
+
15
+ orderedListOf<a, b, sep> =
16
+ | nonemptyOrderedListOf<a, b, sep>
17
+ | emptyListOf<a, sep>
18
+ nonemptyOrderedListOf<a, b, sep> =
19
+ | nonemptyListOf<b, sep>
20
+ | nonemptyOrderedListOfBoth<a, b, sep>
21
+ | nonemptyListOf<a, sep>
22
+ nonemptyOrderedListOfBoth<a, b, sep> =
23
+ nonemptyListOf<a, sep> (sep nonemptyListOf<b, sep>)
24
+
25
+ singleQuote = "'" | "‘" | "’"
26
+ doubleQuote = "\"" | "“" | "”"
27
+ controls = "\u{007F}".."\u{009F}"
28
+ noncharacters = "\u{FDD0}".."\u{FDEF}"
29
+ newline = "\r"? "\n"
30
+ }
31
+
32
+ Liquid <: Helpers {
33
+ Node := (liquidNode | TextNode)*
34
+ openControl := "{{" | "{%"
35
+ endOfTagName = &("-%}" | "-}}" | "%}" | "}}")
36
+ endOfVarName = ~identifierCharacter
37
+ endOfIdentifier = endOfTagName | endOfVarName
38
+
39
+ liquidNode =
40
+ | liquidDoc
41
+ | liquidBlockComment
42
+ | liquidRawTag
43
+ | liquidDrop
44
+ | liquidTagClose
45
+ | liquidTagOpen
46
+ | liquidTag
47
+ | liquidInlineComment
48
+
49
+ liquidTagStrict =
50
+ | liquidTagAssign
51
+ | liquidTagHashAssign
52
+ | liquidTagBreak
53
+ | liquidTagContinue
54
+ | liquidTagCycle
55
+ | liquidTagContentFor
56
+ | liquidTagDecrement
57
+ | liquidTagEcho
58
+ | liquidTagElse
59
+ | liquidTagElsif
60
+ | liquidTagInclude
61
+ | liquidTagIncrement
62
+ | liquidTagLayout
63
+ | liquidTagLiquid
64
+ | liquidTagRender
65
+ | liquidTagFunction
66
+ | liquidTagGraphQL
67
+ | liquidTagSection
68
+ | liquidTagSections
69
+ | liquidTagWhen
70
+ // platformos tags
71
+ | liquidTagBackground
72
+ | liquidTagCatch
73
+ | liquidTagContext
74
+ | liquidTagExport
75
+ | liquidTagIncludeForm
76
+ | liquidTagLog
77
+ | liquidTagPrint
78
+ | liquidTagRedirectTo
79
+ | liquidTagResponseHeaders
80
+ | liquidTagResponseStatus
81
+ | liquidTagReturn
82
+ | liquidTagRollback
83
+ | liquidTagSession
84
+ | liquidTagSignIn
85
+ | liquidTagSpamProtection
86
+ | liquidTagThemeRenderRc
87
+ | liquidTagYield
88
+
89
+ liquidTag =
90
+ | liquidTagStrict
91
+ | liquidTagBaseCase
92
+
93
+ liquidTagOpenStrict =
94
+ | liquidTagOpenCase
95
+ | liquidTagOpenCapture
96
+ | liquidTagOpenForm
97
+ | liquidTagOpenFor
98
+ | liquidTagOpenGraphQL
99
+ | liquidTagOpenTablerow
100
+ | liquidTagOpenIf
101
+ | liquidTagOpenPaginate
102
+ | liquidTagOpenUnless
103
+ // platformos block tags
104
+ | liquidTagOpenBackground
105
+ | liquidTagOpenCache
106
+ | liquidTagOpenParseJson
107
+ | liquidTagOpenTransaction
108
+ | liquidTagOpenTry
109
+
110
+ liquidTagOpenGraphQL = liquidTagOpenRule<"graphql", liquidTagGraphQLInlineMarkup>
111
+ // Use negative lookahead to exclude file-based syntax (with "=")
112
+ liquidTagGraphQLInlineMarkup = variableSegment ~(space* "=") renderArguments
113
+
114
+ liquidTagOpen =
115
+ | liquidTagOpenStrict
116
+ | liquidTagOpenBaseCase
117
+
118
+ liquidTagClose = "{%" "-"? space* "end" blockName space* tagMarkup "-"? "%}"
119
+
120
+ // These two are the same but transformed differently
121
+ liquidTagRule<name, markup> =
122
+ "{%" "-"? space* (name endOfIdentifier) space* markup "-"? "%}"
123
+ liquidTagOpenRule<name, markup> =
124
+ "{%" "-"? space* (name endOfIdentifier) space* markup "-"? "%}"
125
+
126
+ liquidTagBaseCase = liquidTagRule<liquidTagName, tagMarkup>
127
+
128
+ liquidTagEcho = liquidTagRule<"echo", liquidTagEchoMarkup>
129
+ liquidTagEchoMarkup = liquidVariable<delimTag>
130
+
131
+ liquidTagAssign = liquidTagRule<"assign", liquidTagAssignMarkup>
132
+ liquidTagAssignMarkup = variableSegment space* "=" space* liquidVariable<delimTag>
133
+
134
+ liquidTagHashAssign = liquidTagRule<"hash_assign", liquidTagHashAssignMarkup>
135
+ liquidTagHashAssignMarkup = liquidVariableLookup<delimTag> space* "=" space* liquidVariable<delimTag>
136
+
137
+ liquidTagCycle = liquidTagRule<"cycle", liquidTagCycleMarkup>
138
+ liquidTagCycleMarkup = (liquidExpression<delimTag> ":")? space* nonemptyListOf<liquidExpression<delimTag>, argumentSeparator> space*
139
+
140
+ liquidTagIncrement = liquidTagRule<"increment", variableSegmentAsLookupMarkup>
141
+ liquidTagDecrement = liquidTagRule<"decrement", variableSegmentAsLookupMarkup>
142
+ liquidTagOpenCapture = liquidTagOpenRule<"capture", variableSegmentAsLookupMarkup>
143
+ variableSegmentAsLookupMarkup = variableSegmentAsLookup space*
144
+
145
+ liquidTagSection = liquidTagRule<"section", liquidTagSectionMarkup>
146
+ liquidTagSectionMarkup = liquidString<delimTag> space*
147
+
148
+ liquidTagSections = liquidTagRule<"sections", liquidTagSectionsMarkup>
149
+ liquidTagSectionsMarkup = liquidString<delimTag> space*
150
+
151
+ liquidTagLayout = liquidTagRule<"layout", liquidTagLayoutMarkup>
152
+ liquidTagLayoutMarkup = liquidExpression<delimTag> space*
153
+
154
+ // We'll black hole the statement and switch parser in the cst builder
155
+ // We do this because it's technically the same grammar (with minor redefinitions)
156
+ // and it would be a huge chore and maintenance hell to rewrite all the rules with
157
+ // hspace = " " | "\t"
158
+ //
159
+ // The alternative is that this grammar parses the {% liquid tagMarkup %} as its own string,
160
+ // and then we switch to the LiquidStatement grammar that
161
+ // redefines liquidTagOpenRule, liquidTagRule, and space.
162
+ liquidTagLiquid = liquidTagRule<"liquid", liquidTagLiquidMarkup>
163
+ liquidTagLiquidMarkup = tagMarkup
164
+
165
+ liquidTagContentFor = liquidTagRule<"content_for", liquidTagContentForMarkup>
166
+
167
+ liquidTagContentForMarkup =
168
+ contentForType (argumentSeparatorOptionalComma contentForTagArgument) (space* ",")? space*
169
+
170
+ contentForTagArgument = listOf<contentForNamedArgument<delimTag>, argumentSeparatorOptionalComma>
171
+ completionModeContentForTagArgument = listOf<contentForNamedArgument<delimTag>, argumentSeparatorOptionalComma> (argumentSeparator? (liquidVariableLookup<delimTag>))?
172
+ contentForNamedArgument<delim> = (variableSegment ("." variableSegment)*) space* ":" space* (liquidExpression<delim>)
173
+
174
+ contentForType = liquidString<delimTag>
175
+
176
+ liquidTagInclude = liquidTagRule<"include", liquidTagRenderMarkup>
177
+ liquidTagRender = liquidTagRule<"render", liquidTagRenderMarkup>
178
+ liquidTagRenderMarkup =
179
+ snippetExpression renderVariableExpression? renderAliasExpression? renderArguments
180
+
181
+ liquidTagFunction = liquidTagRule<"function", liquidTagFunctionMarkup>
182
+ liquidTagFunctionMarkup = variableSegment space* "=" space* snippetExpression renderArguments
183
+
184
+ liquidTagGraphQL = liquidTagRule<"graphql", liquidTagGraphQLMarkup>
185
+ liquidTagGraphQLMarkup = variableSegment space* "=" space* snippetExpression renderArguments
186
+
187
+ // platformos simple tags
188
+ liquidTagBackground = liquidTagRule<"background", liquidTagBackgroundMarkup>
189
+ liquidTagBackgroundMarkup = variableSegment space* "=" space* snippetExpression renderArguments
190
+
191
+ liquidTagOpenBackground = liquidTagOpenRule<"background", liquidTagBackgroundInlineMarkup>
192
+ // Use negative lookahead to exclude file-based syntax (with "=")
193
+ // jobId is required before any arguments
194
+ liquidTagBackgroundInlineMarkup = ~(variableSegment space* "=") variableSegmentAsLookup argumentSeparatorOptionalComma tagArguments space*
195
+
196
+ liquidTagOpenCache = liquidTagOpenRule<"cache", liquidTagCacheMarkup>
197
+ liquidTagCacheMarkup = liquidExpression<delimTag> renderArguments
198
+
199
+ liquidTagOpenParseJson = liquidTagOpenRule<"parse_json", variableSegmentAsLookupMarkup>
200
+
201
+ liquidTagOpenTransaction = liquidTagOpenRule<"transaction", liquidTagOpenTransactionMarkup>
202
+ liquidTagOpenTransactionMarkup = tagArguments space*
203
+
204
+ liquidTagOpenTry = liquidTagOpenRule<"try", empty>
205
+
206
+ liquidTagCatch = liquidTagRule<"catch", variableSegmentAsLookupMarkup>
207
+
208
+ liquidTagLog = liquidTagRule<"log", liquidTagLogMarkup>
209
+ liquidTagLogMarkup = liquidExpression<delimTag> renderArguments
210
+
211
+ liquidTagPrint = liquidTagRule<"print", liquidTagEchoMarkup>
212
+
213
+ liquidTagReturn = liquidTagRule<"return", liquidTagEchoMarkup>
214
+
215
+ liquidTagYield = liquidTagRule<"yield", liquidTagYieldMarkup>
216
+ liquidTagYieldMarkup = liquidExpression<delimTag> space*
217
+
218
+ liquidTagSession = liquidTagRule<"session", liquidTagSessionMarkup>
219
+ liquidTagSessionMarkup = variableSegment space* "=" space* liquidExpression<delimTag> space*
220
+
221
+ liquidTagExport = liquidTagRule<"export", liquidTagExportMarkup>
222
+ liquidTagExportMarkup = nonemptyListOf<exportVariable, argumentSeparator> argumentSeparatorOptionalComma namedArgument<delimTag> space*
223
+ // Variable lookup that is NOT followed by ":" (to distinguish from named arguments)
224
+ exportVariable = variableSegmentAsLookup ~(space* ":")
225
+
226
+ liquidTagContext = liquidTagRule<"context", liquidTagContextMarkup>
227
+ liquidTagContextMarkup = tagArguments space*
228
+
229
+ liquidTagSignIn = liquidTagRule<"sign_in", liquidTagSignInMarkup>
230
+ liquidTagSignInMarkup = tagArguments space*
231
+
232
+ liquidTagRedirectTo = liquidTagRule<"redirect_to", liquidTagRedirectToMarkup>
233
+ liquidTagRedirectToMarkup = liquidExpression<delimTag> renderArguments
234
+
235
+ liquidTagIncludeForm = liquidTagRule<"include_form", liquidTagIncludeFormMarkup>
236
+ liquidTagIncludeFormMarkup = snippetExpression renderArguments
237
+
238
+ liquidTagSpamProtection = liquidTagRule<"spam_protection", liquidTagSpamProtectionMarkup>
239
+ liquidTagSpamProtectionMarkup = liquidExpression<delimTag> renderArguments
240
+
241
+ liquidTagThemeRenderRc = liquidTagRule<"theme_render_rc", liquidTagRenderMarkup>
242
+
243
+ liquidTagResponseStatus = liquidTagRule<"response_status", liquidTagResponseStatusMarkup>
244
+ liquidTagResponseStatusMarkup = liquidNumber space*
245
+
246
+ liquidTagResponseHeaders = liquidTagRule<"response_headers", liquidTagResponseHeadersMarkup>
247
+ liquidTagResponseHeadersMarkup = liquidExpression<delimTag> space*
248
+
249
+ liquidTagRollback = liquidTagRule<"rollback", empty>
250
+
251
+ renderArguments = (argumentSeparatorOptionalComma tagArguments) (space* ",")? space*
252
+ completionModeRenderArguments = (argumentSeparatorOptionalComma tagArguments) (space* ",")? space* (argumentSeparator? liquidVariableLookup<delimTag> space*)?
253
+ snippetExpression = liquidString<delimTag> | variableSegmentAsLookup
254
+ partialExpression = liquidString<delimTag> | variableSegmentAsLookup
255
+ renderVariableExpression = space+ ("for" | "with") space+ liquidExpression<delimTag>
256
+ renderAliasExpression = space+ "as" space+ variableSegment
257
+
258
+ // Exclude graphql and background from base case since they have special handling
259
+ liquidTagOpenBaseCase = liquidTagOpenRule<blockNameNotSpecial, tagMarkup>
260
+ blockNameNotSpecial =
261
+ ( "form"
262
+ | "paginate"
263
+ | "capture"
264
+ | "case"
265
+ | "for"
266
+ | "ifchanged"
267
+ | "if"
268
+ | "unless"
269
+ | "tablerow"
270
+ // platformos blocks that need base case handling
271
+ | "cache"
272
+ | "parse_json"
273
+ | "transaction"
274
+ | "try"
275
+ ) endOfIdentifier
276
+
277
+ liquidTagOpenForm = liquidTagOpenRule<"form", liquidTagOpenFormMarkup>
278
+ liquidTagOpenFormMarkup = arguments<delimTag> (space* ",")? space*
279
+
280
+ liquidTagOpenFor = liquidTagOpenRule<"for", liquidTagOpenForMarkup>
281
+ liquidTagOpenForMarkup =
282
+ variableSegment space* "in" space* liquidExpression<delimTag>
283
+ (space* "reversed")? argumentSeparatorOptionalComma
284
+ tagArguments (space* ",")? space*
285
+
286
+ // It's the same, the difference is support for different named arguments<delim>
287
+ liquidTagOpenTablerow = liquidTagOpenRule<"tablerow", liquidTagOpenForMarkup>
288
+
289
+ liquidTagOpenCase = liquidTagOpenRule<"case", liquidTagOpenCaseMarkup>
290
+ liquidTagOpenCaseMarkup = liquidExpression<delimTag> space*
291
+
292
+ liquidTagWhen = liquidTagRule<"when", liquidTagWhenMarkup>
293
+ liquidTagWhenMarkup = nonemptyListOf<liquidExpression<delimTag>, whenMarkupSep> space*
294
+ whenMarkupSep = space* ("," | "or" ~identifier) space*
295
+
296
+ liquidTagOpenIf = liquidTagOpenRule<"if", liquidTagOpenConditionalMarkup>
297
+ liquidTagOpenUnless = liquidTagOpenRule<"unless", liquidTagOpenConditionalMarkup>
298
+ liquidTagElsif = liquidTagRule<"elsif", liquidTagOpenConditionalMarkup>
299
+
300
+ liquidTagBreak = liquidTagRule<"break", empty>
301
+ liquidTagContinue = liquidTagRule<"continue", empty>
302
+ liquidTagElse = liquidTagRule<"else", empty>
303
+
304
+ liquidTagOpenConditionalMarkup = nonemptyListOf<condition<delimTag>, conditionSeparator> space*
305
+ conditionSeparator = &logicalOperator
306
+ condition<delim> = logicalOperator? space* (comparison<delim> | liquidExpression<delim>) space*
307
+ logicalOperator = ("and" | "or") ~identifier
308
+ comparison<delim> = liquidExpression<delim> space* comparator space* liquidExpression<delim>
309
+ comparator =
310
+ ( "=="
311
+ | "!="
312
+ | ">="
313
+ | "<="
314
+ | ">"
315
+ | "<")
316
+ | ("contains" ~identifier)
317
+
318
+ liquidTagOpenPaginate = liquidTagOpenRule<"paginate", liquidTagOpenPaginateMarkup>
319
+ liquidTagOpenPaginateMarkup =
320
+ liquidExpression<delimTag> space+ "by" space+ liquidExpression<delimTag> argumentSeparatorOptionalComma tagArguments (space* ",")? space*
321
+
322
+ liquidDrop = "{{" "-"? space* liquidDropCases "-"? "}}"
323
+ liquidDropCases = liquidVariable<delimVO> | liquidDropBaseCase
324
+ liquidDropBaseCase = anyExceptStar<delimVO>
325
+ liquidInlineComment = "{%" "-"? space* "#" space? tagMarkup "-"? "%}"
326
+
327
+ liquidRawTag =
328
+ | liquidRawTagImpl<"raw">
329
+ | liquidRawTagImpl<"javascript">
330
+ | liquidRawTagImpl<"schema">
331
+ | liquidRawTagImpl<"stylesheet">
332
+ | liquidRawTagImpl<"style">
333
+ liquidRawTagImpl<name> =
334
+ "{%" "-"? space* (name endOfIdentifier) space* tagMarkup "-"? "%}"
335
+ anyExceptStar<liquidRawTagClose<name>>
336
+ "{%" "-"? space* "end" (name endOfIdentifier) space* "-"? "%}"
337
+ liquidRawTagClose<name> =
338
+ "{%" "-"? space* "end" (name endOfIdentifier) space* "-"? "%}"
339
+
340
+ liquidBlockComment =
341
+ commentBlockStart
342
+ (liquidBlockComment | anyExceptPlus<(commentBlockStart | commentBlockEnd)>)*
343
+ commentBlockEnd
344
+ commentBlockStart = "{%" "-"? space* ("comment" endOfIdentifier) space* tagMarkup "-"? "%}"
345
+ commentBlockEnd = "{%" "-"? space* ("endcomment" endOfIdentifier) space* tagMarkup "-"? "%}"
346
+
347
+ liquidDoc =
348
+ liquidDocStart
349
+ liquidDocBody
350
+ liquidDocEnd
351
+
352
+ liquidDocStart = "{%" "-"? space* ("doc" endOfIdentifier) space* tagMarkup "-"? "%}"
353
+ liquidDocEnd = "{%" "-"? space* ("enddoc" endOfIdentifier) space* tagMarkup "-"? "%}"
354
+ liquidDocBody = anyExceptStar<(liquidDocStart | liquidDocEnd)>
355
+
356
+ // In order for the grammar to "fallback" to the base case, this
357
+ // rule must pass if and only if we support what we parse. This
358
+ // implies that—since we don't support filters yet—we have a
359
+ // positive lookahead on "-}}" or "}}" in the rule. We do this
360
+ // because we'd otherwise positively match the following string
361
+ // instead of falling back to the other rule:
362
+ // {{ 'string' | some_filter }}
363
+ liquidVariable<delim> = liquidComplexExpression<delim> liquidFilter<delim>* space* &delim
364
+
365
+ liquidExpression<delim> =
366
+ | liquidString<delim>
367
+ | liquidNumber
368
+ | liquidLiteral
369
+ | liquidRange<delim>
370
+ | liquidVariableLookup<delim>
371
+
372
+ liquidComplexExpression<delim> =
373
+ | liquidBooleanExpression<delim>
374
+ | liquidExpression<delim>
375
+
376
+ liquidBooleanExpression<delim> = booleanExpressionCondition<delim> listOf<booleanExpressionSubsequentCondition<delim>, conditionSeparator>
377
+
378
+ // This might over-capture things since the conditions below can contain any ${"`"}liquidExpression${"`"}s.
379
+ // We will need to clean this up in CST. If we restrict the conditions to only contain comparisons and
380
+ // variable lookups, we can't support "truthy" expressions like ${"`"}{{ some_var and 'this' }}${"`"}
381
+ booleanExpressionSubsequentCondition<delim> = space* logicalOperator space* (comparison<delim> | liquidExpression<delim>) space*
382
+ booleanExpressionCondition<delim> = comparison<delim> | liquidExpression<delim>
383
+
384
+ liquidString<delim> = liquidSingleQuotedString<delim> | liquidDoubleQuotedString<delim>
385
+ liquidSingleQuotedString<delim> = "'" anyExceptStar<("'"| delim)> "'"
386
+ liquidDoubleQuotedString<delim> = "\"" anyExceptStar<("\""| delim)> "\""
387
+
388
+ liquidNumber = liquidFloat | liquidInteger
389
+ liquidInteger = "-"? digit+
390
+ liquidFloat = "-"? digit+ "." digit+
391
+
392
+ liquidLiteral =
393
+ ( "true"
394
+ | "false"
395
+ | "blank"
396
+ | "empty"
397
+ | "nil"
398
+ | "null"
399
+ ) endOfIdentifier
400
+
401
+ liquidRange<delim> =
402
+ "(" space* liquidExpression<delim> space* ".." space* liquidExpression<delim> space* ")"
403
+
404
+ liquidVariableLookup<delim> =
405
+ | variableSegment lookup<delim>*
406
+ | empty lookup<delim>+
407
+ lookup<delim> =
408
+ | indexLookup<delim>
409
+ | dotLookup
410
+ indexLookup<delim> = space* "[" space* liquidExpression<delim> space* "]"
411
+ dotLookup = space* "." space* identifier
412
+
413
+ liquidFilter<delim> = space* "|" space* identifier (space* ":" space* arguments<delim> (space* ",")?)?
414
+
415
+ arguments<delim> = nonemptyOrderedListOf<positionalArgument<delim>, namedArgument<delim>, argumentSeparator>
416
+ argumentSeparator = space* "," space*
417
+ argumentSeparatorOptionalComma = space* ","? space*
418
+ positionalArgument<delim> = liquidExpression<delim> ~(space* ":")
419
+ namedArgument<delim> = variableSegment space* ":" space* liquidExpression<delim>
420
+ tagArguments = listOf<namedArgument<delimTag>, argumentSeparatorOptionalComma>
421
+ filterArguments<delim> =
422
+ | complexArguments<delim>
423
+ | simpleArgument<delim>
424
+ complexArguments<delim> = arguments<delim> (space* "," space* simpleArgument<delim>)?
425
+ simpleArgument<delim> = liquidVariableLookup<delim>
426
+
427
+ variableSegment = (letter | "_") (~endOfTagName identifierCharacter)*
428
+ variableSegmentAsLookup = variableSegment
429
+ identifier = variableSegment "?"?
430
+
431
+ tagMarkup = anyExceptStar<delimTag>
432
+
433
+ liquidTagName =
434
+ letter (alnum | "_")*
435
+
436
+ blockName =
437
+ // Shopify blocks
438
+ ( "form"
439
+ | "paginate"
440
+ // Base blocks
441
+ | "capture"
442
+ | "case"
443
+ | "for"
444
+ | "graphql"
445
+ | "ifchanged"
446
+ | "if"
447
+ | "unless"
448
+ | "tablerow"
449
+ // platformos blocks
450
+ | "background"
451
+ | "cache"
452
+ | "parse_json"
453
+ | "transaction"
454
+ | "try"
455
+ ) endOfIdentifier
456
+
457
+ delimTag = "-%}" | "%}"
458
+ delimVO = "-}}" | "}}"
459
+ }
460
+
461
+ LiquidStatement <: Liquid {
462
+ Node := listOf<LiquidStatement, statementSep> (space | newline)*
463
+
464
+ // This is the big brains moment: we redefine space to exclude newlines.
465
+ //
466
+ // Which means that all our other Liquid rules can be reused
467
+ // without modification(!)
468
+ //
469
+ // We don't need to maintain rules like this:
470
+ // - liquidVariable<space>
471
+ // - liquidExpression<space>
472
+ // - variableLookup<space>
473
+ // - ... long list of stuff that takes space as param
474
+ // - liquidString<space>
475
+ //
476
+ // All we need is this little, VERY IMPORTANT, part right here that
477
+ // make it so we can parse the same way in Liquid tags.
478
+ //
479
+ // I'm putting in this huge comment so that it's more obvious.
480
+ space := " " | "\t"
481
+
482
+ LiquidStatement =
483
+ | liquidBlockComment
484
+ | liquidRawTag
485
+ | liquidTagClose
486
+ | liquidTagOpen
487
+ | liquidTag
488
+ | liquidInlineComment
489
+
490
+ liquidTagOpenRule<name, markup>
491
+ := (name ~identifierCharacter) space* markup &liquidStatementEnd
492
+
493
+ liquidTagRule<name, markup>
494
+ := (name ~identifierCharacter) space* markup &liquidStatementEnd
495
+
496
+ liquidTagClose
497
+ := "end" (blockName ~identifierCharacter) space* tagMarkup &liquidStatementEnd
498
+
499
+ liquidRawTagImpl<name>
500
+ := (name ~identifierCharacter) space* tagMarkup newline
501
+ anyExceptStar<liquidRawTagClose<name>>
502
+ "end" name space* &liquidStatementEnd
503
+
504
+ liquidRawTagClose<name>
505
+ := "end" name space* &liquidStatementEnd
506
+
507
+ liquidBlockComment :=
508
+ commentBlockStart statementSep
509
+ (listOf<liquidCommentBlockStatement, statementSep> statementSep)?
510
+ commentBlockEnd
511
+
512
+ liquidCommentBlockStatement =
513
+ | liquidBlockComment
514
+ | nonTerminalCommentLine
515
+
516
+ commentBlockStart
517
+ := ("comment" ~identifierCharacter) space* tagMarkup
518
+
519
+ commentBlockEnd
520
+ := ("endcomment" ~identifierCharacter) space* tagMarkup
521
+
522
+ nonTerminalCommentLine
523
+ = ~commentBlockEnd anyExceptPlus<newline>
524
+
525
+ liquidInlineComment
526
+ := "#" space? tagMarkup &liquidStatementEnd
527
+
528
+ tagMarkup := anyExceptStar<liquidStatementEnd>
529
+
530
+ // trailing whitespace, newline, + anything else before the next tag
531
+ statementSep = space* newline (space | newline)*
532
+
533
+ liquidStatementEnd = newline | end
534
+ delimTag := liquidStatementEnd
535
+ }
536
+
537
+ LiquidDoc <: Helpers {
538
+ Node := ImplicitDescription (LiquidDocNode | TextNode)*
539
+ LiquidDocNode =
540
+ | paramNode
541
+ | exampleNode
542
+ | descriptionNode
543
+ | promptNode
544
+ | fallbackNode
545
+
546
+ endOfDescription = strictSpace* openControl
547
+ descriptionContent = anyExceptStar<endOfDescription>
548
+ ImplicitDescription = descriptionContent
549
+
550
+ // By default, space matches new lines as well. We override it here to make writing rules easier.
551
+ strictSpace = " " | "\t"
552
+ // We use this as an escape hatch to stop matching TextNode and try again when one of these characters is encountered
553
+ openControl:= strictSpace* ("@" | end)
554
+ // List of supported tags we use to identify boundaries
555
+ supportedTags = "@prompt" | "@example" | "@description" | "@param"
556
+
557
+
558
+ paramNode = "@param" strictSpace* paramType? strictSpace* (optionalParamName | paramName) (strictSpace* "-")? strictSpace* paramDescription
559
+ paramType = "{" strictSpace* paramTypeContent strictSpace* "}"
560
+ paramTypeContent = anyExceptStar<("}"| strictSpace)>
561
+
562
+ paramName = textValue
563
+ optionalParamName = "[" strictSpace* textValue strictSpace* "]"
564
+ textValue = identifierCharacter+
565
+
566
+ paramDescription = (~"]" anyExceptStar<endOfParam>)
567
+ endOfParam = strictSpace* (newline | end)
568
+
569
+ // Prompt node is system-controlled, so we don't strip the leading spaces to maintain indentation
570
+ promptNode = "@prompt" multilineTextContent
571
+ exampleNode = "@example" space* multilineTextContent
572
+ descriptionNode = "@description" space* multilineTextContent
573
+
574
+ // We want multilineTextContent to be free-form, so instead of terminating the match at "@" we explicitly look for a suppported tag
575
+ // This means that malformed tags will be considered part of the multilineTextContent
576
+ multilineTextContent = anyExceptStar<endOfMultilineText>
577
+ endOfMultilineText = strictSpace* (supportedTags | end)
578
+
579
+ fallbackNode = "@" anyExceptStar<endOfParam>
580
+ }
581
+
582
+ LiquidHTML <: Liquid {
583
+ Node := yamlFrontmatter? (HtmlNode | liquidNode | TextNode)*
584
+ openControl += "<"
585
+
586
+ yamlFrontmatter =
587
+ "---" newline anyExceptStar<"---"> "---" newline
588
+
589
+ HtmlNode =
590
+ | HtmlDoctype
591
+ | HtmlComment
592
+ | HtmlRawTag
593
+ | HtmlVoidElement
594
+ | HtmlSelfClosingElement
595
+ | HtmlTagClose
596
+ | HtmlTagOpen
597
+
598
+ // https://html.spec.whatwg.org/multipage/syntax.html#the-doctype
599
+ HtmlDoctype =
600
+ #("<!" caseInsensitive<"doctype"> space+ caseInsensitive<"html">) legacyDoctypeString? ">"
601
+ legacyDoctypeString
602
+ = anyExceptPlus<">">
603
+
604
+ HtmlComment = "<!--" #(anyExceptStar<"-->"> "-->")
605
+
606
+ // These are black holes, we'll ignore what's in them
607
+ HtmlRawTag =
608
+ | HtmlRawTagImpl<"script">
609
+ | HtmlRawTagImpl<"style">
610
+ | HtmlRawTagImpl<"svg">
611
+
612
+ HtmlRawTagImpl<name> =
613
+ TagStart<name>
614
+ (HtmlRawTagImpl<name> | AnyExceptPlus<(TagStart<name> | TagEnd<name>)>)*
615
+ TagEnd<name>
616
+ TagStart<name> = "<" name AttrList ">"
617
+ TagEnd<name> = "</" name ">"
618
+
619
+ HtmlVoidElement =
620
+ #("<" voidElementName &(space | "/" | ">")) AttrList "/"? ">"
621
+
622
+ HtmlSelfClosingElement =
623
+ #("<" tagName) AttrList "/>"
624
+
625
+ HtmlTagOpen =
626
+ #("<" tagName) AttrList ">"
627
+
628
+ HtmlTagClose =
629
+ #("</" tagName) ">"
630
+
631
+ tagName = leadingTagNamePart trailingTagNamePart*
632
+
633
+ // The difference here is that the first text part must start
634
+ // with a letter, but trailing text parts don't have that
635
+ // requirement
636
+ leadingTagNamePart =
637
+ | liquidDrop
638
+ | leadingTagNameTextNode
639
+
640
+ trailingTagNamePart =
641
+ | liquidDrop
642
+ | trailingTagNameTextNode
643
+
644
+ leadingTagNameTextNode = letter (alnum | "-" | ":")*
645
+ trailingTagNameTextNode = (alnum | "-" | ":")+
646
+
647
+ AttrList = Attr*
648
+
649
+ Attr =
650
+ AttrSingleQuoted | AttrDoubleQuoted | AttrUnquoted | liquidNode | attrEmpty
651
+
652
+ attrEmpty = attrName
653
+
654
+ AttrUnquoted = attrName "=" attrUnquotedValue
655
+ AttrSingleQuoted = attrName "=" singleQuote #(attrSingleQuotedValue singleQuote)
656
+ AttrDoubleQuoted = attrName "=" doubleQuote #(attrDoubleQuotedValue doubleQuote)
657
+
658
+ attrName = (liquidDrop | attrNameTextNode)+
659
+
660
+ // https://html.spec.whatwg.org/#attributes-2
661
+ attrNameTextNode = anyExceptPlus<(space | quotes | "=" | ">" | "/>" | "{{" | "{%" | controls | noncharacters)>
662
+ attrUnquotedValue = (liquidDrop | attrUnquotedTextNode)*
663
+ attrSingleQuotedValue = (liquidNode | attrSingleQuotedTextNode)*
664
+ attrDoubleQuotedValue = (liquidNode | attrDoubleQuotedTextNode)*
665
+
666
+ attrUnquotedTextNode = anyExceptPlus<(space | quotes | "=" | "<" | ">" | "${"`"}" | "{{" | "{%")>
667
+ attrSingleQuotedTextNode = anyExceptPlus<(singleQuote | "{{" | "{%")>
668
+ attrDoubleQuotedTextNode = anyExceptPlus<(doubleQuote | "{{" | "{%")>
669
+
670
+ quotes = singleQuote | doubleQuote
671
+
672
+ // https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-element
673
+ voidElementName =
674
+ ( caseInsensitive<"area">
675
+ | caseInsensitive<"base">
676
+ | caseInsensitive<"br">
677
+ | caseInsensitive<"col">
678
+ | caseInsensitive<"command">
679
+ | caseInsensitive<"embed">
680
+ | caseInsensitive<"hr">
681
+ | caseInsensitive<"img">
682
+ | caseInsensitive<"input">
683
+ | caseInsensitive<"keygen">
684
+ | caseInsensitive<"link">
685
+ | caseInsensitive<"meta">
686
+ | caseInsensitive<"param">
687
+ | caseInsensitive<"source">
688
+ | caseInsensitive<"track">
689
+ | caseInsensitive<"wbr">
690
+ ) ~identifierCharacter
691
+ }
692
+
693
+ StrictLiquid <: Liquid {
694
+ liquidTag := liquidTagStrict
695
+ liquidTagOpen := liquidTagOpenStrict
696
+ }
697
+
698
+ StrictLiquidStatement <: LiquidStatement {
699
+ liquidTag := liquidTagStrict
700
+ liquidTagOpen := liquidTagOpenStrict
701
+ }
702
+
703
+ StrictLiquidHTML <: LiquidHTML {
704
+ liquidTag := liquidTagStrict
705
+ liquidTagOpen := liquidTagOpenStrict
706
+ }
707
+
708
+ WithPlaceholderLiquid <: Liquid {
709
+ liquidFilter<delim> := space* "|" space* identifier (space* ":" space* filterArguments<delim> (space* ",")?)?
710
+ liquidTagContentForMarkup :=
711
+ contentForType (argumentSeparatorOptionalComma completionModeContentForTagArgument) (space* ",")? space*
712
+ liquidTagRenderMarkup :=
713
+ snippetExpression renderVariableExpression? renderAliasExpression? completionModeRenderArguments
714
+ liquidTagName := (letter | "█") (alnum | "_")*
715
+ variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
716
+ liquidDoc :=
717
+ liquidDocStart
718
+ liquidDocBody
719
+ liquidDocEnd?
720
+ }
721
+
722
+ WithPlaceholderLiquidStatement <: LiquidStatement {
723
+ liquidFilter<delim> := space* "|" space* identifier (space* ":" space* filterArguments<delim> (space* ",")?)?
724
+ liquidTagContentForMarkup :=
725
+ contentForType (argumentSeparatorOptionalComma completionModeContentForTagArgument) (space* ",")? space*
726
+ liquidTagRenderMarkup :=
727
+ snippetExpression renderVariableExpression? renderAliasExpression? completionModeRenderArguments
728
+ liquidTagName := (letter | "█") (alnum | "_")*
729
+ variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
730
+ liquidDoc :=
731
+ liquidDocStart
732
+ liquidDocBody
733
+ liquidDocEnd?
734
+ }
735
+
736
+ WithPlaceholderLiquidHTML <: LiquidHTML {
737
+ liquidFilter<delim> := space* "|" space* identifier (space* ":" space* filterArguments<delim> (space* ",")?)?
738
+ liquidTagContentForMarkup :=
739
+ contentForType (argumentSeparatorOptionalComma completionModeContentForTagArgument) (space* ",")? space*
740
+ liquidTagRenderMarkup :=
741
+ snippetExpression renderVariableExpression? renderAliasExpression? completionModeRenderArguments
742
+ liquidTagName := (letter | "█") (alnum | "_")*
743
+ variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
744
+ leadingTagNameTextNode := (letter | "█") (alnum | "-" | ":" | "█")*
745
+ trailingTagNameTextNode := (alnum | "-" | ":" | "█")+
746
+ liquidDoc :=
747
+ liquidDocStart
748
+ liquidDocBody
749
+ liquidDocEnd?
750
+ }
751
+ `;