@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,143 @@
|
|
|
1
|
+
import { isBlank, pushIfNotIn } from "@alanscodelog/utils"
|
|
2
|
+
|
|
3
|
+
import { ERROR_CODES } from "../../types/errors.js"
|
|
4
|
+
import type { FullParserOptions, ParserOptions } from "../../types/parser.js"
|
|
5
|
+
import { BooleanParserLibraryError } from "../errors.js"
|
|
6
|
+
import { defaultConditionNormalizer } from "../general/defaultConditionNormalizer.js"
|
|
7
|
+
import { defaultValueComparer } from "../general/defaultValueComparer.js"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export function checkParserOpts<T extends {}>(opts: FullParserOptions<T>, evaluatorChecks: boolean = false, validatorChecks: boolean = false): void {
|
|
11
|
+
if (!evaluatorChecks) {
|
|
12
|
+
const keywordsList = [...opts.keywords.and, ...opts.keywords.or, ...opts.keywords.not].map(keyword => keyword.value)
|
|
13
|
+
const symNots = opts.keywords.not.filter(op => op.isSymbol).map(op => op.value)
|
|
14
|
+
const keywords = [
|
|
15
|
+
...[...keywordsList].filter(val => !symNots.includes(val)),
|
|
16
|
+
...["(", ")"],
|
|
17
|
+
...(opts.arrayValues ? ["[", "]"] : []),
|
|
18
|
+
...(opts.regexValues ? ["/"] : []),
|
|
19
|
+
]
|
|
20
|
+
const extra: string[] = []
|
|
21
|
+
if (opts.expandedPropertySeparator) extra.push(opts.expandedPropertySeparator)
|
|
22
|
+
if (opts.customPropertyOperators) pushIfNotIn(extra, ...opts.customPropertyOperators)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/* #region Blank Operator Checks */
|
|
26
|
+
if (opts.expandedPropertySeparator && isBlank(opts.expandedPropertySeparator)) {
|
|
27
|
+
throw new BooleanParserLibraryError(
|
|
28
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
29
|
+
{ prohibited: [""], invalid: opts.expandedPropertySeparator },
|
|
30
|
+
`expandedPropertySeparator cannot be blank`
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
const customInvalid = opts.customPropertyOperators?.find(_ => isBlank(_))
|
|
34
|
+
if (customInvalid !== undefined) {
|
|
35
|
+
throw new BooleanParserLibraryError(
|
|
36
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
37
|
+
{ prohibited: [""], invalid: customInvalid },
|
|
38
|
+
`customPropertyOperators cannot contain blank entries`
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
const prefixInvalid = opts.prefixableStrings?.find(_ => isBlank(_))
|
|
42
|
+
if (prefixInvalid !== undefined) {
|
|
43
|
+
throw new BooleanParserLibraryError(
|
|
44
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
45
|
+
{ prohibited: [""], invalid: prefixInvalid },
|
|
46
|
+
`prefixableStrings cannot contain blank entries`
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
for (const key of ["and", "or", "not"]) {
|
|
50
|
+
const invalid = opts.keywords[key as keyof FullParserOptions["keywords"]]
|
|
51
|
+
?.find(_ => isBlank(_.value))
|
|
52
|
+
?.value
|
|
53
|
+
if (invalid !== undefined) {
|
|
54
|
+
throw new BooleanParserLibraryError(
|
|
55
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
56
|
+
{ prohibited: [""], invalid },
|
|
57
|
+
`keywords.${key} cannot contain entries with blank values`
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/* #regionend */
|
|
62
|
+
|
|
63
|
+
/* #region Prohibited Keyword Checks */
|
|
64
|
+
const all = [...keywords, ...symNots, ...extra]
|
|
65
|
+
const allKeywords = [...keywords, ...symNots]
|
|
66
|
+
/**
|
|
67
|
+
* Allowed:
|
|
68
|
+
* - custom property operators can be the same as symbol not operators
|
|
69
|
+
* - custom property operators can be the same as expanded property separators
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
const messageInvalidAny = "cannot contain operators or special symbols, but found"
|
|
73
|
+
const messageInvalidBool = "cannot contain boolean operators or special symbols, but found"
|
|
74
|
+
|
|
75
|
+
const invalidPrefixableString = opts.prefixableStrings?.find(val => all.includes(val))
|
|
76
|
+
if (invalidPrefixableString) {
|
|
77
|
+
throw new BooleanParserLibraryError(
|
|
78
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
79
|
+
{ prohibited: all, invalid: invalidPrefixableString },
|
|
80
|
+
`prefixableStrings ${messageInvalidAny} "${invalidPrefixableString}"`
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const invalidExpandedPropertySeparator = allKeywords
|
|
85
|
+
.find(_ => _ === opts.expandedPropertySeparator as any)
|
|
86
|
+
if (invalidExpandedPropertySeparator) {
|
|
87
|
+
throw new BooleanParserLibraryError(
|
|
88
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
89
|
+
{ prohibited: allKeywords, invalid: invalidExpandedPropertySeparator },
|
|
90
|
+
`expandedPropertySeparator ${messageInvalidBool} "${invalidExpandedPropertySeparator}"`
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const invalidCustomPropertyOperator = opts.customPropertyOperators?.find(val => keywords.includes(val))
|
|
95
|
+
? opts.expandedPropertySeparator
|
|
96
|
+
: undefined
|
|
97
|
+
if (invalidCustomPropertyOperator) {
|
|
98
|
+
throw new BooleanParserLibraryError(
|
|
99
|
+
ERROR_CODES.PARSER_CONFLICTING_OPTIONS_ERROR,
|
|
100
|
+
{ prohibited: keywords, invalid: invalidCustomPropertyOperator },
|
|
101
|
+
`customPropertyOperator ${messageInvalidBool} "${invalidCustomPropertyOperator}"`
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
/* #regionend */
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (evaluatorChecks) {
|
|
108
|
+
const requireCustomNormalizer: (keyof ParserOptions)[] = [] as any
|
|
109
|
+
if ((opts.prefixableStrings?.length ?? 0) > 0) requireCustomNormalizer.push("prefixableStrings")
|
|
110
|
+
if ((opts.customPropertyOperators?.length ?? 0) > 0) requireCustomNormalizer.push("customPropertyOperators")
|
|
111
|
+
if ((opts.expandedPropertySeparator?.length ?? 0) > 0) requireCustomNormalizer.push("expandedPropertySeparator")
|
|
112
|
+
if (opts.regexValues) requireCustomNormalizer.push("regexValues")
|
|
113
|
+
|
|
114
|
+
if (requireCustomNormalizer.length > 0 && opts.conditionNormalizer === defaultConditionNormalizer) {
|
|
115
|
+
throw new BooleanParserLibraryError(
|
|
116
|
+
ERROR_CODES.PARSER_OPTION_REQUIRED_ERROR,
|
|
117
|
+
{ options: requireCustomNormalizer, requires: "conditionNormalizer" },
|
|
118
|
+
`A custom conditionNormalizer function must be specified when using the following options: ${requireCustomNormalizer.join(", ")}`
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
const requireCustomComparer: (keyof ParserOptions)[] = [] as any
|
|
122
|
+
if (opts.regexValues) requireCustomComparer.push("regexValues")
|
|
123
|
+
if (opts.regexValues) requireCustomComparer.push("arrayValues")
|
|
124
|
+
|
|
125
|
+
if (requireCustomComparer.length > 0 && opts.valueComparer === defaultValueComparer) {
|
|
126
|
+
throw new BooleanParserLibraryError(
|
|
127
|
+
ERROR_CODES.PARSER_OPTION_REQUIRED_ERROR,
|
|
128
|
+
{ options: requireCustomComparer, requires: "valueComparer" },
|
|
129
|
+
`A custom valueComparer function must be specified when using the following options: ${requireCustomComparer.join(", ")}`
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (validatorChecks) {
|
|
134
|
+
if (opts.valueValidator === undefined) {
|
|
135
|
+
throw new BooleanParserLibraryError(
|
|
136
|
+
ERROR_CODES.PARSER_OPTION_REQUIRED_ERROR,
|
|
137
|
+
{ requires: "valueValidator" },
|
|
138
|
+
`A custom valueValidator function must be specified when using the validate method.`
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Position } from "../../types/ast.js"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type ChevrotainLocation = {
|
|
5
|
+
startOffset?: number
|
|
6
|
+
endOffset?: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** @internal */
|
|
10
|
+
export function extractPosition(loc: ChevrotainLocation, shift: number): Position {
|
|
11
|
+
return {
|
|
12
|
+
start: loc.startOffset! - (shift ?? 0),
|
|
13
|
+
end: loc.endOffset! + 1 - (shift ?? 0),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type IToken, tokenMatcher } from "chevrotain"
|
|
2
|
+
|
|
3
|
+
import type { createTokens } from "../../grammar/createTokens.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/** @internal */
|
|
7
|
+
export function getUnclosedRightParenCount(tokens: IToken[], t: ReturnType<typeof createTokens>["tokens"]): number {
|
|
8
|
+
let open = 0
|
|
9
|
+
let unclosed = 0
|
|
10
|
+
for (const token of tokens) {
|
|
11
|
+
if (tokenMatcher(token, t.PAREN_R)) {
|
|
12
|
+
if (open > 0) {
|
|
13
|
+
open--
|
|
14
|
+
} else {
|
|
15
|
+
unclosed++
|
|
16
|
+
}
|
|
17
|
+
} else if (tokenMatcher(token, t.PAREN_L)) {
|
|
18
|
+
open++
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return unclosed
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Autogenerated Index */
|
|
2
|
+
|
|
3
|
+
export { assignParents } from "./assignParents.js"
|
|
4
|
+
export { checkParserOpts } from "./checkParserOpts.js"
|
|
5
|
+
export { extractPosition } from "./extractPosition.js"
|
|
6
|
+
export { getUnclosedRightParenCount } from "./getUnclosedRightParenCount.js"
|
|
7
|
+
export { parseParserOptions } from "./parseParserOptions.js"
|
|
8
|
+
export { seal } from "./seal.js"
|
|
9
|
+
export { setParent } from "./setParent.js"
|
|
10
|
+
export { unescape } from "./unescape.js"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { FullParserOptions, ParserOptions } from "../../types/parser.js"
|
|
2
|
+
import { defaultConditionNormalizer } from "../general/defaultConditionNormalizer.js"
|
|
3
|
+
import { defaultKeyParser } from "../general/defaultKeyParser.js"
|
|
4
|
+
import { defaultPrefixApplier } from "../general/defaultPrefixApplier.js"
|
|
5
|
+
import { defaultValueComparer } from "../general/defaultValueComparer.js"
|
|
6
|
+
|
|
7
|
+
/** @internal */
|
|
8
|
+
export function parseParserOptions<T extends {} = {}>(
|
|
9
|
+
options: ParserOptions<T>
|
|
10
|
+
): FullParserOptions<T> {
|
|
11
|
+
const opts: ParserOptions = {
|
|
12
|
+
prefixApplier: defaultPrefixApplier,
|
|
13
|
+
keyParser: defaultKeyParser,
|
|
14
|
+
valueComparer: defaultValueComparer,
|
|
15
|
+
conditionNormalizer: defaultConditionNormalizer,
|
|
16
|
+
prefixableGroups: true,
|
|
17
|
+
prefixableStrings: undefined,
|
|
18
|
+
expandedPropertySeparator: undefined,
|
|
19
|
+
customPropertyOperators: undefined,
|
|
20
|
+
onMissingBooleanOperator: "error",
|
|
21
|
+
arrayValues: true,
|
|
22
|
+
regexValues: true,
|
|
23
|
+
...options, // todo fix
|
|
24
|
+
keywords: {
|
|
25
|
+
or:
|
|
26
|
+
options.keywords?.or
|
|
27
|
+
? options.keywords.or as any
|
|
28
|
+
: [
|
|
29
|
+
{ value: "||", isSymbol: true },
|
|
30
|
+
{ value: "|", isSymbol: true },
|
|
31
|
+
{ value: "or", isSymbol: false },
|
|
32
|
+
{ value: "OR", isSymbol: false },
|
|
33
|
+
],
|
|
34
|
+
and:
|
|
35
|
+
options.keywords?.and
|
|
36
|
+
? options.keywords.and as any
|
|
37
|
+
: [
|
|
38
|
+
{ value: "&&", isSymbol: true },
|
|
39
|
+
{ value: "&", isSymbol: true },
|
|
40
|
+
{ value: "and", isSymbol: false },
|
|
41
|
+
{ value: "AND", isSymbol: false },
|
|
42
|
+
],
|
|
43
|
+
not:
|
|
44
|
+
options.keywords?.not
|
|
45
|
+
? options.keywords.not as any
|
|
46
|
+
: [
|
|
47
|
+
{ value: "!", isSymbol: true },
|
|
48
|
+
{ value: "not", isSymbol: false },
|
|
49
|
+
{ value: "NOT", isSymbol: false },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
return opts as any
|
|
54
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { assignParents } from "./assignParents.js"
|
|
2
|
+
|
|
3
|
+
import { Node } from "../../ast/classes/Node.js"
|
|
4
|
+
import type { AnyToken, Nodes } from "../../types/ast.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* set and "seals" all parent properties
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export function seal(ast: Nodes | AnyToken): void {
|
|
12
|
+
if (ast instanceof Node) assignParents(ast)
|
|
13
|
+
ast.parent = undefined
|
|
14
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { unreachable } from "@alanscodelog/utils"
|
|
2
|
+
|
|
3
|
+
import { ConditionNode } from "../ast/classes/ConditionNode.js"
|
|
4
|
+
import { VariableNode } from "../ast/classes/VariableNode.js"
|
|
5
|
+
import type { Parser } from "../parser.js"
|
|
6
|
+
import { type Completion, type Suggestion, SUGGESTION_TYPE } from "../types/autocomplete.js"
|
|
7
|
+
import type { FullParserOptions } from "../types/parser.js"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export class AutocompleteMixin<T extends {}> {
|
|
11
|
+
/**
|
|
12
|
+
* 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.
|
|
13
|
+
*
|
|
14
|
+
* 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.
|
|
15
|
+
*
|
|
16
|
+
* 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.
|
|
17
|
+
*
|
|
18
|
+
* 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.
|
|
19
|
+
*/
|
|
20
|
+
autocomplete(
|
|
21
|
+
suggestions: Suggestion[],
|
|
22
|
+
{
|
|
23
|
+
values = [], arrayValues = [], variables = [], prefixes = [], properties = [], expandedPropertyOperators = [], customPropertyOperators = (this as any as Parser<T>).options.customPropertyOperators ?? [], keywords = (this as any as Parser<T>).options.keywords, regexFlags = ["i", "m", "u"], quote = "\"",
|
|
24
|
+
}: Partial<Record<
|
|
25
|
+
"variables" |
|
|
26
|
+
"values" |
|
|
27
|
+
"arrayValues" |
|
|
28
|
+
"prefixes" |
|
|
29
|
+
"properties" |
|
|
30
|
+
"regexFlags" |
|
|
31
|
+
"expandedPropertyOperators" |
|
|
32
|
+
"customPropertyOperators", string[]>> & {
|
|
33
|
+
quote?: string
|
|
34
|
+
keywords?: FullParserOptions<T>["keywords"]
|
|
35
|
+
} = {}
|
|
36
|
+
): Completion[] {
|
|
37
|
+
const self = (this as any as Parser<T>)
|
|
38
|
+
return suggestions.map(suggestion => {
|
|
39
|
+
const type = suggestion.type
|
|
40
|
+
switch (type) {
|
|
41
|
+
case SUGGESTION_TYPE.BACKTICK: return [{ suggestion, value: "`" }]
|
|
42
|
+
case SUGGESTION_TYPE.DOUBLEQUOTE: return [{ suggestion, value: "\"" }]
|
|
43
|
+
case SUGGESTION_TYPE.SINGLEQUOTE: return [{ suggestion, value: "'" }]
|
|
44
|
+
case SUGGESTION_TYPE.PARENL: return [{ suggestion, value: "(" }]
|
|
45
|
+
case SUGGESTION_TYPE.PARENR: return [{ suggestion, value: ")" }]
|
|
46
|
+
case SUGGESTION_TYPE.BRAKCETR: return [{ suggestion, value: "]" }] // L not needed
|
|
47
|
+
case SUGGESTION_TYPE.REGEX: return [{ suggestion, value: "/" }]
|
|
48
|
+
case SUGGESTION_TYPE.REGEX_FLAGS:
|
|
49
|
+
return regexFlags
|
|
50
|
+
.map(value => ({ suggestion, value }))
|
|
51
|
+
// remove existing flags from suggestions
|
|
52
|
+
.filter(completion => {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
54
|
+
const { suggestion, value } = completion
|
|
55
|
+
if (suggestion.type !== SUGGESTION_TYPE.REGEX_FLAGS) {return true}
|
|
56
|
+
|
|
57
|
+
const token = suggestion.cursorInfo
|
|
58
|
+
const flags = token.at && (token.at.parent as VariableNode)?.quote?.flags === suggestion.cursorInfo.at
|
|
59
|
+
? token.at
|
|
60
|
+
: token.next && (token.next.parent as VariableNode)?.quote?.flags === suggestion.cursorInfo.next
|
|
61
|
+
? token.next
|
|
62
|
+
: token.prev && (token.prev.parent as VariableNode)?.quote?.flags === suggestion.cursorInfo.prev
|
|
63
|
+
? token.prev
|
|
64
|
+
: undefined
|
|
65
|
+
|
|
66
|
+
if (flags?.value?.includes(value)) {return false}
|
|
67
|
+
return true
|
|
68
|
+
})
|
|
69
|
+
case SUGGESTION_TYPE.PROPERTY: {
|
|
70
|
+
return properties.map(value => ({ suggestion, value }))
|
|
71
|
+
}
|
|
72
|
+
case SUGGESTION_TYPE.PROPERTY_SEP: {
|
|
73
|
+
return [{ suggestion, value: self.options.expandedPropertySeparator! }]
|
|
74
|
+
}
|
|
75
|
+
case SUGGESTION_TYPE.EXPANDED_PROPERTY_OPERATOR: {
|
|
76
|
+
return expandedPropertyOperators.map(value => ({ suggestion, value }))
|
|
77
|
+
}
|
|
78
|
+
case SUGGESTION_TYPE.CUSTOM_PROPERTY_OPERATOR: {
|
|
79
|
+
return customPropertyOperators.map(value => ({ suggestion, value }))
|
|
80
|
+
}
|
|
81
|
+
case SUGGESTION_TYPE.BOOLEAN_SYMBOL_OP: {
|
|
82
|
+
const keywordsList = [...keywords.and, ...keywords.or]
|
|
83
|
+
const symOpts = keywordsList.filter(_ => _.isSymbol)
|
|
84
|
+
return symOpts.map(({ value }) => ({ suggestion, value }))
|
|
85
|
+
}
|
|
86
|
+
case SUGGESTION_TYPE.BOOLEAN_WORD_OP: {
|
|
87
|
+
const keywordsList = [...keywords.and, ...keywords.or]
|
|
88
|
+
const wordOpts = keywordsList.filter(_ => !_.isSymbol)
|
|
89
|
+
return wordOpts.map(({ value }) => ({ suggestion, value }))
|
|
90
|
+
}
|
|
91
|
+
case SUGGESTION_TYPE.VALUE:
|
|
92
|
+
case SUGGESTION_TYPE.ARRAY_VALUE:
|
|
93
|
+
case SUGGESTION_TYPE.VARIABLE: {
|
|
94
|
+
const arr = type === SUGGESTION_TYPE.VARIABLE
|
|
95
|
+
? variables
|
|
96
|
+
: type === SUGGESTION_TYPE.ARRAY_VALUE
|
|
97
|
+
? arrayValues
|
|
98
|
+
: type === SUGGESTION_TYPE.VALUE
|
|
99
|
+
? values
|
|
100
|
+
: unreachable()
|
|
101
|
+
return arr.map(variable => {
|
|
102
|
+
// we don't need to alter options since we can just check there are no quotes (also tells us no prefixes are used) and no operators are defined
|
|
103
|
+
const res = self.parse(variable)
|
|
104
|
+
if (res instanceof ConditionNode &&
|
|
105
|
+
res.operator === undefined &&
|
|
106
|
+
res.value instanceof VariableNode &&
|
|
107
|
+
res.value.quote === undefined) {
|
|
108
|
+
return { suggestion, value: res.value.value.value }
|
|
109
|
+
} else {
|
|
110
|
+
return { suggestion, value: quote + variable.replace(new RegExp(quote, "g"), `\\${quote}`) + quote }
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
case SUGGESTION_TYPE.PREFIX: return prefixes.map(prefix => {
|
|
115
|
+
const res = self.parse(prefix)
|
|
116
|
+
if (res instanceof ConditionNode &&
|
|
117
|
+
res.operator === undefined &&
|
|
118
|
+
res.value instanceof VariableNode &&
|
|
119
|
+
res.value.quote === undefined) {
|
|
120
|
+
return { suggestion, value: res.value.value.value }
|
|
121
|
+
} else {
|
|
122
|
+
return { suggestion, value: quote + prefix.replace(new RegExp(quote, "g"), `\\${quote}`) + quote }
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}).flat()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { insert } from "@alanscodelog/utils"
|
|
2
|
+
|
|
3
|
+
import type { Completion } from "../types/autocomplete.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class AutoreplaceMixin {
|
|
7
|
+
/**
|
|
8
|
+
* 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.
|
|
9
|
+
*
|
|
10
|
+
* 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.
|
|
11
|
+
*/
|
|
12
|
+
autoreplace(
|
|
13
|
+
input: string,
|
|
14
|
+
{ value, suggestion }: Completion
|
|
15
|
+
): { replacement: string, cursor: number } {
|
|
16
|
+
const isQuotedLeft = ["\"", "'", "`"].includes(value[0])
|
|
17
|
+
const isQuotedRight = ["\"", "'", "`"].includes(value[value.length - 1])
|
|
18
|
+
if ((isQuotedLeft && !isQuotedRight) || (!isQuotedLeft && isQuotedRight)) {
|
|
19
|
+
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"}.`)
|
|
20
|
+
}
|
|
21
|
+
let cursor = suggestion.range.start + value.length
|
|
22
|
+
|
|
23
|
+
if (suggestion.requires.prefix) {
|
|
24
|
+
value = suggestion.requires.prefix + (isQuotedLeft ? "" : "\"") + value + (isQuotedRight ? "" : "\"")
|
|
25
|
+
|
|
26
|
+
cursor += suggestion.requires.prefix.length + Number(!isQuotedLeft) + Number(!isQuotedRight)
|
|
27
|
+
}
|
|
28
|
+
if (suggestion.requires.group) {
|
|
29
|
+
value += "()"
|
|
30
|
+
cursor++
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (suggestion.requires.whitespace.before // &&
|
|
34
|
+
) {
|
|
35
|
+
value = ` ${value}`
|
|
36
|
+
cursor++
|
|
37
|
+
}
|
|
38
|
+
if (suggestion.requires.whitespace.after // &&
|
|
39
|
+
) {
|
|
40
|
+
value = `${value} `
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const replacement = insert(value, input, [suggestion.range.start, suggestion.range.end])
|
|
44
|
+
return { replacement, cursor }
|
|
45
|
+
}
|
|
46
|
+
}
|