@witchcraft/expressit 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -0
- package/dist/ast/builders/array.d.ts +8 -0
- package/dist/ast/builders/array.d.ts.map +1 -0
- package/dist/ast/builders/array.js +31 -0
- package/dist/ast/builders/condition.d.ts +20 -0
- package/dist/ast/builders/condition.d.ts.map +1 -0
- package/dist/ast/builders/condition.js +28 -0
- package/dist/ast/builders/delim.d.ts +11 -0
- package/dist/ast/builders/delim.d.ts.map +1 -0
- package/dist/ast/builders/delim.js +20 -0
- package/dist/ast/builders/error.d.ts +9 -0
- package/dist/ast/builders/error.d.ts.map +1 -0
- package/dist/ast/builders/error.js +16 -0
- package/dist/ast/builders/expression.d.ts +12 -0
- package/dist/ast/builders/expression.d.ts.map +1 -0
- package/dist/ast/builders/expression.js +31 -0
- package/dist/ast/builders/group.d.ts +20 -0
- package/dist/ast/builders/group.d.ts.map +1 -0
- package/dist/ast/builders/group.js +47 -0
- package/dist/ast/builders/index.d.ts +12 -0
- package/dist/ast/builders/index.d.ts.map +1 -0
- package/dist/ast/builders/index.js +24 -0
- package/dist/ast/builders/isFullPos.d.ts +6 -0
- package/dist/ast/builders/isFullPos.d.ts.map +1 -0
- package/dist/ast/builders/isFullPos.js +6 -0
- package/dist/ast/builders/pos.d.ts +21 -0
- package/dist/ast/builders/pos.d.ts.map +1 -0
- package/dist/ast/builders/pos.js +28 -0
- package/dist/ast/builders/token.d.ts +12 -0
- package/dist/ast/builders/token.d.ts.map +1 -0
- package/dist/ast/builders/token.js +26 -0
- package/dist/ast/builders/type.d.ts +6 -0
- package/dist/ast/builders/type.d.ts.map +1 -0
- package/dist/ast/builders/type.js +37 -0
- package/dist/ast/builders/variable.d.ts +17 -0
- package/dist/ast/builders/variable.d.ts.map +1 -0
- package/dist/ast/builders/variable.js +62 -0
- package/dist/ast/classes/ArrayNode.d.ts +18 -0
- package/dist/ast/classes/ArrayNode.d.ts.map +1 -0
- package/dist/ast/classes/ArrayNode.js +55 -0
- package/dist/ast/classes/Condition.d.ts +13 -0
- package/dist/ast/classes/Condition.d.ts.map +1 -0
- package/dist/ast/classes/Condition.js +21 -0
- package/dist/ast/classes/ConditionNode.d.ts +73 -0
- package/dist/ast/classes/ConditionNode.d.ts.map +1 -0
- package/dist/ast/classes/ConditionNode.js +101 -0
- package/dist/ast/classes/ErrorToken.d.ts +27 -0
- package/dist/ast/classes/ErrorToken.d.ts.map +1 -0
- package/dist/ast/classes/ErrorToken.js +47 -0
- package/dist/ast/classes/Expression.d.ts +13 -0
- package/dist/ast/classes/Expression.d.ts.map +1 -0
- package/dist/ast/classes/Expression.js +19 -0
- package/dist/ast/classes/ExpressionNode.d.ts +21 -0
- package/dist/ast/classes/ExpressionNode.d.ts.map +1 -0
- package/dist/ast/classes/ExpressionNode.js +57 -0
- package/dist/ast/classes/GroupNode.d.ts +64 -0
- package/dist/ast/classes/GroupNode.d.ts.map +1 -0
- package/dist/ast/classes/GroupNode.js +69 -0
- package/dist/ast/classes/Node.d.ts +22 -0
- package/dist/ast/classes/Node.d.ts.map +1 -0
- package/dist/ast/classes/Node.js +28 -0
- package/dist/ast/classes/Token.d.ts +27 -0
- package/dist/ast/classes/Token.d.ts.map +1 -0
- package/dist/ast/classes/Token.js +28 -0
- package/dist/ast/classes/ValidToken.d.ts +26 -0
- package/dist/ast/classes/ValidToken.d.ts.map +1 -0
- package/dist/ast/classes/ValidToken.js +49 -0
- package/dist/ast/classes/VariableNode.d.ts +33 -0
- package/dist/ast/classes/VariableNode.d.ts.map +1 -0
- package/dist/ast/classes/VariableNode.js +58 -0
- package/dist/ast/classes/index.d.ts +12 -0
- package/dist/ast/classes/index.d.ts.map +1 -0
- package/dist/ast/classes/index.js +24 -0
- package/dist/ast/handlers.d.ts +42 -0
- package/dist/ast/handlers.d.ts.map +1 -0
- package/dist/ast/handlers.js +150 -0
- package/dist/ast/index.d.ts +4 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +8 -0
- package/dist/examples/advancedValueComparer.d.ts +3 -0
- package/dist/examples/advancedValueComparer.d.ts.map +1 -0
- package/dist/examples/advancedValueComparer.js +28 -0
- package/dist/examples/shortcutContextParser.d.ts +22 -0
- package/dist/examples/shortcutContextParser.d.ts.map +1 -0
- package/dist/examples/shortcutContextParser.js +126 -0
- package/dist/global.d.js +1 -0
- package/dist/grammar/ParserBase.d.ts +51 -0
- package/dist/grammar/ParserBase.d.ts.map +1 -0
- package/dist/grammar/ParserBase.js +516 -0
- package/dist/grammar/createTokens.d.ts +56 -0
- package/dist/grammar/createTokens.d.ts.map +1 -0
- package/dist/grammar/createTokens.js +843 -0
- package/dist/grammar/index.d.ts +3 -0
- package/dist/grammar/index.d.ts.map +1 -0
- package/dist/grammar/index.js +6 -0
- package/dist/helpers/errors.d.ts +9 -0
- package/dist/helpers/errors.d.ts.map +1 -0
- package/dist/helpers/errors.js +41 -0
- package/dist/helpers/general/applyBoolean.d.ts +3 -0
- package/dist/helpers/general/applyBoolean.d.ts.map +1 -0
- package/dist/helpers/general/applyBoolean.js +17 -0
- package/dist/helpers/general/applyPrefix.d.ts +4 -0
- package/dist/helpers/general/applyPrefix.d.ts.map +1 -0
- package/dist/helpers/general/applyPrefix.js +9 -0
- package/dist/helpers/general/defaultConditionNormalizer.d.ts +3 -0
- package/dist/helpers/general/defaultConditionNormalizer.d.ts.map +1 -0
- package/dist/helpers/general/defaultConditionNormalizer.js +6 -0
- package/dist/helpers/general/defaultKeyParser.d.ts +3 -0
- package/dist/helpers/general/defaultKeyParser.d.ts.map +1 -0
- package/dist/helpers/general/defaultKeyParser.js +8 -0
- package/dist/helpers/general/defaultPrefixApplier.d.ts +3 -0
- package/dist/helpers/general/defaultPrefixApplier.d.ts.map +1 -0
- package/dist/helpers/general/defaultPrefixApplier.js +6 -0
- package/dist/helpers/general/defaultValueComparer.d.ts +3 -0
- package/dist/helpers/general/defaultValueComparer.d.ts.map +1 -0
- package/dist/helpers/general/defaultValueComparer.js +6 -0
- package/dist/helpers/general/index.d.ts +7 -0
- package/dist/helpers/general/index.d.ts.map +1 -0
- package/dist/helpers/general/index.js +14 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +8 -0
- package/dist/helpers/parser/assignParents.d.ts +4 -0
- package/dist/helpers/parser/assignParents.d.ts.map +1 -0
- package/dist/helpers/parser/assignParents.js +71 -0
- package/dist/helpers/parser/checkParserOpts.d.ts +3 -0
- package/dist/helpers/parser/checkParserOpts.d.ts.map +1 -0
- package/dist/helpers/parser/checkParserOpts.js +126 -0
- package/dist/helpers/parser/extractPosition.d.ts +9 -0
- package/dist/helpers/parser/extractPosition.d.ts.map +1 -0
- package/dist/helpers/parser/extractPosition.js +9 -0
- package/dist/helpers/parser/getUnclosedRightParenCount.d.ts +5 -0
- package/dist/helpers/parser/getUnclosedRightParenCount.d.ts.map +1 -0
- package/dist/helpers/parser/getUnclosedRightParenCount.js +20 -0
- package/dist/helpers/parser/index.d.ts +9 -0
- package/dist/helpers/parser/index.d.ts.map +1 -0
- package/dist/helpers/parser/index.js +18 -0
- package/dist/helpers/parser/parseParserOptions.d.ts +4 -0
- package/dist/helpers/parser/parseParserOptions.d.ts.map +1 -0
- package/dist/helpers/parser/parseParserOptions.js +45 -0
- package/dist/helpers/parser/seal.d.ts +8 -0
- package/dist/helpers/parser/seal.d.ts.map +1 -0
- package/dist/helpers/parser/seal.js +10 -0
- package/dist/helpers/parser/setParent.d.ts +6 -0
- package/dist/helpers/parser/setParent.d.ts.map +1 -0
- package/dist/helpers/parser/setParent.js +4 -0
- package/dist/helpers/parser/unescape.d.ts +3 -0
- package/dist/helpers/parser/unescape.d.ts.map +1 -0
- package/dist/helpers/parser/unescape.js +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/methods/autocomplete.d.ts +18 -0
- package/dist/methods/autocomplete.d.ts.map +1 -0
- package/dist/methods/autocomplete.js +109 -0
- package/dist/methods/autoreplace.d.ts +13 -0
- package/dist/methods/autoreplace.d.ts.map +1 -0
- package/dist/methods/autoreplace.js +36 -0
- package/dist/methods/autosuggest.d.ts +28 -0
- package/dist/methods/autosuggest.d.ts.map +1 -0
- package/dist/methods/autosuggest.js +371 -0
- package/dist/methods/evaluate.d.ts +11 -0
- package/dist/methods/evaluate.d.ts.map +1 -0
- package/dist/methods/evaluate.js +30 -0
- package/dist/methods/getBestIndex.d.ts +19 -0
- package/dist/methods/getBestIndex.d.ts.map +1 -0
- package/dist/methods/getBestIndex.js +53 -0
- package/dist/methods/getIndexes.d.ts +17 -0
- package/dist/methods/getIndexes.d.ts.map +1 -0
- package/dist/methods/getIndexes.js +97 -0
- package/dist/methods/index.d.ts +9 -0
- package/dist/methods/index.d.ts.map +1 -0
- package/dist/methods/index.js +18 -0
- package/dist/methods/normalize.d.ts +12 -0
- package/dist/methods/normalize.d.ts.map +1 -0
- package/dist/methods/normalize.js +99 -0
- package/dist/methods/validate.d.ts +11 -0
- package/dist/methods/validate.d.ts.map +1 -0
- package/dist/methods/validate.js +111 -0
- package/dist/package.js +7 -0
- package/dist/package.json.js +193 -0
- package/dist/parser.d.ts +58 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +136 -0
- package/dist/types/ast.d.ts +70 -0
- package/dist/types/ast.d.ts.map +1 -0
- package/dist/types/ast.js +29 -0
- package/dist/types/autocomplete.d.ts +143 -0
- package/dist/types/autocomplete.d.ts.map +1 -0
- package/dist/types/autocomplete.js +24 -0
- package/dist/types/errors.d.ts +34 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +10 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +9 -0
- package/dist/types/parser.d.ts +451 -0
- package/dist/types/parser.d.ts.map +1 -0
- package/dist/types/parser.js +1 -0
- package/dist/utils/extractTokens.d.ts +8 -0
- package/dist/utils/extractTokens.d.ts.map +1 -0
- package/dist/utils/extractTokens.js +50 -0
- package/dist/utils/getCursorInfo.d.ts +7 -0
- package/dist/utils/getCursorInfo.d.ts.map +1 -0
- package/dist/utils/getCursorInfo.js +86 -0
- package/dist/utils/getOppositeDelimiter.d.ts +6 -0
- package/dist/utils/getOppositeDelimiter.d.ts.map +1 -0
- package/dist/utils/getOppositeDelimiter.js +35 -0
- package/dist/utils/getSurroundingErrors.d.ts +25 -0
- package/dist/utils/getSurroundingErrors.d.ts.map +1 -0
- package/dist/utils/getSurroundingErrors.js +37 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +20 -0
- package/dist/utils/isBracket.d.ts +3 -0
- package/dist/utils/isBracket.d.ts.map +1 -0
- package/dist/utils/isBracket.js +7 -0
- package/dist/utils/isDelimiter.d.ts +6 -0
- package/dist/utils/isDelimiter.d.ts.map +1 -0
- package/dist/utils/isDelimiter.js +17 -0
- package/dist/utils/isParen.d.ts +3 -0
- package/dist/utils/isParen.d.ts.map +1 -0
- package/dist/utils/isParen.js +7 -0
- package/dist/utils/isQuote.d.ts +4 -0
- package/dist/utils/isQuote.d.ts.map +1 -0
- package/dist/utils/isQuote.js +7 -0
- package/dist/utils/prettyAst.d.ts +35 -0
- package/dist/utils/prettyAst.d.ts.map +1 -0
- package/dist/utils/prettyAst.js +112 -0
- package/package.json +152 -0
- package/src/ast/builders/array.ts +45 -0
- package/src/ast/builders/condition.ts +56 -0
- package/src/ast/builders/delim.ts +39 -0
- package/src/ast/builders/error.ts +22 -0
- package/src/ast/builders/expression.ts +66 -0
- package/src/ast/builders/group.ts +79 -0
- package/src/ast/builders/index.ts +13 -0
- package/src/ast/builders/isFullPos.ts +10 -0
- package/src/ast/builders/pos.ts +57 -0
- package/src/ast/builders/token.ts +46 -0
- package/src/ast/builders/type.ts +32 -0
- package/src/ast/builders/variable.ts +89 -0
- package/src/ast/classes/ArrayNode.ts +46 -0
- package/src/ast/classes/Condition.ts +22 -0
- package/src/ast/classes/ConditionNode.ts +141 -0
- package/src/ast/classes/ErrorToken.ts +49 -0
- package/src/ast/classes/Expression.ts +26 -0
- package/src/ast/classes/ExpressionNode.ts +62 -0
- package/src/ast/classes/GroupNode.ts +127 -0
- package/src/ast/classes/Node.ts +47 -0
- package/src/ast/classes/Token.ts +59 -0
- package/src/ast/classes/ValidToken.ts +56 -0
- package/src/ast/classes/VariableNode.ts +67 -0
- package/src/ast/classes/index.ts +13 -0
- package/src/ast/handlers.ts +190 -0
- package/src/ast/index.ts +5 -0
- package/src/examples/advancedValueComparer.ts +31 -0
- package/src/examples/shortcutContextParser.ts +140 -0
- package/src/global.d.ts +4 -0
- package/src/grammar/ParserBase.ts +715 -0
- package/src/grammar/createTokens.ts +512 -0
- package/src/grammar/index.ts +4 -0
- package/src/helpers/errors.ts +45 -0
- package/src/helpers/general/applyBoolean.ts +9 -0
- package/src/helpers/general/applyPrefix.ts +7 -0
- package/src/helpers/general/defaultConditionNormalizer.ts +9 -0
- package/src/helpers/general/defaultKeyParser.ts +8 -0
- package/src/helpers/general/defaultPrefixApplier.ts +7 -0
- package/src/helpers/general/defaultValueComparer.ts +7 -0
- package/src/helpers/general/index.ts +8 -0
- package/src/helpers/index.ts +5 -0
- package/src/helpers/parser/assignParents.ts +51 -0
- package/src/helpers/parser/checkParserOpts.ts +143 -0
- package/src/helpers/parser/extractPosition.ts +15 -0
- package/src/helpers/parser/getUnclosedRightParenCount.ts +22 -0
- package/src/helpers/parser/index.ts +10 -0
- package/src/helpers/parser/parseParserOptions.ts +54 -0
- package/src/helpers/parser/seal.ts +14 -0
- package/src/helpers/parser/setParent.ts +5 -0
- package/src/helpers/parser/unescape.ts +4 -0
- package/src/index.ts +7 -0
- package/src/methods/autocomplete.ts +128 -0
- package/src/methods/autoreplace.ts +46 -0
- package/src/methods/autosuggest.ts +543 -0
- package/src/methods/evaluate.ts +37 -0
- package/src/methods/getBestIndex.ts +53 -0
- package/src/methods/getIndexes.ts +99 -0
- package/src/methods/index.ts +10 -0
- package/src/methods/normalize.ts +138 -0
- package/src/methods/validate.ts +141 -0
- package/src/package.js +11 -0
- package/src/parser.ts +183 -0
- package/src/types/ast.ts +148 -0
- package/src/types/autocomplete.ts +152 -0
- package/src/types/errors.ts +40 -0
- package/src/types/index.ts +6 -0
- package/src/types/parser.ts +479 -0
- package/src/utils/extractTokens.ts +67 -0
- package/src/utils/getCursorInfo.ts +106 -0
- package/src/utils/getOppositeDelimiter.ts +36 -0
- package/src/utils/getSurroundingErrors.ts +57 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/isBracket.ts +6 -0
- package/src/utils/isDelimiter.ts +18 -0
- package/src/utils/isParen.ts +6 -0
- package/src/utils/isQuote.ts +6 -0
- package/src/utils/prettyAst.ts +152 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* eslint-disable no-labels */
|
|
2
|
+
|
|
3
|
+
import { type AddParameters, unreachable } from "@alanscodelog/utils"
|
|
4
|
+
|
|
5
|
+
import { Condition } from "../ast/classes/Condition.js"
|
|
6
|
+
import { Expression } from "../ast/classes/Expression.js"
|
|
7
|
+
import { TOKEN_TYPE } from "../types/ast.js"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export class GetIndexMixin<T> {
|
|
11
|
+
/**
|
|
12
|
+
* 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.
|
|
13
|
+
*
|
|
14
|
+
* For example, the expression `a || b` requires both `a` AND `b` be indexed to use an index. The function would return `[Set(a, b)]`.
|
|
15
|
+
*
|
|
16
|
+
* 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.
|
|
17
|
+
*
|
|
18
|
+
* 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)]`).
|
|
19
|
+
*
|
|
20
|
+
* Queries like `(a || b) && (a || c)` would require all the variables to be indexed `[Set(a), Set(b), Set(c)]`.
|
|
21
|
+
*/
|
|
22
|
+
getIndexes(ast: Condition | Expression): Set<string>[] {
|
|
23
|
+
const self_ = this as any as GetIndexMixin<T> & { indexes: AddParameters<GetIndexMixin<T>["getIndexes"], []> }
|
|
24
|
+
if (ast instanceof Condition) {
|
|
25
|
+
return [new Set(ast.property.join("."))]
|
|
26
|
+
}
|
|
27
|
+
if (ast instanceof Expression) {
|
|
28
|
+
const left = self_.getIndexes(ast.left)
|
|
29
|
+
const right = self_.getIndexes(ast.right)
|
|
30
|
+
|
|
31
|
+
if (ast.operator === TOKEN_TYPE.AND) {
|
|
32
|
+
const sets: Set<string>[] = []
|
|
33
|
+
const allKeys: Set<string> = new Set()
|
|
34
|
+
|
|
35
|
+
for (const leftSet of left) {
|
|
36
|
+
const exists = sets.find(set => isEqualSet(set, leftSet))
|
|
37
|
+
if (exists) continue
|
|
38
|
+
sets.push(leftSet)
|
|
39
|
+
for (const key of leftSet) {
|
|
40
|
+
allKeys.add(key)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const rightSet of right) {
|
|
44
|
+
const exists = sets.find(set => isEqualSet(set, rightSet))
|
|
45
|
+
if (exists) continue
|
|
46
|
+
sets.push(rightSet)
|
|
47
|
+
for (const key of rightSet) {
|
|
48
|
+
allKeys.add(key)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const commonKeys: Set<string> = new Set()
|
|
53
|
+
|
|
54
|
+
outerCheck: for (const key of allKeys) {
|
|
55
|
+
for (const set of sets) {
|
|
56
|
+
if (!set.has(key)) continue outerCheck
|
|
57
|
+
}
|
|
58
|
+
commonKeys.add(key)
|
|
59
|
+
}
|
|
60
|
+
if (commonKeys.size > 0) {
|
|
61
|
+
return [commonKeys, allKeys]
|
|
62
|
+
} else {
|
|
63
|
+
return sets
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (ast.operator === TOKEN_TYPE.OR) {
|
|
67
|
+
for (const rightSet of right) {
|
|
68
|
+
for (const leftSet of left) {
|
|
69
|
+
if (isEqualSet(leftSet, rightSet)) {
|
|
70
|
+
return [rightSet]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const res = new Set<string>()
|
|
75
|
+
for (const leftSet of left) {
|
|
76
|
+
for (const key of leftSet) {
|
|
77
|
+
res.add(key)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const rightSet of right) {
|
|
81
|
+
for (const key of rightSet) {
|
|
82
|
+
res.add(key)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return [res]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return unreachable()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isEqualSet(setA: Set<any>, setB: Set<any>): boolean {
|
|
94
|
+
if (setA.size !== setB.size) return false
|
|
95
|
+
for (const key of setA) {
|
|
96
|
+
if (!setB.has(key)) return false
|
|
97
|
+
}
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Autogenerated Index */
|
|
2
|
+
|
|
3
|
+
export { AutocompleteMixin } from "./autocomplete.js"
|
|
4
|
+
export { AutoreplaceMixin } from "./autoreplace.js"
|
|
5
|
+
export { Autosuggest } from "./autosuggest.js"
|
|
6
|
+
export { EvaluateMixin } from "./evaluate.js"
|
|
7
|
+
export { GetBestIndexesMixin } from "./getBestIndex.js"
|
|
8
|
+
export { GetIndexMixin } from "./getIndexes.js"
|
|
9
|
+
export { NormalizeMixin } from "./normalize.js"
|
|
10
|
+
export { ValidateMixin } from "./validate.js"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { type AddParameters, unreachable } from "@alanscodelog/utils"
|
|
2
|
+
|
|
3
|
+
import { ArrayNode } from "../ast/classes/ArrayNode.js"
|
|
4
|
+
import { Condition } from "../ast/classes/Condition.js"
|
|
5
|
+
import { ConditionNode } from "../ast/classes/ConditionNode.js"
|
|
6
|
+
import { ErrorToken } from "../ast/classes/ErrorToken.js"
|
|
7
|
+
import { Expression } from "../ast/classes/Expression.js"
|
|
8
|
+
import { ExpressionNode } from "../ast/classes/ExpressionNode.js"
|
|
9
|
+
import { GroupNode } from "../ast/classes/GroupNode.js"
|
|
10
|
+
import { ValidToken } from "../ast/classes/ValidToken.js"
|
|
11
|
+
import { VariableNode } from "../ast/classes/VariableNode.js"
|
|
12
|
+
import { applyBoolean } from "../helpers/general/applyBoolean.js"
|
|
13
|
+
import { applyPrefix } from "../helpers/general/applyPrefix.js"
|
|
14
|
+
import type { Parser } from "../parser.js"
|
|
15
|
+
import { type ParserResults, TOKEN_TYPE, type TokenBooleanTypes } from "../types/ast.js"
|
|
16
|
+
import type { ValueQuery } from "../types/parser.js"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
const OPPOSITE = {
|
|
20
|
+
[TOKEN_TYPE.AND]: TOKEN_TYPE.OR,
|
|
21
|
+
[TOKEN_TYPE.OR]: TOKEN_TYPE.AND,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class NormalizeMixin<T extends {}> {
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes the ast by applying {@link GroupNode GroupNodes} and converting {@link ConditionNode ConditionNodes} to {@link NormalizedConditionNode NormalizedConditionNodes}.
|
|
27
|
+
*
|
|
28
|
+
* To do this, a
|
|
29
|
+
*/
|
|
30
|
+
normalize<TType extends string, TValue>(ast: ParserResults): Condition<TType, TValue> | Expression<TType, TValue> {
|
|
31
|
+
// @ts-expect-error private method
|
|
32
|
+
this._checkEvaluationOptions()
|
|
33
|
+
const opts = (this as any as Parser<T>).options
|
|
34
|
+
if (ast instanceof ErrorToken || !ast.valid) {
|
|
35
|
+
throw new Error("AST node must be valid.")
|
|
36
|
+
}
|
|
37
|
+
// eslint-disable-next-line prefer-rest-params
|
|
38
|
+
const prefix: string | undefined = arguments[1]
|
|
39
|
+
// eslint-disable-next-line prefer-rest-params
|
|
40
|
+
const groupValue: boolean | undefined = arguments[2]
|
|
41
|
+
// eslint-disable-next-line prefer-rest-params
|
|
42
|
+
let operator: string | undefined = arguments[3]
|
|
43
|
+
|
|
44
|
+
const self_ = this as any as NormalizeMixin<T> & { normalize: AddParameters<NormalizeMixin<T>["normalize"], [typeof prefix, typeof groupValue, typeof operator]> }
|
|
45
|
+
|
|
46
|
+
if (ast instanceof ConditionNode) {
|
|
47
|
+
if (!(ast.value instanceof GroupNode)) {
|
|
48
|
+
const isValue = ast.value instanceof ArrayNode || (ast.value as VariableNode)?.quote?.left.type === TOKEN_TYPE.REGEX
|
|
49
|
+
let name = ast.property
|
|
50
|
+
? unescape(ast.property.value.value)
|
|
51
|
+
: isValue
|
|
52
|
+
// the property might be missing, whether this is valid or not is up to the user
|
|
53
|
+
// e.g. if prefix is defined this would make some sense
|
|
54
|
+
? undefined
|
|
55
|
+
: unescape((ast.value as VariableNode)?.value.value)
|
|
56
|
+
// some ancestor node went through the else block because it was a group node (e.g. prop:op(val))
|
|
57
|
+
// so the "prefix" we passed is actually the name of the property (e.g. prop) and the value is the name we're getting here (e.g. val)
|
|
58
|
+
const isNested = operator !== undefined
|
|
59
|
+
if (prefix !== undefined && !isNested) {
|
|
60
|
+
name = name ? applyPrefix(prefix, name, opts.prefixApplier) : prefix
|
|
61
|
+
}
|
|
62
|
+
let value: any
|
|
63
|
+
if (isNested) {
|
|
64
|
+
value = name ?? true
|
|
65
|
+
name = prefix
|
|
66
|
+
} else {
|
|
67
|
+
value = ast.value instanceof ArrayNode
|
|
68
|
+
? ast.value.values.map(val => unescape(val.value.value))
|
|
69
|
+
: (ast.value as VariableNode)?.quote?.left.type === TOKEN_TYPE.REGEX
|
|
70
|
+
? ast.value.value.value
|
|
71
|
+
: ast.property && ast.value instanceof VariableNode
|
|
72
|
+
? unescape(ast.value.value.value)
|
|
73
|
+
: true
|
|
74
|
+
}
|
|
75
|
+
const propertyKeys = name ? opts.keyParser(name) : []
|
|
76
|
+
|
|
77
|
+
const boolValue = applyBoolean(groupValue, ast.operator === undefined)
|
|
78
|
+
const valuePrefix = ast.value instanceof VariableNode && ast.value.prefix
|
|
79
|
+
? unescape(ast.value.prefix.value)
|
|
80
|
+
: undefined
|
|
81
|
+
// one or the other might be defined, but never both since nested properties (e.g. `prop:op(prop:op(...))`) are not allowed
|
|
82
|
+
operator ??= ast.propertyOperator?.value
|
|
83
|
+
const isRegex = (ast.value as VariableNode)?.quote?.left.type === TOKEN_TYPE.REGEX
|
|
84
|
+
const isQuoted = (ast.value as VariableNode)?.quote !== undefined
|
|
85
|
+
const isExpanded = ast.sep !== undefined
|
|
86
|
+
const regexFlags = (ast.value as VariableNode)?.quote?.flags?.value
|
|
87
|
+
const query: ValueQuery = {
|
|
88
|
+
value,
|
|
89
|
+
operator,
|
|
90
|
+
prefix: valuePrefix,
|
|
91
|
+
regexFlags,
|
|
92
|
+
property: propertyKeys,
|
|
93
|
+
isRegex,
|
|
94
|
+
isQuoted,
|
|
95
|
+
isExpanded,
|
|
96
|
+
isNegated: !boolValue,
|
|
97
|
+
condition: ast,
|
|
98
|
+
}
|
|
99
|
+
const res = opts.conditionNormalizer(query)
|
|
100
|
+
return new Condition({ property: propertyKeys, ...res })
|
|
101
|
+
} else {
|
|
102
|
+
let name = unescape((ast.property as VariableNode).value.value) // this is always a variable node
|
|
103
|
+
if (prefix !== undefined) {
|
|
104
|
+
name = applyPrefix(prefix, name, opts.prefixApplier)
|
|
105
|
+
}
|
|
106
|
+
const boolValue = applyBoolean(groupValue, ast.operator === undefined)
|
|
107
|
+
// other operator is never defined see comments in other block above
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
109
|
+
const operator = ast.propertyOperator?.value
|
|
110
|
+
// this call will at some point lead us to the above block with isNested = true
|
|
111
|
+
return self_.normalize(ast.value, name, boolValue, operator) as any
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (ast instanceof GroupNode) {
|
|
116
|
+
const _prefix = ast.prefix instanceof ConditionNode && ast.prefix.value instanceof VariableNode
|
|
117
|
+
? unescape(ast.prefix.value.value.value)
|
|
118
|
+
: undefined // we do not want to apply not tokens
|
|
119
|
+
const _groupValue = ast.prefix instanceof ConditionNode
|
|
120
|
+
? ast.prefix.operator === undefined
|
|
121
|
+
: !(ast.prefix instanceof ValidToken)
|
|
122
|
+
|
|
123
|
+
const applied = applyPrefix(prefix, _prefix ?? "", opts.prefixApplier)
|
|
124
|
+
|
|
125
|
+
return self_.normalize(ast.expression as any, applied, applyBoolean(groupValue, _groupValue), operator) as any
|
|
126
|
+
}
|
|
127
|
+
if (ast instanceof ExpressionNode) {
|
|
128
|
+
const left = self_.normalize(ast.left, prefix, groupValue, operator)
|
|
129
|
+
const right = self_.normalize(ast.right, prefix, groupValue, operator)
|
|
130
|
+
|
|
131
|
+
// apply De Morgan's laws if group prefix was negative
|
|
132
|
+
// the values are already flipped, we just have to flip the operator
|
|
133
|
+
const type: TokenBooleanTypes = (groupValue === false ? OPPOSITE[ast.operator.type] : ast.operator.type) as TokenBooleanTypes
|
|
134
|
+
return new Expression<TType, TValue>({ operator: type, left: left as any, right: right as any })
|
|
135
|
+
}
|
|
136
|
+
return unreachable()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { type AddParameters, get, isArray } from "@alanscodelog/utils"
|
|
2
|
+
|
|
3
|
+
import { ArrayNode } from "../ast/classes/ArrayNode.js"
|
|
4
|
+
import { ConditionNode } from "../ast/classes/ConditionNode.js"
|
|
5
|
+
import { ErrorToken } from "../ast/classes/ErrorToken.js"
|
|
6
|
+
import { ExpressionNode } from "../ast/classes/ExpressionNode.js"
|
|
7
|
+
import { GroupNode } from "../ast/classes/GroupNode.js"
|
|
8
|
+
import { ValidToken } from "../ast/classes/ValidToken.js"
|
|
9
|
+
import { VariableNode } from "../ast/classes/VariableNode.js"
|
|
10
|
+
import { applyBoolean } from "../helpers/general/applyBoolean.js"
|
|
11
|
+
import { applyPrefix } from "../helpers/general/applyPrefix.js"
|
|
12
|
+
import type { Parser } from "../parser.js"
|
|
13
|
+
import { type ParserResults, type Position, TOKEN_TYPE } from "../types/ast.js"
|
|
14
|
+
import type { ValidationQuery } from "../types/parser.js"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export class ValidateMixin<T extends {}> {
|
|
18
|
+
/**
|
|
19
|
+
* Allows pre-validating ASTs for syntax highlighting purposes.
|
|
20
|
+
* Works similar to evaluate. Internally it will use the prefixApplier, keyParser, and valueValidator (instead of comparer).
|
|
21
|
+
*
|
|
22
|
+
* 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.
|
|
23
|
+
*/
|
|
24
|
+
validate(ast: ParserResults, context?: Record<string, any>): (Position & T)[] {
|
|
25
|
+
const self = (this as any as Parser<T>)
|
|
26
|
+
self._checkValidationOptions()
|
|
27
|
+
const opts = self.options
|
|
28
|
+
// see evaluate function, this method is practically identical, except we don't keep track of the real value (since we are not evaluating) and the actual nodes/tokens are passed to the valueValidator, not just the string values.
|
|
29
|
+
if (ast instanceof ErrorToken || !ast.valid) {
|
|
30
|
+
throw new Error("AST node must be valid.")
|
|
31
|
+
}
|
|
32
|
+
/** Handle hidden recursive version of the function. */
|
|
33
|
+
// eslint-disable-next-line prefer-rest-params
|
|
34
|
+
const prefix: string | undefined = arguments[2]
|
|
35
|
+
// eslint-disable-next-line prefer-rest-params
|
|
36
|
+
const groupValue: boolean | undefined = arguments[3]
|
|
37
|
+
// eslint-disable-next-line prefer-rest-params
|
|
38
|
+
const results: (Position & T)[] = arguments[4] ?? []
|
|
39
|
+
// eslint-disable-next-line prefer-rest-params
|
|
40
|
+
const prefixes: VariableNode[] = arguments[5] ?? []
|
|
41
|
+
// eslint-disable-next-line prefer-rest-params
|
|
42
|
+
let operator: ValidToken<TOKEN_TYPE.VALUE | TOKEN_TYPE.OP_CUSTOM> | undefined = arguments[6]
|
|
43
|
+
|
|
44
|
+
const self_ = this as any as ValidateMixin<T> & { validate: AddParameters<ValidateMixin<T>["validate"], [typeof prefix, typeof groupValue, typeof results, typeof prefixes, typeof operator]> }
|
|
45
|
+
|
|
46
|
+
if (ast instanceof ConditionNode) {
|
|
47
|
+
if (!(ast.value instanceof GroupNode)) {
|
|
48
|
+
const isValue = ast.value instanceof ArrayNode || (ast.value as VariableNode)?.quote?.left.type === TOKEN_TYPE.REGEX
|
|
49
|
+
const nameNode = ast.property
|
|
50
|
+
? ast.property as VariableNode
|
|
51
|
+
: isValue
|
|
52
|
+
? undefined
|
|
53
|
+
: ast.value as VariableNode
|
|
54
|
+
|
|
55
|
+
let name = nameNode ? unescape(nameNode.value.value) : undefined
|
|
56
|
+
const isNested = operator !== undefined
|
|
57
|
+
if (prefix !== undefined && !isNested) {
|
|
58
|
+
name = name ? applyPrefix(prefix, name, opts.prefixApplier) : prefix
|
|
59
|
+
}
|
|
60
|
+
let value: any
|
|
61
|
+
let propertyNodes: VariableNode[] = []
|
|
62
|
+
|
|
63
|
+
if (isNested) {
|
|
64
|
+
value = name
|
|
65
|
+
name = prefix
|
|
66
|
+
propertyNodes = [...prefixes]
|
|
67
|
+
} else {
|
|
68
|
+
propertyNodes = [...prefixes, ...(nameNode ? [nameNode] : [])]
|
|
69
|
+
value = ast.value instanceof ArrayNode
|
|
70
|
+
? ast.value.values
|
|
71
|
+
: (ast.value as VariableNode)?.quote?.left.type === TOKEN_TYPE.REGEX
|
|
72
|
+
? ast.value
|
|
73
|
+
: ast.property && ast.value instanceof VariableNode
|
|
74
|
+
? ast.value
|
|
75
|
+
: true
|
|
76
|
+
}
|
|
77
|
+
const propertyKeys = name ? opts.keyParser(name) : []
|
|
78
|
+
const contextValue = context !== undefined ? get(context, propertyKeys) : undefined
|
|
79
|
+
|
|
80
|
+
const boolValue = applyBoolean(groupValue, ast.operator === undefined)
|
|
81
|
+
const valuePrefix = ast.value instanceof VariableNode && ast.value.prefix
|
|
82
|
+
? ast.value.prefix
|
|
83
|
+
: undefined
|
|
84
|
+
operator ??= ast.propertyOperator as ValidToken<TOKEN_TYPE.VALUE | TOKEN_TYPE.OP_CUSTOM>
|
|
85
|
+
const isRegex = (ast.value as VariableNode)?.quote?.left.type === TOKEN_TYPE.REGEX
|
|
86
|
+
const isQuoted = (ast.value as VariableNode)?.quote !== undefined
|
|
87
|
+
const isExpanded = ast.sep !== undefined
|
|
88
|
+
const regexFlags = (ast.value as VariableNode)?.quote?.flags
|
|
89
|
+
const query: ValidationQuery = {
|
|
90
|
+
value,
|
|
91
|
+
operator,
|
|
92
|
+
prefix: valuePrefix,
|
|
93
|
+
prefixes,
|
|
94
|
+
property: propertyNodes,
|
|
95
|
+
propertyKeys,
|
|
96
|
+
propertyName: name,
|
|
97
|
+
regexFlags,
|
|
98
|
+
isRegex,
|
|
99
|
+
isNegated: !boolValue,
|
|
100
|
+
isQuoted,
|
|
101
|
+
isExpanded,
|
|
102
|
+
condition: ast,
|
|
103
|
+
}
|
|
104
|
+
const res = opts.valueValidator(contextValue, query, context)
|
|
105
|
+
if (res && !isArray(res)) throw new Error("The valueValidator must return an array or nothing/undefined")
|
|
106
|
+
if (res) { for (const entry of res) results.push(entry) }
|
|
107
|
+
} else {
|
|
108
|
+
let name = unescape((ast.property as VariableNode).value.value) // this is always a variable node
|
|
109
|
+
if (prefix !== undefined) {
|
|
110
|
+
name = applyPrefix(prefix, name, opts.prefixApplier)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const boolValue = applyBoolean(groupValue, ast.operator === undefined)
|
|
114
|
+
|
|
115
|
+
if (ast.property) prefixes.push((ast.property as any))
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
117
|
+
const operator = ast.propertyOperator as ValidToken<TOKEN_TYPE.VALUE | TOKEN_TYPE.OP_CUSTOM>
|
|
118
|
+
self_.validate(ast.value, context, name, boolValue, results, prefixes, operator)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (ast instanceof GroupNode) {
|
|
123
|
+
const _prefix = ast.prefix instanceof ConditionNode && ast.prefix.value instanceof VariableNode
|
|
124
|
+
? ast.prefix.value
|
|
125
|
+
: undefined // we do not want to apply not tokens
|
|
126
|
+
if (_prefix) prefixes.push(_prefix)
|
|
127
|
+
|
|
128
|
+
const _groupValue = ast.prefix instanceof ConditionNode
|
|
129
|
+
? ast.prefix.operator === undefined
|
|
130
|
+
: !(ast.prefix instanceof ValidToken)
|
|
131
|
+
|
|
132
|
+
self_.validate(ast.expression as any, context, applyPrefix(prefix, _prefix?.value.value ?? "", opts.prefixApplier), applyBoolean(groupValue, _groupValue), results, prefixes, operator)
|
|
133
|
+
}
|
|
134
|
+
if (ast instanceof ExpressionNode) {
|
|
135
|
+
// prefixes must be spread because we don't want the left branch (if it goes deeper) to affect the right
|
|
136
|
+
self_.validate(ast.left, context, prefix, groupValue, results, [...prefixes], operator)
|
|
137
|
+
self_.validate(ast.right, context, prefix, groupValue, results, [...prefixes], operator)
|
|
138
|
+
}
|
|
139
|
+
return results
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/package.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import pkg from "../package.json"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This library imports it's own package.json for inserting the version number into errors. Doing this is not a problem normally (because babel is used to transpile the typescript instead of tsc), but when tsc is used to output the types, the output is nested (dist-types/src/*) because of the json import being outside the src dir. There is a way around this but it's complicated. The easier way is to just cheat and do the importing in this js file. Typescript consumes it none the wiser and babel transpiles it to cjs/es accordingly.
|
|
5
|
+
*
|
|
6
|
+
* The values could be inlined, but there are no battle tested actively maintained babel plugins for this, and problems would be hard to debug (given when developing, the package.json never contains the real version because the library is semantically released.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const version = pkg.version
|
|
11
|
+
export const repository = pkg.repository
|
package/src/parser.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 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.
|
|
3
|
+
*
|
|
4
|
+
* Writing from within any of these methods is like writing a method from here except:
|
|
5
|
+
* - `this` calls are wrapped in `(this as any as Parser<T>)`
|
|
6
|
+
* - private method/property access requires `// @ts-expect-error`.
|
|
7
|
+
* - 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`.
|
|
8
|
+
*
|
|
9
|
+
* Docs work like normal (on methods). From the outside, users of the library cannot even tell the class is composed of mixins.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Mixin } from "@alanscodelog/utils"
|
|
13
|
+
import { isWhitespace, mixin } from "@alanscodelog/utils"
|
|
14
|
+
import { createSyntaxDiagramsCode, type ILexingResult, type Lexer } from "chevrotain"
|
|
15
|
+
|
|
16
|
+
import { token as tokenHandler } from "./ast/handlers.js"
|
|
17
|
+
import { createTokens } from "./grammar/createTokens.js"
|
|
18
|
+
import { ParserBase } from "./grammar/ParserBase.js"
|
|
19
|
+
import { BooleanParserLibraryError } from "./helpers/errors.js"
|
|
20
|
+
import { checkParserOpts } from "./helpers/parser/checkParserOpts.js"
|
|
21
|
+
import { getUnclosedRightParenCount } from "./helpers/parser/getUnclosedRightParenCount.js"
|
|
22
|
+
import { parseParserOptions, seal } from "./helpers/parser/index.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, ValidateMixin } from "./methods/index.js"
|
|
30
|
+
import type { ParserResults } from "./types/ast.js"
|
|
31
|
+
import { ERROR_CODES } from "./types/errors.js"
|
|
32
|
+
import type { FullParserOptions, ParserOptions } from "./types/parser.js"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates the main parser class which handles all functionality (evaluation, validation, etc).
|
|
37
|
+
*/
|
|
38
|
+
export class Parser<T extends {} = {}> {
|
|
39
|
+
options: FullParserOptions<T>
|
|
40
|
+
|
|
41
|
+
private readonly rawOptions: ParserOptions<T>
|
|
42
|
+
|
|
43
|
+
parser: ParserBase<T>
|
|
44
|
+
|
|
45
|
+
private readonly lexer: Lexer
|
|
46
|
+
|
|
47
|
+
private readonly tokens: ReturnType<typeof createTokens>["tokens"]
|
|
48
|
+
|
|
49
|
+
info: ReturnType<typeof createTokens>["info"]
|
|
50
|
+
|
|
51
|
+
constructor(options?: ParserOptions<T>) {
|
|
52
|
+
this.rawOptions = options ?? {}
|
|
53
|
+
const opts = parseParserOptions<T>(this.rawOptions)
|
|
54
|
+
checkParserOpts<T>(opts)
|
|
55
|
+
this.options = opts
|
|
56
|
+
const { lexer, tokens, info } = createTokens<T>(opts)
|
|
57
|
+
this.lexer = lexer
|
|
58
|
+
this.tokens = tokens
|
|
59
|
+
this.info = info
|
|
60
|
+
this.parser = new ParserBase<T>(opts, this.tokens, this.info)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parses a string.
|
|
65
|
+
*/
|
|
66
|
+
parse(input: string): ParserResults {
|
|
67
|
+
if (isWhitespace(input)) {
|
|
68
|
+
return tokenHandler.value(undefined, { start: 0, end: 0 }) as any
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let lexed = this._lex(input)
|
|
72
|
+
|
|
73
|
+
const shift = getUnclosedRightParenCount(lexed.tokens, this.tokens)
|
|
74
|
+
if (shift) {
|
|
75
|
+
input = "(".repeat(shift) + input
|
|
76
|
+
lexed = this._lex(input)
|
|
77
|
+
}
|
|
78
|
+
this.parser.shift = shift
|
|
79
|
+
this.parser.input = lexed.tokens
|
|
80
|
+
this.parser.rawInput = input
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 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.
|
|
84
|
+
*/
|
|
85
|
+
try {
|
|
86
|
+
if (lexed.errors.length > 0) throw new Error("Unexpected Lexer Errors")
|
|
87
|
+
this.parser.input = lexed.tokens
|
|
88
|
+
const res = this.parser.main()
|
|
89
|
+
if (res === undefined) { throw new Error("throw") }
|
|
90
|
+
// hidden param
|
|
91
|
+
// eslint-disable-next-line prefer-rest-params
|
|
92
|
+
if (!arguments[1]?.unsealed) seal(res)
|
|
93
|
+
return res
|
|
94
|
+
} catch (error: unknown) {
|
|
95
|
+
// eslint-disable-next-line no-ex-assign
|
|
96
|
+
if ((error as Error).message === "throw") error = undefined
|
|
97
|
+
const err = new BooleanParserLibraryError(ERROR_CODES.PARSER_ERROR, {
|
|
98
|
+
input,
|
|
99
|
+
options: this.rawOptions,
|
|
100
|
+
"parsed options": this.options,
|
|
101
|
+
error: error as Error,
|
|
102
|
+
"lexed tokens": lexed.tokens,
|
|
103
|
+
"lexer errors": lexed.errors,
|
|
104
|
+
"parser errors": this.parser.errors,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
throw err
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// needed for evaluate and validate so they are only checked on demand
|
|
112
|
+
private evaluationOptionsChecked: boolean = false
|
|
113
|
+
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
115
|
+
_checkEvaluationOptions(): void {
|
|
116
|
+
if (!this.evaluationOptionsChecked) {
|
|
117
|
+
checkParserOpts(this.options, true)
|
|
118
|
+
this.evaluationOptionsChecked = true
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private validationOptionsChecked: boolean = false
|
|
123
|
+
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
125
|
+
_checkValidationOptions(): void {
|
|
126
|
+
if (!this.validationOptionsChecked) {
|
|
127
|
+
checkParserOpts(this.options, false, true)
|
|
128
|
+
this.validationOptionsChecked = true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generates a railroad diagram for debugging. Does not 100% represent how things are actually handled internally.
|
|
134
|
+
*
|
|
135
|
+
* Not exposed because it uses the raw chevrotain tokens.
|
|
136
|
+
*
|
|
137
|
+
* **Note: It is not 100% accurate. Some special cases are parsed one way but handled internally differently.**
|
|
138
|
+
*/
|
|
139
|
+
private _generateRailRoadDiagram(): string {
|
|
140
|
+
const serialized = this.parser.getSerializedGastProductions()
|
|
141
|
+
const html = createSyntaxDiagramsCode(serialized)
|
|
142
|
+
return html
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* For debugging.
|
|
147
|
+
* Not exposed because it returns the raw chevrotain tokens.
|
|
148
|
+
*/
|
|
149
|
+
private _lex(input: string): ILexingResult {
|
|
150
|
+
return this.lexer.tokenize(input)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface Parser<T extends {} = {}> extends Mixin<
|
|
155
|
+
| AutocompleteMixin<T>
|
|
156
|
+
| AutoreplaceMixin
|
|
157
|
+
| Autosuggest<T>
|
|
158
|
+
| EvaluateMixin<T>
|
|
159
|
+
| ValidateMixin<T>
|
|
160
|
+
| NormalizeMixin<T>
|
|
161
|
+
| GetIndexMixin<T>
|
|
162
|
+
| GetBestIndexesMixin
|
|
163
|
+
>,
|
|
164
|
+
AutocompleteMixin<T>,
|
|
165
|
+
AutoreplaceMixin,
|
|
166
|
+
Autosuggest<T>,
|
|
167
|
+
EvaluateMixin<T>,
|
|
168
|
+
ValidateMixin < T >,
|
|
169
|
+
NormalizeMixin<T>,
|
|
170
|
+
GetIndexMixin<T>,
|
|
171
|
+
GetBestIndexesMixin
|
|
172
|
+
{}
|
|
173
|
+
|
|
174
|
+
mixin(Parser, [
|
|
175
|
+
AutocompleteMixin,
|
|
176
|
+
AutoreplaceMixin,
|
|
177
|
+
Autosuggest,
|
|
178
|
+
EvaluateMixin,
|
|
179
|
+
ValidateMixin,
|
|
180
|
+
NormalizeMixin,
|
|
181
|
+
GetIndexMixin,
|
|
182
|
+
GetBestIndexesMixin,
|
|
183
|
+
])
|