@swaggerexpert/jsonpath 3.2.3 → 3.2.5
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/cjs/apg-lite.cjs +1221 -0
- package/cjs/compile.cjs +50 -0
- package/cjs/errors/JSONPathCompileError.cjs +8 -0
- package/cjs/errors/JSONPathError.cjs +44 -0
- package/cjs/errors/JSONPathParseError.cjs +8 -0
- package/cjs/escape.cjs +59 -0
- package/cjs/grammar.cjs +2839 -0
- package/cjs/index.cjs +31 -0
- package/cjs/parse/callbacks/cst.cjs +49 -0
- package/cjs/parse/index.cjs +41 -0
- package/cjs/parse/trace/Expectations.cjs +10 -0
- package/cjs/parse/trace/Trace.cjs +35 -0
- package/cjs/parse/translators/ASTTranslator/decoders.cjs +83 -0
- package/cjs/parse/translators/ASTTranslator/index.cjs +15 -0
- package/cjs/parse/translators/ASTTranslator/transformers.cjs +411 -0
- package/cjs/parse/translators/CSTOptimizedTranslator.cjs +39 -0
- package/cjs/parse/translators/CSTTranslator.cjs +118 -0
- package/cjs/parse/translators/XMLTranslator.cjs +12 -0
- package/cjs/test/index.cjs +25 -0
- package/es/compile.mjs +45 -0
- package/es/errors/JSONPathCompileError.mjs +3 -0
- package/es/errors/JSONPathError.mjs +40 -0
- package/es/errors/JSONPathParseError.mjs +3 -0
- package/es/escape.mjs +55 -0
- package/es/grammar.mjs +2835 -0
- package/es/index.mjs +13 -0
- package/es/parse/callbacks/cst.mjs +44 -0
- package/es/parse/index.mjs +36 -0
- package/es/parse/trace/Expectations.mjs +6 -0
- package/es/parse/trace/Trace.mjs +30 -0
- package/es/parse/translators/ASTTranslator/decoders.mjs +75 -0
- package/es/parse/translators/ASTTranslator/index.mjs +9 -0
- package/es/parse/translators/ASTTranslator/transformers.mjs +405 -0
- package/es/parse/translators/CSTOptimizedTranslator.mjs +34 -0
- package/es/parse/translators/CSTTranslator.mjs +113 -0
- package/es/parse/translators/XMLTranslator.mjs +7 -0
- package/es/test/index.mjs +20 -0
- package/package.json +1 -1
- package/types/index.d.ts +24 -0
package/es/index.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { default as Grammar } from "./grammar.mjs";
|
|
2
|
+
export { default as parse } from "./parse/index.mjs";
|
|
3
|
+
export { default as CSTTranslator } from "./parse/translators/CSTTranslator.mjs";
|
|
4
|
+
export { default as CSTOptimizedTranslator } from "./parse/translators/CSTOptimizedTranslator.mjs";
|
|
5
|
+
export { default as ASTTranslator } from "./parse/translators/ASTTranslator/index.mjs";
|
|
6
|
+
export { default as XMLTranslator } from "./parse/translators/XMLTranslator.mjs";
|
|
7
|
+
export { default as Trace } from "./parse/trace/Trace.mjs";
|
|
8
|
+
export { default as test } from "./test/index.mjs";
|
|
9
|
+
export { default as compile } from "./compile.mjs";
|
|
10
|
+
export { default as escape } from "./escape.mjs";
|
|
11
|
+
export { default as JSONPathError } from "./errors/JSONPathError.mjs";
|
|
12
|
+
export { default as JSONPathParseError } from "./errors/JSONPathParseError.mjs";
|
|
13
|
+
export { default as JSONPathCompileError } from "./errors/JSONPathCompileError.mjs";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { utilities, identifiers } from 'apg-lite';
|
|
2
|
+
import JSONPathParseError from "../../errors/JSONPathParseError.mjs";
|
|
3
|
+
const cst = nodeType => {
|
|
4
|
+
return (state, chars, phraseIndex, phraseLength, data) => {
|
|
5
|
+
var _data$options, _data$options2;
|
|
6
|
+
if (!(typeof data === 'object' && data !== null && !Array.isArray(data))) {
|
|
7
|
+
throw new JSONPathParseError("parser's user data must be an object");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// drop the empty nodes
|
|
11
|
+
if ((_data$options = data.options) !== null && _data$options !== void 0 && _data$options.optimize && phraseLength === 0 && (_data$options2 = data.options) !== null && _data$options2 !== void 0 && (_data$options2 = _data$options2.droppableTypes) !== null && _data$options2 !== void 0 && _data$options2.includes(nodeType)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (state === identifiers.SEM_PRE) {
|
|
15
|
+
const node = {
|
|
16
|
+
type: nodeType,
|
|
17
|
+
text: utilities.charsToString(chars, phraseIndex, phraseLength),
|
|
18
|
+
start: phraseIndex,
|
|
19
|
+
length: phraseLength,
|
|
20
|
+
children: []
|
|
21
|
+
};
|
|
22
|
+
if (data.stack.length > 0) {
|
|
23
|
+
var _data$options3, _data$options4;
|
|
24
|
+
const parent = data.stack[data.stack.length - 1];
|
|
25
|
+
const prevSibling = parent.children[parent.children.length - 1];
|
|
26
|
+
const isTextNodeWithinTextNode = parent.type === 'text' && node.type === 'text';
|
|
27
|
+
const shouldCollapse = ((_data$options3 = data.options) === null || _data$options3 === void 0 ? void 0 : _data$options3.optimize) && ((_data$options4 = data.options) === null || _data$options4 === void 0 || (_data$options4 = _data$options4.collapsibleTypes) === null || _data$options4 === void 0 ? void 0 : _data$options4.includes(node.type)) && (prevSibling === null || prevSibling === void 0 ? void 0 : prevSibling.type) === node.type;
|
|
28
|
+
if (shouldCollapse) {
|
|
29
|
+
prevSibling.text += node.text;
|
|
30
|
+
prevSibling.length += node.length;
|
|
31
|
+
} else if (!isTextNodeWithinTextNode) {
|
|
32
|
+
parent.children.push(node);
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
data.root = node;
|
|
36
|
+
}
|
|
37
|
+
data.stack.push(node);
|
|
38
|
+
}
|
|
39
|
+
if (state === identifiers.SEM_POST) {
|
|
40
|
+
data.stack.pop();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export default cst;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Parser, Stats } from 'apg-lite';
|
|
2
|
+
import Trace from "./trace/Trace.mjs";
|
|
3
|
+
import Grammar from "../grammar.mjs";
|
|
4
|
+
import ASTTranslator from "./translators/ASTTranslator/index.mjs";
|
|
5
|
+
import JSONPathParseError from "../errors/JSONPathParseError.mjs";
|
|
6
|
+
const grammar = new Grammar();
|
|
7
|
+
const parse = (jsonPath, {
|
|
8
|
+
normalized = false,
|
|
9
|
+
stats = false,
|
|
10
|
+
trace = false,
|
|
11
|
+
translator = new ASTTranslator()
|
|
12
|
+
} = {}) => {
|
|
13
|
+
if (typeof jsonPath !== 'string') {
|
|
14
|
+
throw new TypeError('JSONPath must be a string');
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const parser = new Parser();
|
|
18
|
+
if (translator) parser.ast = translator;
|
|
19
|
+
if (stats) parser.stats = new Stats();
|
|
20
|
+
if (trace) parser.trace = new Trace();
|
|
21
|
+
const startRule = normalized ? 'normalized-path' : 'jsonpath-query';
|
|
22
|
+
const result = parser.parse(grammar, startRule, jsonPath);
|
|
23
|
+
return {
|
|
24
|
+
result,
|
|
25
|
+
tree: result.success && translator ? parser.ast.getTree() : undefined,
|
|
26
|
+
stats: parser.stats,
|
|
27
|
+
trace: parser.trace
|
|
28
|
+
};
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new JSONPathParseError('Unexpected error during JSONPath parsing', {
|
|
31
|
+
cause: error,
|
|
32
|
+
jsonPath
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export default parse;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Trace as BaseTrace } from 'apg-lite';
|
|
2
|
+
import Expectations from "./Expectations.mjs";
|
|
3
|
+
class Trace extends BaseTrace {
|
|
4
|
+
inferExpectations() {
|
|
5
|
+
const lines = this.displayTrace().split('\n');
|
|
6
|
+
const expectations = new Set();
|
|
7
|
+
let lastMatchedIndex = -1;
|
|
8
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9
|
+
const line = lines[i];
|
|
10
|
+
|
|
11
|
+
// capture the max match line (first one that ends in a single character match)
|
|
12
|
+
if (line.includes('M|')) {
|
|
13
|
+
const textMatch = line.match(/]'(.*)'$/);
|
|
14
|
+
if (textMatch && textMatch[1]) {
|
|
15
|
+
lastMatchedIndex = i;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// collect terminal failures after the deepest successful match
|
|
20
|
+
if (i > lastMatchedIndex) {
|
|
21
|
+
const terminalFailMatch = line.match(/N\|\[TLS\(([^)]+)\)]/);
|
|
22
|
+
if (terminalFailMatch) {
|
|
23
|
+
expectations.add(terminalFailMatch[1]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return new Expectations(...expectations);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export default Trace;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const decodeDoubleQuotedString = str => {
|
|
2
|
+
return decodeJSONValue(`"${str}"`);
|
|
3
|
+
};
|
|
4
|
+
export const decodeSingleQuotedString = str => {
|
|
5
|
+
// Decode single-quoted string escape sequences into raw text, then let JSON.stringify
|
|
6
|
+
// produce a correctly escaped double-quoted JSON string.
|
|
7
|
+
let decoded = '';
|
|
8
|
+
for (let i = 0; i < str.length; i++) {
|
|
9
|
+
const ch = str[i];
|
|
10
|
+
if (ch === '\\') {
|
|
11
|
+
i++;
|
|
12
|
+
if (i >= str.length) {
|
|
13
|
+
// Trailing backslash, treat it as a literal backslash
|
|
14
|
+
decoded += '\\';
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
const esc = str[i];
|
|
18
|
+
switch (esc) {
|
|
19
|
+
case 'n':
|
|
20
|
+
decoded += '\n';
|
|
21
|
+
break;
|
|
22
|
+
case 'r':
|
|
23
|
+
decoded += '\r';
|
|
24
|
+
break;
|
|
25
|
+
case 't':
|
|
26
|
+
decoded += '\t';
|
|
27
|
+
break;
|
|
28
|
+
case 'b':
|
|
29
|
+
decoded += '\b';
|
|
30
|
+
break;
|
|
31
|
+
case 'f':
|
|
32
|
+
decoded += '\f';
|
|
33
|
+
break;
|
|
34
|
+
case '/':
|
|
35
|
+
decoded += '/';
|
|
36
|
+
break;
|
|
37
|
+
case '\\':
|
|
38
|
+
decoded += '\\';
|
|
39
|
+
break;
|
|
40
|
+
case "'":
|
|
41
|
+
decoded += "'";
|
|
42
|
+
break;
|
|
43
|
+
case '"':
|
|
44
|
+
decoded += '"';
|
|
45
|
+
break;
|
|
46
|
+
case 'u':
|
|
47
|
+
{
|
|
48
|
+
// Unicode escape \uXXXX - grammar guarantees exactly 4 hex digits
|
|
49
|
+
const hex = str.slice(i + 1, i + 5);
|
|
50
|
+
decoded += String.fromCharCode(parseInt(hex, 16));
|
|
51
|
+
i += 4;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
default:
|
|
55
|
+
// Unrecognized escape, keep the escaped character literally
|
|
56
|
+
decoded += esc;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
decoded += ch;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Use JSON.stringify to produce a valid JSON string literal
|
|
64
|
+
return decodeJSONValue(JSON.stringify(decoded));
|
|
65
|
+
};
|
|
66
|
+
export const decodeInteger = str => {
|
|
67
|
+
const value = parseInt(str, 10);
|
|
68
|
+
if (!Number.isSafeInteger(value)) {
|
|
69
|
+
throw new RangeError(`Integer value out of safe range [-(2^53)+1, (2^53)-1], got: ${str}`);
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
};
|
|
73
|
+
export const decodeJSONValue = str => {
|
|
74
|
+
return JSON.parse(str);
|
|
75
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import CSTOptimizedTranslator from "../CSTOptimizedTranslator.mjs";
|
|
2
|
+
import { transformCSTtoAST, default as transformers } from "./transformers.mjs";
|
|
3
|
+
class ASTTranslator extends CSTOptimizedTranslator {
|
|
4
|
+
getTree() {
|
|
5
|
+
const cst = super.getTree();
|
|
6
|
+
return transformCSTtoAST(cst.root, transformers);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export default ASTTranslator;
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import JSONPathParseError from "../../../errors/JSONPathParseError.mjs";
|
|
2
|
+
import { decodeSingleQuotedString, decodeDoubleQuotedString, decodeInteger, decodeJSONValue } from "./decoders.mjs";
|
|
3
|
+
export const transformCSTtoAST = (node, transformerMap, ctx = {
|
|
4
|
+
parent: null,
|
|
5
|
+
path: []
|
|
6
|
+
}) => {
|
|
7
|
+
const transformer = transformerMap[node.type];
|
|
8
|
+
if (!transformer) {
|
|
9
|
+
throw new JSONPathParseError(`No transformer for CST node type: ${node.type}`);
|
|
10
|
+
}
|
|
11
|
+
const nextCtx = {
|
|
12
|
+
parent: node,
|
|
13
|
+
path: [...ctx.path, node]
|
|
14
|
+
};
|
|
15
|
+
return transformer(node, nextCtx);
|
|
16
|
+
};
|
|
17
|
+
const transformers = {
|
|
18
|
+
/**
|
|
19
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.1.1
|
|
20
|
+
*/
|
|
21
|
+
['jsonpath-query'](node, ctx) {
|
|
22
|
+
const segments = node.children.find(c => c.type === 'segments');
|
|
23
|
+
return {
|
|
24
|
+
type: 'JsonPathQuery',
|
|
25
|
+
segments: segments ? segments.children.filter(({
|
|
26
|
+
type
|
|
27
|
+
}) => type === 'segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx)) : []
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5
|
|
32
|
+
*/
|
|
33
|
+
segment(node, ctx) {
|
|
34
|
+
const child = node.children.find(({
|
|
35
|
+
type
|
|
36
|
+
}) => ['child-segment', 'descendant-segment'].includes(type));
|
|
37
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3
|
|
41
|
+
*/
|
|
42
|
+
selector(node, ctx) {
|
|
43
|
+
const child = node.children.find(({
|
|
44
|
+
type
|
|
45
|
+
}) => ['name-selector', 'wildcard-selector', 'slice-selector', 'index-selector', 'filter-selector'].includes(type));
|
|
46
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.1.1
|
|
50
|
+
*/
|
|
51
|
+
['name-selector'](node, ctx) {
|
|
52
|
+
const stringLiteralCSTNode = node.children.find(({
|
|
53
|
+
type
|
|
54
|
+
}) => type === 'string-literal');
|
|
55
|
+
const stringLiteralASTNode = transformCSTtoAST(stringLiteralCSTNode, transformers, ctx);
|
|
56
|
+
return {
|
|
57
|
+
type: 'NameSelector',
|
|
58
|
+
value: stringLiteralASTNode.value,
|
|
59
|
+
format: stringLiteralASTNode.format
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
['string-literal'](node) {
|
|
63
|
+
const isSingleQuoted = node.children.find(({
|
|
64
|
+
type,
|
|
65
|
+
text
|
|
66
|
+
}) => type === 'text' && text === "'");
|
|
67
|
+
const quoted = node.children.find(({
|
|
68
|
+
type
|
|
69
|
+
}) => ['double-quoted', 'single-quoted'].includes(type));
|
|
70
|
+
const decodeString = isSingleQuoted ? decodeSingleQuotedString : decodeDoubleQuotedString;
|
|
71
|
+
return {
|
|
72
|
+
type: 'StringLiteral',
|
|
73
|
+
value: quoted ? decodeString(quoted.text) : '',
|
|
74
|
+
format: isSingleQuoted ? 'single-quoted' : 'double-quoted'
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
|
|
79
|
+
*/
|
|
80
|
+
['wildcard-selector']() {
|
|
81
|
+
return {
|
|
82
|
+
type: 'WildcardSelector'
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.3.1
|
|
87
|
+
*/
|
|
88
|
+
['index-selector'](node) {
|
|
89
|
+
return {
|
|
90
|
+
type: 'IndexSelector',
|
|
91
|
+
value: decodeInteger(node.text)
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
|
|
96
|
+
*/
|
|
97
|
+
['slice-selector'](node) {
|
|
98
|
+
const start = node.children.find(({
|
|
99
|
+
type
|
|
100
|
+
}) => type === 'start');
|
|
101
|
+
const end = node.children.find(({
|
|
102
|
+
type
|
|
103
|
+
}) => type === 'end');
|
|
104
|
+
const step = node.children.find(({
|
|
105
|
+
type
|
|
106
|
+
}) => type === 'step');
|
|
107
|
+
return {
|
|
108
|
+
type: 'SliceSelector',
|
|
109
|
+
start: start ? decodeInteger(start.text) : null,
|
|
110
|
+
end: end ? decodeInteger(end.text) : null,
|
|
111
|
+
step: step ? decodeInteger(step.text) : null
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
/**
|
|
115
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
|
|
116
|
+
*/
|
|
117
|
+
['filter-selector'](node, ctx) {
|
|
118
|
+
const child = node.children.find(({
|
|
119
|
+
type
|
|
120
|
+
}) => type === 'logical-expr');
|
|
121
|
+
return {
|
|
122
|
+
type: 'FilterSelector',
|
|
123
|
+
expression: transformCSTtoAST(child, transformers, ctx)
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
['logical-expr'](node, ctx) {
|
|
127
|
+
const child = node.children.find(({
|
|
128
|
+
type
|
|
129
|
+
}) => type === 'logical-or-expr');
|
|
130
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
131
|
+
},
|
|
132
|
+
['logical-or-expr'](node, ctx) {
|
|
133
|
+
const logicalAndExprs = node.children.filter(({
|
|
134
|
+
type
|
|
135
|
+
}) => type === 'logical-and-expr');
|
|
136
|
+
if (logicalAndExprs.length === 1) {
|
|
137
|
+
return transformCSTtoAST(logicalAndExprs[0], transformers, ctx);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// fold left for left-associativity
|
|
141
|
+
let left = transformCSTtoAST(logicalAndExprs[0], transformers, ctx);
|
|
142
|
+
for (let i = 1; i < logicalAndExprs.length; i += 1) {
|
|
143
|
+
const right = transformCSTtoAST(logicalAndExprs[i], transformers, ctx);
|
|
144
|
+
left = {
|
|
145
|
+
type: 'LogicalOrExpr',
|
|
146
|
+
left,
|
|
147
|
+
right
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
['logical-and-expr'](node, ctx) {
|
|
152
|
+
const basicExprs = node.children.filter(({
|
|
153
|
+
type
|
|
154
|
+
}) => type === 'basic-expr');
|
|
155
|
+
if (basicExprs.length === 1) {
|
|
156
|
+
return transformCSTtoAST(basicExprs[0], transformers, ctx);
|
|
157
|
+
}
|
|
158
|
+
let left = transformCSTtoAST(basicExprs[0], transformers, ctx);
|
|
159
|
+
for (let i = 1; i < basicExprs.length; i += 1) {
|
|
160
|
+
const right = transformCSTtoAST(basicExprs[i], transformers, ctx);
|
|
161
|
+
left = {
|
|
162
|
+
type: 'LogicalAndExpr',
|
|
163
|
+
left,
|
|
164
|
+
right
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return left;
|
|
168
|
+
},
|
|
169
|
+
['basic-expr'](node, ctx) {
|
|
170
|
+
const child = node.children.find(({
|
|
171
|
+
type
|
|
172
|
+
}) => ['paren-expr', 'comparison-expr', 'test-expr'].includes(type));
|
|
173
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
174
|
+
},
|
|
175
|
+
['paren-expr'](node, ctx) {
|
|
176
|
+
const isNegated = node.children.some(child => child.type === 'logical-not-op');
|
|
177
|
+
const logicalExprCSTNode = node.children.find(child => child.type === 'logical-expr');
|
|
178
|
+
const logicalExpressionASTNode = transformCSTtoAST(logicalExprCSTNode, transformers, ctx);
|
|
179
|
+
if (isNegated) {
|
|
180
|
+
return {
|
|
181
|
+
type: 'LogicalNotExpr',
|
|
182
|
+
expression: logicalExpressionASTNode
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return logicalExpressionASTNode;
|
|
186
|
+
},
|
|
187
|
+
['test-expr'](node, ctx) {
|
|
188
|
+
const isNegated = node.children.some(({
|
|
189
|
+
type
|
|
190
|
+
}) => type === 'logical-not-op');
|
|
191
|
+
const expression = node.children.find(({
|
|
192
|
+
type
|
|
193
|
+
}) => ['filter-query', 'function-expr'].includes(type));
|
|
194
|
+
const testExpr = {
|
|
195
|
+
type: 'TestExpr',
|
|
196
|
+
expression: transformCSTtoAST(expression, transformers, ctx)
|
|
197
|
+
};
|
|
198
|
+
return isNegated ? {
|
|
199
|
+
type: 'LogicalNotExpr',
|
|
200
|
+
expression: testExpr
|
|
201
|
+
} : testExpr;
|
|
202
|
+
},
|
|
203
|
+
['filter-query'](node, ctx) {
|
|
204
|
+
const child = node.children.find(({
|
|
205
|
+
type
|
|
206
|
+
}) => ['rel-query', 'jsonpath-query'].includes(type));
|
|
207
|
+
return {
|
|
208
|
+
type: 'FilterQuery',
|
|
209
|
+
query: transformCSTtoAST(child, transformers, ctx)
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
['rel-query'](node, ctx) {
|
|
213
|
+
const segments = node.children.find(c => c.type === 'segments');
|
|
214
|
+
return {
|
|
215
|
+
type: 'RelQuery',
|
|
216
|
+
segments: segments ? segments.children.filter(n => n.type === 'segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx)) : []
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
['comparison-expr'](node, ctx) {
|
|
220
|
+
const children = node.children.filter(({
|
|
221
|
+
type
|
|
222
|
+
}) => ['comparable', 'comparison-op'].includes(type));
|
|
223
|
+
const [left, op, right] = children;
|
|
224
|
+
return {
|
|
225
|
+
type: 'ComparisonExpr',
|
|
226
|
+
left: transformCSTtoAST(left, transformers, ctx),
|
|
227
|
+
op: op.text,
|
|
228
|
+
right: transformCSTtoAST(right, transformers, ctx)
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
['literal'](node, ctx) {
|
|
232
|
+
const child = node.children.find(({
|
|
233
|
+
type
|
|
234
|
+
}) => ['number', 'string-literal', 'true', 'false', 'null'].includes(type));
|
|
235
|
+
if (child.type === 'string-literal') {
|
|
236
|
+
const stringLiteralASTNode = transformCSTtoAST(child, transformers, ctx);
|
|
237
|
+
return {
|
|
238
|
+
type: 'Literal',
|
|
239
|
+
value: stringLiteralASTNode.value
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
type: 'Literal',
|
|
244
|
+
value: decodeJSONValue(child.text)
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
['comparable'](node, ctx) {
|
|
248
|
+
const child = node.children.find(({
|
|
249
|
+
type
|
|
250
|
+
}) => ['singular-query', 'function-expr', 'literal'].includes(type));
|
|
251
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
252
|
+
},
|
|
253
|
+
['singular-query'](node, ctx) {
|
|
254
|
+
const child = node.children.find(({
|
|
255
|
+
type
|
|
256
|
+
}) => ['rel-singular-query', 'abs-singular-query'].includes(type));
|
|
257
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
258
|
+
},
|
|
259
|
+
['rel-singular-query'](node, ctx) {
|
|
260
|
+
const segmentsNode = node.children.find(({
|
|
261
|
+
type
|
|
262
|
+
}) => type === 'singular-query-segments');
|
|
263
|
+
const segments = segmentsNode ? segmentsNode.children.filter(({
|
|
264
|
+
type
|
|
265
|
+
}) => ['name-segment', 'index-segment'].includes(type)).map(segNode => ({
|
|
266
|
+
type: 'SingularQuerySegment',
|
|
267
|
+
selector: transformCSTtoAST(segNode, transformers, ctx)
|
|
268
|
+
})) : [];
|
|
269
|
+
return {
|
|
270
|
+
type: 'RelSingularQuery',
|
|
271
|
+
segments
|
|
272
|
+
};
|
|
273
|
+
},
|
|
274
|
+
['abs-singular-query'](node, ctx) {
|
|
275
|
+
const segmentsNode = node.children.find(({
|
|
276
|
+
type
|
|
277
|
+
}) => type === 'singular-query-segments');
|
|
278
|
+
const segments = segmentsNode ? segmentsNode.children.filter(({
|
|
279
|
+
type
|
|
280
|
+
}) => ['name-segment', 'index-segment'].includes(type)).map(segNode => ({
|
|
281
|
+
type: 'SingularQuerySegment',
|
|
282
|
+
selector: transformCSTtoAST(segNode, transformers, ctx)
|
|
283
|
+
})) : [];
|
|
284
|
+
return {
|
|
285
|
+
type: 'AbsSingularQuery',
|
|
286
|
+
segments
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
['name-segment'](node, ctx) {
|
|
290
|
+
const child = node.children.find(({
|
|
291
|
+
type
|
|
292
|
+
}) => ['name-selector', 'member-name-shorthand'].includes(type));
|
|
293
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
294
|
+
},
|
|
295
|
+
['index-segment'](node, ctx) {
|
|
296
|
+
const child = node.children.find(({
|
|
297
|
+
type
|
|
298
|
+
}) => type === 'index-selector');
|
|
299
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
300
|
+
},
|
|
301
|
+
/**
|
|
302
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.4
|
|
303
|
+
*/
|
|
304
|
+
['function-expr'](node, ctx) {
|
|
305
|
+
const name = node.children.find(({
|
|
306
|
+
type
|
|
307
|
+
}) => type === 'function-name');
|
|
308
|
+
const args = node.children.filter(({
|
|
309
|
+
type
|
|
310
|
+
}) => type === 'function-argument');
|
|
311
|
+
return {
|
|
312
|
+
type: 'FunctionExpr',
|
|
313
|
+
name: name.text,
|
|
314
|
+
arguments: args.map(arg => transformCSTtoAST(arg, transformers, ctx))
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
['function-argument'](node, ctx) {
|
|
318
|
+
const child = node.children.find(({
|
|
319
|
+
type
|
|
320
|
+
}) => ['logical-expr', 'function-expr', 'filter-query', 'literal'].includes(type));
|
|
321
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
322
|
+
},
|
|
323
|
+
/**
|
|
324
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
|
|
325
|
+
*/
|
|
326
|
+
['child-segment'](node, ctx) {
|
|
327
|
+
const child = node.children.find(({
|
|
328
|
+
type
|
|
329
|
+
}) => ['bracketed-selection', 'wildcard-selector', 'member-name-shorthand'].includes(type));
|
|
330
|
+
return {
|
|
331
|
+
type: 'ChildSegment',
|
|
332
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
['bracketed-selection'](node, ctx) {
|
|
336
|
+
return {
|
|
337
|
+
type: 'BracketedSelection',
|
|
338
|
+
selectors: node.children.filter(({
|
|
339
|
+
type
|
|
340
|
+
}) => type === 'selector').map(selectorNode => transformCSTtoAST(selectorNode, transformers, ctx))
|
|
341
|
+
};
|
|
342
|
+
},
|
|
343
|
+
['member-name-shorthand'](node) {
|
|
344
|
+
return {
|
|
345
|
+
type: 'NameSelector',
|
|
346
|
+
value: node.text,
|
|
347
|
+
format: 'shorthand'
|
|
348
|
+
};
|
|
349
|
+
},
|
|
350
|
+
/**
|
|
351
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
|
|
352
|
+
*/
|
|
353
|
+
['descendant-segment'](node, ctx) {
|
|
354
|
+
const child = node.children.find(({
|
|
355
|
+
type
|
|
356
|
+
}) => ['bracketed-selection', 'wildcard-selector', 'member-name-shorthand'].includes(type));
|
|
357
|
+
return {
|
|
358
|
+
type: 'DescendantSegment',
|
|
359
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
/**
|
|
363
|
+
* https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
|
|
364
|
+
*/
|
|
365
|
+
['normalized-path'](node, ctx) {
|
|
366
|
+
return {
|
|
367
|
+
type: 'JsonPathQuery',
|
|
368
|
+
segments: node.children.filter(({
|
|
369
|
+
type
|
|
370
|
+
}) => type === 'normal-index-segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx))
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
['normal-index-segment'](node, ctx) {
|
|
374
|
+
const child = node.children.find(({
|
|
375
|
+
type
|
|
376
|
+
}) => type === 'normal-selector');
|
|
377
|
+
return {
|
|
378
|
+
type: 'ChildSegment',
|
|
379
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
380
|
+
};
|
|
381
|
+
},
|
|
382
|
+
['normal-selector'](node, ctx) {
|
|
383
|
+
const child = node.children.find(({
|
|
384
|
+
type
|
|
385
|
+
}) => ['normal-name-selector', 'normal-index-selector'].includes(type));
|
|
386
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
387
|
+
},
|
|
388
|
+
['normal-name-selector'](node) {
|
|
389
|
+
const child = node.children.find(({
|
|
390
|
+
type
|
|
391
|
+
}) => type === 'normal-single-quoted');
|
|
392
|
+
return {
|
|
393
|
+
type: 'NameSelector',
|
|
394
|
+
value: child ? decodeSingleQuotedString(child.text) : '',
|
|
395
|
+
format: 'single-quoted'
|
|
396
|
+
};
|
|
397
|
+
},
|
|
398
|
+
['normal-index-selector'](node) {
|
|
399
|
+
return {
|
|
400
|
+
type: 'IndexSelector',
|
|
401
|
+
value: decodeInteger(node.text)
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
export default transformers;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import CSTTranslator from "./CSTTranslator.mjs";
|
|
2
|
+
class CSTOptimizedTranslator extends CSTTranslator {
|
|
3
|
+
collapsibleTypes = ['single-quoted', 'double-quoted', 'normal-single-quoted'];
|
|
4
|
+
droppableTypes = ['text', 'segments', 'singular-query-segments'];
|
|
5
|
+
constructor({
|
|
6
|
+
collapsibleTypes,
|
|
7
|
+
droppableTypes
|
|
8
|
+
} = {}) {
|
|
9
|
+
super();
|
|
10
|
+
if (Array.isArray(collapsibleTypes)) {
|
|
11
|
+
this.collapsibleTypes = collapsibleTypes;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(droppableTypes)) {
|
|
14
|
+
this.droppableTypes = droppableTypes;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
getTree() {
|
|
18
|
+
const options = {
|
|
19
|
+
optimize: true,
|
|
20
|
+
collapsibleTypes: this.collapsibleTypes,
|
|
21
|
+
droppableTypes: this.droppableTypes
|
|
22
|
+
};
|
|
23
|
+
const data = {
|
|
24
|
+
stack: [],
|
|
25
|
+
root: null,
|
|
26
|
+
options
|
|
27
|
+
};
|
|
28
|
+
this.translate(data);
|
|
29
|
+
delete data.stack;
|
|
30
|
+
delete data.options;
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export default CSTOptimizedTranslator;
|