@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.
- package/README.md +62 -0
- package/dist/conditional-comment.d.ts +5 -0
- package/dist/conditional-comment.js +16 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +57 -0
- package/dist/grammar.d.ts +16 -0
- package/dist/grammar.js +42 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +28 -0
- package/dist/stage-1-cst.d.ts +477 -0
- package/dist/stage-1-cst.js +1104 -0
- package/dist/stage-2-ast.d.ts +794 -0
- package/dist/stage-2-ast.js +1513 -0
- package/dist/types.d.ts +124 -0
- package/dist/types.js +146 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +31 -0
- package/grammar/liquid-html.ohm +750 -0
- package/grammar/liquid-html.ohm.js +751 -0
- package/package.json +41 -0
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
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
|
+
}
|