@witchcraft/expressit 0.0.3 → 0.1.1
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 +3 -1
- package/dist/Lexer.d.ts +146 -0
- package/dist/Lexer.d.ts.map +1 -0
- package/dist/Lexer.js +960 -0
- package/dist/Parser.d.ts +211 -0
- package/dist/Parser.d.ts.map +1 -0
- package/dist/Parser.js +1476 -0
- package/dist/ast/builders/token.js +1 -1
- package/dist/ast/handlers.d.ts +3 -3
- package/dist/ast/handlers.d.ts.map +1 -1
- package/dist/examples/shortcutContextParser.d.ts +1 -1
- package/dist/examples/shortcutContextParser.js +1 -1
- package/dist/helpers/errors.js +3 -3
- package/dist/helpers/parser/checkParserOpts.js +2 -2
- package/dist/helpers/parser/extractPosition.d.ts +2 -6
- package/dist/helpers/parser/extractPosition.d.ts.map +1 -1
- package/dist/helpers/parser/extractPosition.js +3 -3
- package/dist/helpers/parser/getUnclosedRightParenCount.d.ts +2 -3
- package/dist/helpers/parser/getUnclosedRightParenCount.d.ts.map +1 -1
- package/dist/helpers/parser/getUnclosedRightParenCount.js +4 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/package.json.js +27 -41
- package/dist/types/ast.d.ts +1 -7
- package/dist/types/ast.d.ts.map +1 -1
- package/dist/types/errors.d.ts +5 -17
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +0 -1
- package/dist/types/parser.d.ts.map +1 -1
- package/dist/utils/extractTokens.js +1 -1
- package/dist/utils/getCursorInfo.d.ts +2 -2
- package/dist/utils/getCursorInfo.d.ts.map +1 -1
- package/dist/utils/getCursorInfo.js +3 -6
- package/dist/utils/getOppositeDelimiter.d.ts.map +1 -1
- package/dist/utils/getOppositeDelimiter.js +1 -1
- package/dist/utils/prettyAst.js +7 -6
- package/package.json +25 -41
- package/src/Lexer.ts +704 -0
- package/src/Parser.ts +2014 -0
- package/src/ast/builders/array.ts +1 -1
- package/src/ast/builders/condition.ts +1 -1
- package/src/ast/builders/expression.ts +1 -1
- package/src/ast/builders/group.ts +1 -1
- package/src/ast/builders/pos.ts +1 -1
- package/src/ast/builders/token.ts +2 -2
- package/src/ast/builders/type.ts +1 -1
- package/src/ast/builders/variable.ts +1 -1
- package/src/ast/classes/ConditionNode.ts +1 -1
- package/src/ast/classes/ErrorToken.ts +1 -1
- package/src/ast/classes/ValidToken.ts +2 -2
- package/src/ast/handlers.ts +6 -6
- package/src/examples/shortcutContextParser.ts +2 -2
- package/src/helpers/errors.ts +4 -4
- package/src/helpers/general/defaultConditionNormalizer.ts +1 -1
- package/src/helpers/parser/checkParserOpts.ts +12 -12
- package/src/helpers/parser/extractPosition.ts +4 -8
- package/src/helpers/parser/getUnclosedRightParenCount.ts +6 -6
- package/src/helpers/parser/parseParserOptions.ts +1 -1
- package/src/index.ts +1 -2
- package/src/types/ast.ts +1 -8
- package/src/types/errors.ts +12 -22
- package/src/types/parser.ts +0 -1
- package/src/utils/extractTokens.ts +1 -1
- package/src/utils/getCursorInfo.ts +6 -5
- package/src/utils/getOppositeDelimiter.ts +5 -2
- package/src/utils/prettyAst.ts +4 -4
- package/dist/grammar/ParserBase.d.ts +0 -51
- package/dist/grammar/ParserBase.d.ts.map +0 -1
- package/dist/grammar/ParserBase.js +0 -517
- package/dist/grammar/createTokens.d.ts +0 -56
- package/dist/grammar/createTokens.d.ts.map +0 -1
- package/dist/grammar/createTokens.js +0 -844
- package/dist/grammar/index.d.ts +0 -3
- package/dist/grammar/index.d.ts.map +0 -1
- package/dist/grammar/index.js +0 -6
- package/dist/methods/autocomplete.d.ts +0 -18
- package/dist/methods/autocomplete.d.ts.map +0 -1
- package/dist/methods/autocomplete.js +0 -109
- package/dist/methods/autoreplace.d.ts +0 -13
- package/dist/methods/autoreplace.d.ts.map +0 -1
- package/dist/methods/autoreplace.js +0 -36
- package/dist/methods/autosuggest.d.ts +0 -28
- package/dist/methods/autosuggest.d.ts.map +0 -1
- package/dist/methods/autosuggest.js +0 -371
- package/dist/methods/evaluate.d.ts +0 -11
- package/dist/methods/evaluate.d.ts.map +0 -1
- package/dist/methods/evaluate.js +0 -32
- package/dist/methods/getBestIndex.d.ts +0 -19
- package/dist/methods/getBestIndex.d.ts.map +0 -1
- package/dist/methods/getBestIndex.js +0 -53
- package/dist/methods/getIndexes.d.ts +0 -17
- package/dist/methods/getIndexes.d.ts.map +0 -1
- package/dist/methods/getIndexes.js +0 -98
- package/dist/methods/index.d.ts +0 -9
- package/dist/methods/index.d.ts.map +0 -1
- package/dist/methods/index.js +0 -18
- package/dist/methods/normalize.d.ts +0 -10
- package/dist/methods/normalize.d.ts.map +0 -1
- package/dist/methods/normalize.js +0 -98
- package/dist/methods/validate.d.ts +0 -11
- package/dist/methods/validate.d.ts.map +0 -1
- package/dist/methods/validate.js +0 -112
- package/dist/parser.d.ts +0 -58
- package/dist/parser.d.ts.map +0 -1
- package/dist/parser.js +0 -137
- package/src/grammar/ParserBase.ts +0 -716
- package/src/grammar/createTokens.ts +0 -513
- package/src/grammar/index.ts +0 -4
- package/src/methods/autocomplete.ts +0 -128
- package/src/methods/autoreplace.ts +0 -46
- package/src/methods/autosuggest.ts +0 -543
- package/src/methods/evaluate.ts +0 -39
- package/src/methods/getBestIndex.ts +0 -53
- package/src/methods/getIndexes.ts +0 -100
- package/src/methods/index.ts +0 -10
- package/src/methods/normalize.ts +0 -137
- package/src/methods/validate.ts +0 -143
- package/src/parser.ts +0 -184
package/dist/Parser.js
ADDED
|
@@ -0,0 +1,1476 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => {
|
|
4
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
return value;
|
|
6
|
+
};
|
|
7
|
+
import { get } from "@alanscodelog/utils/get.js";
|
|
8
|
+
import { insert } from "@alanscodelog/utils/insert.js";
|
|
9
|
+
import { isArray } from "@alanscodelog/utils/isArray.js";
|
|
10
|
+
import { isWhitespace } from "@alanscodelog/utils/isWhitespace.js";
|
|
11
|
+
import { setReadOnly } from "@alanscodelog/utils/setReadOnly.js";
|
|
12
|
+
import { unreachable } from "@alanscodelog/utils/unreachable.js";
|
|
13
|
+
import { pos } from "./ast/builders/pos.js";
|
|
14
|
+
import { ArrayNode } from "./ast/classes/ArrayNode.js";
|
|
15
|
+
import { ConditionNode } from "./ast/classes/ConditionNode.js";
|
|
16
|
+
import { ErrorToken } from "./ast/classes/ErrorToken.js";
|
|
17
|
+
import { ExpressionNode } from "./ast/classes/ExpressionNode.js";
|
|
18
|
+
import { GroupNode } from "./ast/classes/GroupNode.js";
|
|
19
|
+
import { Condition } from "./ast/classes/Condition.js";
|
|
20
|
+
import { Expression } from "./ast/classes/Expression.js";
|
|
21
|
+
import "@alanscodelog/utils/crop.js";
|
|
22
|
+
import "@alanscodelog/utils/indent.js";
|
|
23
|
+
import "@alanscodelog/utils/pretty.js";
|
|
24
|
+
import { ValidToken } from "./ast/classes/ValidToken.js";
|
|
25
|
+
import { VariableNode } from "./ast/classes/VariableNode.js";
|
|
26
|
+
import { token, operator, expression, variable, condition, group, array, delimiter } from "./ast/handlers.js";
|
|
27
|
+
import { applyBoolean } from "./helpers/general/applyBoolean.js";
|
|
28
|
+
import { applyPrefix } from "./helpers/general/applyPrefix.js";
|
|
29
|
+
import { checkParserOpts } from "./helpers/parser/checkParserOpts.js";
|
|
30
|
+
import { extractPosition } from "./helpers/parser/extractPosition.js";
|
|
31
|
+
import { getUnclosedRightParenCount } from "./helpers/parser/getUnclosedRightParenCount.js";
|
|
32
|
+
import { parseParserOptions } from "./helpers/parser/parseParserOptions.js";
|
|
33
|
+
import { seal } from "./helpers/parser/seal.js";
|
|
34
|
+
import { Lexer, $C, $T } from "./Lexer.js";
|
|
35
|
+
import { TOKEN_TYPE } from "./types/ast.js";
|
|
36
|
+
import { SUGGESTION_TYPE } from "./types/autocomplete.js";
|
|
37
|
+
import { extractTokens } from "./utils/extractTokens.js";
|
|
38
|
+
import { getCursorInfo } from "./utils/getCursorInfo.js";
|
|
39
|
+
import { getSurroundingErrors } from "./utils/getSurroundingErrors.js";
|
|
40
|
+
const OPPOSITE = {
|
|
41
|
+
[TOKEN_TYPE.AND]: TOKEN_TYPE.OR,
|
|
42
|
+
[TOKEN_TYPE.OR]: TOKEN_TYPE.AND
|
|
43
|
+
};
|
|
44
|
+
function isEqualSet(setA, setB) {
|
|
45
|
+
if (setA.size !== setB.size)
|
|
46
|
+
return false;
|
|
47
|
+
for (const key of setA) {
|
|
48
|
+
if (!setB.has(key))
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
const defaultNodeDirs = {
|
|
54
|
+
before: false,
|
|
55
|
+
after: false
|
|
56
|
+
};
|
|
57
|
+
const createDefaultRequires = (partial = {}) => ({
|
|
58
|
+
whitespace: {
|
|
59
|
+
...defaultNodeDirs,
|
|
60
|
+
...partial.whitespace ? partial.whitespace : {}
|
|
61
|
+
},
|
|
62
|
+
group: partial.group ?? false,
|
|
63
|
+
prefix: partial.prefix ?? false
|
|
64
|
+
});
|
|
65
|
+
const tokenRequiresWhitespace = (validToken, whitespace, wordOps) => {
|
|
66
|
+
if (whitespace || validToken === void 0)
|
|
67
|
+
return false;
|
|
68
|
+
return validToken.type === TOKEN_TYPE.VALUE || [TOKEN_TYPE.AND, TOKEN_TYPE.OR, TOKEN_TYPE.NOT].includes(validToken.type) && wordOps.find((_) => _.value === validToken.value) !== void 0;
|
|
69
|
+
};
|
|
70
|
+
const tokenVariable = [TOKEN_TYPE.BACKTICK, TOKEN_TYPE.DOUBLEQUOTE, TOKEN_TYPE.SINGLEQUOTE, TOKEN_TYPE.VALUE, TOKEN_TYPE.REGEX];
|
|
71
|
+
class Parser {
|
|
72
|
+
constructor(options) {
|
|
73
|
+
// needed for evaluate and validate so they are only checked on demand
|
|
74
|
+
__publicField(this, "evaluationOptionsChecked", false);
|
|
75
|
+
__publicField(this, "validationOptionsChecked", false);
|
|
76
|
+
__publicField(this, "options");
|
|
77
|
+
__publicField(this, "lexer");
|
|
78
|
+
__publicField(this, "$");
|
|
79
|
+
__publicField(this, "$categories");
|
|
80
|
+
__publicField(this, "info");
|
|
81
|
+
__publicField(this, "state", {
|
|
82
|
+
rawInput: "",
|
|
83
|
+
lexedTokens: [],
|
|
84
|
+
index: 0,
|
|
85
|
+
shift: 0
|
|
86
|
+
});
|
|
87
|
+
__publicField(this, "subParserOne");
|
|
88
|
+
__publicField(this, "subParserTwo");
|
|
89
|
+
const opts = parseParserOptions(options ?? {});
|
|
90
|
+
checkParserOpts(opts);
|
|
91
|
+
this.options = opts;
|
|
92
|
+
this.lexer = new Lexer(opts);
|
|
93
|
+
this.$ = this.lexer.$;
|
|
94
|
+
this.$categories = this.lexer.$categories;
|
|
95
|
+
this.info = {
|
|
96
|
+
expandedSepAlsoCustom: this.lexer.symbols.expandedSepAlsoCustom,
|
|
97
|
+
customOpAlsoNegation: this.lexer.symbols.customOpAlsoNegation
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
101
|
+
_checkEvaluationOptions() {
|
|
102
|
+
if (!this.evaluationOptionsChecked) {
|
|
103
|
+
checkParserOpts(this.options, true);
|
|
104
|
+
this.evaluationOptionsChecked = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
108
|
+
_checkValidationOptions() {
|
|
109
|
+
if (!this.validationOptionsChecked) {
|
|
110
|
+
checkParserOpts(this.options, false, true);
|
|
111
|
+
this.validationOptionsChecked = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* This is exposed mainly for debugging purposes. Use parse directly instead.
|
|
116
|
+
*/
|
|
117
|
+
lex(input) {
|
|
118
|
+
if (isWhitespace(input)) {
|
|
119
|
+
return { tokens: [], shift: 0, rawInput: input };
|
|
120
|
+
}
|
|
121
|
+
let lexed = this.lexer.tokenize(input);
|
|
122
|
+
const shift = getUnclosedRightParenCount(lexed);
|
|
123
|
+
const rawInput = input;
|
|
124
|
+
if (shift) {
|
|
125
|
+
input = "(".repeat(shift) + input;
|
|
126
|
+
lexed = this.lexer.tokenize(input);
|
|
127
|
+
}
|
|
128
|
+
const lexedTokens = lexed.filter((token2) => {
|
|
129
|
+
const tokenType = this.getTokenType(token2.type);
|
|
130
|
+
if (tokenType) {
|
|
131
|
+
return !tokenType.skip;
|
|
132
|
+
} else {
|
|
133
|
+
throw new Error(`Unknown token type ${token2.type}`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return { tokens: lexedTokens, shift, rawInput };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Parse an input string into an AST.
|
|
140
|
+
* It can also parse the result from `lex`, but that is really only for internal use.
|
|
141
|
+
*/
|
|
142
|
+
parse(input) {
|
|
143
|
+
var _a;
|
|
144
|
+
const doSeal = ((_a = arguments[1]) == null ? void 0 : _a.seal) ?? true;
|
|
145
|
+
if (typeof input === "string" && isWhitespace(input)) {
|
|
146
|
+
return token.value(void 0, { start: 0, end: 0 });
|
|
147
|
+
}
|
|
148
|
+
const { tokens: lexedTokens, shift, rawInput } = typeof input === "string" ? this.lex(input) : input;
|
|
149
|
+
this.state = {
|
|
150
|
+
rawInput,
|
|
151
|
+
shift,
|
|
152
|
+
index: -1,
|
|
153
|
+
lexedTokens
|
|
154
|
+
};
|
|
155
|
+
const res = this.ruleMain();
|
|
156
|
+
if (doSeal) {
|
|
157
|
+
seal(res);
|
|
158
|
+
}
|
|
159
|
+
this.state = {
|
|
160
|
+
rawInput: "",
|
|
161
|
+
shift: 0,
|
|
162
|
+
index: -1,
|
|
163
|
+
lexedTokens: []
|
|
164
|
+
};
|
|
165
|
+
return res;
|
|
166
|
+
}
|
|
167
|
+
createSubParserIfNotExists(opts, which = "One") {
|
|
168
|
+
if (this[`subParser${which}`] === void 0) {
|
|
169
|
+
this[`subParser${which}`] = new Parser(opts);
|
|
170
|
+
}
|
|
171
|
+
return this[`subParser${which}`];
|
|
172
|
+
}
|
|
173
|
+
transformCategoryToken(token2, categoryToken) {
|
|
174
|
+
return {
|
|
175
|
+
...token2,
|
|
176
|
+
type: categoryToken.type
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
getCategoryTokens(type) {
|
|
180
|
+
var _a;
|
|
181
|
+
return (_a = this.$categories[type]) == null ? void 0 : _a.entries;
|
|
182
|
+
}
|
|
183
|
+
getTokenType(type) {
|
|
184
|
+
return this.$[type];
|
|
185
|
+
}
|
|
186
|
+
isExactType(token2, type) {
|
|
187
|
+
if (this.$[type]) {
|
|
188
|
+
return this.isType(token2, type);
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
isType(token2, type) {
|
|
193
|
+
if (token2 === void 0)
|
|
194
|
+
return false;
|
|
195
|
+
if (token2.type === type)
|
|
196
|
+
return true;
|
|
197
|
+
const tokenType = this.getTokenType(token2.type);
|
|
198
|
+
if ((tokenType == null ? void 0 : tokenType.type) === type)
|
|
199
|
+
return true;
|
|
200
|
+
const category = this.$categories[type];
|
|
201
|
+
if ((category == null ? void 0 : category.entries[token2.type]) !== void 0) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
createErrorToken(type, index) {
|
|
207
|
+
return {
|
|
208
|
+
type,
|
|
209
|
+
value: "",
|
|
210
|
+
startOffset: index ?? this.state.index,
|
|
211
|
+
endOffset: index ?? this.state.index,
|
|
212
|
+
isError: true
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
processToken(token2) {
|
|
216
|
+
if (token2 === void 0) {
|
|
217
|
+
return [void 0, extractPosition({ startOffset: 0, endOffset: 0 }, this.state.shift)];
|
|
218
|
+
} else {
|
|
219
|
+
return [token2.value, extractPosition(token2, this.state.shift)];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
peek(n = 1) {
|
|
223
|
+
return this.state.lexedTokens[this.state.index + n];
|
|
224
|
+
}
|
|
225
|
+
nextIsEof() {
|
|
226
|
+
return this.peek(1) === void 0;
|
|
227
|
+
}
|
|
228
|
+
consumeAny() {
|
|
229
|
+
var _a;
|
|
230
|
+
return this.consume((_a = this.peek(1)) == null ? void 0 : _a.type);
|
|
231
|
+
}
|
|
232
|
+
consume(type) {
|
|
233
|
+
if (type === void 0) {
|
|
234
|
+
throw new Error("type is undefined");
|
|
235
|
+
}
|
|
236
|
+
const nextToken = this.peek(1);
|
|
237
|
+
if (nextToken === void 0) {
|
|
238
|
+
throw new Error(`Reached end of input without consuming a token of type ${type}`);
|
|
239
|
+
}
|
|
240
|
+
if (this.$categories[type] !== void 0) {
|
|
241
|
+
const categoryToken = this.$categories[type];
|
|
242
|
+
const tokenType = categoryToken == null ? void 0 : categoryToken.entries[nextToken.type];
|
|
243
|
+
if (categoryToken && tokenType) {
|
|
244
|
+
this.state.index++;
|
|
245
|
+
return this.transformCategoryToken(nextToken, categoryToken);
|
|
246
|
+
} else {
|
|
247
|
+
throw new Error("here");
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
const tokenType = this.getTokenType(type);
|
|
251
|
+
if (tokenType !== void 0) {
|
|
252
|
+
if ((nextToken == null ? void 0 : nextToken.type) === tokenType.type) {
|
|
253
|
+
this.state.index++;
|
|
254
|
+
return nextToken;
|
|
255
|
+
} else {
|
|
256
|
+
throw new Error(`Expected token type ${tokenType.type}, got ${nextToken == null ? void 0 : nextToken.type}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
throw new Error(`Unknown token type ${type}`);
|
|
261
|
+
}
|
|
262
|
+
saveState() {
|
|
263
|
+
return { ...this.state };
|
|
264
|
+
}
|
|
265
|
+
restoreState(state) {
|
|
266
|
+
this.state = state;
|
|
267
|
+
}
|
|
268
|
+
ruleMain() {
|
|
269
|
+
const res = this.ruleBool("OR");
|
|
270
|
+
if (res === void 0) {
|
|
271
|
+
const error = token.value(void 0, { start: 0, end: 0 });
|
|
272
|
+
return error;
|
|
273
|
+
}
|
|
274
|
+
return res;
|
|
275
|
+
}
|
|
276
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
277
|
+
ruleBool(type) {
|
|
278
|
+
var _a;
|
|
279
|
+
const OP_TYPE = type === "AND" ? $C.OPERATOR_AND : $C.OPERATOR_OR;
|
|
280
|
+
const pairs = [];
|
|
281
|
+
let next = this.peek(1);
|
|
282
|
+
while (pairs.length < 1 || ((_a = pairs[pairs.length - 1]) == null ? void 0 : _a[1]) !== void 0) {
|
|
283
|
+
const exp = type === "AND" ? this.ruleCondition() : this.ruleBool("AND");
|
|
284
|
+
next = this.peek(1);
|
|
285
|
+
const canAttemptErrorRecovery = type === "AND" ? ["error", "and"].includes(this.options.onMissingBooleanOperator) : this.options.onMissingBooleanOperator === "or";
|
|
286
|
+
const extras = [];
|
|
287
|
+
if (canAttemptErrorRecovery && (this.isType(next, $C.VALUE) || this.isType(next, $C.QUOTE_ANY) || this.isType(next, $T.PAREN_L) || this.isType(next, $T.EXP_PROP_OP) || this.isType(next, $T.REGEX_START) || this.isType(next, $T.CUSTOM_PROP_OP))) {
|
|
288
|
+
let state = this.saveState();
|
|
289
|
+
let cond = this.ruleCondition();
|
|
290
|
+
if (type === "AND") {
|
|
291
|
+
let dummyOp;
|
|
292
|
+
while (cond !== void 0) {
|
|
293
|
+
if (this.options.onMissingBooleanOperator === "and") {
|
|
294
|
+
const prev = this.peek(-1);
|
|
295
|
+
const start = prev.endOffset + 1;
|
|
296
|
+
dummyOp = operator.and("", pos({ start }, { fill: true }));
|
|
297
|
+
}
|
|
298
|
+
extras.push([dummyOp, cond]);
|
|
299
|
+
state = this.saveState();
|
|
300
|
+
cond = this.ruleCondition();
|
|
301
|
+
}
|
|
302
|
+
this.restoreState(state);
|
|
303
|
+
} else {
|
|
304
|
+
const prev = this.peek(-1);
|
|
305
|
+
const start = prev.endOffset + 1;
|
|
306
|
+
const dummyOp = operator.or("", pos({ start }, { fill: true }));
|
|
307
|
+
extras.push([dummyOp, cond]);
|
|
308
|
+
}
|
|
309
|
+
next = this.peek(1);
|
|
310
|
+
}
|
|
311
|
+
const sepToken = this.isType(next, OP_TYPE) && next ? type === "AND" ? operator.and(...this.processToken(this.consume(next.type))) : operator.or(...this.processToken(this.consume(next.type))) : void 0;
|
|
312
|
+
pairs.push([
|
|
313
|
+
exp,
|
|
314
|
+
sepToken
|
|
315
|
+
]);
|
|
316
|
+
next = this.peek(1);
|
|
317
|
+
for (const extra of extras) {
|
|
318
|
+
pairs[pairs.length - 1].splice(1, 1, extra[0]);
|
|
319
|
+
pairs.push([extra[1]]);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (pairs.length === 0 && this.isType(this.peek(1), OP_TYPE)) {
|
|
323
|
+
next = this.peek(-1);
|
|
324
|
+
let state = this.saveState();
|
|
325
|
+
while (this.isType(next, $C.OPERATOR_AND)) {
|
|
326
|
+
const token2 = this.consume($C.OPERATOR_AND);
|
|
327
|
+
pairs.push([
|
|
328
|
+
void 0,
|
|
329
|
+
type === "AND" ? operator.and(...this.processToken(token2)) : operator.or(...this.processToken(token2))
|
|
330
|
+
]);
|
|
331
|
+
next = this.peek(-1);
|
|
332
|
+
while (this.isType(next, $C.VALUE) || this.isType(next, $C.QUOTE_ANY) || this.isType(next, $T.PAREN_L)) {
|
|
333
|
+
pairs.push([this.ruleCondition()]);
|
|
334
|
+
next = this.peek(-1);
|
|
335
|
+
}
|
|
336
|
+
state = this.saveState();
|
|
337
|
+
}
|
|
338
|
+
this.restoreState(state);
|
|
339
|
+
}
|
|
340
|
+
if (type === "AND" && pairs.length === 0)
|
|
341
|
+
return void 0;
|
|
342
|
+
let res = pairs[pairs.length - 1][0];
|
|
343
|
+
for (let i = pairs.length - 1; i > 0; i--) {
|
|
344
|
+
const before = pairs[i - 1];
|
|
345
|
+
if (type === "OR" && res === void 0 && before === void 0)
|
|
346
|
+
return void 0;
|
|
347
|
+
res = expression(before[0], before[1], res);
|
|
348
|
+
}
|
|
349
|
+
return res;
|
|
350
|
+
}
|
|
351
|
+
ruleCondition() {
|
|
352
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
353
|
+
const not = this.ruleNot();
|
|
354
|
+
const property = this.ruleConditionProperty();
|
|
355
|
+
const propVal = ((_a = property == null ? void 0 : property.prop) == null ? void 0 : _a.value) === void 0 ? void 0 : property.prop.value instanceof ErrorToken ? "" : property.prop.value.value;
|
|
356
|
+
const propOpVal = ((_b = property == null ? void 0 : property.rest) == null ? void 0 : _b.propertyOperator) === void 0 ? void 0 : property.rest.propertyOperator instanceof ErrorToken ? "" : (_c = property.rest.propertyOperator) == null ? void 0 : _c.value;
|
|
357
|
+
const isExpanded = (((_d = property == null ? void 0 : property.rest) == null ? void 0 : _d.sepL) ?? ((_e = property == null ? void 0 : property.rest) == null ? void 0 : _e.sepR)) !== void 0;
|
|
358
|
+
const convertRegexValues = typeof this.options.regexValues === "function" && !this.options.regexValues(propVal, propOpVal, isExpanded);
|
|
359
|
+
const convertArrayValues = typeof this.options.arrayValues === "function" && !this.options.arrayValues(propVal, propOpVal, isExpanded);
|
|
360
|
+
let value = this.ruleConditionValue(property, { convertRegexValues, convertArrayValues });
|
|
361
|
+
let group$1;
|
|
362
|
+
if (!(value instanceof ArrayNode) && !isArray(value) && (!value || this.options.prefixableGroups) && this.isType(this.peek(1), $T.PAREN_L)) {
|
|
363
|
+
group$1 = this.rulePlainGroup({ onlyValues: property !== void 0, convertRegexValues, convertArrayValues });
|
|
364
|
+
}
|
|
365
|
+
if (isArray(value)) {
|
|
366
|
+
group$1 = value;
|
|
367
|
+
value = void 0;
|
|
368
|
+
}
|
|
369
|
+
if (convertRegexValues && value instanceof VariableNode && ((_f = value.quote) == null ? void 0 : _f.left.type) === TOKEN_TYPE.REGEX) {
|
|
370
|
+
value = variable(void 0, void 0, token.value(
|
|
371
|
+
(((_h = (_g = value.quote) == null ? void 0 : _g.left) == null ? void 0 : _h.value) ?? "") + (value.value.value ?? "") + (((_j = (_i = value.quote) == null ? void 0 : _i.right) == null ? void 0 : _j.value) ?? ""),
|
|
372
|
+
pos(value)
|
|
373
|
+
), void 0);
|
|
374
|
+
}
|
|
375
|
+
if (group$1) {
|
|
376
|
+
if (property) {
|
|
377
|
+
return condition(not, property == null ? void 0 : property.prop, property == null ? void 0 : property.rest, group(void 0, void 0, ...group$1));
|
|
378
|
+
}
|
|
379
|
+
if (value) {
|
|
380
|
+
return group(void 0, condition(not, void 0, void 0, value), ...group$1);
|
|
381
|
+
}
|
|
382
|
+
return group(not, value, ...group$1);
|
|
383
|
+
}
|
|
384
|
+
if ([not, property, value].every((_) => _ === void 0))
|
|
385
|
+
return void 0;
|
|
386
|
+
return condition(not, property == null ? void 0 : property.prop, property == null ? void 0 : property.rest, value);
|
|
387
|
+
}
|
|
388
|
+
ruleConditionValue(property, { convertRegexValues = false, convertArrayValues = false } = {}) {
|
|
389
|
+
const next = this.peek(1);
|
|
390
|
+
const next2 = this.peek(2);
|
|
391
|
+
const next3 = this.peek(3);
|
|
392
|
+
const next4 = this.peek(4);
|
|
393
|
+
if (this.options.prefixableGroups && property === void 0 && (next == null ? void 0 : next.type) !== $T.PAREN_L && (this.isType(next, $C.VALUE) && (this.isType(next2, $T.PAREN_L) || this.isType(next2, $C.QUOTE_ANY) && this.isType(next3, $T.PAREN_L)) || this.isType(next, $C.QUOTE_ANY) && (this.isType(next2, $T.PAREN_L) || this.isType(next2, $C.VALUE) && (this.isType(next3, $T.PAREN_L) || // "a(
|
|
394
|
+
this.isType(next3, $C.QUOTE_ANY) && this.isType(next4, $T.PAREN_L))))) {
|
|
395
|
+
const res = this.ruleVariable({ unprefixed: true });
|
|
396
|
+
if (res)
|
|
397
|
+
return res;
|
|
398
|
+
}
|
|
399
|
+
if (!this.isType(next, $T.PAREN_L)) {
|
|
400
|
+
const res = this.ruleVariable({ unprefixed: false });
|
|
401
|
+
if (res)
|
|
402
|
+
return res;
|
|
403
|
+
}
|
|
404
|
+
if (this.isType(next, $T.PAREN_L)) {
|
|
405
|
+
const res = this.rulePlainGroup({ onlyValues: property !== void 0, convertRegexValues, convertArrayValues });
|
|
406
|
+
if (res)
|
|
407
|
+
return res;
|
|
408
|
+
}
|
|
409
|
+
if (this.isType(next, $T.BRACKET_L)) {
|
|
410
|
+
const res = this.rulePlainBracketGroup({ convertArrayValues });
|
|
411
|
+
if (res)
|
|
412
|
+
return res;
|
|
413
|
+
}
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
416
|
+
rulePlainGroup({ onlyValues = false, convertRegexValues = false, convertArrayValues = false } = {}) {
|
|
417
|
+
const parenL = this.ruleParenL();
|
|
418
|
+
let parenLeftCount = 0;
|
|
419
|
+
let start;
|
|
420
|
+
let end;
|
|
421
|
+
const condition2 = !onlyValues ? this.ruleBool("OR") : void 0;
|
|
422
|
+
if (onlyValues && !this.nextIsEof()) {
|
|
423
|
+
while (!this.nextIsEof() && (!this.isType(this.peek(1), $T.PAREN_R) || parenLeftCount !== 0)) {
|
|
424
|
+
const token2 = this.consumeAny();
|
|
425
|
+
start ?? (start = extractPosition(token2, this.state.shift).start);
|
|
426
|
+
if (token2.type === $T.PAREN_L) {
|
|
427
|
+
parenLeftCount++;
|
|
428
|
+
}
|
|
429
|
+
if (token2.type === $T.PAREN_R) {
|
|
430
|
+
parenLeftCount--;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (start !== void 0) {
|
|
435
|
+
end ?? (end = extractPosition(this.peek(0), this.state.shift).end);
|
|
436
|
+
}
|
|
437
|
+
const parenR = this.isType(this.peek(1), $T.PAREN_R) ? this.ruleParenR() : void 0;
|
|
438
|
+
if (start !== void 0) {
|
|
439
|
+
const subInput = this.state.rawInput.slice(start, end);
|
|
440
|
+
this.createSubParserIfNotExists({
|
|
441
|
+
...this.options,
|
|
442
|
+
customPropertyOperators: [],
|
|
443
|
+
expandedPropertySeparator: void 0,
|
|
444
|
+
regexValues: convertRegexValues,
|
|
445
|
+
arrayValues: convertArrayValues
|
|
446
|
+
}, "One");
|
|
447
|
+
const parsed = this.subParserOne.parse(" ".repeat(start) + subInput, { seal: false });
|
|
448
|
+
return [parenL, parsed, parenR];
|
|
449
|
+
}
|
|
450
|
+
return [parenL, condition2, parenR];
|
|
451
|
+
}
|
|
452
|
+
rulePlainBracketGroup({ convertArrayValues = false } = {}) {
|
|
453
|
+
const bracketL = this.ruleBracketL();
|
|
454
|
+
const values = [];
|
|
455
|
+
if (!convertArrayValues) {
|
|
456
|
+
let state = this.saveState();
|
|
457
|
+
let variable2 = this.ruleVariable({ unprefixed: false });
|
|
458
|
+
while (variable2 !== void 0) {
|
|
459
|
+
values.push(variable2);
|
|
460
|
+
state = this.saveState();
|
|
461
|
+
variable2 = this.ruleVariable({ unprefixed: false });
|
|
462
|
+
}
|
|
463
|
+
this.restoreState(state);
|
|
464
|
+
} else if (convertArrayValues && !this.nextIsEof()) {
|
|
465
|
+
while (!this.nextIsEof() && !this.isType(this.peek(1), $T.BRACKET_R)) {
|
|
466
|
+
this.consumeAny();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const bracketR = this.isType(this.peek(1), $T.BRACKET_R) ? this.ruleBracketR() : void 0;
|
|
470
|
+
if (bracketL === void 0)
|
|
471
|
+
throw new Error("bracketL is undefined, peek before using rule.");
|
|
472
|
+
if (!convertArrayValues) {
|
|
473
|
+
return array(bracketL, values, bracketR);
|
|
474
|
+
}
|
|
475
|
+
const start = bracketL.start;
|
|
476
|
+
const end = bracketR == null ? void 0 : bracketR.end;
|
|
477
|
+
const subInput = this.state.rawInput.slice(start, end);
|
|
478
|
+
this.createSubParserIfNotExists({
|
|
479
|
+
...this.options,
|
|
480
|
+
customPropertyOperators: [],
|
|
481
|
+
expandedPropertySeparator: void 0,
|
|
482
|
+
arrayValues: false
|
|
483
|
+
}, "Two");
|
|
484
|
+
const parsed = this.subParserTwo.parse(" ".repeat(start) + subInput, { seal: false });
|
|
485
|
+
if (parsed instanceof ConditionNode) {
|
|
486
|
+
return parsed.value;
|
|
487
|
+
}
|
|
488
|
+
if (parsed instanceof ErrorToken || parsed instanceof ExpressionNode || parsed instanceof GroupNode) {
|
|
489
|
+
unreachable("parsed.value should not be an ErrorToken, ExpressionNode, or GroupNode.");
|
|
490
|
+
}
|
|
491
|
+
return parsed;
|
|
492
|
+
}
|
|
493
|
+
ruleConditionProperty() {
|
|
494
|
+
const current = this.peek(0);
|
|
495
|
+
const next = this.peek(1);
|
|
496
|
+
const next2 = this.peek(2);
|
|
497
|
+
if (this.isType(next, $T.EXP_PROP_OP) || this.isType(next, $T.CUSTOM_PROP_OP) || this.isType(next, $T.VALUE_UNQUOTED) && (this.isType(next2, $T.EXP_PROP_OP) || this.isType(next2, $T.CUSTOM_PROP_OP)) || this.info.customOpAlsoNegation && (this.isType(next2, $T.SYM_NOT) || this.isType(current, $T.SYM_NOT) && this.isType(next, $T.SYM_NOT))) {
|
|
498
|
+
return this.ruleProperty();
|
|
499
|
+
}
|
|
500
|
+
return void 0;
|
|
501
|
+
}
|
|
502
|
+
ruleProperty() {
|
|
503
|
+
const prop = this.ruleVariable({ unprefixed: true });
|
|
504
|
+
const next = this.peek(1);
|
|
505
|
+
let rest = {};
|
|
506
|
+
if (this.isType(next, $T.EXP_PROP_OP)) {
|
|
507
|
+
const sepL = token.sep(...this.processToken(this.consume($T.EXP_PROP_OP)));
|
|
508
|
+
const op = this.isType(this.peek(1), $T.VALUE_UNQUOTED) ? token.value(...this.processToken(this.consume($T.VALUE_UNQUOTED))) : void 0;
|
|
509
|
+
const sepR = this.isType(this.peek(1), $T.EXP_PROP_OP) ? token.sep(...this.processToken(this.consume($T.EXP_PROP_OP))) : void 0;
|
|
510
|
+
if (this.info.expandedSepAlsoCustom && op === void 0 && sepR === void 0) {
|
|
511
|
+
setReadOnly(sepL, "type", TOKEN_TYPE.OP_CUSTOM);
|
|
512
|
+
rest = {
|
|
513
|
+
sepL: void 0,
|
|
514
|
+
sepR,
|
|
515
|
+
propertyOperator: sepL
|
|
516
|
+
};
|
|
517
|
+
} else {
|
|
518
|
+
rest = { sepL, sepR, propertyOperator: op };
|
|
519
|
+
}
|
|
520
|
+
} else if (this.isType(next, $T.CUSTOM_PROP_OP)) {
|
|
521
|
+
const op = token.custom(...this.processToken(this.consume($T.CUSTOM_PROP_OP)));
|
|
522
|
+
rest = { propertyOperator: op };
|
|
523
|
+
} else if (this.info.customOpAlsoNegation && this.isType(next, $T.SYM_NOT)) {
|
|
524
|
+
const op = token.custom(...this.processToken(this.consume($T.SYM_NOT)));
|
|
525
|
+
rest = { propertyOperator: op };
|
|
526
|
+
}
|
|
527
|
+
return { prop, rest };
|
|
528
|
+
}
|
|
529
|
+
ruleVariable({
|
|
530
|
+
unprefixed = false
|
|
531
|
+
} = {}) {
|
|
532
|
+
const prefix = this.ruleVariablePrefix({ onlyToken: true, unprefixed });
|
|
533
|
+
const next = this.peek(1);
|
|
534
|
+
const next2 = this.peek(2);
|
|
535
|
+
const next3 = this.peek(3);
|
|
536
|
+
if (next && (this.isExactType(next, $T.QUOTE_DOUBLE) || this.isExactType(next, $T.QUOTE_SINGLE) || this.isExactType(next, $T.QUOTE_BACKTICK))) {
|
|
537
|
+
const quoteType = next.type;
|
|
538
|
+
if ((next2 == null ? void 0 : next2.type) === quoteType) {
|
|
539
|
+
const quoteL = this.ruleQuote(quoteType);
|
|
540
|
+
const quoteR = this.ruleQuote(quoteType);
|
|
541
|
+
return variable(void 0, quoteL, void 0, quoteR);
|
|
542
|
+
}
|
|
543
|
+
if ((next3 == null ? void 0 : next3.type) === next.type) {
|
|
544
|
+
const quoteL = this.ruleQuote(quoteType);
|
|
545
|
+
const value = this.isType(next2, $T.VALUE_UNQUOTED) ? this.ruleValueUnquoted({}) : this.ruleValueNot(quoteType);
|
|
546
|
+
const quoteR = this.ruleQuote(quoteType);
|
|
547
|
+
const prefixToken = prefix ? token.value(...this.processToken(prefix)) : void 0;
|
|
548
|
+
return variable(prefixToken, quoteL, value, quoteR);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (this.isType(next, $C.REGEX_ANY)) {
|
|
552
|
+
const quoteL = this.ruleRegexAny();
|
|
553
|
+
const maybeValue = this.peek(1);
|
|
554
|
+
const value = this.isType(maybeValue, $T.VALUE_REGEX) ? this.ruleValueNot($C.REGEX_ANY) : void 0;
|
|
555
|
+
const quoteR = this.isType(this.peek(1), $C.REGEX_ANY) ? this.ruleRegexAny() : void 0;
|
|
556
|
+
const args = isArray(quoteR) ? quoteR : [quoteR, void 0];
|
|
557
|
+
return variable(void 0, quoteL, value, args[0], args[1]);
|
|
558
|
+
}
|
|
559
|
+
if (this.isType(next, $T.VALUE_UNQUOTED) && this.isType(next2, $C.QUOTE_ANY)) {
|
|
560
|
+
const value = this.ruleValueUnquoted();
|
|
561
|
+
const quoteR = this.ruleValueDelimAny();
|
|
562
|
+
return variable(void 0, void 0, value, quoteR);
|
|
563
|
+
}
|
|
564
|
+
if (this.isType(next, $C.QUOTE_ANY)) {
|
|
565
|
+
const quoteToken = next;
|
|
566
|
+
const quoteL = this.ruleValueDelimAny();
|
|
567
|
+
const maybeValue = this.peek(1);
|
|
568
|
+
const value = !quoteL && this.isType(maybeValue, $T.VALUE_UNQUOTED) ? this.ruleValueUnquoted() : quoteL && this.isType(maybeValue, quoteToken.type.replace("QUOTE", "VALUE_FOR")) ? this.ruleValueNot(quoteToken.type) : void 0;
|
|
569
|
+
return variable(void 0, quoteL, value, void 0);
|
|
570
|
+
}
|
|
571
|
+
if (this.isType(next, $T.VALUE_UNQUOTED)) {
|
|
572
|
+
const value = this.ruleValueUnquoted();
|
|
573
|
+
return variable(void 0, void 0, value, void 0);
|
|
574
|
+
}
|
|
575
|
+
return void 0;
|
|
576
|
+
}
|
|
577
|
+
ruleValueDelimAny() {
|
|
578
|
+
const next = this.peek(1);
|
|
579
|
+
if (this.isType(next, $C.QUOTE_ANY)) {
|
|
580
|
+
const type = next.value === `"` ? "double" : next.value === "'" ? "single" : next.value === "`" ? "tick" : "regex";
|
|
581
|
+
return delimiter[type](...this.processToken(this.consume($C.QUOTE_ANY)));
|
|
582
|
+
}
|
|
583
|
+
return void 0;
|
|
584
|
+
}
|
|
585
|
+
ruleRegexAny() {
|
|
586
|
+
const value = this.consume($C.REGEX_ANY);
|
|
587
|
+
if (value.value.length > 1) {
|
|
588
|
+
const delim = {
|
|
589
|
+
value: "/",
|
|
590
|
+
startOffset: value.startOffset,
|
|
591
|
+
endOffset: value.startOffset
|
|
592
|
+
};
|
|
593
|
+
const flags = {
|
|
594
|
+
value: value.value.slice(1),
|
|
595
|
+
startOffset: value.startOffset + 1,
|
|
596
|
+
endOffset: value.endOffset
|
|
597
|
+
};
|
|
598
|
+
return [
|
|
599
|
+
// why the ! ??? todo
|
|
600
|
+
delimiter.regex(...this.processToken(delim)),
|
|
601
|
+
token.value(...this.processToken(flags))
|
|
602
|
+
];
|
|
603
|
+
}
|
|
604
|
+
return delimiter.regex(...this.processToken(value));
|
|
605
|
+
}
|
|
606
|
+
ruleValueNot(type) {
|
|
607
|
+
const realType = {
|
|
608
|
+
[$T.QUOTE_SINGLE]: $C.VALUE_FOR_SINGLE,
|
|
609
|
+
[$T.QUOTE_DOUBLE]: $C.VALUE_FOR_DOUBLE,
|
|
610
|
+
[$T.QUOTE_BACKTICK]: $C.VALUE_FOR_BACKTICK,
|
|
611
|
+
[$C.REGEX_ANY]: $T.VALUE_REGEX
|
|
612
|
+
}[type];
|
|
613
|
+
if (realType === void 0) {
|
|
614
|
+
unreachable(`Unknown quote/regex type ${type}`);
|
|
615
|
+
}
|
|
616
|
+
const value = this.consume(realType);
|
|
617
|
+
if (realType !== value.type) {
|
|
618
|
+
unreachable(`Expected value type ${realType}, got ${value.type}`);
|
|
619
|
+
}
|
|
620
|
+
return token.value(...this.processToken(value));
|
|
621
|
+
}
|
|
622
|
+
ruleQuote(type) {
|
|
623
|
+
const quote = this.peek(1);
|
|
624
|
+
if (type !== (quote == null ? void 0 : quote.type)) {
|
|
625
|
+
throw new Error(`Expected quote type ${type}, got ${quote == null ? void 0 : quote.type}`);
|
|
626
|
+
}
|
|
627
|
+
switch (type) {
|
|
628
|
+
case $T.QUOTE_SINGLE:
|
|
629
|
+
return delimiter.single(
|
|
630
|
+
...this.processToken(this.consume($T.QUOTE_SINGLE))
|
|
631
|
+
);
|
|
632
|
+
case $T.QUOTE_DOUBLE:
|
|
633
|
+
return delimiter.double(
|
|
634
|
+
...this.processToken(this.consume($T.QUOTE_DOUBLE))
|
|
635
|
+
);
|
|
636
|
+
case $T.QUOTE_BACKTICK:
|
|
637
|
+
return delimiter.tick(
|
|
638
|
+
...this.processToken(this.consume($T.QUOTE_BACKTICK))
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
throw new Error(`Expected quote type ${type}`);
|
|
642
|
+
}
|
|
643
|
+
ruleVariablePrefix({
|
|
644
|
+
onlyToken = false,
|
|
645
|
+
unprefixed = false
|
|
646
|
+
} = {}) {
|
|
647
|
+
const next = this.peek(1);
|
|
648
|
+
const next2 = this.peek(2);
|
|
649
|
+
const next4 = this.peek(4);
|
|
650
|
+
if (!unprefixed && this.options.prefixableStrings !== void 0 && this.isType(next2, $C.QUOTE_ANY) && next2 && this.isType(next4, next2.type) && next && this.options.prefixableStrings.includes(next.value)) {
|
|
651
|
+
return this.ruleValueUnquoted({ onlyToken });
|
|
652
|
+
}
|
|
653
|
+
if (onlyToken)
|
|
654
|
+
return void 0;
|
|
655
|
+
return token.value(...this.processToken());
|
|
656
|
+
}
|
|
657
|
+
ruleValueUnquoted({
|
|
658
|
+
onlyToken = false
|
|
659
|
+
} = {}) {
|
|
660
|
+
const t = this.consume($T.VALUE_UNQUOTED);
|
|
661
|
+
const res = onlyToken ? t : token.value(...this.processToken(t));
|
|
662
|
+
return res;
|
|
663
|
+
}
|
|
664
|
+
ruleParenL() {
|
|
665
|
+
const next = this.peek(1);
|
|
666
|
+
const value = (next == null ? void 0 : next.type) === $T.PAREN_L ? this.consume($T.PAREN_L) : this.createErrorToken($T.PAREN_L);
|
|
667
|
+
const loc = extractPosition(value, this.state.shift);
|
|
668
|
+
return this.state.shift === 0 || loc.start > 0 ? delimiter.parenL(value.isError ? void 0 : value.value, loc) : void 0;
|
|
669
|
+
}
|
|
670
|
+
ruleParenR() {
|
|
671
|
+
const value = this.consume($T.PAREN_R);
|
|
672
|
+
return delimiter.parenR(...this.processToken(value));
|
|
673
|
+
}
|
|
674
|
+
ruleBracketL() {
|
|
675
|
+
const next = this.peek(1);
|
|
676
|
+
const value = (next == null ? void 0 : next.type) === $T.BRACKET_L ? this.consume($T.BRACKET_L) : this.createErrorToken($T.BRACKET_L);
|
|
677
|
+
const loc = extractPosition(value, this.state.shift);
|
|
678
|
+
return this.state.shift === 0 || loc.start > 0 ? delimiter.bracketL(value.isError ? void 0 : value.value, loc) : void 0;
|
|
679
|
+
}
|
|
680
|
+
ruleBracketR() {
|
|
681
|
+
const value = this.consume($T.BRACKET_R);
|
|
682
|
+
return delimiter.bracketR(...this.processToken(value));
|
|
683
|
+
}
|
|
684
|
+
ruleNot() {
|
|
685
|
+
if (this.isType(this.peek(1), $C.OPERATOR_NOT)) {
|
|
686
|
+
const op = this.consume($C.OPERATOR_NOT);
|
|
687
|
+
return operator.not(...this.processToken(op));
|
|
688
|
+
}
|
|
689
|
+
return void 0;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Given a list of @see Suggestion entries, the parser options, and a list of variables, prefixes, operators, etc, and the preferred quote type, returns a list of @see Completion entries.
|
|
693
|
+
*
|
|
694
|
+
* It takes care of suggesting the correct delimiters for fixes, quoting variables/prefixes if it would not be possible to parse them unquoted, and separating symbol from non-symbol (word) operators.
|
|
695
|
+
*
|
|
696
|
+
* Does not add whitespace or group requirements. The suggestion information is still in the completion if you wish to show these. But they should not be added to the completion value if using @see autoreplace which will take care of it.
|
|
697
|
+
*
|
|
698
|
+
* Is not aware of existing values. You will have to use @see getCursorInfo to understand the context in which the suggestion was made, so that, for example, you could filter out used regex flags.
|
|
699
|
+
*/
|
|
700
|
+
autocomplete(suggestions, {
|
|
701
|
+
values = [],
|
|
702
|
+
arrayValues = [],
|
|
703
|
+
variables = [],
|
|
704
|
+
prefixes = [],
|
|
705
|
+
properties = [],
|
|
706
|
+
expandedPropertyOperators = [],
|
|
707
|
+
customPropertyOperators = this.options.customPropertyOperators ?? [],
|
|
708
|
+
keywords = this.options.keywords,
|
|
709
|
+
regexFlags = ["i", "m", "u"],
|
|
710
|
+
quote = '"'
|
|
711
|
+
} = {}) {
|
|
712
|
+
const self = this;
|
|
713
|
+
return suggestions.map((suggestion) => {
|
|
714
|
+
const type = suggestion.type;
|
|
715
|
+
switch (type) {
|
|
716
|
+
case SUGGESTION_TYPE.BACKTICK:
|
|
717
|
+
return [{ suggestion, value: "`" }];
|
|
718
|
+
case SUGGESTION_TYPE.DOUBLEQUOTE:
|
|
719
|
+
return [{ suggestion, value: '"' }];
|
|
720
|
+
case SUGGESTION_TYPE.SINGLEQUOTE:
|
|
721
|
+
return [{ suggestion, value: "'" }];
|
|
722
|
+
case SUGGESTION_TYPE.PARENL:
|
|
723
|
+
return [{ suggestion, value: "(" }];
|
|
724
|
+
case SUGGESTION_TYPE.PARENR:
|
|
725
|
+
return [{ suggestion, value: ")" }];
|
|
726
|
+
case SUGGESTION_TYPE.BRAKCETR:
|
|
727
|
+
return [{ suggestion, value: "]" }];
|
|
728
|
+
case SUGGESTION_TYPE.REGEX:
|
|
729
|
+
return [{ suggestion, value: "/" }];
|
|
730
|
+
case SUGGESTION_TYPE.REGEX_FLAGS:
|
|
731
|
+
return regexFlags.map((value) => ({ suggestion, value })).filter((completion) => {
|
|
732
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
733
|
+
const { suggestion: suggestion2, value } = completion;
|
|
734
|
+
if (suggestion2.type !== SUGGESTION_TYPE.REGEX_FLAGS) {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
const token2 = suggestion2.cursorInfo;
|
|
738
|
+
const flags = token2.at && ((_b = (_a = token2.at.parent) == null ? void 0 : _a.quote) == null ? void 0 : _b.flags) === suggestion2.cursorInfo.at ? token2.at : token2.next && ((_d = (_c = token2.next.parent) == null ? void 0 : _c.quote) == null ? void 0 : _d.flags) === suggestion2.cursorInfo.next ? token2.next : token2.prev && ((_f = (_e = token2.prev.parent) == null ? void 0 : _e.quote) == null ? void 0 : _f.flags) === suggestion2.cursorInfo.prev ? token2.prev : void 0;
|
|
739
|
+
if ((_g = flags == null ? void 0 : flags.value) == null ? void 0 : _g.includes(value)) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
return true;
|
|
743
|
+
});
|
|
744
|
+
case SUGGESTION_TYPE.PROPERTY: {
|
|
745
|
+
return properties.map((value) => ({ suggestion, value }));
|
|
746
|
+
}
|
|
747
|
+
case SUGGESTION_TYPE.PROPERTY_SEP: {
|
|
748
|
+
return [{ suggestion, value: self.options.expandedPropertySeparator }];
|
|
749
|
+
}
|
|
750
|
+
case SUGGESTION_TYPE.EXPANDED_PROPERTY_OPERATOR: {
|
|
751
|
+
return expandedPropertyOperators.map((value) => ({ suggestion, value }));
|
|
752
|
+
}
|
|
753
|
+
case SUGGESTION_TYPE.CUSTOM_PROPERTY_OPERATOR: {
|
|
754
|
+
return customPropertyOperators.map((value) => ({ suggestion, value }));
|
|
755
|
+
}
|
|
756
|
+
case SUGGESTION_TYPE.BOOLEAN_SYMBOL_OP: {
|
|
757
|
+
const keywordsList = [...keywords.and, ...keywords.or];
|
|
758
|
+
const symOpts = keywordsList.filter((_) => _.isSymbol);
|
|
759
|
+
return symOpts.map(({ value }) => ({ suggestion, value }));
|
|
760
|
+
}
|
|
761
|
+
case SUGGESTION_TYPE.BOOLEAN_WORD_OP: {
|
|
762
|
+
const keywordsList = [...keywords.and, ...keywords.or];
|
|
763
|
+
const wordOpts = keywordsList.filter((_) => !_.isSymbol);
|
|
764
|
+
return wordOpts.map(({ value }) => ({ suggestion, value }));
|
|
765
|
+
}
|
|
766
|
+
case SUGGESTION_TYPE.VALUE:
|
|
767
|
+
case SUGGESTION_TYPE.ARRAY_VALUE:
|
|
768
|
+
case SUGGESTION_TYPE.VARIABLE: {
|
|
769
|
+
const arr = type === SUGGESTION_TYPE.VARIABLE ? variables : type === SUGGESTION_TYPE.ARRAY_VALUE ? arrayValues : type === SUGGESTION_TYPE.VALUE ? values : unreachable();
|
|
770
|
+
return arr.map((variable2) => {
|
|
771
|
+
const res = self.parse(variable2);
|
|
772
|
+
if (res instanceof ConditionNode && res.operator === void 0 && res.value instanceof VariableNode && res.value.quote === void 0) {
|
|
773
|
+
return { suggestion, value: res.value.value.value };
|
|
774
|
+
} else {
|
|
775
|
+
return { suggestion, value: quote + variable2.replace(new RegExp(quote, "g"), `\\${quote}`) + quote };
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
case SUGGESTION_TYPE.PREFIX:
|
|
780
|
+
return prefixes.map((prefix) => {
|
|
781
|
+
const res = self.parse(prefix);
|
|
782
|
+
if (res instanceof ConditionNode && res.operator === void 0 && res.value instanceof VariableNode && res.value.quote === void 0) {
|
|
783
|
+
return { suggestion, value: res.value.value.value };
|
|
784
|
+
} else {
|
|
785
|
+
return { suggestion, value: quote + prefix.replace(new RegExp(quote, "g"), `\\${quote}`) + quote };
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}).flat();
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Given the input string and a @see Completion consisting of the value of the replacement and a @see Suggestion entry, returns the replacement string and the new position of the cursor.
|
|
793
|
+
*
|
|
794
|
+
* The value passed should be escaped if it's needed (or quoted). @see autocomplete already takes care of quoting variables if you're using it.
|
|
795
|
+
*/
|
|
796
|
+
autoreplace(input, { value, suggestion }) {
|
|
797
|
+
const isQuotedLeft = ['"', "'", "`"].includes(value[0]);
|
|
798
|
+
const isQuotedRight = ['"', "'", "`"].includes(value[value.length - 1]);
|
|
799
|
+
if (isQuotedLeft && !isQuotedRight || !isQuotedLeft && isQuotedRight) {
|
|
800
|
+
throw new Error(`Completion value must either be entirely quoted or entirely unquoted. But the left side is ${isQuotedLeft ? "quoted" : "unquoted"} and the right side is ${isQuotedRight ? "quoted" : "unquoted"}.`);
|
|
801
|
+
}
|
|
802
|
+
let cursor = suggestion.range.start + value.length;
|
|
803
|
+
if (suggestion.requires.prefix) {
|
|
804
|
+
value = suggestion.requires.prefix + (isQuotedLeft ? "" : '"') + value + (isQuotedRight ? "" : '"');
|
|
805
|
+
cursor += suggestion.requires.prefix.length + Number(!isQuotedLeft) + Number(!isQuotedRight);
|
|
806
|
+
}
|
|
807
|
+
if (suggestion.requires.group) {
|
|
808
|
+
value += "()";
|
|
809
|
+
cursor++;
|
|
810
|
+
}
|
|
811
|
+
if (suggestion.requires.whitespace.before) {
|
|
812
|
+
value = ` ${value}`;
|
|
813
|
+
cursor++;
|
|
814
|
+
}
|
|
815
|
+
if (suggestion.requires.whitespace.after) {
|
|
816
|
+
value = `${value} `;
|
|
817
|
+
}
|
|
818
|
+
const replacement = insert(value, input, [suggestion.range.start, suggestion.range.end]);
|
|
819
|
+
return { replacement, cursor };
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Returns a list of suggestions ( @see Suggestion ). These are not a list of autocomplete entries (with values), but more a list of entries describing possible suggestions. This list can then be passed to @see Parser["autocomplete"] to build a list to show users, from which you can then pick an entry to pass to @see Parser["autoreplace"] .
|
|
823
|
+
*
|
|
824
|
+
* The list returned is "unsorted", but there is still some logic to the order. Fixes for errors are suggested first, in the order returned by @see getSurroundingErrors. Regular suggestions come after in the following order: prefixes if enabled, variables, boolean symbol operators, then boolean word operators.
|
|
825
|
+
*
|
|
826
|
+
* When the cursor is between two tokens that have possible suggestions, only suggestion types for the token before are returned. For example:
|
|
827
|
+
*
|
|
828
|
+
* ```js
|
|
829
|
+
* prop="val"
|
|
830
|
+
* prop|="val" //returns a property suggestions to replace `prop`
|
|
831
|
+
* prop=|"val" //returns a custom operator suggestion to replace `=`
|
|
832
|
+
* prop="|val" //returns a value suggestion
|
|
833
|
+
* ```
|
|
834
|
+
*
|
|
835
|
+
* And if there are no suggestions for the previous token but there are for the next ones, they are suggested:
|
|
836
|
+
* ```js
|
|
837
|
+
* prop:op:"val"
|
|
838
|
+
* prop:op|:"val" // returns an operator suggestion
|
|
839
|
+
* prop:op:|"val" // returns a value suggestion
|
|
840
|
+
* prop:op|"val" // returns a suggestion for the missing separator
|
|
841
|
+
* ```
|
|
842
|
+
*/
|
|
843
|
+
autosuggest(input, ast, index) {
|
|
844
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L;
|
|
845
|
+
const opts = this.options;
|
|
846
|
+
const tokens = extractTokens(ast);
|
|
847
|
+
const token2 = getCursorInfo(input, tokens, index);
|
|
848
|
+
const wordOps = [...opts.keywords.and, ...opts.keywords.or, ...opts.keywords.not].filter((op) => !op.isSymbol);
|
|
849
|
+
const canSuggestOpAfterPrev = token2.valid.prev && tokenVariable.includes((_a = token2.valid.prev) == null ? void 0 : _a.type) && (token2.whitespace.prev || token2.valid.prev.type === TOKEN_TYPE.PARENR) && !token2.at && token2.valid.next === void 0;
|
|
850
|
+
const canSuggestOpBeforeNext = token2.valid.next && tokenVariable.includes((_b = token2.valid.next) == null ? void 0 : _b.type) && token2.whitespace.next && // no parenL allowed since check since there will already be prefix suggestions
|
|
851
|
+
!token2.at && token2.valid.prev === void 0;
|
|
852
|
+
const requiresWhitespacePrev = tokenRequiresWhitespace(token2.valid.prev, token2.whitespace.prev, wordOps);
|
|
853
|
+
const requiresWhitespaceNext = tokenRequiresWhitespace(token2.valid.next, token2.whitespace.next, wordOps);
|
|
854
|
+
const requiresWhitespacePrevOp = canSuggestOpAfterPrev ? false : requiresWhitespacePrev;
|
|
855
|
+
const requireWhitespaceNextOp = !canSuggestOpAfterPrev && canSuggestOpBeforeNext ? false : requiresWhitespaceNext;
|
|
856
|
+
const suggestions = [];
|
|
857
|
+
if (ast instanceof ErrorToken) {
|
|
858
|
+
suggestions.push({
|
|
859
|
+
type: SUGGESTION_TYPE.PREFIX,
|
|
860
|
+
requires: createDefaultRequires({ group: true }),
|
|
861
|
+
range: pos({ start: index }, { fill: true }),
|
|
862
|
+
isError: true,
|
|
863
|
+
cursorInfo: token2
|
|
864
|
+
});
|
|
865
|
+
suggestions.push({
|
|
866
|
+
type: SUGGESTION_TYPE.VARIABLE,
|
|
867
|
+
requires: createDefaultRequires(),
|
|
868
|
+
range: pos({ start: index }, { fill: true }),
|
|
869
|
+
isError: true,
|
|
870
|
+
cursorInfo: token2
|
|
871
|
+
});
|
|
872
|
+
} else {
|
|
873
|
+
const surroundingErrors = getSurroundingErrors(tokens, token2);
|
|
874
|
+
const errorTypesHandled = [];
|
|
875
|
+
const errorSuggestion = {
|
|
876
|
+
isError: true,
|
|
877
|
+
cursorInfo: token2
|
|
878
|
+
};
|
|
879
|
+
const baseSuggestion = {
|
|
880
|
+
isError: false,
|
|
881
|
+
cursorInfo: token2
|
|
882
|
+
};
|
|
883
|
+
for (const error of surroundingErrors) {
|
|
884
|
+
for (const type of error.expected) {
|
|
885
|
+
if (errorTypesHandled.includes(type))
|
|
886
|
+
continue;
|
|
887
|
+
errorTypesHandled.push(type);
|
|
888
|
+
switch (type) {
|
|
889
|
+
case TOKEN_TYPE.DOUBLEQUOTE:
|
|
890
|
+
case TOKEN_TYPE.SINGLEQUOTE:
|
|
891
|
+
case TOKEN_TYPE.BACKTICK:
|
|
892
|
+
{
|
|
893
|
+
const isLeft = error.parent.quote.left === error;
|
|
894
|
+
const isRight = error.parent.quote.right === error;
|
|
895
|
+
suggestions.push({
|
|
896
|
+
...errorSuggestion,
|
|
897
|
+
type,
|
|
898
|
+
requires: createDefaultRequires({
|
|
899
|
+
whitespace: {
|
|
900
|
+
before: isRight ? false : requiresWhitespacePrev,
|
|
901
|
+
after: isLeft ? false : requiresWhitespaceNext
|
|
902
|
+
}
|
|
903
|
+
}),
|
|
904
|
+
range: pos({ start: index }, { fill: true })
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
break;
|
|
908
|
+
case TOKEN_TYPE.AND:
|
|
909
|
+
case TOKEN_TYPE.OR:
|
|
910
|
+
suggestions.push({
|
|
911
|
+
...errorSuggestion,
|
|
912
|
+
type: SUGGESTION_TYPE.BOOLEAN_SYMBOL_OP,
|
|
913
|
+
requires: createDefaultRequires(),
|
|
914
|
+
range: pos({ start: index }, { fill: true })
|
|
915
|
+
});
|
|
916
|
+
suggestions.push({
|
|
917
|
+
...errorSuggestion,
|
|
918
|
+
type: SUGGESTION_TYPE.BOOLEAN_WORD_OP,
|
|
919
|
+
requires: createDefaultRequires({
|
|
920
|
+
whitespace: {
|
|
921
|
+
before: requiresWhitespacePrevOp,
|
|
922
|
+
after: requireWhitespaceNextOp
|
|
923
|
+
}
|
|
924
|
+
}),
|
|
925
|
+
range: pos({ start: index }, { fill: true })
|
|
926
|
+
});
|
|
927
|
+
if (type === TOKEN_TYPE.AND)
|
|
928
|
+
errorTypesHandled.push(TOKEN_TYPE.OR);
|
|
929
|
+
if (type === TOKEN_TYPE.OR)
|
|
930
|
+
errorTypesHandled.push(TOKEN_TYPE.AND);
|
|
931
|
+
break;
|
|
932
|
+
case TOKEN_TYPE.PARENL:
|
|
933
|
+
case TOKEN_TYPE.PARENR:
|
|
934
|
+
suggestions.push({
|
|
935
|
+
...errorSuggestion,
|
|
936
|
+
type,
|
|
937
|
+
requires: createDefaultRequires(),
|
|
938
|
+
range: pos({ start: index }, { fill: true })
|
|
939
|
+
});
|
|
940
|
+
break;
|
|
941
|
+
case TOKEN_TYPE.VALUE: {
|
|
942
|
+
const prefixedValue = error.parent instanceof VariableNode ? (_d = (_c = error.parent) == null ? void 0 : _c.prefix) == null ? void 0 : _d.value : false;
|
|
943
|
+
const isRegexValue = error.parent instanceof VariableNode && (((_e = error.parent.quote) == null ? void 0 : _e.left.type) === TOKEN_TYPE.REGEX || ((_f = error.parent.quote) == null ? void 0 : _f.right.type) === TOKEN_TYPE.REGEX);
|
|
944
|
+
if (!isRegexValue) {
|
|
945
|
+
if (!prefixedValue && opts.prefixableGroups) {
|
|
946
|
+
suggestions.push({
|
|
947
|
+
...errorSuggestion,
|
|
948
|
+
type: SUGGESTION_TYPE.PREFIX,
|
|
949
|
+
requires: createDefaultRequires({
|
|
950
|
+
whitespace: {
|
|
951
|
+
before: requiresWhitespacePrev,
|
|
952
|
+
after: false
|
|
953
|
+
/* parens get inserted */
|
|
954
|
+
},
|
|
955
|
+
group: true
|
|
956
|
+
// is always needed
|
|
957
|
+
}),
|
|
958
|
+
range: pos({ start: index }, { fill: true })
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
suggestions.push({
|
|
962
|
+
...errorSuggestion,
|
|
963
|
+
type: SUGGESTION_TYPE.VARIABLE,
|
|
964
|
+
requires: createDefaultRequires({
|
|
965
|
+
whitespace: {
|
|
966
|
+
before: requiresWhitespacePrev,
|
|
967
|
+
after: requiresWhitespaceNext
|
|
968
|
+
},
|
|
969
|
+
prefix: prefixedValue
|
|
970
|
+
}),
|
|
971
|
+
range: pos({ start: index }, { fill: true })
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
break;
|
|
975
|
+
}
|
|
976
|
+
case TOKEN_TYPE.BRACKETR: {
|
|
977
|
+
suggestions.push({
|
|
978
|
+
...errorSuggestion,
|
|
979
|
+
type: SUGGESTION_TYPE.BRAKCETR,
|
|
980
|
+
requires: createDefaultRequires(),
|
|
981
|
+
range: pos({ start: index }, { fill: true })
|
|
982
|
+
});
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
case TOKEN_TYPE.OP_EXPANDED_SEP:
|
|
986
|
+
suggestions.push({
|
|
987
|
+
...errorSuggestion,
|
|
988
|
+
type: SUGGESTION_TYPE.PROPERTY_SEP,
|
|
989
|
+
requires: createDefaultRequires(),
|
|
990
|
+
range: pos({ start: index }, { fill: true })
|
|
991
|
+
});
|
|
992
|
+
break;
|
|
993
|
+
case TOKEN_TYPE.REGEX:
|
|
994
|
+
suggestions.push({
|
|
995
|
+
...errorSuggestion,
|
|
996
|
+
type: SUGGESTION_TYPE.REGEX,
|
|
997
|
+
requires: createDefaultRequires(),
|
|
998
|
+
range: pos({ start: index }, { fill: true })
|
|
999
|
+
});
|
|
1000
|
+
break;
|
|
1001
|
+
case TOKEN_TYPE.OP_CUSTOM:
|
|
1002
|
+
case TOKEN_TYPE.BRACKETL:
|
|
1003
|
+
case TOKEN_TYPE.NOT:
|
|
1004
|
+
unreachable();
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
const prevVar = (_g = token2.valid.prev) == null ? void 0 : _g.parent;
|
|
1009
|
+
const nextVar = (_h = token2.valid.next) == null ? void 0 : _h.parent;
|
|
1010
|
+
const prevCondition = prevVar == null ? void 0 : prevVar.parent;
|
|
1011
|
+
const nextCondition = nextVar == null ? void 0 : nextVar.parent;
|
|
1012
|
+
const atVar = (_i = token2.at) == null ? void 0 : _i.parent;
|
|
1013
|
+
const atCondition = atVar == null ? void 0 : atVar.parent;
|
|
1014
|
+
const isVarPrev = !token2.whitespace.prev && ((_j = token2.valid.prev) == null ? void 0 : _j.type) !== TOKEN_TYPE.REGEX && prevVar instanceof VariableNode && (prevCondition instanceof ConditionNode && prevCondition.value === prevVar && (((_k = prevVar.quote) == null ? void 0 : _k.right) === token2.valid.prev || prevVar.value === token2.valid.prev) || prevCondition instanceof ArrayNode);
|
|
1015
|
+
const isVarNext = !token2.whitespace.next && ((_l = token2.valid.next) == null ? void 0 : _l.type) !== TOKEN_TYPE.REGEX && nextVar instanceof VariableNode && (nextCondition instanceof ConditionNode && nextCondition.value === nextVar && (((_m = nextVar.quote) == null ? void 0 : _m.left) === token2.valid.next || nextVar.value === token2.valid.next) || nextCondition instanceof ArrayNode);
|
|
1016
|
+
const isVarAt = atVar instanceof VariableNode && atCondition instanceof ConditionNode || prevVar instanceof VariableNode && token2.valid.prev === ((_n = prevVar == null ? void 0 : prevVar.quote) == null ? void 0 : _n.left) || nextVar instanceof VariableNode && token2.valid.next === ((_o = nextVar == null ? void 0 : nextVar.quote) == null ? void 0 : _o.right);
|
|
1017
|
+
const isPropertyPrev = prevCondition instanceof ConditionNode && prevVar !== void 0 && prevVar === (prevCondition == null ? void 0 : prevCondition.property);
|
|
1018
|
+
const isPropertyNext = nextCondition instanceof ConditionNode && nextVar !== void 0 && nextVar === (nextCondition == null ? void 0 : nextCondition.property);
|
|
1019
|
+
const isPropertyAt = atCondition instanceof ConditionNode && atVar !== void 0 && atVar === (atCondition == null ? void 0 : atCondition.property);
|
|
1020
|
+
const isPropertyOperatorPrev = prevVar instanceof ConditionNode && token2.valid.prev === (prevVar == null ? void 0 : prevVar.propertyOperator);
|
|
1021
|
+
const isPropertyOperatorNext = nextVar instanceof ConditionNode && token2.valid.next === (nextVar == null ? void 0 : nextVar.propertyOperator);
|
|
1022
|
+
const isPropertyOperatorAt = atVar instanceof ConditionNode && token2.at === (atVar == null ? void 0 : atVar.propertyOperator);
|
|
1023
|
+
const noArrayValuesTarget = ((_p = token2.valid.prev) == null ? void 0 : _p.type) === TOKEN_TYPE.BRACKETL && (token2.valid.next === void 0 || ((_q = token2.valid.next) == null ? void 0 : _q.type) === TOKEN_TYPE.BRACKETR);
|
|
1024
|
+
const target = isVarPrev ? token2.valid.prev : !noArrayValuesTarget && !isPropertyPrev && !isPropertyOperatorPrev && isVarNext ? token2.valid.next : isVarAt ? token2.at : void 0;
|
|
1025
|
+
const propertyTarget = isPropertyPrev ? token2.valid.prev : !noArrayValuesTarget && !isVarPrev && !isPropertyOperatorPrev && isPropertyNext ? token2.valid.next : isPropertyAt ? token2.at : void 0;
|
|
1026
|
+
const propOpTarget = isPropertyOperatorPrev ? token2.valid.prev : !noArrayValuesTarget && !isVarPrev && !isPropertyPrev && isPropertyOperatorNext ? token2.valid.next : isPropertyOperatorAt ? token2.at : void 0;
|
|
1027
|
+
if (target) {
|
|
1028
|
+
const parent = target.parent;
|
|
1029
|
+
if (parent instanceof VariableNode) {
|
|
1030
|
+
const range = pos(parent);
|
|
1031
|
+
const condition2 = parent == null ? void 0 : parent.parent;
|
|
1032
|
+
const isValue = condition2.propertyOperator !== void 0 && condition2.value === parent;
|
|
1033
|
+
const maybeGroup = (_r = parent == null ? void 0 : parent.parent) == null ? void 0 : _r.parent;
|
|
1034
|
+
const isPrefix = maybeGroup instanceof GroupNode && maybeGroup.prefix === condition2;
|
|
1035
|
+
const varStart = getCursorInfo(input, ast, parent.start);
|
|
1036
|
+
const varEnd = getCursorInfo(input, ast, parent.end);
|
|
1037
|
+
const targetRequiresWhitespacePrev = tokenRequiresWhitespace(varStart.valid.prev, varStart.whitespace.prev, wordOps);
|
|
1038
|
+
const targetRequiresWhitespaceNext = tokenRequiresWhitespace(varEnd.valid.next, varEnd.whitespace.next, wordOps);
|
|
1039
|
+
const prefixedValue = target.parent instanceof VariableNode ? (_t = (_s = target.parent) == null ? void 0 : _s.prefix) == null ? void 0 : _t.value : false;
|
|
1040
|
+
const isSepPrev = ((_u = token2.prev) == null ? void 0 : _u.type) === TOKEN_TYPE.OP_EXPANDED_SEP;
|
|
1041
|
+
const arrayValue = ((_v = target.parent) == null ? void 0 : _v.parent) instanceof ArrayNode;
|
|
1042
|
+
const isRegexFlag = target === ((_w = parent.quote) == null ? void 0 : _w.flags);
|
|
1043
|
+
if (!isRegexFlag && !isSepPrev && !isValue && !arrayValue && !prefixedValue && opts.prefixableGroups) {
|
|
1044
|
+
suggestions.push({
|
|
1045
|
+
...baseSuggestion,
|
|
1046
|
+
type: SUGGESTION_TYPE.PREFIX,
|
|
1047
|
+
requires: createDefaultRequires({
|
|
1048
|
+
group: !isPrefix,
|
|
1049
|
+
whitespace: {
|
|
1050
|
+
before: targetRequiresWhitespacePrev && !isPrefix,
|
|
1051
|
+
after: false
|
|
1052
|
+
// parens exist or get inserted
|
|
1053
|
+
}
|
|
1054
|
+
}),
|
|
1055
|
+
range
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
if (!isRegexFlag && !isPrefix) {
|
|
1059
|
+
suggestions.push({
|
|
1060
|
+
...baseSuggestion,
|
|
1061
|
+
type: arrayValue ? SUGGESTION_TYPE.ARRAY_VALUE : isValue ? SUGGESTION_TYPE.VALUE : SUGGESTION_TYPE.VARIABLE,
|
|
1062
|
+
requires: createDefaultRequires({
|
|
1063
|
+
whitespace: {
|
|
1064
|
+
before: targetRequiresWhitespacePrev,
|
|
1065
|
+
after: targetRequiresWhitespaceNext
|
|
1066
|
+
},
|
|
1067
|
+
prefix: prefixedValue
|
|
1068
|
+
}),
|
|
1069
|
+
range
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (noArrayValuesTarget) {
|
|
1075
|
+
suggestions.push({
|
|
1076
|
+
...baseSuggestion,
|
|
1077
|
+
type: SUGGESTION_TYPE.ARRAY_VALUE,
|
|
1078
|
+
requires: createDefaultRequires(),
|
|
1079
|
+
range: pos({ start: index }, { fill: true })
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
if (propertyTarget) {
|
|
1083
|
+
suggestions.push({
|
|
1084
|
+
...baseSuggestion,
|
|
1085
|
+
type: SUGGESTION_TYPE.PROPERTY,
|
|
1086
|
+
requires: createDefaultRequires(),
|
|
1087
|
+
range: pos(propertyTarget)
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
if (propOpTarget) {
|
|
1091
|
+
suggestions.push({
|
|
1092
|
+
...baseSuggestion,
|
|
1093
|
+
type: propOpTarget.parent.sep ? SUGGESTION_TYPE.EXPANDED_PROPERTY_OPERATOR : SUGGESTION_TYPE.CUSTOM_PROPERTY_OPERATOR,
|
|
1094
|
+
requires: createDefaultRequires(),
|
|
1095
|
+
range: pos(propOpTarget)
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
const canSuggestValue = token2.whitespace.next && (token2.whitespace.prev || ((_x = token2.prev) == null ? void 0 : _x.type) === TOKEN_TYPE.BRACKETL || ((_y = token2.prev) == null ? void 0 : _y.type) === TOKEN_TYPE.PARENL) || token2.whitespace.prev && (token2.whitespace.next || ((_z = token2.next) == null ? void 0 : _z.type) === TOKEN_TYPE.BRACKETR || ((_A = token2.next) == null ? void 0 : _A.type) === TOKEN_TYPE.PARENR);
|
|
1099
|
+
if (canSuggestValue) {
|
|
1100
|
+
const inArrayNode = [nextCondition, prevCondition, nextVar, prevVar].find((_) => _ instanceof ArrayNode) !== void 0;
|
|
1101
|
+
const opsNotNeeded = ["and", "or"].includes(opts.onMissingBooleanOperator);
|
|
1102
|
+
if (inArrayNode || opsNotNeeded) {
|
|
1103
|
+
suggestions.push({
|
|
1104
|
+
type: inArrayNode ? SUGGESTION_TYPE.ARRAY_VALUE : SUGGESTION_TYPE.VARIABLE,
|
|
1105
|
+
requires: createDefaultRequires({}),
|
|
1106
|
+
range: pos({ start: index }, { fill: true }),
|
|
1107
|
+
...baseSuggestion
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
if (!inArrayNode && opsNotNeeded) {
|
|
1111
|
+
suggestions.push({
|
|
1112
|
+
...baseSuggestion,
|
|
1113
|
+
type: SUGGESTION_TYPE.PREFIX,
|
|
1114
|
+
requires: createDefaultRequires({
|
|
1115
|
+
group: true
|
|
1116
|
+
}),
|
|
1117
|
+
range: pos({ start: index }, { fill: true })
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
const canSuggestRegexFlags = (
|
|
1122
|
+
// has existing flags before/after
|
|
1123
|
+
token2.at && token2.at === ((_D = (_C = (_B = token2.at) == null ? void 0 : _B.parent) == null ? void 0 : _C.quote) == null ? void 0 : _D.flags) || token2.valid.prev && token2.valid.prev === ((_G = (_F = (_E = token2.valid.prev) == null ? void 0 : _E.parent) == null ? void 0 : _F.quote) == null ? void 0 : _G.flags) || token2.valid.next && token2.valid.next === ((_J = (_I = (_H = token2.valid.next) == null ? void 0 : _H.parent) == null ? void 0 : _I.quote) == null ? void 0 : _J.flags) || // no flags
|
|
1124
|
+
((_K = token2.valid.prev) == null ? void 0 : _K.type) === TOKEN_TYPE.REGEX && token2.valid.prev === ((_L = token2.valid.prev.parent.quote) == null ? void 0 : _L.right)
|
|
1125
|
+
);
|
|
1126
|
+
if (canSuggestRegexFlags) {
|
|
1127
|
+
suggestions.push({
|
|
1128
|
+
...baseSuggestion,
|
|
1129
|
+
type: SUGGESTION_TYPE.REGEX_FLAGS,
|
|
1130
|
+
requires: createDefaultRequires(),
|
|
1131
|
+
range: pos({ start: index }, { fill: true })
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
if (canSuggestOpAfterPrev || canSuggestOpBeforeNext) {
|
|
1135
|
+
const range = pos({ start: index }, { fill: true });
|
|
1136
|
+
suggestions.push({
|
|
1137
|
+
...baseSuggestion,
|
|
1138
|
+
type: SUGGESTION_TYPE.BOOLEAN_SYMBOL_OP,
|
|
1139
|
+
requires: createDefaultRequires(),
|
|
1140
|
+
range
|
|
1141
|
+
});
|
|
1142
|
+
suggestions.push({
|
|
1143
|
+
...baseSuggestion,
|
|
1144
|
+
type: SUGGESTION_TYPE.BOOLEAN_WORD_OP,
|
|
1145
|
+
requires: createDefaultRequires({
|
|
1146
|
+
whitespace: {
|
|
1147
|
+
before: requiresWhitespacePrevOp,
|
|
1148
|
+
after: requireWhitespaceNextOp
|
|
1149
|
+
}
|
|
1150
|
+
}),
|
|
1151
|
+
range
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return suggestions;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Evaluates a {@link Parser.normalize normalized} ast.
|
|
1159
|
+
*
|
|
1160
|
+
* How the ast is evaluated for different operators can be controlled by the {@link ParserOptions.valueComparer valueComparer} option.
|
|
1161
|
+
*/
|
|
1162
|
+
evaluate(ast, context) {
|
|
1163
|
+
this._checkEvaluationOptions();
|
|
1164
|
+
const opts = this.options;
|
|
1165
|
+
if (ast instanceof Condition) {
|
|
1166
|
+
const contextValue = get(context, ast.property);
|
|
1167
|
+
const res = opts.valueComparer({ property: ast.property, value: ast.value, operator: ast.operator }, contextValue, context);
|
|
1168
|
+
return ast.negate ? !res : res;
|
|
1169
|
+
}
|
|
1170
|
+
if (ast instanceof Expression) {
|
|
1171
|
+
const left = this.evaluate(ast.left, context);
|
|
1172
|
+
const right = this.evaluate(ast.right, context);
|
|
1173
|
+
return ast.operator === TOKEN_TYPE.AND ? left && right : left || right;
|
|
1174
|
+
}
|
|
1175
|
+
return unreachable();
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Given the set of indexes returned by {@link getBestIndex}, the set of existing indexes in a database, and the index to sort by\*, will return a list of the best/shortest sets of indexes.
|
|
1179
|
+
*
|
|
1180
|
+
* For example, given the query `a && b && c`, `getBestIndex` will return `[Set(a), Set(b)]`.
|
|
1181
|
+
*
|
|
1182
|
+
* Suppose we have indexes on all the variables and that the user wants to sort by `c`, this function will return [`Set(c)`].
|
|
1183
|
+
*
|
|
1184
|
+
* Suppose instead we have indexes only on `a` and `b` and that the user wants to sort by `c`, this function will return [`Set(a), Set(b)`]. Either can be picked by some other criteria (e.g. size of the indexes). Sort should then be done in memory.
|
|
1185
|
+
*
|
|
1186
|
+
* And then finally, if we have no existing indexes on any of the variables, the function will return `[]`.
|
|
1187
|
+
*
|
|
1188
|
+
* Note: This is a simple algorithm and is not designed to take into account instances where entries are indexed by two or more properties as their keys (i.e. multicolumn indexes).
|
|
1189
|
+
*
|
|
1190
|
+
* \* If the sort index is not in the list of existing indexes it is not taken into account.
|
|
1191
|
+
*/
|
|
1192
|
+
getBestIndexes(indexes, existing, sortIndex = "") {
|
|
1193
|
+
indexes = indexes.filter((set) => {
|
|
1194
|
+
for (const key of set) {
|
|
1195
|
+
if (!existing.has(key))
|
|
1196
|
+
return false;
|
|
1197
|
+
}
|
|
1198
|
+
return true;
|
|
1199
|
+
});
|
|
1200
|
+
let finalIndexes = indexes;
|
|
1201
|
+
if (existing.has(sortIndex)) {
|
|
1202
|
+
const indexesWithSortIndex = indexes.filter((set) => set.has(sortIndex));
|
|
1203
|
+
if (indexesWithSortIndex.length > 0)
|
|
1204
|
+
finalIndexes = indexesWithSortIndex;
|
|
1205
|
+
}
|
|
1206
|
+
let smallest = Infinity;
|
|
1207
|
+
if (existing instanceof Map) {
|
|
1208
|
+
const scores = /* @__PURE__ */ new Map();
|
|
1209
|
+
for (const set of finalIndexes) {
|
|
1210
|
+
let score = 0;
|
|
1211
|
+
for (const key of set) {
|
|
1212
|
+
score += existing.get(key) ?? 0;
|
|
1213
|
+
}
|
|
1214
|
+
scores.set(set, score);
|
|
1215
|
+
smallest = score < smallest ? score : smallest;
|
|
1216
|
+
}
|
|
1217
|
+
return indexes.filter((set) => smallest === Infinity || scores.get(set) === smallest);
|
|
1218
|
+
} else {
|
|
1219
|
+
for (const set of finalIndexes) {
|
|
1220
|
+
smallest = set.size < smallest ? set.size : smallest;
|
|
1221
|
+
}
|
|
1222
|
+
return indexes.filter((set) => smallest === Infinity || set.size === smallest);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Returns a list of the different sets of keys that need to be indexed to run a normalized query on a database and hit an existing index.
|
|
1227
|
+
*
|
|
1228
|
+
* For example, the expression `a || b` requires both `a` AND `b` be indexed to use an index. The function would return `[Set(a, b)]`.
|
|
1229
|
+
*
|
|
1230
|
+
* On the otherhand, the expression `a && b` only requires `a` OR `b` to be indexed (`[Set(a), Set(b)]`) If at least one is indexed, the rest of the filtering can be done in memory. There is no need to in memory filter the entire database.
|
|
1231
|
+
*
|
|
1232
|
+
* Now take a more complicated query like `(a && b) || (a && c)`. This only requires `a` be indexed, or both `b` AND `c`. (`[Set(a)], [Set(b), Set(c)]`).
|
|
1233
|
+
*
|
|
1234
|
+
* Queries like `(a || b) && (a || c)` would require all the variables to be indexed `[Set(a), Set(b), Set(c)]`.
|
|
1235
|
+
*/
|
|
1236
|
+
getIndexes(ast) {
|
|
1237
|
+
if (ast instanceof Condition) {
|
|
1238
|
+
return [new Set(ast.property.join("."))];
|
|
1239
|
+
}
|
|
1240
|
+
if (ast instanceof Expression) {
|
|
1241
|
+
const left = this.getIndexes(ast.left);
|
|
1242
|
+
const right = this.getIndexes(ast.right);
|
|
1243
|
+
if (ast.operator === TOKEN_TYPE.AND) {
|
|
1244
|
+
const sets = [];
|
|
1245
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
1246
|
+
for (const leftSet of left) {
|
|
1247
|
+
const exists = sets.find((set) => isEqualSet(set, leftSet));
|
|
1248
|
+
if (exists)
|
|
1249
|
+
continue;
|
|
1250
|
+
sets.push(leftSet);
|
|
1251
|
+
for (const key of leftSet) {
|
|
1252
|
+
allKeys.add(key);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
for (const rightSet of right) {
|
|
1256
|
+
const exists = sets.find((set) => isEqualSet(set, rightSet));
|
|
1257
|
+
if (exists)
|
|
1258
|
+
continue;
|
|
1259
|
+
sets.push(rightSet);
|
|
1260
|
+
for (const key of rightSet) {
|
|
1261
|
+
allKeys.add(key);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
const commonKeys = /* @__PURE__ */ new Set();
|
|
1265
|
+
outerCheck:
|
|
1266
|
+
for (const key of allKeys) {
|
|
1267
|
+
for (const set of sets) {
|
|
1268
|
+
if (!set.has(key))
|
|
1269
|
+
continue outerCheck;
|
|
1270
|
+
}
|
|
1271
|
+
commonKeys.add(key);
|
|
1272
|
+
}
|
|
1273
|
+
if (commonKeys.size > 0) {
|
|
1274
|
+
return [commonKeys, allKeys];
|
|
1275
|
+
} else {
|
|
1276
|
+
return sets;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
if (ast.operator === TOKEN_TYPE.OR) {
|
|
1280
|
+
for (const rightSet of right) {
|
|
1281
|
+
for (const leftSet of left) {
|
|
1282
|
+
if (isEqualSet(leftSet, rightSet)) {
|
|
1283
|
+
return [rightSet];
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
const res = /* @__PURE__ */ new Set();
|
|
1288
|
+
for (const leftSet of left) {
|
|
1289
|
+
for (const key of leftSet) {
|
|
1290
|
+
res.add(key);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
for (const rightSet of right) {
|
|
1294
|
+
for (const key of rightSet) {
|
|
1295
|
+
res.add(key);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
return [res];
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return unreachable();
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Normalizes the ast by applying {@link GroupNode GroupNodes} and converting {@link ConditionNode ConditionNodes} to {@link NormalizedConditionNode NormalizedConditionNodes}.
|
|
1305
|
+
*/
|
|
1306
|
+
normalize(ast) {
|
|
1307
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
1308
|
+
this._checkEvaluationOptions();
|
|
1309
|
+
const opts = this.options;
|
|
1310
|
+
if (ast instanceof ErrorToken || !ast.valid) {
|
|
1311
|
+
throw new Error("AST node must be valid.");
|
|
1312
|
+
}
|
|
1313
|
+
const prefix = arguments[1];
|
|
1314
|
+
const groupValue = arguments[2];
|
|
1315
|
+
let operator2 = arguments[3];
|
|
1316
|
+
const self_ = this;
|
|
1317
|
+
if (ast instanceof ConditionNode) {
|
|
1318
|
+
if (!(ast.value instanceof GroupNode)) {
|
|
1319
|
+
const isValue = ast.value instanceof ArrayNode || ((_b = (_a = ast.value) == null ? void 0 : _a.quote) == null ? void 0 : _b.left.type) === TOKEN_TYPE.REGEX;
|
|
1320
|
+
let name = ast.property ? unescape(ast.property.value.value) : isValue ? void 0 : unescape((_c = ast.value) == null ? void 0 : _c.value.value);
|
|
1321
|
+
const isNested = operator2 !== void 0;
|
|
1322
|
+
if (prefix !== void 0 && !isNested) {
|
|
1323
|
+
name = name ? applyPrefix(prefix, name, opts.prefixApplier) : prefix;
|
|
1324
|
+
}
|
|
1325
|
+
let value;
|
|
1326
|
+
if (isNested) {
|
|
1327
|
+
value = name ?? true;
|
|
1328
|
+
name = prefix;
|
|
1329
|
+
} else {
|
|
1330
|
+
value = ast.value instanceof ArrayNode ? ast.value.values.map((val) => unescape(val.value.value)) : ((_e = (_d = ast.value) == null ? void 0 : _d.quote) == null ? void 0 : _e.left.type) === TOKEN_TYPE.REGEX ? ast.value.value.value : ast.property && ast.value instanceof VariableNode ? unescape(ast.value.value.value) : true;
|
|
1331
|
+
}
|
|
1332
|
+
const propertyKeys = name ? opts.keyParser(name) : [];
|
|
1333
|
+
const boolValue = applyBoolean(groupValue, ast.operator === void 0);
|
|
1334
|
+
const valuePrefix = ast.value instanceof VariableNode && ast.value.prefix ? unescape(ast.value.prefix.value) : void 0;
|
|
1335
|
+
operator2 ?? (operator2 = (_f = ast.propertyOperator) == null ? void 0 : _f.value);
|
|
1336
|
+
const isRegex = ((_h = (_g = ast.value) == null ? void 0 : _g.quote) == null ? void 0 : _h.left.type) === TOKEN_TYPE.REGEX;
|
|
1337
|
+
const isQuoted = ((_i = ast.value) == null ? void 0 : _i.quote) !== void 0;
|
|
1338
|
+
const isExpanded = ast.sep !== void 0;
|
|
1339
|
+
const regexFlags = (_l = (_k = (_j = ast.value) == null ? void 0 : _j.quote) == null ? void 0 : _k.flags) == null ? void 0 : _l.value;
|
|
1340
|
+
const query = {
|
|
1341
|
+
value,
|
|
1342
|
+
operator: operator2,
|
|
1343
|
+
prefix: valuePrefix,
|
|
1344
|
+
regexFlags,
|
|
1345
|
+
property: propertyKeys,
|
|
1346
|
+
isRegex,
|
|
1347
|
+
isQuoted,
|
|
1348
|
+
isExpanded,
|
|
1349
|
+
isNegated: !boolValue,
|
|
1350
|
+
condition: ast
|
|
1351
|
+
};
|
|
1352
|
+
const res = opts.conditionNormalizer(query);
|
|
1353
|
+
return new Condition({ property: propertyKeys, ...res });
|
|
1354
|
+
} else {
|
|
1355
|
+
let name = unescape(ast.property.value.value);
|
|
1356
|
+
if (prefix !== void 0) {
|
|
1357
|
+
name = applyPrefix(prefix, name, opts.prefixApplier);
|
|
1358
|
+
}
|
|
1359
|
+
const boolValue = applyBoolean(groupValue, ast.operator === void 0);
|
|
1360
|
+
const operator22 = (_m = ast.propertyOperator) == null ? void 0 : _m.value;
|
|
1361
|
+
return self_.normalize(ast.value, name, boolValue, operator22);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
if (ast instanceof GroupNode) {
|
|
1365
|
+
const _prefix = ast.prefix instanceof ConditionNode && ast.prefix.value instanceof VariableNode ? unescape(ast.prefix.value.value.value) : void 0;
|
|
1366
|
+
const _groupValue = ast.prefix instanceof ConditionNode ? ast.prefix.operator === void 0 : !(ast.prefix instanceof ValidToken);
|
|
1367
|
+
const applied = applyPrefix(prefix, _prefix ?? "", opts.prefixApplier);
|
|
1368
|
+
return self_.normalize(ast.expression, applied, applyBoolean(groupValue, _groupValue), operator2);
|
|
1369
|
+
}
|
|
1370
|
+
if (ast instanceof ExpressionNode) {
|
|
1371
|
+
const left = self_.normalize(ast.left, prefix, groupValue, operator2);
|
|
1372
|
+
const right = self_.normalize(ast.right, prefix, groupValue, operator2);
|
|
1373
|
+
const type = groupValue === false ? OPPOSITE[ast.operator.type] : ast.operator.type;
|
|
1374
|
+
return new Expression({ operator: type, left, right });
|
|
1375
|
+
}
|
|
1376
|
+
return unreachable();
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Allows pre-validating ASTs for syntax highlighting purposes.
|
|
1380
|
+
* Works similar to evaluate. Internally it will use the prefixApplier, keyParser, and valueValidator (instead of comparer).
|
|
1381
|
+
*
|
|
1382
|
+
* The context does not need to be passed. If it's not passed, the function will not attempt to get the values (so it will not error) and the contextValue param of the valueValidator will be undefined.
|
|
1383
|
+
*/
|
|
1384
|
+
validate(ast, context) {
|
|
1385
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1386
|
+
const self = this;
|
|
1387
|
+
self._checkValidationOptions();
|
|
1388
|
+
const opts = self.options;
|
|
1389
|
+
if (ast instanceof ErrorToken || !ast.valid) {
|
|
1390
|
+
throw new Error("AST node must be valid.");
|
|
1391
|
+
}
|
|
1392
|
+
const prefix = arguments[2];
|
|
1393
|
+
const groupValue = arguments[3];
|
|
1394
|
+
const results = arguments[4] ?? [];
|
|
1395
|
+
const prefixes = arguments[5] ?? [];
|
|
1396
|
+
let operator2 = arguments[6];
|
|
1397
|
+
const self_ = this;
|
|
1398
|
+
if (ast instanceof ConditionNode) {
|
|
1399
|
+
if (!(ast.value instanceof GroupNode)) {
|
|
1400
|
+
const isValue = ast.value instanceof ArrayNode || ((_b = (_a = ast.value) == null ? void 0 : _a.quote) == null ? void 0 : _b.left.type) === TOKEN_TYPE.REGEX;
|
|
1401
|
+
const nameNode = ast.property ? ast.property : isValue ? void 0 : ast.value;
|
|
1402
|
+
let name = nameNode ? unescape(nameNode.value.value) : void 0;
|
|
1403
|
+
const isNested = operator2 !== void 0;
|
|
1404
|
+
if (prefix !== void 0 && !isNested) {
|
|
1405
|
+
name = name ? applyPrefix(prefix, name, opts.prefixApplier) : prefix;
|
|
1406
|
+
}
|
|
1407
|
+
let value;
|
|
1408
|
+
let propertyNodes = [];
|
|
1409
|
+
if (isNested) {
|
|
1410
|
+
value = name;
|
|
1411
|
+
name = prefix;
|
|
1412
|
+
propertyNodes = [...prefixes];
|
|
1413
|
+
} else {
|
|
1414
|
+
propertyNodes = [...prefixes, ...nameNode ? [nameNode] : []];
|
|
1415
|
+
value = ast.value instanceof ArrayNode ? ast.value.values : ((_d = (_c = ast.value) == null ? void 0 : _c.quote) == null ? void 0 : _d.left.type) === TOKEN_TYPE.REGEX ? ast.value : ast.property && ast.value instanceof VariableNode ? ast.value : true;
|
|
1416
|
+
}
|
|
1417
|
+
const propertyKeys = name ? opts.keyParser(name) : [];
|
|
1418
|
+
const contextValue = context !== void 0 ? get(context, propertyKeys) : void 0;
|
|
1419
|
+
const boolValue = applyBoolean(groupValue, ast.operator === void 0);
|
|
1420
|
+
const valuePrefix = ast.value instanceof VariableNode && ast.value.prefix ? ast.value.prefix : void 0;
|
|
1421
|
+
operator2 ?? (operator2 = ast.propertyOperator);
|
|
1422
|
+
const isRegex = ((_f = (_e = ast.value) == null ? void 0 : _e.quote) == null ? void 0 : _f.left.type) === TOKEN_TYPE.REGEX;
|
|
1423
|
+
const isQuoted = ((_g = ast.value) == null ? void 0 : _g.quote) !== void 0;
|
|
1424
|
+
const isExpanded = ast.sep !== void 0;
|
|
1425
|
+
const regexFlags = (_i = (_h = ast.value) == null ? void 0 : _h.quote) == null ? void 0 : _i.flags;
|
|
1426
|
+
const query = {
|
|
1427
|
+
value,
|
|
1428
|
+
operator: operator2,
|
|
1429
|
+
prefix: valuePrefix,
|
|
1430
|
+
prefixes,
|
|
1431
|
+
property: propertyNodes,
|
|
1432
|
+
propertyKeys,
|
|
1433
|
+
propertyName: name,
|
|
1434
|
+
regexFlags,
|
|
1435
|
+
isRegex,
|
|
1436
|
+
isNegated: !boolValue,
|
|
1437
|
+
isQuoted,
|
|
1438
|
+
isExpanded,
|
|
1439
|
+
condition: ast
|
|
1440
|
+
};
|
|
1441
|
+
const res = opts.valueValidator(contextValue, query, context);
|
|
1442
|
+
if (res && !isArray(res))
|
|
1443
|
+
throw new Error("The valueValidator must return an array or nothing/undefined");
|
|
1444
|
+
if (res) {
|
|
1445
|
+
for (const entry of res)
|
|
1446
|
+
results.push(entry);
|
|
1447
|
+
}
|
|
1448
|
+
} else {
|
|
1449
|
+
let name = unescape(ast.property.value.value);
|
|
1450
|
+
if (prefix !== void 0) {
|
|
1451
|
+
name = applyPrefix(prefix, name, opts.prefixApplier);
|
|
1452
|
+
}
|
|
1453
|
+
const boolValue = applyBoolean(groupValue, ast.operator === void 0);
|
|
1454
|
+
if (ast.property)
|
|
1455
|
+
prefixes.push(ast.property);
|
|
1456
|
+
const operator22 = ast.propertyOperator;
|
|
1457
|
+
self_.validate(ast.value, context, name, boolValue, results, prefixes, operator22);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
if (ast instanceof GroupNode) {
|
|
1461
|
+
const _prefix = ast.prefix instanceof ConditionNode && ast.prefix.value instanceof VariableNode ? ast.prefix.value : void 0;
|
|
1462
|
+
if (_prefix)
|
|
1463
|
+
prefixes.push(_prefix);
|
|
1464
|
+
const _groupValue = ast.prefix instanceof ConditionNode ? ast.prefix.operator === void 0 : !(ast.prefix instanceof ValidToken);
|
|
1465
|
+
self_.validate(ast.expression, context, applyPrefix(prefix, (_prefix == null ? void 0 : _prefix.value.value) ?? "", opts.prefixApplier), applyBoolean(groupValue, _groupValue), results, prefixes, operator2);
|
|
1466
|
+
}
|
|
1467
|
+
if (ast instanceof ExpressionNode) {
|
|
1468
|
+
self_.validate(ast.left, context, prefix, groupValue, results, [...prefixes], operator2);
|
|
1469
|
+
self_.validate(ast.right, context, prefix, groupValue, results, [...prefixes], operator2);
|
|
1470
|
+
}
|
|
1471
|
+
return results;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
export {
|
|
1475
|
+
Parser
|
|
1476
|
+
};
|