@witchcraft/expressit 0.0.2 → 0.1.0

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.
Files changed (124) hide show
  1. package/README.md +6 -4
  2. package/dist/Lexer.d.ts +146 -0
  3. package/dist/Lexer.d.ts.map +1 -0
  4. package/dist/Lexer.js +960 -0
  5. package/dist/Parser.d.ts +140 -0
  6. package/dist/Parser.d.ts.map +1 -0
  7. package/dist/Parser.js +668 -0
  8. package/dist/ast/builders/token.js +1 -1
  9. package/dist/ast/handlers.d.ts +3 -3
  10. package/dist/ast/handlers.d.ts.map +1 -1
  11. package/dist/ast/index.d.ts.map +1 -1
  12. package/dist/examples/index.d.ts +2 -0
  13. package/dist/examples/index.d.ts.map +1 -0
  14. package/dist/examples/index.js +4 -0
  15. package/dist/examples/shortcutContextParser.d.ts +2 -1
  16. package/dist/examples/shortcutContextParser.d.ts.map +1 -1
  17. package/dist/examples/shortcutContextParser.js +9 -5
  18. package/dist/helpers/errors.d.ts.map +1 -1
  19. package/dist/helpers/errors.js +3 -1
  20. package/dist/helpers/index.d.ts.map +1 -1
  21. package/dist/helpers/parser/checkParserOpts.d.ts.map +1 -1
  22. package/dist/helpers/parser/checkParserOpts.js +3 -2
  23. package/dist/helpers/parser/extractPosition.d.ts +2 -6
  24. package/dist/helpers/parser/extractPosition.d.ts.map +1 -1
  25. package/dist/helpers/parser/extractPosition.js +3 -3
  26. package/dist/helpers/parser/getUnclosedRightParenCount.d.ts +2 -3
  27. package/dist/helpers/parser/getUnclosedRightParenCount.d.ts.map +1 -1
  28. package/dist/helpers/parser/getUnclosedRightParenCount.js +4 -4
  29. package/dist/index.d.ts +1 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +3 -5
  32. package/dist/methods/autocomplete.d.ts.map +1 -1
  33. package/dist/methods/autocomplete.js +1 -1
  34. package/dist/methods/autoreplace.js +1 -1
  35. package/dist/methods/autosuggest.js +1 -1
  36. package/dist/methods/evaluate.d.ts.map +1 -1
  37. package/dist/methods/evaluate.js +3 -1
  38. package/dist/methods/getIndexes.d.ts.map +1 -1
  39. package/dist/methods/getIndexes.js +2 -1
  40. package/dist/methods/normalize.d.ts +0 -2
  41. package/dist/methods/normalize.d.ts.map +1 -1
  42. package/dist/methods/normalize.js +2 -3
  43. package/dist/methods/validate.d.ts.map +1 -1
  44. package/dist/methods/validate.js +3 -1
  45. package/dist/package.json.js +44 -37
  46. package/dist/types/ast.d.ts +2 -8
  47. package/dist/types/ast.d.ts.map +1 -1
  48. package/dist/types/errors.d.ts +5 -17
  49. package/dist/types/errors.d.ts.map +1 -1
  50. package/dist/types/errors.js +0 -1
  51. package/dist/types/parser.d.ts +6 -2
  52. package/dist/types/parser.d.ts.map +1 -1
  53. package/dist/utils/extractTokens.js +1 -1
  54. package/dist/utils/getCursorInfo.d.ts +2 -2
  55. package/dist/utils/getCursorInfo.d.ts.map +1 -1
  56. package/dist/utils/getCursorInfo.js +3 -2
  57. package/dist/utils/getOppositeDelimiter.d.ts.map +1 -1
  58. package/dist/utils/getOppositeDelimiter.js +1 -1
  59. package/dist/utils/prettyAst.d.ts.map +1 -1
  60. package/dist/utils/prettyAst.js +15 -9
  61. package/package.json +42 -37
  62. package/src/Lexer.ts +704 -0
  63. package/src/Parser.ts +972 -0
  64. package/src/ast/builders/array.ts +2 -2
  65. package/src/ast/builders/condition.ts +1 -1
  66. package/src/ast/builders/expression.ts +1 -1
  67. package/src/ast/builders/group.ts +1 -1
  68. package/src/ast/builders/index.ts +1 -1
  69. package/src/ast/builders/pos.ts +1 -1
  70. package/src/ast/builders/token.ts +2 -2
  71. package/src/ast/builders/type.ts +1 -1
  72. package/src/ast/builders/variable.ts +1 -1
  73. package/src/ast/classes/ConditionNode.ts +1 -1
  74. package/src/ast/classes/ErrorToken.ts +1 -1
  75. package/src/ast/classes/ValidToken.ts +2 -2
  76. package/src/ast/classes/index.ts +1 -1
  77. package/src/ast/handlers.ts +6 -6
  78. package/src/ast/index.ts +2 -2
  79. package/src/examples/index.ts +3 -0
  80. package/src/examples/shortcutContextParser.ts +11 -6
  81. package/src/helpers/errors.ts +5 -3
  82. package/src/helpers/general/defaultConditionNormalizer.ts +1 -1
  83. package/src/helpers/general/index.ts +1 -1
  84. package/src/helpers/index.ts +3 -2
  85. package/src/helpers/parser/checkParserOpts.ts +13 -12
  86. package/src/helpers/parser/extractPosition.ts +4 -8
  87. package/src/helpers/parser/getUnclosedRightParenCount.ts +6 -6
  88. package/src/helpers/parser/index.ts +1 -1
  89. package/src/helpers/parser/parseParserOptions.ts +1 -1
  90. package/src/index.ts +2 -2
  91. package/src/methods/autocomplete.ts +5 -5
  92. package/src/methods/autoreplace.ts +2 -2
  93. package/src/methods/autosuggest.ts +3 -3
  94. package/src/methods/evaluate.ts +4 -2
  95. package/src/methods/getIndexes.ts +2 -1
  96. package/src/methods/normalize.ts +3 -4
  97. package/src/methods/validate.ts +4 -2
  98. package/src/types/ast.ts +2 -9
  99. package/src/types/errors.ts +12 -22
  100. package/src/types/parser.ts +6 -4
  101. package/src/utils/extractTokens.ts +1 -1
  102. package/src/utils/getCursorInfo.ts +6 -4
  103. package/src/utils/getOppositeDelimiter.ts +5 -2
  104. package/src/utils/prettyAst.ts +5 -3
  105. package/dist/examples/advancedValueComparer.d.ts +0 -3
  106. package/dist/examples/advancedValueComparer.d.ts.map +0 -1
  107. package/dist/examples/advancedValueComparer.js +0 -28
  108. package/dist/grammar/ParserBase.d.ts +0 -51
  109. package/dist/grammar/ParserBase.d.ts.map +0 -1
  110. package/dist/grammar/ParserBase.js +0 -516
  111. package/dist/grammar/createTokens.d.ts +0 -56
  112. package/dist/grammar/createTokens.d.ts.map +0 -1
  113. package/dist/grammar/createTokens.js +0 -843
  114. package/dist/grammar/index.d.ts +0 -3
  115. package/dist/grammar/index.d.ts.map +0 -1
  116. package/dist/grammar/index.js +0 -6
  117. package/dist/parser.d.ts +0 -58
  118. package/dist/parser.d.ts.map +0 -1
  119. package/dist/parser.js +0 -136
  120. package/src/examples/advancedValueComparer.ts +0 -31
  121. package/src/grammar/ParserBase.ts +0 -715
  122. package/src/grammar/createTokens.ts +0 -512
  123. package/src/grammar/index.ts +0 -4
  124. package/src/parser.ts +0 -183
package/src/Parser.ts ADDED
@@ -0,0 +1,972 @@
1
+ import { isArray } from "@alanscodelog/utils/isArray"
2
+ import { isWhitespace } from "@alanscodelog/utils/isWhitespace"
3
+ import { mixin } from "@alanscodelog/utils/mixin"
4
+ import { setReadOnly } from "@alanscodelog/utils/setReadOnly"
5
+ import type { AddParameters, Mixin } from "@alanscodelog/utils/types"
6
+ import { unreachable } from "@alanscodelog/utils/unreachable"
7
+
8
+ import { pos } from "./ast/builders/pos.js"
9
+ import { ArrayNode } from "./ast/classes/ArrayNode.js"
10
+ import { ConditionNode } from "./ast/classes/ConditionNode.js"
11
+ import { ErrorToken } from "./ast/classes/ErrorToken.js"
12
+ import { ExpressionNode } from "./ast/classes/ExpressionNode.js"
13
+ import { GroupNode } from "./ast/classes/GroupNode.js"
14
+ import type { ValidToken } from "./ast/classes/ValidToken.js"
15
+ import { VariableNode } from "./ast/classes/VariableNode.js"
16
+ import * as handle from "./ast/handlers.js"
17
+ import { checkParserOpts } from "./helpers/parser/checkParserOpts.js"
18
+ import { extractPosition } from "./helpers/parser/extractPosition.js"
19
+ import { getUnclosedRightParenCount } from "./helpers/parser/getUnclosedRightParenCount.js"
20
+ import { parseParserOptions } from "./helpers/parser/parseParserOptions.js"
21
+ import { seal } from "./helpers/parser/seal.js"
22
+ import { $C, $T, Lexer,type RealTokenType, type Token, type TokenCategoryType, type TokenType } from "./Lexer.js"
23
+ import { AutocompleteMixin } from "./methods/autocomplete.js"
24
+ import { AutoreplaceMixin } from "./methods/autoreplace.js"
25
+ import { Autosuggest } from "./methods/autosuggest.js"
26
+ import { EvaluateMixin } from "./methods/evaluate.js"
27
+ import { GetBestIndexesMixin } from "./methods/getBestIndex.js"
28
+ import { GetIndexMixin } from "./methods/getIndexes.js"
29
+ import { NormalizeMixin } from "./methods/normalize.js"
30
+ import { ValidateMixin } from "./methods/validate.js"
31
+ import type { ParserResults } from "./types/ast.js"
32
+ import { type AnyToken, type Position, TOKEN_TYPE } from "./types/index.js"
33
+ import type { FullParserOptions, ParserOptions } from "./types/parser.js"
34
+
35
+ /**
36
+ * The parser's methods are often long and have a lot of documentation per method, so it's methods have been split into mixins. They can be found in the `./methods` folder.
37
+ *
38
+ * Writing from within any of these methods is like writing a method from here except:
39
+ * - `this` calls are wrapped in `(this as any as Parser<T>)`
40
+ * - private method/property access requires `// @ts-expect-error`.
41
+ * - recursion with hidden parameters requires re-typing this (see evaluate/validate for examples) since otherwise if we only retyped the function it would become unbound from `this`.
42
+ *
43
+ * Docs work like normal (on methods). From the outside, users of the library cannot even tell the class is composed of mixins.
44
+ */
45
+
46
+
47
+ export interface Parser<T extends {} = {}> extends Mixin<
48
+ | AutocompleteMixin<T>
49
+ | AutoreplaceMixin
50
+ | Autosuggest<T>
51
+ | EvaluateMixin<T>
52
+ | ValidateMixin<T>
53
+ | NormalizeMixin<T>
54
+ | GetIndexMixin<T>
55
+ | GetBestIndexesMixin
56
+ >,
57
+ AutocompleteMixin<T>,
58
+ AutoreplaceMixin,
59
+ Autosuggest<T>,
60
+ EvaluateMixin<T>,
61
+ ValidateMixin < T >,
62
+ NormalizeMixin<T>,
63
+ GetIndexMixin<T>,
64
+ GetBestIndexesMixin
65
+ {}
66
+
67
+
68
+ /**
69
+ * Creates the main parser class which handles all functionality (evaluation, validation, etc).
70
+ */
71
+ export class Parser<T extends {} = {}> {
72
+ // needed for evaluate and validate so they are only checked on demand
73
+ private evaluationOptionsChecked: boolean = false
74
+
75
+ // eslint-disable-next-line @typescript-eslint/naming-convention
76
+ _checkEvaluationOptions(): void {
77
+ if (!this.evaluationOptionsChecked) {
78
+ checkParserOpts(this.options, true)
79
+ this.evaluationOptionsChecked = true
80
+ }
81
+ }
82
+
83
+ private validationOptionsChecked: boolean = false
84
+
85
+ // eslint-disable-next-line @typescript-eslint/naming-convention
86
+ _checkValidationOptions(): void {
87
+ if (!this.validationOptionsChecked) {
88
+ checkParserOpts(this.options, false, true)
89
+ this.validationOptionsChecked = true
90
+ }
91
+ }
92
+
93
+ options: FullParserOptions<T>
94
+
95
+ private readonly lexer: Lexer
96
+
97
+ private readonly $: Record<$T, RealTokenType<$T>>
98
+
99
+ private readonly $categories: Partial<Record<$C, TokenCategoryType<$C>>>
100
+
101
+ private readonly info: Pick<ReturnType<Lexer["calculateSymbolInfo"]>, "expandedSepAlsoCustom" | "customOpAlsoNegation">
102
+
103
+ constructor(options?: ParserOptions<T>) {
104
+ const opts = parseParserOptions<T>(options ?? {})
105
+ checkParserOpts<T>(opts)
106
+ this.options = opts
107
+ this.lexer = new Lexer(opts)
108
+ this.$ = this.lexer.$
109
+ this.$categories = this.lexer.$categories
110
+ this.info = {
111
+ expandedSepAlsoCustom: this.lexer.symbols.expandedSepAlsoCustom,
112
+ customOpAlsoNegation: this.lexer.symbols.customOpAlsoNegation,
113
+ }
114
+ }
115
+
116
+ state: {
117
+ rawInput: string
118
+ lexedTokens: Token<$T>[]
119
+ index: number
120
+ shift: number
121
+ } = {
122
+ rawInput: "",
123
+ lexedTokens: [],
124
+ index: 0,
125
+ shift: 0,
126
+ }
127
+
128
+ /**
129
+ * This is exposed mainly for debugging purposes. Use parse directly instead.
130
+ */
131
+ lex(input: string): {
132
+ tokens: Token<$T> []
133
+ shift: number
134
+ rawInput: string
135
+ } {
136
+ if (isWhitespace(input)) {
137
+ return { tokens: [], shift: 0, rawInput: input }
138
+ }
139
+ let lexed = this.lexer.tokenize(input)
140
+ /**
141
+ * The parser can't handle unmatched right parens (i.e. left is missing) so we just insert them and shift the locations of all the tokens. Then the parser is designed to ignore parenthesis we added at the start and just return undefined for that rule as if the parenthesis didn't exist.
142
+ */
143
+
144
+
145
+ const shift = getUnclosedRightParenCount(lexed)
146
+ const rawInput = input
147
+ if (shift) {
148
+ input = "(".repeat(shift) + input
149
+ lexed = this.lexer.tokenize(input)
150
+ }
151
+ const lexedTokens = lexed.filter(token => {
152
+ const tokenType = this.getTokenType(token.type)
153
+ if (tokenType) {
154
+ return !tokenType.skip
155
+ } else {
156
+ throw new Error(`Unknown token type ${token.type}`)
157
+ }
158
+ })
159
+ return { tokens: lexedTokens, shift, rawInput }
160
+ }
161
+
162
+ /**
163
+ * Parse an input string into an AST.
164
+ * It can also parse the result from `lex`, but that is really only for internal use.
165
+ */
166
+ parse(
167
+ input: string
168
+ | {
169
+ tokens: Token<$T> []
170
+ shift: number
171
+ rawInput: string
172
+ },
173
+ ): ParserResults {
174
+ // eslint-disable-next-line prefer-rest-params
175
+ const doSeal = arguments[1]?.seal ?? true
176
+ if (typeof input === "string" && isWhitespace(input)) {
177
+ return handle.token.value(undefined, { start: 0, end: 0 }) as any
178
+ }
179
+ const { tokens: lexedTokens, shift, rawInput } = typeof input === "string" ? this.lex(input) : input
180
+
181
+ this.state = {
182
+ rawInput,
183
+ shift,
184
+ index: -1,
185
+ lexedTokens,
186
+ }
187
+ const res = this.ruleMain()
188
+ if (doSeal) {
189
+ seal(res)
190
+ }
191
+ this.state = {
192
+ rawInput: "",
193
+ shift: 0,
194
+ index: -1,
195
+ lexedTokens: [],
196
+ }
197
+ return res
198
+ }
199
+
200
+ subParserOne?: Parser<T> & {
201
+ parse: AddParameters<Parser<T>["parse"], [{ seal: boolean }]>
202
+ }
203
+
204
+ subParserTwo?: Parser<T> & {
205
+ parse: AddParameters<Parser<T>["parse"], [{ seal: boolean }]>
206
+ }
207
+
208
+
209
+ createSubParserIfNotExists(opts: ParserOptions<T>, which: "One" | "Two" = "One"): Parser["subParserOne"] {
210
+ if (this[`subParser${which}`] === undefined) {
211
+ this[`subParser${which}`] = new Parser(opts)
212
+ }
213
+ return this[`subParser${which}`]!
214
+ }
215
+
216
+
217
+ transformCategoryToken<TC extends $C>(
218
+ token: Token,
219
+ categoryToken: TokenCategoryType<TC>,
220
+ ): Token<TC> {
221
+ return {
222
+ ...token,
223
+ type: categoryToken.type,
224
+ }
225
+ }
226
+
227
+ getCategoryTokens<TType extends $C>(
228
+ type: TType,
229
+ ): TokenCategoryType<TType>["entries"] | undefined {
230
+ return this.$categories[type as $C]?.entries as any
231
+ }
232
+
233
+ getTokenType(type: $T | $C): TokenType<$T> | undefined {
234
+ return this.$[type as any as $T] as any
235
+ }
236
+
237
+ isExactType<TType extends $T>(token: Token, type: TType): token is Token<TType> {
238
+ if (this.$[type]) {
239
+ return this.isType(token, type)
240
+ }
241
+ return false
242
+ }
243
+
244
+ isType(token: Token | undefined, type: $T | $C): boolean {
245
+ if (token === undefined) return false
246
+ if (token.type === type) return true
247
+ const tokenType = this.getTokenType(token.type)
248
+
249
+ if (tokenType?.type === type) return true
250
+ const category = this.$categories[type as $C]
251
+ if (category?.entries[token.type as $T] !== undefined) {
252
+ return true
253
+ }
254
+ return false
255
+ }
256
+
257
+ createErrorToken(type: $T, index?: number): Token {
258
+ return {
259
+ type,
260
+ value: "",
261
+ startOffset: index ?? this.state.index,
262
+ endOffset: index ?? this.state.index,
263
+ isError: true,
264
+ }
265
+ }
266
+
267
+ processToken<TDefined extends boolean = boolean>(token?: Pick<Token, "value" | "startOffset" | "endOffset">): [TDefined extends true ? string : string | undefined, Position] {
268
+ if (token === undefined) {
269
+ return [undefined as any, extractPosition({ startOffset: 0, endOffset: 0 }, this.state.shift)]
270
+ } else {
271
+ return [token.value, extractPosition(token, this.state.shift)]
272
+ }
273
+ }
274
+
275
+
276
+ peek(n = 1): Token<$T> | undefined {
277
+ return this.state.lexedTokens[this.state.index + n]
278
+ }
279
+
280
+ nextIsEof(): boolean {
281
+ return this.peek(1) === undefined
282
+ }
283
+
284
+ consumeAny(): Token<$T> {
285
+ return this.consume(this.peek(1)?.type)
286
+ }
287
+
288
+ consume<
289
+ TType extends $T | $C,
290
+ >(
291
+ type: TType | undefined,
292
+ ): Token<TType> {
293
+ if (type === undefined) {
294
+ throw new Error("type is undefined")
295
+ }
296
+ const nextToken = this.peek(1)
297
+ if (nextToken === undefined) {
298
+ throw new Error(`Reached end of input without consuming a token of type ${type}`)
299
+ }
300
+ if (this.$categories[type as $C] !== undefined) {
301
+ const categoryToken = this.$categories[type as $C]
302
+ const tokenType = categoryToken?.entries[nextToken.type as $T]
303
+ if (categoryToken && tokenType) {
304
+ this.state.index++
305
+ return this.transformCategoryToken(nextToken, categoryToken) as Token<TType>
306
+ } else {
307
+ throw new Error("here")
308
+ }
309
+ } else {
310
+ const tokenType = this.getTokenType(type as $T)
311
+ if (tokenType !== undefined) {
312
+ if (nextToken?.type === tokenType.type) {
313
+ this.state.index++
314
+ return nextToken as any
315
+ } else {
316
+ throw new Error(`Expected token type ${tokenType.type}, got ${nextToken?.type}`)
317
+ }
318
+ }
319
+ }
320
+ throw new Error(`Unknown token type ${type}`)
321
+ }
322
+
323
+ saveState(): Parser["state"] {
324
+ return { ...this.state }
325
+ }
326
+
327
+ restoreState(state: Parser["state"]): void {
328
+ this.state = state // careful, we assume this is an untouched copy
329
+ }
330
+
331
+ ruleMain(): ParserResults {
332
+ const res = this.ruleBool("OR")
333
+ if (res === undefined) {
334
+ const error = handle.token.value(undefined, { start: 0, end: 0 })
335
+ return error
336
+ }
337
+ return res
338
+ }
339
+
340
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
341
+ ruleBool<TType extends"AND" | "OR">(type: TType) {
342
+ const OP_TYPE = type === "AND" ? $C.OPERATOR_AND : $C.OPERATOR_OR
343
+
344
+ const pairs: any[][] = [] as any
345
+ let next = this.peek(1)
346
+
347
+ while (pairs.length < 1 || pairs[pairs.length - 1]?.[1] !== undefined) {
348
+ const exp = type === "AND" ? this.ruleCondition() : this.ruleBool("AND")
349
+ next = this.peek(1)
350
+ const canAttemptErrorRecovery = type === "AND"
351
+ ? ["error", "and"].includes(this.options.onMissingBooleanOperator)
352
+ : this.options.onMissingBooleanOperator === "or"
353
+ const extras: any[] = []
354
+ if (
355
+ canAttemptErrorRecovery
356
+ && (
357
+ this.isType(next, $C.VALUE)
358
+ || this.isType(next, $C.QUOTE_ANY)
359
+ || this.isType(next, $T.PAREN_L)
360
+ || this.isType(next, $T.EXP_PROP_OP)
361
+ || this.isType(next, $T.REGEX_START)
362
+ || this.isType(next, $T.CUSTOM_PROP_OP)
363
+ )
364
+ ) {
365
+ let state = this.saveState()
366
+ let cond = this.ruleCondition()
367
+ if (type === "AND") {
368
+ let dummyOp
369
+ while (cond !== undefined) {
370
+ if (this.options.onMissingBooleanOperator === "and") {
371
+ // the operator is missing between the previous token and this exp
372
+ const prev = this.peek(-1)!
373
+ const start = prev.endOffset! + 1
374
+ dummyOp = handle.operator.and("", pos({ start }, { fill: true }))
375
+ }
376
+ extras.push([dummyOp, cond])
377
+ state = this.saveState()
378
+ cond = this.ruleCondition()
379
+ }
380
+ // todo i don't think we need to backtrack
381
+ this.restoreState(state)
382
+ } else {
383
+ // the operator is missing between the previous token and this exp
384
+ const prev = this.peek(-1)!
385
+ const start = prev.endOffset! + 1
386
+ const dummyOp = handle.operator.or("", pos({ start }, { fill: true }))
387
+ extras.push([dummyOp, cond])
388
+ }
389
+ next = this.peek(1)
390
+ }
391
+ const sepToken = this.isType(next, OP_TYPE) && next
392
+ ? type === "AND"
393
+ ? handle.operator.and(...this.processToken<true>(this.consume(next.type)))
394
+ : handle.operator.or(...this.processToken<true>(this.consume(next.type)))
395
+ : undefined
396
+
397
+ pairs.push([
398
+ exp,
399
+ sepToken,
400
+ ])
401
+ next = this.peek(1)
402
+ for (const extra of extras) {
403
+ pairs[pairs.length - 1].splice(1, 1, extra[0])
404
+ pairs.push([extra[1]])
405
+ }
406
+ }
407
+
408
+ if (pairs.length === 0 && this.isType(this.peek(1), OP_TYPE)) {
409
+ next = this.peek(-1)
410
+ let state = this.saveState()
411
+ while (this.isType(next, $C.OPERATOR_AND)) {
412
+ const token = this.consume($C.OPERATOR_AND)
413
+ pairs.push([
414
+ undefined,
415
+ type === "AND"
416
+ ? handle.operator.and(...this.processToken<true>(token))
417
+ : handle.operator.or(...this.processToken<true>(token)),
418
+ ])
419
+ next = this.peek(-1)
420
+ while (this.isType(next, $C.VALUE) || this.isType(next, $C.QUOTE_ANY) || this.isType(next, $T.PAREN_L)) {
421
+ pairs.push([this.ruleCondition()])
422
+ next = this.peek(-1)
423
+ }
424
+ state = this.saveState()
425
+ }
426
+ this.restoreState(state)
427
+ }
428
+
429
+ if (type === "AND" && pairs.length === 0) return undefined
430
+ // handle situations like `a ||` where b is missing
431
+ let res = pairs[pairs.length - 1][0]
432
+ for (let i = pairs.length - 1; i > 0; i--) {
433
+ const before = pairs[i - 1]
434
+ if (type === "OR" && res === undefined && before === undefined) return undefined
435
+ res = handle.expression(before[0], before[1], res)
436
+ }
437
+ return res
438
+ }
439
+
440
+ ruleCondition(): ConditionNode | GroupNode | ConditionNode<boolean> | undefined {
441
+ const not = this.ruleNot()
442
+ const property = this.ruleConditionProperty()
443
+ const propVal = property?.prop?.value === undefined
444
+ ? undefined
445
+ : property.prop.value instanceof ErrorToken
446
+ ? ""
447
+ : property.prop.value.value
448
+
449
+ const propOpVal = property?.rest?.propertyOperator === undefined
450
+ ? undefined
451
+ : property.rest.propertyOperator instanceof ErrorToken
452
+ ? ""
453
+ : property.rest.propertyOperator?.value
454
+
455
+
456
+ const isExpanded = (property?.rest?.sepL ?? property?.rest?.sepR) !== undefined
457
+
458
+ const convertRegexValues = typeof this.options.regexValues === "function"
459
+ && !this.options.regexValues(propVal, propOpVal, isExpanded)
460
+
461
+ const convertArrayValues = typeof this.options.arrayValues === "function"
462
+ && !this.options.arrayValues(propVal, propOpVal, isExpanded)
463
+
464
+ let value = this.ruleConditionValue(property, { convertRegexValues, convertArrayValues })
465
+
466
+ let group
467
+ if (!(value instanceof ArrayNode)
468
+ && !isArray(value)
469
+ && (!value || this.options.prefixableGroups)
470
+ && this.isType(this.peek(1), $T.PAREN_L) // is not already plain group
471
+ ) {
472
+ group = this.rulePlainGroup({ onlyValues: property !== undefined, convertRegexValues, convertArrayValues })
473
+ }
474
+
475
+ if (isArray(value)) {
476
+ group = value
477
+ value = undefined
478
+ }
479
+ if (convertRegexValues && value instanceof VariableNode && value.quote?.left.type === TOKEN_TYPE.REGEX) {
480
+ value = handle.variable(undefined, undefined, handle.token.value(
481
+ (value.quote?.left?.value ?? "") + (value.value.value ?? "") + (value.quote?.right?.value ?? ""),
482
+ pos(value),
483
+ ), undefined) as ReturnType<Parser["ruleVariable"]>
484
+ }
485
+ if (group) {
486
+ if (property) {
487
+ return handle.condition(not, property?.prop, property?.rest, handle.group(undefined, undefined, ...group))
488
+ }
489
+ if (value) {
490
+ return handle.group(undefined, handle.condition(not, undefined, undefined, value), ...group)
491
+ }
492
+ return handle.group(not, value, ...group)
493
+ }
494
+ if ([not, property, value].every(_ => _ === undefined)) return undefined
495
+
496
+ return handle.condition(not, property?.prop, property?.rest, value as any)
497
+ }
498
+
499
+ ruleConditionValue(
500
+ property: ReturnType<Parser<T>["ruleConditionProperty"]>,
501
+ { convertRegexValues = false, convertArrayValues = false }:
502
+ { convertRegexValues?: boolean, convertArrayValues?: boolean } = {},
503
+ ): ReturnType<Parser["rulePlainGroup"]>
504
+ | ReturnType<Parser["rulePlainBracketGroup"]>
505
+ | ReturnType<Parser["ruleVariable"]>
506
+ | undefined
507
+ {
508
+ const next = this.peek(1)
509
+ const next2 = this.peek(2)
510
+ const next3 = this.peek(3)
511
+ const next4 = this.peek(4)
512
+ if (this.options.prefixableGroups
513
+ && property === undefined
514
+ && next?.type !== $T.PAREN_L // moves to parsing group below instead
515
+ && (
516
+ (
517
+ this.isType(next, $C.VALUE)
518
+ && (
519
+ this.isType(next2, $T.PAREN_L) // a(
520
+ || (this.isType(next2, $C.QUOTE_ANY) && this.isType(next3, $T.PAREN_L)) // a"(
521
+ )
522
+ )
523
+ || (
524
+ this.isType(next, $C.QUOTE_ANY)
525
+ && (
526
+ this.isType(next2, $T.PAREN_L) // "(
527
+ || (this.isType(next2, $C.VALUE)
528
+ && (
529
+ this.isType(next3, $T.PAREN_L) || // "a(
530
+ (this.isType(next3, $C.QUOTE_ANY) && this.isType(next4, $T.PAREN_L)) // "a"(
531
+ )
532
+ )
533
+ )
534
+ )
535
+ )
536
+ ) {
537
+ const res = this.ruleVariable({ unprefixed: true })
538
+ if (res) return res
539
+ }
540
+ if (!this.isType(next, $T.PAREN_L)) {
541
+ const res = this.ruleVariable({ unprefixed: false })
542
+ if (res) return res
543
+ }
544
+ if (this.isType(next, $T.PAREN_L)) {
545
+ const res = this.rulePlainGroup({ onlyValues: property !== undefined, convertRegexValues, convertArrayValues })
546
+ if (res) return res
547
+ }
548
+ if (this.isType(next, $T.BRACKET_L)) {
549
+ const res = this.rulePlainBracketGroup({ convertArrayValues })
550
+ if (res) return res
551
+ }
552
+ return undefined
553
+ }
554
+
555
+ rulePlainGroup(
556
+ { onlyValues = false, convertRegexValues = false, convertArrayValues = false }:
557
+ { onlyValues?: boolean, convertRegexValues?: boolean, convertArrayValues?: boolean } = {},
558
+ ): [
559
+ ValidToken<TOKEN_TYPE.PARENL> | undefined,
560
+ GroupNode["expression"],
561
+ ValidToken<TOKEN_TYPE.PARENR> | undefined,
562
+ ] {
563
+ const parenL = this.ruleParenL()
564
+ let parenLeftCount = 0
565
+ let start: undefined | number
566
+ let end: undefined | number
567
+ const condition = !onlyValues ? this.ruleBool("OR") : undefined
568
+
569
+ /**
570
+ * The following a bit of a hack to ignore forbidden expressions in groups when used as values (it would make no sense to do something like `prop:op(prop:op(...)))` or `prop:op:(prefix(...))`).
571
+ *
572
+ * Doing this from the tokenizer is very complicated because it would require keeping track of a lot of state since we need to know when a group follows something that even looks like a property/operator. Doing it from the parser is possible, but it would involve ignoring lots of token types and converting them.
573
+ *
574
+ * This way we just consume all input until the correct next matching paren (or EOF) and re-parse it with a restricted version of the parser, which is easier to understand.
575
+ *
576
+ * Performance wise this should not be a problem since at most we add the time of one initialization per Parser/ParserBase class instance and only on demand. After that the parser is re-used when needed for any future parse calls. Additionally it only needs to be called once for the outer group used in a property value (i.e. `prop:OP:((()))` will only cause a single "sub parse").
577
+ */
578
+
579
+ if (onlyValues && !this.nextIsEof()) {
580
+ while (
581
+ !this.nextIsEof()
582
+ && (!this.isType(this.peek(1), $T.PAREN_R) || parenLeftCount !== 0)
583
+ ) {
584
+ const token = this.consumeAny()
585
+ start ??= extractPosition(token, this.state.shift).start
586
+ if (token.type === $T.PAREN_L) {
587
+ parenLeftCount++
588
+ }
589
+ if (token.type === $T.PAREN_R) {
590
+ parenLeftCount--
591
+ }
592
+ }
593
+ }
594
+
595
+ if (start !== undefined) {
596
+ end ??= extractPosition(this.peek(0)!, this.state.shift).end
597
+ }
598
+ const parenR = this.isType(this.peek(1), $T.PAREN_R) ? this.ruleParenR() : undefined
599
+ if (start !== undefined) {
600
+ const subInput = this.state.rawInput.slice(start, end)
601
+ this.createSubParserIfNotExists({
602
+ ...this.options,
603
+ customPropertyOperators: [],
604
+ expandedPropertySeparator: undefined,
605
+ regexValues: convertRegexValues,
606
+ arrayValues: convertArrayValues,
607
+ }, "One")
608
+ const parsed = this.subParserOne!.parse(" ".repeat(start) + subInput, { seal: false })
609
+ return [parenL, parsed, parenR]
610
+ }
611
+ return [parenL, condition, parenR]
612
+ }
613
+
614
+ rulePlainBracketGroup(
615
+ { convertArrayValues = false }:
616
+ { convertArrayValues?: boolean } = {},
617
+ ): ArrayNode | VariableNode {
618
+ const bracketL = this.ruleBracketL()
619
+
620
+ const values: any[] = []
621
+
622
+ if (!convertArrayValues) {
623
+ let state = this.saveState()
624
+ let variable = this.ruleVariable({ unprefixed: false })
625
+ while (variable !== undefined) {
626
+ values.push(variable)
627
+ state = this.saveState()
628
+ variable = this.ruleVariable({ unprefixed: false })
629
+ }
630
+ this.restoreState(state)
631
+ } else if (convertArrayValues && !this.nextIsEof()) {
632
+ while (
633
+ !this.nextIsEof()
634
+ && !this.isType(this.peek(1), $T.BRACKET_R)
635
+ ) {
636
+ this.consumeAny()
637
+ }
638
+ }
639
+ const bracketR = this.isType(this.peek(1), $T.BRACKET_R) ? this.ruleBracketR() : undefined
640
+ if (bracketL === undefined) throw new Error("bracketL is undefined, peek before using rule.")
641
+ if (!convertArrayValues) {
642
+ return handle.array(bracketL, values, bracketR)
643
+ }
644
+ const start = bracketL.start
645
+ const end = bracketR?.end
646
+ /**
647
+ * Similar problem as with plain groups above.
648
+ */
649
+ const subInput = this.state.rawInput.slice(start, end)
650
+ this.createSubParserIfNotExists({
651
+ ...this.options,
652
+ customPropertyOperators: [],
653
+ expandedPropertySeparator: undefined,
654
+ arrayValues: false,
655
+ }, "Two")
656
+ const parsed = this.subParserTwo!.parse(" ".repeat(start) + subInput, { seal: false })
657
+ if (parsed instanceof ConditionNode) {
658
+ return parsed.value as ArrayNode
659
+ }
660
+ if (parsed instanceof ErrorToken || parsed instanceof ExpressionNode || parsed instanceof GroupNode) {
661
+ unreachable("parsed.value should not be an ErrorToken, ExpressionNode, or GroupNode.")
662
+ }
663
+ return parsed
664
+ }
665
+
666
+ ruleConditionProperty(): {
667
+ prop?: VariableNode
668
+ rest: Parameters<typeof handle.condition>[2]
669
+ } | undefined {
670
+ const current = this.peek(0)
671
+ const next = this.peek(1)
672
+ const next2 = this.peek(2)
673
+ if (this.isType(next, $T.EXP_PROP_OP)
674
+ || this.isType(next, $T.CUSTOM_PROP_OP)
675
+ || (this.isType(next, $T.VALUE_UNQUOTED) && (
676
+ this.isType(next2, $T.EXP_PROP_OP)
677
+ || this.isType(next2, $T.CUSTOM_PROP_OP)
678
+ ))
679
+ || (
680
+ this.info.customOpAlsoNegation
681
+ && (
682
+ this.isType(next2, $T.SYM_NOT)
683
+ || (this.isType(current, $T.SYM_NOT) && this.isType(next, $T.SYM_NOT))
684
+ )
685
+ )
686
+ ) {
687
+ return this.ruleProperty()
688
+ }
689
+ return undefined
690
+ }
691
+
692
+ ruleProperty(): {
693
+ prop?: VariableNode
694
+ rest: Parameters<typeof handle.condition>[2]
695
+ } {
696
+ const prop = this.ruleVariable({ unprefixed: true })
697
+ const next = this.peek(1)
698
+ let rest: Parameters<typeof handle.condition>[2] = {} as any
699
+ if (this.isType(next, $T.EXP_PROP_OP)) {
700
+ const sepL = handle.token.sep(...this.processToken<true>(this.consume($T.EXP_PROP_OP)))
701
+ const op = this.isType(this.peek(1), $T.VALUE_UNQUOTED)
702
+ ? handle.token.value(...this.processToken<true>(this.consume($T.VALUE_UNQUOTED)))
703
+ : undefined
704
+ const sepR = this.isType(this.peek(1), $T.EXP_PROP_OP)
705
+ ? handle.token.sep(...this.processToken<true>(this.consume($T.EXP_PROP_OP)))
706
+ : undefined
707
+ if (this.info.expandedSepAlsoCustom && op === undefined && sepR === undefined) {
708
+ setReadOnly(sepL, "type", TOKEN_TYPE.OP_CUSTOM as any)
709
+ rest = {
710
+ sepL: undefined,
711
+ sepR,
712
+ propertyOperator: sepL as any as AnyToken<TOKEN_TYPE.OP_CUSTOM>,
713
+ }
714
+ } else {
715
+ rest = { sepL, sepR, propertyOperator: op }
716
+ }
717
+ } else if (this.isType(next, $T.CUSTOM_PROP_OP)) {
718
+ const op = handle.token.custom(...this.processToken(this.consume($T.CUSTOM_PROP_OP)))
719
+ rest = { propertyOperator: op }
720
+ } else if (this.info.customOpAlsoNegation && this.isType(next, $T.SYM_NOT)) {
721
+ const op = handle.token.custom(...this.processToken(this.consume($T.SYM_NOT)))
722
+ rest = { propertyOperator: op }
723
+ }
724
+ return { prop, rest }
725
+ }
726
+
727
+ ruleVariable({
728
+ unprefixed = false,
729
+ }: {
730
+ unprefixed?: boolean
731
+ } = {}): VariableNode | undefined {
732
+ const prefix = this.ruleVariablePrefix({ onlyToken: true, unprefixed })
733
+
734
+ const next = this.peek(1)
735
+ const next2 = this.peek(2)
736
+ const next3 = this.peek(3)
737
+ // quoted values
738
+ if (next && (this.isExactType(next, $T.QUOTE_DOUBLE)
739
+ || this.isExactType(next, $T.QUOTE_SINGLE)
740
+ || this.isExactType(next, $T.QUOTE_BACKTICK)
741
+ )) {
742
+ const quoteType = next.type
743
+ if (next2?.type === quoteType) {
744
+ // value is missing
745
+ const quoteL = this.ruleQuote(quoteType)
746
+ const quoteR = this.ruleQuote(quoteType)
747
+ return handle.variable(undefined, quoteL, undefined, quoteR)
748
+ }
749
+ if (next3?.type === next.type) {
750
+ const quoteL = this.ruleQuote(quoteType)
751
+ const value = this.isType(next2, $T.VALUE_UNQUOTED) ? this.ruleValueUnquoted({ }) : this.ruleValueNot(quoteType)
752
+ const quoteR = this.ruleQuote(quoteType)
753
+ const prefixToken = prefix ? handle.token.value(...this.processToken<true>(prefix)) : undefined
754
+ return handle.variable(prefixToken, quoteL, value, quoteR)
755
+ }
756
+ }
757
+ if (this.isType(next, $C.REGEX_ANY)) {
758
+ // this is safe since the start can never match flags
759
+ const quoteL = this.ruleRegexAny() as ValidToken<TOKEN_TYPE.REGEX>
760
+ // unlike other values, regexes will swallow all input if incorrect
761
+ const maybeValue = this.peek(1)
762
+ // note the inversion (todo inverse map)
763
+ const value = this.isType(maybeValue, $T.VALUE_REGEX)
764
+ ? this.ruleValueNot($C.REGEX_ANY)
765
+ : undefined
766
+
767
+ const quoteR = this.isType(this.peek(1), $C.REGEX_ANY) ? this.ruleRegexAny() : undefined
768
+ const args = isArray(quoteR) ? quoteR : [quoteR, undefined] as const
769
+ return handle.variable(undefined, quoteL, value, args[0], args[1] as any)
770
+ }
771
+ if (this.isType(next, $T.VALUE_UNQUOTED) && this.isType(next2, $C.QUOTE_ANY)) {
772
+ const value = this.ruleValueUnquoted()
773
+ const quoteR = this.ruleValueDelimAny()
774
+ return handle.variable(undefined, undefined, value, quoteR)
775
+ }
776
+ if (this.isType(next, $C.QUOTE_ANY)) {
777
+ const quoteToken = next as Token<$T.QUOTE_BACKTICK | $T.QUOTE_DOUBLE | $T.QUOTE_SINGLE>
778
+ const quoteL = this.ruleValueDelimAny()
779
+ const maybeValue = this.peek(1)
780
+ const value = !quoteL && this.isType(maybeValue, $T.VALUE_UNQUOTED)
781
+ ? this.ruleValueUnquoted()
782
+ // todo, move inverse quote map out of ruleValueNot
783
+ : quoteL && this.isType(maybeValue, quoteToken.type.replace("QUOTE", "VALUE_FOR") as any)
784
+ ? this.ruleValueNot(quoteToken.type)
785
+ : undefined
786
+ return handle.variable(undefined, quoteL, value, undefined)
787
+ }
788
+ if (this.isType(next, $T.VALUE_UNQUOTED)) {
789
+ const value = this.ruleValueUnquoted()
790
+ return handle.variable(undefined, undefined, value, undefined)
791
+ }
792
+ return undefined
793
+ }
794
+
795
+ ruleValueDelimAny(): ValidToken<TOKEN_TYPE.SINGLEQUOTE | TOKEN_TYPE.DOUBLEQUOTE | TOKEN_TYPE.BACKTICK | TOKEN_TYPE.REGEX> | undefined {
796
+ const next = this.peek(1)!
797
+
798
+ if (this.isType(next, $C.QUOTE_ANY)) {
799
+ const type = next.value === `"` ? "double" : next.value === "'" ? "single" : next.value === "`" ? "tick" : "regex"
800
+ return handle.delimiter[type](...this.processToken(this.consume($C.QUOTE_ANY)))
801
+ }
802
+ return undefined
803
+ }
804
+
805
+ ruleRegexAny(): ValidToken<TOKEN_TYPE.REGEX> | [ValidToken<TOKEN_TYPE.REGEX>, AnyToken<TOKEN_TYPE.VALUE>] {
806
+ const value = this.consume($C.REGEX_ANY)
807
+ if (value.value.length > 1) {
808
+ // cheat a bit to extract the flags
809
+ const delim = {
810
+ value: "/",
811
+ startOffset: value.startOffset,
812
+ endOffset: value.startOffset,
813
+ }
814
+ const flags = {
815
+ value: value.value.slice(1),
816
+ startOffset: value.startOffset + 1,
817
+ endOffset: value.endOffset,
818
+ }
819
+ return [
820
+ // why the ! ??? todo
821
+ handle.delimiter.regex(...this.processToken(delim))!,
822
+ handle.token.value(...this.processToken(flags)),
823
+ ]
824
+ }
825
+ return handle.delimiter.regex(...this.processToken(value))!
826
+ }
827
+
828
+ ruleValueNot<
829
+ TType extends $T.QUOTE_SINGLE | $T.QUOTE_DOUBLE | $T.QUOTE_BACKTICK | $C.REGEX_ANY,
830
+ >(
831
+ type: TType,
832
+ ): ValidToken<
833
+ TOKEN_TYPE.VALUE
834
+ > {
835
+ const realType = {
836
+ [$T.QUOTE_SINGLE]: $C.VALUE_FOR_SINGLE,
837
+ [$T.QUOTE_DOUBLE]: $C.VALUE_FOR_DOUBLE,
838
+ [$T.QUOTE_BACKTICK]: $C.VALUE_FOR_BACKTICK,
839
+ [$C.REGEX_ANY]: $T.VALUE_REGEX,
840
+ }[type]
841
+ if (realType === undefined) {
842
+ unreachable(`Unknown quote/regex type ${type}`)
843
+ }
844
+ const value = this.consume(realType)
845
+ if (realType !== value.type) {
846
+ unreachable(`Expected value type ${realType}, got ${value.type}`)
847
+ }
848
+ return handle.token.value(...this.processToken(value)) as any
849
+ }
850
+
851
+ ruleQuote<TType extends $T.QUOTE_SINGLE | $T.QUOTE_DOUBLE | $T.QUOTE_BACKTICK >(
852
+ type: TType,
853
+ ): ValidToken<
854
+ TType extends $T.QUOTE_SINGLE
855
+ ? TOKEN_TYPE.SINGLEQUOTE
856
+ : TType extends $T.QUOTE_DOUBLE
857
+ ? TOKEN_TYPE.DOUBLEQUOTE
858
+ : TType extends $T.QUOTE_BACKTICK
859
+ ? TOKEN_TYPE.BACKTICK
860
+ : never
861
+ > {
862
+ const quote = this.peek(1)
863
+ if (type !== quote?.type) {
864
+ throw new Error(`Expected quote type ${type}, got ${quote?.type}`)
865
+ }
866
+
867
+ switch (type) {
868
+ case $T.QUOTE_SINGLE:
869
+ return handle.delimiter.single(
870
+ ...this.processToken(this.consume($T.QUOTE_SINGLE)),
871
+ ) as any
872
+ case $T.QUOTE_DOUBLE:
873
+ return handle.delimiter.double(
874
+ ...this.processToken(this.consume($T.QUOTE_DOUBLE)),
875
+ ) as any
876
+ case $T.QUOTE_BACKTICK:
877
+ return handle.delimiter.tick(
878
+ ...this.processToken(this.consume($T.QUOTE_BACKTICK)),
879
+ ) as any
880
+ }
881
+ throw new Error(`Expected quote type ${type}`)
882
+ }
883
+
884
+
885
+ ruleVariablePrefix<TOnlyToken extends boolean = false>(
886
+ {
887
+
888
+ onlyToken = false as TOnlyToken,
889
+ unprefixed = false,
890
+ }: {
891
+ onlyToken?: TOnlyToken
892
+ unprefixed?: boolean
893
+ } = {},
894
+ ): TOnlyToken extends true ? Token<$T.VALUE_UNQUOTED> | undefined : AnyToken<TOKEN_TYPE.VALUE> | undefined {
895
+ const next = this.peek(1)
896
+ const next2 = this.peek(2)
897
+ const next4 = this.peek(4)
898
+ if (!unprefixed && this.options.prefixableStrings !== undefined
899
+ && this.isType(next2, $C.QUOTE_ANY)
900
+ && next2 && this.isType(next4, next2.type)
901
+ && next && this.options.prefixableStrings.includes(next.value)
902
+ ) {
903
+ return this.ruleValueUnquoted({ onlyToken }) as any
904
+ }
905
+ if (onlyToken) return undefined as any
906
+ return handle.token.value(...this.processToken()) as any
907
+ }
908
+
909
+ ruleValueUnquoted<TOnlyToken extends boolean = false>(
910
+ {
911
+ onlyToken = false as TOnlyToken,
912
+ }: {
913
+ onlyToken?: TOnlyToken
914
+ } = {},
915
+ ): TOnlyToken extends true ? Token<$T.VALUE_UNQUOTED> : AnyToken<TOKEN_TYPE.VALUE> {
916
+ const t = this.consume($T.VALUE_UNQUOTED)
917
+ const res = onlyToken ? t : handle.token.value(...this.processToken(t))
918
+ return (res) as any
919
+ }
920
+
921
+ ruleParenL(): ValidToken<TOKEN_TYPE.PARENL> | undefined {
922
+ const next = this.peek(1)
923
+ const value = next?.type === $T.PAREN_L
924
+ ? this.consume($T.PAREN_L)
925
+ : this.createErrorToken($T.PAREN_L)
926
+ const loc = extractPosition(value, this.state.shift)
927
+ return this.state.shift === 0 || loc.start > 0
928
+ ? handle.delimiter.parenL(value.isError ? undefined : value.value, loc)
929
+ : undefined
930
+ }
931
+
932
+ ruleParenR(): ValidToken<TOKEN_TYPE.PARENR> | undefined {
933
+ const value = this.consume($T.PAREN_R)
934
+ return handle.delimiter.parenR(...this.processToken(value))
935
+ }
936
+
937
+ ruleBracketL(): ValidToken<TOKEN_TYPE.BRACKETL> | undefined {
938
+ const next = this.peek(1)
939
+ const value = next?.type === $T.BRACKET_L
940
+ ? this.consume($T.BRACKET_L)
941
+ : this.createErrorToken($T.BRACKET_L)
942
+ const loc = extractPosition(value, this.state.shift)
943
+ return this.state.shift === 0 || loc.start > 0
944
+ ? handle.delimiter.bracketL(value.isError ? undefined : value.value, loc)
945
+ : undefined
946
+ }
947
+
948
+ ruleBracketR(): ValidToken<TOKEN_TYPE.BRACKETR> | undefined {
949
+ const value = this.consume($T.BRACKET_R)
950
+ return handle.delimiter.bracketR(...this.processToken(value))
951
+ }
952
+
953
+ ruleNot(): ValidToken<TOKEN_TYPE.NOT> | undefined {
954
+ if (this.isType(this.peek(1), $C.OPERATOR_NOT)) {
955
+ const op = this.consume($C.OPERATOR_NOT)
956
+ return handle.operator.not(...this.processToken<true>(op))
957
+ }
958
+ return undefined
959
+ }
960
+ }
961
+
962
+ mixin(Parser, [
963
+ AutocompleteMixin,
964
+ AutoreplaceMixin,
965
+ Autosuggest,
966
+ EvaluateMixin,
967
+ ValidateMixin,
968
+ NormalizeMixin,
969
+ GetIndexMixin,
970
+ GetBestIndexesMixin,
971
+ ])
972
+