@swaggerexpert/jsonpath 2.5.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -24
- package/cjs/index.cjs +5 -3
- package/cjs/parse/index.cjs +2 -2
- package/cjs/parse/translators/ASTTranslator/decoders.cjs +16 -0
- package/cjs/parse/translators/ASTTranslator/index.cjs +16 -0
- package/cjs/parse/translators/ASTTranslator/transformers.cjs +410 -0
- package/cjs/parse/translators/CSTOptimizedTranslator.cjs +5 -1
- package/es/index.mjs +1 -0
- package/es/parse/index.mjs +2 -2
- package/es/parse/translators/ASTTranslator/decoders.mjs +9 -0
- package/es/parse/translators/ASTTranslator/index.mjs +9 -0
- package/es/parse/translators/ASTTranslator/transformers.mjs +404 -0
- package/es/parse/translators/CSTOptimizedTranslator.mjs +5 -1
- package/package.json +1 -1
- package/types/index.d.ts +154 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ The development of this library contributed to the identification and formal sub
|
|
|
38
38
|
- [Translators](#translators)
|
|
39
39
|
- [CST](#cst-translator)
|
|
40
40
|
- [CST Optimized](#cst-optimized-translator)
|
|
41
|
+
- [AST](#ast-translator)
|
|
41
42
|
- [XML](#xml-translator)
|
|
42
43
|
- [Statistics](#statistics)
|
|
43
44
|
- [Tracing](#tracing)
|
|
@@ -118,37 +119,16 @@ parse("$['\\u000b']", { normalized: true });
|
|
|
118
119
|
###### CST translator
|
|
119
120
|
|
|
120
121
|
[Concrete Syntax Tree](https://en.wikipedia.org/wiki/Parse_tree) (Parse tree) representation is available on parse result
|
|
121
|
-
|
|
122
|
+
when instance of `CSTTranslator` is provided via a `translator` option to the `parse` function.
|
|
122
123
|
CST is suitable to be consumed by other tools like IDEs, editors, etc...
|
|
123
124
|
|
|
124
|
-
```js
|
|
125
|
-
import { parse } from '@swaggerexpert/jsonpath';
|
|
126
|
-
|
|
127
|
-
const { tree: CST } = parse('$.store.book[0].title');
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
or
|
|
131
|
-
|
|
132
125
|
```js
|
|
133
126
|
import { parse, CSTTranslator } from '@swaggerexpert/jsonpath';
|
|
134
127
|
|
|
135
128
|
const { tree: CST } = parse('$.store.book[0].title', { translator: new CSTTranslator() });
|
|
136
129
|
```
|
|
137
130
|
|
|
138
|
-
CST tree has
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
interface CSTTree {
|
|
142
|
-
readonly root: CSTNode;
|
|
143
|
-
}
|
|
144
|
-
interface CSTNode {
|
|
145
|
-
readonly type: string,
|
|
146
|
-
readonly text: string,
|
|
147
|
-
readonly start: number,
|
|
148
|
-
readonly length: number,
|
|
149
|
-
readonly children: CSTNode[],
|
|
150
|
-
}
|
|
151
|
-
```
|
|
131
|
+
CST tree has a shape documented by [TypeScript typings (CSTTree)](https://github.com/swaggerexpert/jsonpath/blob/main/types/index.d.ts).
|
|
152
132
|
|
|
153
133
|
###### CST Optimized translator
|
|
154
134
|
|
|
@@ -162,6 +142,29 @@ import { parse, CSTOptimizedTranslator } from '@swaggerexpert/jsonpath';
|
|
|
162
142
|
const { tree: CST } = parse('$.store.book[0].title', { translator: new CSTOptimizedTranslator() });
|
|
163
143
|
```
|
|
164
144
|
|
|
145
|
+
###### AST translator
|
|
146
|
+
|
|
147
|
+
**Default translator**. [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) representation is available on parse result
|
|
148
|
+
by default or when instance of `ASTTranslator` is provided via a `translator` option to the `parse` function.
|
|
149
|
+
AST is suitable to be consumed by implementations that need to analyze the structure of the JSONPath expression
|
|
150
|
+
or for building a custom JSONPath evaluation engine.
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
import { parse } from '@swaggerexpert/jsonpath';
|
|
154
|
+
|
|
155
|
+
const { tree: AST } = parse('$.store.book[0].title');
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
or
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
import { parse, ASTTranslator } from '@swaggerexpert/jsonpath';
|
|
162
|
+
|
|
163
|
+
const { tree: AST } = parse('$.store.book[0].title', { translator: new ASTTranslator() });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
AST tree has a shape documented by [TypeScript typings (ASTTree)](https://github.com/swaggerexpert/jsonpath/blob/main/types/index.d.ts).
|
|
167
|
+
|
|
165
168
|
###### XML translator
|
|
166
169
|
|
|
167
170
|
```js
|
|
@@ -214,7 +217,7 @@ test('$.store.book[0].title'); // => true
|
|
|
214
217
|
test('$$'); // => false
|
|
215
218
|
```
|
|
216
219
|
|
|
217
|
-
Normalized paths can be validated by setting `normalized` option to `true`.
|
|
220
|
+
**Normalized paths** can be validated by setting `normalized` option to `true`.
|
|
218
221
|
|
|
219
222
|
```js
|
|
220
223
|
import { test } from '@swaggerexpert/jsonpath';
|
package/cjs/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.test = exports.parse = exports.XMLTranslator = exports.Grammar = exports.CSTTranslator = exports.CSTOptimizedTranslator = void 0;
|
|
4
|
+
exports.test = exports.parse = exports.XMLTranslator = exports.Grammar = exports.CSTTranslator = exports.CSTOptimizedTranslator = exports.ASTTranslator = void 0;
|
|
5
5
|
var _grammar = _interopRequireDefault(require("./grammar.cjs"));
|
|
6
6
|
exports.Grammar = _grammar.default;
|
|
7
7
|
var _index = _interopRequireDefault(require("./parse/index.cjs"));
|
|
@@ -10,8 +10,10 @@ var _CSTTranslator = _interopRequireDefault(require("./parse/translators/CSTTran
|
|
|
10
10
|
exports.CSTTranslator = _CSTTranslator.default;
|
|
11
11
|
var _CSTOptimizedTranslator = _interopRequireDefault(require("./parse/translators/CSTOptimizedTranslator.cjs"));
|
|
12
12
|
exports.CSTOptimizedTranslator = _CSTOptimizedTranslator.default;
|
|
13
|
+
var _index2 = _interopRequireDefault(require("./parse/translators/ASTTranslator/index.cjs"));
|
|
14
|
+
exports.ASTTranslator = _index2.default;
|
|
13
15
|
var _XMLTranslator = _interopRequireDefault(require("./parse/translators/XMLTranslator.cjs"));
|
|
14
16
|
exports.XMLTranslator = _XMLTranslator.default;
|
|
15
|
-
var
|
|
16
|
-
exports.test =
|
|
17
|
+
var _index3 = _interopRequireDefault(require("./test/index.cjs"));
|
|
18
|
+
exports.test = _index3.default;
|
|
17
19
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
package/cjs/parse/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ exports.__esModule = true;
|
|
|
4
4
|
exports.default = void 0;
|
|
5
5
|
var _apgLite = require("../apg-lite.cjs");
|
|
6
6
|
var _grammar = _interopRequireDefault(require("../grammar.cjs"));
|
|
7
|
-
var
|
|
7
|
+
var _index = _interopRequireDefault(require("./translators/ASTTranslator/index.cjs"));
|
|
8
8
|
var _JSONPathParseError = _interopRequireDefault(require("../errors/JSONPathParseError.cjs"));
|
|
9
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
10
|
const grammar = new _grammar.default();
|
|
@@ -12,7 +12,7 @@ const parse = (jsonPath, {
|
|
|
12
12
|
normalized = false,
|
|
13
13
|
stats = false,
|
|
14
14
|
trace = false,
|
|
15
|
-
translator = new
|
|
15
|
+
translator = new _index.default()
|
|
16
16
|
} = {}) => {
|
|
17
17
|
if (typeof jsonPath !== 'string') {
|
|
18
18
|
throw new TypeError('JSONPath must be a string');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.decodeString = exports.decodeJSONValue = exports.decodeInteger = void 0;
|
|
5
|
+
const decodeString = str => {
|
|
6
|
+
return JSON.parse(`"${str.replace(/"/g, '\\"')}"`);
|
|
7
|
+
};
|
|
8
|
+
exports.decodeString = decodeString;
|
|
9
|
+
const decodeInteger = str => {
|
|
10
|
+
return parseInt(str, 10);
|
|
11
|
+
};
|
|
12
|
+
exports.decodeInteger = decodeInteger;
|
|
13
|
+
const decodeJSONValue = str => {
|
|
14
|
+
return JSON.parse(str);
|
|
15
|
+
};
|
|
16
|
+
exports.decodeJSONValue = decodeJSONValue;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = void 0;
|
|
5
|
+
var _CSTOptimizedTranslator = _interopRequireDefault(require("../CSTOptimizedTranslator.cjs"));
|
|
6
|
+
var _transformers = _interopRequireWildcard(require("./transformers.cjs"));
|
|
7
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
8
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
class ASTTranslator extends _CSTOptimizedTranslator.default {
|
|
11
|
+
getTree() {
|
|
12
|
+
const cst = super.getTree();
|
|
13
|
+
return (0, _transformers.transformCSTtoAST)(cst.root, _transformers.default);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
var _default = exports.default = ASTTranslator;
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.transformCSTtoAST = exports.default = void 0;
|
|
5
|
+
var _JSONPathParseError = _interopRequireDefault(require("../../../errors/JSONPathParseError.cjs"));
|
|
6
|
+
var _decoders = require("./decoders.cjs");
|
|
7
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
const transformCSTtoAST = (node, transformerMap, ctx = {
|
|
9
|
+
parent: null,
|
|
10
|
+
path: []
|
|
11
|
+
}) => {
|
|
12
|
+
const transformer = transformerMap[node.type];
|
|
13
|
+
if (!transformer) {
|
|
14
|
+
throw new _JSONPathParseError.default(`No transformer for CST node type: ${node.type}`);
|
|
15
|
+
}
|
|
16
|
+
const nextCtx = {
|
|
17
|
+
parent: node,
|
|
18
|
+
path: [...ctx.path, node]
|
|
19
|
+
};
|
|
20
|
+
return transformer(node, nextCtx);
|
|
21
|
+
};
|
|
22
|
+
exports.transformCSTtoAST = transformCSTtoAST;
|
|
23
|
+
const transformers = {
|
|
24
|
+
/**
|
|
25
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.1.1
|
|
26
|
+
*/
|
|
27
|
+
['jsonpath-query'](node, ctx) {
|
|
28
|
+
const segments = node.children.find(c => c.type === 'segments');
|
|
29
|
+
return {
|
|
30
|
+
type: 'JsonPathQuery',
|
|
31
|
+
segments: segments ? segments.children.filter(({
|
|
32
|
+
type
|
|
33
|
+
}) => type === 'segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx)) : []
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5
|
|
38
|
+
*/
|
|
39
|
+
segment(node, ctx) {
|
|
40
|
+
const child = node.children.find(({
|
|
41
|
+
type
|
|
42
|
+
}) => ['child-segment', 'descendant-segment'].includes(type));
|
|
43
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3
|
|
47
|
+
*/
|
|
48
|
+
selector(node, ctx) {
|
|
49
|
+
const child = node.children.find(({
|
|
50
|
+
type
|
|
51
|
+
}) => ['name-selector', 'wildcard-selector', 'slice-selector', 'index-selector', 'filter-selector'].includes(type));
|
|
52
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
53
|
+
},
|
|
54
|
+
/**
|
|
55
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.1.1
|
|
56
|
+
*/
|
|
57
|
+
['name-selector'](node, ctx) {
|
|
58
|
+
const stringLiteralCSTNode = node.children.find(({
|
|
59
|
+
type
|
|
60
|
+
}) => type === 'string-literal');
|
|
61
|
+
const stringLiteralASTNode = transformCSTtoAST(stringLiteralCSTNode, transformers, ctx);
|
|
62
|
+
return {
|
|
63
|
+
type: 'NameSelector',
|
|
64
|
+
value: stringLiteralASTNode.value,
|
|
65
|
+
format: stringLiteralASTNode.format
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
['string-literal'](node) {
|
|
69
|
+
const isSingleQuoted = node.children.find(({
|
|
70
|
+
type,
|
|
71
|
+
text
|
|
72
|
+
}) => type === 'text' && text === "'");
|
|
73
|
+
const quoted = node.children.find(({
|
|
74
|
+
type
|
|
75
|
+
}) => ['double-quoted', 'single-quoted'].includes(type));
|
|
76
|
+
return {
|
|
77
|
+
type: 'StringLiteral',
|
|
78
|
+
value: quoted ? (0, _decoders.decodeString)(quoted.text) : '',
|
|
79
|
+
format: isSingleQuoted ? 'single-quoted' : 'double-quoted'
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
|
|
84
|
+
*/
|
|
85
|
+
['wildcard-selector']() {
|
|
86
|
+
return {
|
|
87
|
+
type: 'WildcardSelector'
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.3.1
|
|
92
|
+
*/
|
|
93
|
+
['index-selector'](node) {
|
|
94
|
+
return {
|
|
95
|
+
type: 'IndexSelector',
|
|
96
|
+
value: (0, _decoders.decodeInteger)(node.text)
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
/**
|
|
100
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
|
|
101
|
+
*/
|
|
102
|
+
['slice-selector'](node) {
|
|
103
|
+
const start = node.children.find(({
|
|
104
|
+
type
|
|
105
|
+
}) => type === 'start');
|
|
106
|
+
const end = node.children.find(({
|
|
107
|
+
type
|
|
108
|
+
}) => type === 'end');
|
|
109
|
+
const step = node.children.find(({
|
|
110
|
+
type
|
|
111
|
+
}) => type === 'step');
|
|
112
|
+
return {
|
|
113
|
+
type: 'SliceSelector',
|
|
114
|
+
start: start ? (0, _decoders.decodeInteger)(start.text) : null,
|
|
115
|
+
end: end ? (0, _decoders.decodeInteger)(end.text) : null,
|
|
116
|
+
step: step ? (0, _decoders.decodeInteger)(step.text) : null
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
|
|
121
|
+
*/
|
|
122
|
+
['filter-selector'](node, ctx) {
|
|
123
|
+
const child = node.children.find(({
|
|
124
|
+
type
|
|
125
|
+
}) => type === 'logical-expr');
|
|
126
|
+
return {
|
|
127
|
+
type: 'FilterSelector',
|
|
128
|
+
expression: transformCSTtoAST(child, transformers, ctx)
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
['logical-expr'](node, ctx) {
|
|
132
|
+
const child = node.children.find(({
|
|
133
|
+
type
|
|
134
|
+
}) => type === 'logical-or-expr');
|
|
135
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
136
|
+
},
|
|
137
|
+
['logical-or-expr'](node, ctx) {
|
|
138
|
+
const logicalAndExprs = node.children.filter(({
|
|
139
|
+
type
|
|
140
|
+
}) => type === 'logical-and-expr');
|
|
141
|
+
if (logicalAndExprs.length === 1) {
|
|
142
|
+
return transformCSTtoAST(logicalAndExprs[0], transformers, ctx);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// fold left for left-associativity
|
|
146
|
+
let left = transformCSTtoAST(logicalAndExprs[0], transformers, ctx);
|
|
147
|
+
for (let i = 1; i < logicalAndExprs.length; i += 1) {
|
|
148
|
+
const right = transformCSTtoAST(logicalAndExprs[i], transformers, ctx);
|
|
149
|
+
left = {
|
|
150
|
+
type: 'LogicalOrExpr',
|
|
151
|
+
left,
|
|
152
|
+
right
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
['logical-and-expr'](node, ctx) {
|
|
157
|
+
const basicExprs = node.children.filter(({
|
|
158
|
+
type
|
|
159
|
+
}) => type === 'basic-expr');
|
|
160
|
+
if (basicExprs.length === 1) {
|
|
161
|
+
return transformCSTtoAST(basicExprs[0], transformers, ctx);
|
|
162
|
+
}
|
|
163
|
+
let left = transformCSTtoAST(basicExprs[0], transformers, ctx);
|
|
164
|
+
for (let i = 1; i < basicExprs.length; i += 1) {
|
|
165
|
+
const right = transformCSTtoAST(basicExprs[i], transformers, ctx);
|
|
166
|
+
left = {
|
|
167
|
+
type: 'LogicalAndExpr',
|
|
168
|
+
left,
|
|
169
|
+
right
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return left;
|
|
173
|
+
},
|
|
174
|
+
['basic-expr'](node, ctx) {
|
|
175
|
+
const child = node.children.find(({
|
|
176
|
+
type
|
|
177
|
+
}) => ['paren-expr', 'comparison-expr', 'test-expr'].includes(type));
|
|
178
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
179
|
+
},
|
|
180
|
+
['paren-expr'](node, ctx) {
|
|
181
|
+
const isNegated = node.children.some(child => child.type === 'logical-not-op');
|
|
182
|
+
const logicalExprCSTNode = node.children.find(child => child.type === 'logical-expr');
|
|
183
|
+
const logicalExpressionASTNode = transformCSTtoAST(logicalExprCSTNode, transformers, ctx);
|
|
184
|
+
if (isNegated) {
|
|
185
|
+
return {
|
|
186
|
+
type: 'LogicalNotExpr',
|
|
187
|
+
expression: logicalExpressionASTNode
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return logicalExpressionASTNode;
|
|
191
|
+
},
|
|
192
|
+
['test-expr'](node, ctx) {
|
|
193
|
+
const isNegated = node.children.some(({
|
|
194
|
+
type
|
|
195
|
+
}) => type === 'logical-not-op');
|
|
196
|
+
const expression = node.children.find(({
|
|
197
|
+
type
|
|
198
|
+
}) => ['filter-query', 'function-expr'].includes(type));
|
|
199
|
+
const testExpr = {
|
|
200
|
+
type: 'TestExpr',
|
|
201
|
+
expression: transformCSTtoAST(expression, transformers, ctx)
|
|
202
|
+
};
|
|
203
|
+
return isNegated ? {
|
|
204
|
+
type: 'LogicalNotExpr',
|
|
205
|
+
expression: testExpr
|
|
206
|
+
} : testExpr;
|
|
207
|
+
},
|
|
208
|
+
['filter-query'](node, ctx) {
|
|
209
|
+
const child = node.children.find(({
|
|
210
|
+
type
|
|
211
|
+
}) => ['rel-query', 'jsonpath-query'].includes(type));
|
|
212
|
+
return {
|
|
213
|
+
type: 'FilterQuery',
|
|
214
|
+
query: transformCSTtoAST(child, transformers, ctx)
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
['rel-query'](node, ctx) {
|
|
218
|
+
const segments = node.children.find(c => c.type === 'segments');
|
|
219
|
+
return {
|
|
220
|
+
type: 'RelQuery',
|
|
221
|
+
segments: segments ? segments.children.filter(n => n.type === 'segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx)) : []
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
['comparison-expr'](node, ctx) {
|
|
225
|
+
const children = node.children.filter(({
|
|
226
|
+
type
|
|
227
|
+
}) => ['comparable', 'comparison-op'].includes(type));
|
|
228
|
+
const [left, op, right] = children;
|
|
229
|
+
return {
|
|
230
|
+
type: 'ComparisonExpr',
|
|
231
|
+
left: transformCSTtoAST(left, transformers, ctx),
|
|
232
|
+
op: op.text,
|
|
233
|
+
right: transformCSTtoAST(right, transformers, ctx)
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
['literal'](node, ctx) {
|
|
237
|
+
const child = node.children.find(({
|
|
238
|
+
type
|
|
239
|
+
}) => ['number', 'string-literal', 'true', 'false', 'null'].includes(type));
|
|
240
|
+
if (child.type === 'string-literal') {
|
|
241
|
+
const stringLiteralASTNode = transformCSTtoAST(child, transformers, ctx);
|
|
242
|
+
return {
|
|
243
|
+
type: 'Literal',
|
|
244
|
+
value: stringLiteralASTNode.value
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
type: 'Literal',
|
|
249
|
+
value: (0, _decoders.decodeJSONValue)(child.text)
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
['comparable'](node, ctx) {
|
|
253
|
+
const child = node.children.find(({
|
|
254
|
+
type
|
|
255
|
+
}) => ['singular-query', 'function-expr', 'literal'].includes(type));
|
|
256
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
257
|
+
},
|
|
258
|
+
['singular-query'](node, ctx) {
|
|
259
|
+
const child = node.children.find(({
|
|
260
|
+
type
|
|
261
|
+
}) => ['rel-singular-query', 'abs-singular-query'].includes(type));
|
|
262
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
263
|
+
},
|
|
264
|
+
['rel-singular-query'](node, ctx) {
|
|
265
|
+
const segmentsNode = node.children.find(({
|
|
266
|
+
type
|
|
267
|
+
}) => type === 'singular-query-segments');
|
|
268
|
+
const segments = segmentsNode ? segmentsNode.children.filter(({
|
|
269
|
+
type
|
|
270
|
+
}) => ['name-segment', 'index-segment'].includes(type)).map(segNode => ({
|
|
271
|
+
type: 'SingularQuerySegment',
|
|
272
|
+
selector: transformCSTtoAST(segNode, transformers, ctx)
|
|
273
|
+
})) : [];
|
|
274
|
+
return {
|
|
275
|
+
type: 'RelSingularQuery',
|
|
276
|
+
segments
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
['abs-singular-query'](node, ctx) {
|
|
280
|
+
const segmentsNode = node.children.find(({
|
|
281
|
+
type
|
|
282
|
+
}) => type === 'singular-query-segments');
|
|
283
|
+
const segments = segmentsNode ? segmentsNode.children.filter(({
|
|
284
|
+
type
|
|
285
|
+
}) => ['name-segment', 'index-segment'].includes(type)).map(segNode => ({
|
|
286
|
+
type: 'SingularQuerySegment',
|
|
287
|
+
selector: transformCSTtoAST(segNode, transformers, ctx)
|
|
288
|
+
})) : [];
|
|
289
|
+
return {
|
|
290
|
+
type: 'AbsSingularQuery',
|
|
291
|
+
segments
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
['name-segment'](node, ctx) {
|
|
295
|
+
const child = node.children.find(({
|
|
296
|
+
type
|
|
297
|
+
}) => ['name-selector', 'member-name-shorthand'].includes(type));
|
|
298
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
299
|
+
},
|
|
300
|
+
['index-segment'](node, ctx) {
|
|
301
|
+
const child = node.children.find(({
|
|
302
|
+
type
|
|
303
|
+
}) => type === 'index-selector');
|
|
304
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.4
|
|
308
|
+
*/
|
|
309
|
+
['function-expr'](node, ctx) {
|
|
310
|
+
const name = node.children.find(({
|
|
311
|
+
type
|
|
312
|
+
}) => type === 'function-name');
|
|
313
|
+
const args = node.children.filter(({
|
|
314
|
+
type
|
|
315
|
+
}) => type === 'function-argument');
|
|
316
|
+
return {
|
|
317
|
+
type: 'FunctionExpr',
|
|
318
|
+
name: name.text,
|
|
319
|
+
arguments: args.map(arg => transformCSTtoAST(arg, transformers, ctx))
|
|
320
|
+
};
|
|
321
|
+
},
|
|
322
|
+
['function-argument'](node, ctx) {
|
|
323
|
+
const child = node.children.find(({
|
|
324
|
+
type
|
|
325
|
+
}) => ['logical-expr', 'function-expr', 'filter-query', 'literal'].includes(type));
|
|
326
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
327
|
+
},
|
|
328
|
+
/**
|
|
329
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
|
|
330
|
+
*/
|
|
331
|
+
['child-segment'](node, ctx) {
|
|
332
|
+
const child = node.children.find(({
|
|
333
|
+
type
|
|
334
|
+
}) => ['bracketed-selection', 'wildcard-selector', 'member-name-shorthand'].includes(type));
|
|
335
|
+
return {
|
|
336
|
+
type: 'ChildSegment',
|
|
337
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
['bracketed-selection'](node, ctx) {
|
|
341
|
+
return {
|
|
342
|
+
type: 'BracketedSelection',
|
|
343
|
+
selectors: node.children.filter(({
|
|
344
|
+
type
|
|
345
|
+
}) => type === 'selector').map(selectorNode => transformCSTtoAST(selectorNode, transformers, ctx))
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
['member-name-shorthand'](node) {
|
|
349
|
+
return {
|
|
350
|
+
type: 'NameSelector',
|
|
351
|
+
value: node.text,
|
|
352
|
+
format: 'shorthand'
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
/**
|
|
356
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
|
|
357
|
+
*/
|
|
358
|
+
['descendant-segment'](node, ctx) {
|
|
359
|
+
const child = node.children.find(({
|
|
360
|
+
type
|
|
361
|
+
}) => ['bracketed-selection', 'wildcard-selector', 'member-name-shorthand'].includes(type));
|
|
362
|
+
return {
|
|
363
|
+
type: 'DescendantSegment',
|
|
364
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
365
|
+
};
|
|
366
|
+
},
|
|
367
|
+
/**
|
|
368
|
+
* https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
|
|
369
|
+
*/
|
|
370
|
+
['normalized-path'](node, ctx) {
|
|
371
|
+
return {
|
|
372
|
+
type: 'JsonPathQuery',
|
|
373
|
+
segments: node.children.filter(({
|
|
374
|
+
type
|
|
375
|
+
}) => type === 'normal-index-segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx))
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
['normal-index-segment'](node, ctx) {
|
|
379
|
+
const child = node.children.find(({
|
|
380
|
+
type
|
|
381
|
+
}) => type === 'normal-selector');
|
|
382
|
+
return {
|
|
383
|
+
type: 'ChildSegment',
|
|
384
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
385
|
+
};
|
|
386
|
+
},
|
|
387
|
+
['normal-selector'](node, ctx) {
|
|
388
|
+
const child = node.children.find(({
|
|
389
|
+
type
|
|
390
|
+
}) => ['normal-name-selector', 'normal-index-selector'].includes(type));
|
|
391
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
392
|
+
},
|
|
393
|
+
['normal-name-selector'](node) {
|
|
394
|
+
const child = node.children.find(({
|
|
395
|
+
type
|
|
396
|
+
}) => type === 'normal-single-quoted');
|
|
397
|
+
return {
|
|
398
|
+
type: 'NameSelector',
|
|
399
|
+
value: child ? (0, _decoders.decodeString)(child.text) : '',
|
|
400
|
+
format: 'single-quoted'
|
|
401
|
+
};
|
|
402
|
+
},
|
|
403
|
+
['normal-index-selector'](node) {
|
|
404
|
+
return {
|
|
405
|
+
type: 'IndexSelector',
|
|
406
|
+
value: (0, _decoders.decodeInteger)(node.text)
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
var _default = exports.default = transformers;
|
|
@@ -8,12 +8,16 @@ class CSTOptimizedTranslator extends _CSTTranslator.default {
|
|
|
8
8
|
collapsibleTypes = ['single-quoted', 'double-quoted', 'normal-single-quoted'];
|
|
9
9
|
droppableTypes = ['text', 'segments', 'singular-query-segments'];
|
|
10
10
|
constructor({
|
|
11
|
-
collapsibleTypes
|
|
11
|
+
collapsibleTypes,
|
|
12
|
+
droppableTypes
|
|
12
13
|
} = {}) {
|
|
13
14
|
super();
|
|
14
15
|
if (Array.isArray(collapsibleTypes)) {
|
|
15
16
|
this.collapsibleTypes = collapsibleTypes;
|
|
16
17
|
}
|
|
18
|
+
if (Array.isArray(droppableTypes)) {
|
|
19
|
+
this.droppableTypes = droppableTypes;
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
getTree() {
|
|
19
23
|
const options = {
|
package/es/index.mjs
CHANGED
|
@@ -2,5 +2,6 @@ export { default as Grammar } from "./grammar.mjs";
|
|
|
2
2
|
export { default as parse } from "./parse/index.mjs";
|
|
3
3
|
export { default as CSTTranslator } from "./parse/translators/CSTTranslator.mjs";
|
|
4
4
|
export { default as CSTOptimizedTranslator } from "./parse/translators/CSTOptimizedTranslator.mjs";
|
|
5
|
+
export { default as ASTTranslator } from "./parse/translators/ASTTranslator/index.mjs";
|
|
5
6
|
export { default as XMLTranslator } from "./parse/translators/XMLTranslator.mjs";
|
|
6
7
|
export { default as test } from "./test/index.mjs";
|
package/es/parse/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Parser, Stats, Trace } from 'apg-lite';
|
|
2
2
|
import Grammar from "../grammar.mjs";
|
|
3
|
-
import
|
|
3
|
+
import ASTTranslator from "./translators/ASTTranslator/index.mjs";
|
|
4
4
|
import JSONPathParseError from "../errors/JSONPathParseError.mjs";
|
|
5
5
|
const grammar = new Grammar();
|
|
6
6
|
const parse = (jsonPath, {
|
|
7
7
|
normalized = false,
|
|
8
8
|
stats = false,
|
|
9
9
|
trace = false,
|
|
10
|
-
translator = new
|
|
10
|
+
translator = new ASTTranslator()
|
|
11
11
|
} = {}) => {
|
|
12
12
|
if (typeof jsonPath !== 'string') {
|
|
13
13
|
throw new TypeError('JSONPath must be a string');
|
|
@@ -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,404 @@
|
|
|
1
|
+
import JSONPathParseError from "../../../errors/JSONPathParseError.mjs";
|
|
2
|
+
import { decodeString, 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
|
+
return {
|
|
71
|
+
type: 'StringLiteral',
|
|
72
|
+
value: quoted ? decodeString(quoted.text) : '',
|
|
73
|
+
format: isSingleQuoted ? 'single-quoted' : 'double-quoted'
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
|
|
78
|
+
*/
|
|
79
|
+
['wildcard-selector']() {
|
|
80
|
+
return {
|
|
81
|
+
type: 'WildcardSelector'
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
/**
|
|
85
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.3.1
|
|
86
|
+
*/
|
|
87
|
+
['index-selector'](node) {
|
|
88
|
+
return {
|
|
89
|
+
type: 'IndexSelector',
|
|
90
|
+
value: decodeInteger(node.text)
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
|
|
95
|
+
*/
|
|
96
|
+
['slice-selector'](node) {
|
|
97
|
+
const start = node.children.find(({
|
|
98
|
+
type
|
|
99
|
+
}) => type === 'start');
|
|
100
|
+
const end = node.children.find(({
|
|
101
|
+
type
|
|
102
|
+
}) => type === 'end');
|
|
103
|
+
const step = node.children.find(({
|
|
104
|
+
type
|
|
105
|
+
}) => type === 'step');
|
|
106
|
+
return {
|
|
107
|
+
type: 'SliceSelector',
|
|
108
|
+
start: start ? decodeInteger(start.text) : null,
|
|
109
|
+
end: end ? decodeInteger(end.text) : null,
|
|
110
|
+
step: step ? decodeInteger(step.text) : null
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
/**
|
|
114
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
|
|
115
|
+
*/
|
|
116
|
+
['filter-selector'](node, ctx) {
|
|
117
|
+
const child = node.children.find(({
|
|
118
|
+
type
|
|
119
|
+
}) => type === 'logical-expr');
|
|
120
|
+
return {
|
|
121
|
+
type: 'FilterSelector',
|
|
122
|
+
expression: transformCSTtoAST(child, transformers, ctx)
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
['logical-expr'](node, ctx) {
|
|
126
|
+
const child = node.children.find(({
|
|
127
|
+
type
|
|
128
|
+
}) => type === 'logical-or-expr');
|
|
129
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
130
|
+
},
|
|
131
|
+
['logical-or-expr'](node, ctx) {
|
|
132
|
+
const logicalAndExprs = node.children.filter(({
|
|
133
|
+
type
|
|
134
|
+
}) => type === 'logical-and-expr');
|
|
135
|
+
if (logicalAndExprs.length === 1) {
|
|
136
|
+
return transformCSTtoAST(logicalAndExprs[0], transformers, ctx);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// fold left for left-associativity
|
|
140
|
+
let left = transformCSTtoAST(logicalAndExprs[0], transformers, ctx);
|
|
141
|
+
for (let i = 1; i < logicalAndExprs.length; i += 1) {
|
|
142
|
+
const right = transformCSTtoAST(logicalAndExprs[i], transformers, ctx);
|
|
143
|
+
left = {
|
|
144
|
+
type: 'LogicalOrExpr',
|
|
145
|
+
left,
|
|
146
|
+
right
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
['logical-and-expr'](node, ctx) {
|
|
151
|
+
const basicExprs = node.children.filter(({
|
|
152
|
+
type
|
|
153
|
+
}) => type === 'basic-expr');
|
|
154
|
+
if (basicExprs.length === 1) {
|
|
155
|
+
return transformCSTtoAST(basicExprs[0], transformers, ctx);
|
|
156
|
+
}
|
|
157
|
+
let left = transformCSTtoAST(basicExprs[0], transformers, ctx);
|
|
158
|
+
for (let i = 1; i < basicExprs.length; i += 1) {
|
|
159
|
+
const right = transformCSTtoAST(basicExprs[i], transformers, ctx);
|
|
160
|
+
left = {
|
|
161
|
+
type: 'LogicalAndExpr',
|
|
162
|
+
left,
|
|
163
|
+
right
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return left;
|
|
167
|
+
},
|
|
168
|
+
['basic-expr'](node, ctx) {
|
|
169
|
+
const child = node.children.find(({
|
|
170
|
+
type
|
|
171
|
+
}) => ['paren-expr', 'comparison-expr', 'test-expr'].includes(type));
|
|
172
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
173
|
+
},
|
|
174
|
+
['paren-expr'](node, ctx) {
|
|
175
|
+
const isNegated = node.children.some(child => child.type === 'logical-not-op');
|
|
176
|
+
const logicalExprCSTNode = node.children.find(child => child.type === 'logical-expr');
|
|
177
|
+
const logicalExpressionASTNode = transformCSTtoAST(logicalExprCSTNode, transformers, ctx);
|
|
178
|
+
if (isNegated) {
|
|
179
|
+
return {
|
|
180
|
+
type: 'LogicalNotExpr',
|
|
181
|
+
expression: logicalExpressionASTNode
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return logicalExpressionASTNode;
|
|
185
|
+
},
|
|
186
|
+
['test-expr'](node, ctx) {
|
|
187
|
+
const isNegated = node.children.some(({
|
|
188
|
+
type
|
|
189
|
+
}) => type === 'logical-not-op');
|
|
190
|
+
const expression = node.children.find(({
|
|
191
|
+
type
|
|
192
|
+
}) => ['filter-query', 'function-expr'].includes(type));
|
|
193
|
+
const testExpr = {
|
|
194
|
+
type: 'TestExpr',
|
|
195
|
+
expression: transformCSTtoAST(expression, transformers, ctx)
|
|
196
|
+
};
|
|
197
|
+
return isNegated ? {
|
|
198
|
+
type: 'LogicalNotExpr',
|
|
199
|
+
expression: testExpr
|
|
200
|
+
} : testExpr;
|
|
201
|
+
},
|
|
202
|
+
['filter-query'](node, ctx) {
|
|
203
|
+
const child = node.children.find(({
|
|
204
|
+
type
|
|
205
|
+
}) => ['rel-query', 'jsonpath-query'].includes(type));
|
|
206
|
+
return {
|
|
207
|
+
type: 'FilterQuery',
|
|
208
|
+
query: transformCSTtoAST(child, transformers, ctx)
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
['rel-query'](node, ctx) {
|
|
212
|
+
const segments = node.children.find(c => c.type === 'segments');
|
|
213
|
+
return {
|
|
214
|
+
type: 'RelQuery',
|
|
215
|
+
segments: segments ? segments.children.filter(n => n.type === 'segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx)) : []
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
['comparison-expr'](node, ctx) {
|
|
219
|
+
const children = node.children.filter(({
|
|
220
|
+
type
|
|
221
|
+
}) => ['comparable', 'comparison-op'].includes(type));
|
|
222
|
+
const [left, op, right] = children;
|
|
223
|
+
return {
|
|
224
|
+
type: 'ComparisonExpr',
|
|
225
|
+
left: transformCSTtoAST(left, transformers, ctx),
|
|
226
|
+
op: op.text,
|
|
227
|
+
right: transformCSTtoAST(right, transformers, ctx)
|
|
228
|
+
};
|
|
229
|
+
},
|
|
230
|
+
['literal'](node, ctx) {
|
|
231
|
+
const child = node.children.find(({
|
|
232
|
+
type
|
|
233
|
+
}) => ['number', 'string-literal', 'true', 'false', 'null'].includes(type));
|
|
234
|
+
if (child.type === 'string-literal') {
|
|
235
|
+
const stringLiteralASTNode = transformCSTtoAST(child, transformers, ctx);
|
|
236
|
+
return {
|
|
237
|
+
type: 'Literal',
|
|
238
|
+
value: stringLiteralASTNode.value
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
type: 'Literal',
|
|
243
|
+
value: decodeJSONValue(child.text)
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
['comparable'](node, ctx) {
|
|
247
|
+
const child = node.children.find(({
|
|
248
|
+
type
|
|
249
|
+
}) => ['singular-query', 'function-expr', 'literal'].includes(type));
|
|
250
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
251
|
+
},
|
|
252
|
+
['singular-query'](node, ctx) {
|
|
253
|
+
const child = node.children.find(({
|
|
254
|
+
type
|
|
255
|
+
}) => ['rel-singular-query', 'abs-singular-query'].includes(type));
|
|
256
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
257
|
+
},
|
|
258
|
+
['rel-singular-query'](node, ctx) {
|
|
259
|
+
const segmentsNode = node.children.find(({
|
|
260
|
+
type
|
|
261
|
+
}) => type === 'singular-query-segments');
|
|
262
|
+
const segments = segmentsNode ? segmentsNode.children.filter(({
|
|
263
|
+
type
|
|
264
|
+
}) => ['name-segment', 'index-segment'].includes(type)).map(segNode => ({
|
|
265
|
+
type: 'SingularQuerySegment',
|
|
266
|
+
selector: transformCSTtoAST(segNode, transformers, ctx)
|
|
267
|
+
})) : [];
|
|
268
|
+
return {
|
|
269
|
+
type: 'RelSingularQuery',
|
|
270
|
+
segments
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
['abs-singular-query'](node, ctx) {
|
|
274
|
+
const segmentsNode = node.children.find(({
|
|
275
|
+
type
|
|
276
|
+
}) => type === 'singular-query-segments');
|
|
277
|
+
const segments = segmentsNode ? segmentsNode.children.filter(({
|
|
278
|
+
type
|
|
279
|
+
}) => ['name-segment', 'index-segment'].includes(type)).map(segNode => ({
|
|
280
|
+
type: 'SingularQuerySegment',
|
|
281
|
+
selector: transformCSTtoAST(segNode, transformers, ctx)
|
|
282
|
+
})) : [];
|
|
283
|
+
return {
|
|
284
|
+
type: 'AbsSingularQuery',
|
|
285
|
+
segments
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
['name-segment'](node, ctx) {
|
|
289
|
+
const child = node.children.find(({
|
|
290
|
+
type
|
|
291
|
+
}) => ['name-selector', 'member-name-shorthand'].includes(type));
|
|
292
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
293
|
+
},
|
|
294
|
+
['index-segment'](node, ctx) {
|
|
295
|
+
const child = node.children.find(({
|
|
296
|
+
type
|
|
297
|
+
}) => type === 'index-selector');
|
|
298
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
299
|
+
},
|
|
300
|
+
/**
|
|
301
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.4
|
|
302
|
+
*/
|
|
303
|
+
['function-expr'](node, ctx) {
|
|
304
|
+
const name = node.children.find(({
|
|
305
|
+
type
|
|
306
|
+
}) => type === 'function-name');
|
|
307
|
+
const args = node.children.filter(({
|
|
308
|
+
type
|
|
309
|
+
}) => type === 'function-argument');
|
|
310
|
+
return {
|
|
311
|
+
type: 'FunctionExpr',
|
|
312
|
+
name: name.text,
|
|
313
|
+
arguments: args.map(arg => transformCSTtoAST(arg, transformers, ctx))
|
|
314
|
+
};
|
|
315
|
+
},
|
|
316
|
+
['function-argument'](node, ctx) {
|
|
317
|
+
const child = node.children.find(({
|
|
318
|
+
type
|
|
319
|
+
}) => ['logical-expr', 'function-expr', 'filter-query', 'literal'].includes(type));
|
|
320
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
321
|
+
},
|
|
322
|
+
/**
|
|
323
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
|
|
324
|
+
*/
|
|
325
|
+
['child-segment'](node, ctx) {
|
|
326
|
+
const child = node.children.find(({
|
|
327
|
+
type
|
|
328
|
+
}) => ['bracketed-selection', 'wildcard-selector', 'member-name-shorthand'].includes(type));
|
|
329
|
+
return {
|
|
330
|
+
type: 'ChildSegment',
|
|
331
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
['bracketed-selection'](node, ctx) {
|
|
335
|
+
return {
|
|
336
|
+
type: 'BracketedSelection',
|
|
337
|
+
selectors: node.children.filter(({
|
|
338
|
+
type
|
|
339
|
+
}) => type === 'selector').map(selectorNode => transformCSTtoAST(selectorNode, transformers, ctx))
|
|
340
|
+
};
|
|
341
|
+
},
|
|
342
|
+
['member-name-shorthand'](node) {
|
|
343
|
+
return {
|
|
344
|
+
type: 'NameSelector',
|
|
345
|
+
value: node.text,
|
|
346
|
+
format: 'shorthand'
|
|
347
|
+
};
|
|
348
|
+
},
|
|
349
|
+
/**
|
|
350
|
+
* https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
|
|
351
|
+
*/
|
|
352
|
+
['descendant-segment'](node, ctx) {
|
|
353
|
+
const child = node.children.find(({
|
|
354
|
+
type
|
|
355
|
+
}) => ['bracketed-selection', 'wildcard-selector', 'member-name-shorthand'].includes(type));
|
|
356
|
+
return {
|
|
357
|
+
type: 'DescendantSegment',
|
|
358
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
/**
|
|
362
|
+
* https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
|
|
363
|
+
*/
|
|
364
|
+
['normalized-path'](node, ctx) {
|
|
365
|
+
return {
|
|
366
|
+
type: 'JsonPathQuery',
|
|
367
|
+
segments: node.children.filter(({
|
|
368
|
+
type
|
|
369
|
+
}) => type === 'normal-index-segment').map(segNode => transformCSTtoAST(segNode, transformers, ctx))
|
|
370
|
+
};
|
|
371
|
+
},
|
|
372
|
+
['normal-index-segment'](node, ctx) {
|
|
373
|
+
const child = node.children.find(({
|
|
374
|
+
type
|
|
375
|
+
}) => type === 'normal-selector');
|
|
376
|
+
return {
|
|
377
|
+
type: 'ChildSegment',
|
|
378
|
+
selector: transformCSTtoAST(child, transformers, ctx)
|
|
379
|
+
};
|
|
380
|
+
},
|
|
381
|
+
['normal-selector'](node, ctx) {
|
|
382
|
+
const child = node.children.find(({
|
|
383
|
+
type
|
|
384
|
+
}) => ['normal-name-selector', 'normal-index-selector'].includes(type));
|
|
385
|
+
return transformCSTtoAST(child, transformers, ctx);
|
|
386
|
+
},
|
|
387
|
+
['normal-name-selector'](node) {
|
|
388
|
+
const child = node.children.find(({
|
|
389
|
+
type
|
|
390
|
+
}) => type === 'normal-single-quoted');
|
|
391
|
+
return {
|
|
392
|
+
type: 'NameSelector',
|
|
393
|
+
value: child ? decodeString(child.text) : '',
|
|
394
|
+
format: 'single-quoted'
|
|
395
|
+
};
|
|
396
|
+
},
|
|
397
|
+
['normal-index-selector'](node) {
|
|
398
|
+
return {
|
|
399
|
+
type: 'IndexSelector',
|
|
400
|
+
value: decodeInteger(node.text)
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
export default transformers;
|
|
@@ -3,12 +3,16 @@ class CSTOptimizedTranslator extends CSTTranslator {
|
|
|
3
3
|
collapsibleTypes = ['single-quoted', 'double-quoted', 'normal-single-quoted'];
|
|
4
4
|
droppableTypes = ['text', 'segments', 'singular-query-segments'];
|
|
5
5
|
constructor({
|
|
6
|
-
collapsibleTypes
|
|
6
|
+
collapsibleTypes,
|
|
7
|
+
droppableTypes
|
|
7
8
|
} = {}) {
|
|
8
9
|
super();
|
|
9
10
|
if (Array.isArray(collapsibleTypes)) {
|
|
10
11
|
this.collapsibleTypes = collapsibleTypes;
|
|
11
12
|
}
|
|
13
|
+
if (Array.isArray(droppableTypes)) {
|
|
14
|
+
this.droppableTypes = droppableTypes;
|
|
15
|
+
}
|
|
12
16
|
}
|
|
13
17
|
getTree() {
|
|
14
18
|
const options = {
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export declare class XMLTranslator implements Translator<XMLTree> {
|
|
|
24
24
|
getTree(): XMLTree;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export interface ParseResult<TTree =
|
|
27
|
+
export interface ParseResult<TTree = ASTTree> {
|
|
28
28
|
readonly result: {
|
|
29
29
|
readonly success: boolean;
|
|
30
30
|
readonly state: number;
|
|
@@ -54,6 +54,159 @@ export interface CSTTree {
|
|
|
54
54
|
|
|
55
55
|
export type XMLTree = string;
|
|
56
56
|
|
|
57
|
+
/* AST Tree start */
|
|
58
|
+
export interface ASTTree {
|
|
59
|
+
readonly root: JSONPathQueryASTNode;
|
|
60
|
+
}
|
|
61
|
+
export interface ASTNode {
|
|
62
|
+
readonly type:
|
|
63
|
+
| 'JSONPathQuery'
|
|
64
|
+
| 'NameSelector'
|
|
65
|
+
| 'WildcardSelector'
|
|
66
|
+
| 'IndexSelector'
|
|
67
|
+
| 'SliceSelector'
|
|
68
|
+
| 'FilterSelector'
|
|
69
|
+
| 'LogicalOrExpr'
|
|
70
|
+
| 'LogicalAndExpr'
|
|
71
|
+
| 'LogicalNotExpr'
|
|
72
|
+
| 'TestExpr'
|
|
73
|
+
| 'FilterQuery'
|
|
74
|
+
| 'RelQuery'
|
|
75
|
+
| 'ComparisonExpr'
|
|
76
|
+
| 'Literal'
|
|
77
|
+
| 'RelSingularQuery'
|
|
78
|
+
| 'AbsSingularQuery'
|
|
79
|
+
| 'FunctionExpr'
|
|
80
|
+
| 'ChildSegment'
|
|
81
|
+
| 'DescendantSegment'
|
|
82
|
+
| 'BracketedSelection';
|
|
83
|
+
}
|
|
84
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.1.1
|
|
85
|
+
export interface JSONPathQueryASTNode extends ASTNode {
|
|
86
|
+
readonly type: 'JSONPathQuery';
|
|
87
|
+
segments: SegmentASTNode[];
|
|
88
|
+
}
|
|
89
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.1.1
|
|
90
|
+
export interface NameSelectorASTNode extends ASTNode {
|
|
91
|
+
type: 'NameSelector';
|
|
92
|
+
value: string;
|
|
93
|
+
format: 'single-quoted' | 'double-quoted' | 'shorthand';
|
|
94
|
+
}
|
|
95
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
|
|
96
|
+
export interface WildcardSelectorASTNode extends ASTNode {
|
|
97
|
+
type: 'WildcardSelector';
|
|
98
|
+
}
|
|
99
|
+
// https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
|
|
100
|
+
export interface IndexSelectorASTNode extends ASTNode {
|
|
101
|
+
type: 'IndexSelector';
|
|
102
|
+
value: number;
|
|
103
|
+
}
|
|
104
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
|
|
105
|
+
export interface SliceSelectorASTNode extends ASTNode {
|
|
106
|
+
type: 'SliceSelector';
|
|
107
|
+
start: number | null;
|
|
108
|
+
end: number | null ;
|
|
109
|
+
step: number | null;
|
|
110
|
+
}
|
|
111
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
|
|
112
|
+
export interface FilterSelectorASTNode extends ASTNode {
|
|
113
|
+
type: 'FilterSelector';
|
|
114
|
+
expression: LogicalExprASTNode;
|
|
115
|
+
}
|
|
116
|
+
export interface LogicalOrExprASTNode extends ASTNode {
|
|
117
|
+
type: 'LogicalOrExpr';
|
|
118
|
+
left: LogicalExprASTNode;
|
|
119
|
+
right: LogicalExprASTNode;
|
|
120
|
+
}
|
|
121
|
+
export interface LogicalAndExprASTNode extends ASTNode {
|
|
122
|
+
type: 'LogicalAndExpr';
|
|
123
|
+
left: LogicalExprASTNode;
|
|
124
|
+
right: LogicalExprASTNode;
|
|
125
|
+
}
|
|
126
|
+
export interface LogicalNotExprASTNode extends ASTNode {
|
|
127
|
+
type: 'LogicalNotExpr';
|
|
128
|
+
expression: LogicalExprASTNode;
|
|
129
|
+
}
|
|
130
|
+
export interface TestExprASTNode extends ASTNode {
|
|
131
|
+
type: 'TestExpr';
|
|
132
|
+
expression: FilterQueryASTNode | FunctionExprASTNode;
|
|
133
|
+
}
|
|
134
|
+
export interface FilterQueryASTNode extends ASTNode {
|
|
135
|
+
type: 'FilterQuery';
|
|
136
|
+
query: RelQueryASTNode | JSONPathQueryASTNode;
|
|
137
|
+
}
|
|
138
|
+
export interface RelQueryASTNode extends ASTNode {
|
|
139
|
+
type: 'RelQuery';
|
|
140
|
+
segments: SegmentASTNode[];
|
|
141
|
+
}
|
|
142
|
+
export interface ComparisonExprASTNode extends ASTNode {
|
|
143
|
+
type: 'ComparisonExpr';
|
|
144
|
+
left: ComparableASTNode;
|
|
145
|
+
operator: '==' | '!=' | '<=' | '>=' | '<' | '>';
|
|
146
|
+
right: ComparableASTNode;
|
|
147
|
+
}
|
|
148
|
+
export interface LiteralASTNode extends ASTNode {
|
|
149
|
+
type: 'Literal';
|
|
150
|
+
value: string | number | boolean | null;
|
|
151
|
+
}
|
|
152
|
+
export interface RelSingularQueryASTNode extends ASTNode {
|
|
153
|
+
type: 'RelSingularQuery';
|
|
154
|
+
segments: SingularQuerySegmentASTNode[];
|
|
155
|
+
}
|
|
156
|
+
export interface AbsSingularQueryASTNode extends ASTNode {
|
|
157
|
+
type: 'AbsSingularQuery';
|
|
158
|
+
segments: SingularQuerySegmentASTNode[];
|
|
159
|
+
}
|
|
160
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.4
|
|
161
|
+
export interface FunctionExprASTNode extends ASTNode {
|
|
162
|
+
type: 'FunctionExpr';
|
|
163
|
+
name: string;
|
|
164
|
+
arguments: FunctionArgumentASTNode[];
|
|
165
|
+
}
|
|
166
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
|
|
167
|
+
export interface ChildSegmentASTNode extends ASTNode {
|
|
168
|
+
type: 'ChildSegment';
|
|
169
|
+
selector: BracketedSelectionASTNode | WildcardSelectorASTNode | NameSelectorASTNode;
|
|
170
|
+
}
|
|
171
|
+
export interface BracketedSelectionASTNode extends ASTNode {
|
|
172
|
+
type: 'BracketedSelection';
|
|
173
|
+
selectors: SelectorASTNode[];
|
|
174
|
+
}
|
|
175
|
+
export interface DescendantSegmentASTNode extends ASTNode {
|
|
176
|
+
type: 'DescendantSegment';
|
|
177
|
+
selector: BracketedSelectionASTNode | WildcardSelectorASTNode | NameSelectorASTNode;
|
|
178
|
+
}
|
|
179
|
+
// union types
|
|
180
|
+
export type SelectorASTNode =
|
|
181
|
+
| NameSelectorASTNode
|
|
182
|
+
| WildcardSelectorASTNode
|
|
183
|
+
| SliceSelectorASTNode
|
|
184
|
+
| IndexSelectorASTNode
|
|
185
|
+
| FilterQueryASTNode;
|
|
186
|
+
export type LogicalExprASTNode =
|
|
187
|
+
| LogicalOrExprASTNode
|
|
188
|
+
| LogicalAndExprASTNode
|
|
189
|
+
| LogicalNotExprASTNode
|
|
190
|
+
| TestExprASTNode
|
|
191
|
+
| ComparisonExprASTNode;
|
|
192
|
+
export type ComparableASTNode =
|
|
193
|
+
| RelSingularQueryASTNode
|
|
194
|
+
| AbsSingularQueryASTNode
|
|
195
|
+
| FunctionExprASTNode
|
|
196
|
+
| LiteralASTNode;
|
|
197
|
+
export type SegmentASTNode =
|
|
198
|
+
| ChildSegmentASTNode
|
|
199
|
+
| DescendantSegmentASTNode;
|
|
200
|
+
export type FunctionArgumentASTNode =
|
|
201
|
+
| LogicalExprASTNode
|
|
202
|
+
| FilterQueryASTNode
|
|
203
|
+
| FunctionExprASTNode
|
|
204
|
+
| LiteralASTNode;
|
|
205
|
+
export type SingularQuerySegmentASTNode =
|
|
206
|
+
| NameSelectorASTNode
|
|
207
|
+
| IndexSelectorASTNode;
|
|
208
|
+
/* AST Tree end */
|
|
209
|
+
|
|
57
210
|
export interface Stats {
|
|
58
211
|
displayStats(): string;
|
|
59
212
|
displayHits(): string;
|