clarity-pattern-parser 11.0.21 → 11.0.23
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/dist/grammar/Grammar.d.ts +0 -1
- package/dist/index.browser.js +280 -256
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +280 -256
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +280 -256
- package/dist/index.js.map +1 -1
- package/dist/patterns/Sequence.d.ts +2 -1
- package/dist/patterns/generate_error_message.d.ts +3 -0
- package/package.json +1 -1
- package/src/grammar/Grammar.ts +11 -22
- package/src/patterns/Literal.ts +4 -0
- package/src/patterns/Reference.ts +3 -1
- package/src/patterns/Sequence.test.ts +11 -0
- package/src/patterns/Sequence.ts +13 -4
- package/src/patterns/generate_error_message.ts +33 -0
- package/src/query/query.test.ts +174 -0
- package/src/query/query.ts +152 -16
- package/src/query/selector.test.ts +262 -0
- package/src/query/selector.ts +280 -0
- package/src/query/selector_parser.ts +44 -3
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Selector } from "./selector";
|
|
3
|
+
|
|
4
|
+
function createNodeTree() {
|
|
5
|
+
const node = Node.createNode("node", "root", [
|
|
6
|
+
Node.createNode("node", "grand-parent", [
|
|
7
|
+
Node.createNode("node", "parent", [
|
|
8
|
+
Node.createValueNode("node", 'child', "1"),
|
|
9
|
+
Node.createValueNode("node", 'child', "2"),
|
|
10
|
+
Node.createValueNode("node", 'child', "3"),
|
|
11
|
+
Node.createValueNode("node", 'child', "4"),
|
|
12
|
+
]),
|
|
13
|
+
Node.createNode("node", "parent", [
|
|
14
|
+
Node.createValueNode("node", 'child', "5"),
|
|
15
|
+
Node.createValueNode("node", 'child', "6"),
|
|
16
|
+
Node.createValueNode("node", 'child', "7"),
|
|
17
|
+
Node.createValueNode("node", 'child', "8"),
|
|
18
|
+
]),
|
|
19
|
+
Node.createNode("node", "parent", [
|
|
20
|
+
Node.createValueNode("node", 'child', "9"),
|
|
21
|
+
Node.createValueNode("node", 'child', "10"),
|
|
22
|
+
Node.createValueNode("node", 'child', "11"),
|
|
23
|
+
Node.createValueNode("node", 'child', "12"),
|
|
24
|
+
])
|
|
25
|
+
]),
|
|
26
|
+
Node.createNode("node", "grand-parent", [
|
|
27
|
+
Node.createNode("node", "parent", [
|
|
28
|
+
Node.createValueNode("node", 'child', "13"),
|
|
29
|
+
Node.createValueNode("node", 'child', "14"),
|
|
30
|
+
Node.createValueNode("node", 'child', "15"),
|
|
31
|
+
Node.createValueNode("node", 'child', "16"),
|
|
32
|
+
]),
|
|
33
|
+
Node.createNode("node", "parent", [
|
|
34
|
+
Node.createValueNode("node", 'child', "17"),
|
|
35
|
+
Node.createValueNode("node", 'child', "18"),
|
|
36
|
+
Node.createValueNode("node", 'child', "19"),
|
|
37
|
+
Node.createValueNode("node", 'child', "20"),
|
|
38
|
+
]),
|
|
39
|
+
Node.createNode("node", "parent", [
|
|
40
|
+
Node.createValueNode("node", 'child', "21"),
|
|
41
|
+
Node.createValueNode("node", 'child', "22"),
|
|
42
|
+
Node.createValueNode("node", 'child', "23"),
|
|
43
|
+
Node.createValueNode("node", 'child', "24"),
|
|
44
|
+
])
|
|
45
|
+
])
|
|
46
|
+
]);
|
|
47
|
+
node.normalize();
|
|
48
|
+
return node;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe("Selector", () => {
|
|
52
|
+
|
|
53
|
+
test("Node Selector", () => {
|
|
54
|
+
const tree = createNodeTree();
|
|
55
|
+
|
|
56
|
+
const selector = new Selector("child");
|
|
57
|
+
const result = selector.find([tree]);
|
|
58
|
+
|
|
59
|
+
expect(result.length).toBe(24);
|
|
60
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("Descendant Selector", () => {
|
|
64
|
+
const tree = createNodeTree();
|
|
65
|
+
|
|
66
|
+
const selector = new Selector("grand-parent child");
|
|
67
|
+
const result = selector.find([tree]);
|
|
68
|
+
|
|
69
|
+
expect(result.length).toBe(24);
|
|
70
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("Descendant Selector Miss", () => {
|
|
74
|
+
const tree = createNodeTree();
|
|
75
|
+
|
|
76
|
+
const selector = new Selector("grand-parent not-a-node");
|
|
77
|
+
const result = selector.find([tree]);
|
|
78
|
+
|
|
79
|
+
expect(result.length).toBe(0);
|
|
80
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("Direct Child", () => {
|
|
84
|
+
const tree = createNodeTree();
|
|
85
|
+
|
|
86
|
+
const selector = new Selector("grand-parent > parent");
|
|
87
|
+
const result = selector.find([tree]);
|
|
88
|
+
|
|
89
|
+
expect(result.length).toBe(6);
|
|
90
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1234,5678,9101112,13141516,17181920,21222324");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("More Than One Direct Child", () => {
|
|
94
|
+
const tree = createNodeTree();
|
|
95
|
+
|
|
96
|
+
const selector = new Selector("grand-parent > parent > child");
|
|
97
|
+
const result = selector.find([tree]);
|
|
98
|
+
|
|
99
|
+
expect(result.length).toBe(24);
|
|
100
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("Direct Child With Name & Attribute", () => {
|
|
104
|
+
const tree = createNodeTree();
|
|
105
|
+
|
|
106
|
+
const selector = new Selector("grand-parent > parent > child[value='4']");
|
|
107
|
+
const result = selector.find([tree]);
|
|
108
|
+
|
|
109
|
+
expect(result.length).toBe(1);
|
|
110
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("4");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("Direct Child With Attribute", () => {
|
|
114
|
+
const tree = createNodeTree();
|
|
115
|
+
|
|
116
|
+
const selector = new Selector("grand-parent > parent > [value='4']");
|
|
117
|
+
const result = selector.find([tree]);
|
|
118
|
+
|
|
119
|
+
expect(result.length).toBe(1);
|
|
120
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("4");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("Adjacent", () => {
|
|
124
|
+
const tree = createNodeTree();
|
|
125
|
+
|
|
126
|
+
const selector = new Selector("child + [value='4']");
|
|
127
|
+
const result = selector.find([tree]);
|
|
128
|
+
|
|
129
|
+
expect(result.length).toBe(1);
|
|
130
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("4");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("Adjacent Miss", () => {
|
|
134
|
+
const tree = createNodeTree();
|
|
135
|
+
|
|
136
|
+
const selector = new Selector("parent + [value='4']");
|
|
137
|
+
const result = selector.find([tree]);
|
|
138
|
+
|
|
139
|
+
expect(result.length).toBe(0);
|
|
140
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("After", () => {
|
|
144
|
+
const tree = createNodeTree();
|
|
145
|
+
|
|
146
|
+
const selector = new Selector("[value='2'] ~ *");
|
|
147
|
+
const result = selector.find([tree]);
|
|
148
|
+
|
|
149
|
+
expect(result.length).toBe(2);
|
|
150
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("3,4");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("After Miss", () => {
|
|
154
|
+
const tree = createNodeTree();
|
|
155
|
+
|
|
156
|
+
const selector = new Selector("[value='2'] ~ not-a-node");
|
|
157
|
+
const result = selector.find([tree]);
|
|
158
|
+
|
|
159
|
+
expect(result.length).toBe(0);
|
|
160
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("Not Equal Attribute", () => {
|
|
164
|
+
const tree = createNodeTree();
|
|
165
|
+
|
|
166
|
+
const selector = new Selector("[value!='4']");
|
|
167
|
+
const result = selector.find([tree]);
|
|
168
|
+
|
|
169
|
+
expect(result.length).toBe(32);
|
|
170
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,2,3,1234,5,6,7,8,5678,9,10,11,12,9101112,123456789101112,13,14,15,16,13141516,17,18,19,20,17181920,21,22,23,24,21222324,131415161718192021222324,123456789101112131415161718192021222324");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("Starts With Attribute", () => {
|
|
174
|
+
const tree = createNodeTree();
|
|
175
|
+
|
|
176
|
+
const selector = new Selector("[value^='1']");
|
|
177
|
+
const result = selector.find([tree]);
|
|
178
|
+
|
|
179
|
+
expect(result.length).toBe(17);
|
|
180
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,1234,10,11,12,123456789101112,13,14,15,16,13141516,17,18,19,17181920,131415161718192021222324,123456789101112131415161718192021222324");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("Ends With Attribute", () => {
|
|
184
|
+
const tree = createNodeTree();
|
|
185
|
+
|
|
186
|
+
const selector = new Selector("[value$='2']");
|
|
187
|
+
const result = selector.find([tree]);
|
|
188
|
+
|
|
189
|
+
expect(result.length).toBe(5);
|
|
190
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("2,12,9101112,123456789101112,22");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("Contains Attribute", () => {
|
|
194
|
+
const tree = createNodeTree();
|
|
195
|
+
|
|
196
|
+
const selector = new Selector("[value*='2']");
|
|
197
|
+
const result = selector.find([tree]);
|
|
198
|
+
|
|
199
|
+
expect(result.length).toBe(14);
|
|
200
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("2,1234,12,9101112,123456789101112,20,17181920,21,22,23,24,21222324,131415161718192021222324,123456789101112131415161718192021222324");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("Greater Than Or Equal Attribute", () => {
|
|
204
|
+
const tree = createNodeTree();
|
|
205
|
+
|
|
206
|
+
const selector = new Selector("[endIndex>=35]");
|
|
207
|
+
const result = selector.find([tree]);
|
|
208
|
+
|
|
209
|
+
expect(result.length).toBe(6);
|
|
210
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("22,23,24,21222324,131415161718192021222324,123456789101112131415161718192021222324");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("Less Than Or Equal Attribute", () => {
|
|
214
|
+
const tree = createNodeTree();
|
|
215
|
+
|
|
216
|
+
const selector = new Selector("[endIndex<=5]");
|
|
217
|
+
const result = selector.find([tree]);
|
|
218
|
+
|
|
219
|
+
expect(result.length).toBe(6);
|
|
220
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,2,3,4,1234,5");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("Greater Than Attribute", () => {
|
|
224
|
+
const tree = createNodeTree();
|
|
225
|
+
|
|
226
|
+
const selector = new Selector("[endIndex>35]");
|
|
227
|
+
const result = selector.find([tree]);
|
|
228
|
+
|
|
229
|
+
expect(result.length).toBe(5);
|
|
230
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("23,24,21222324,131415161718192021222324,123456789101112131415161718192021222324");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("Less Than Attribute", () => {
|
|
234
|
+
const tree = createNodeTree();
|
|
235
|
+
|
|
236
|
+
const selector = new Selector("[endIndex<5]");
|
|
237
|
+
const result = selector.find([tree]);
|
|
238
|
+
|
|
239
|
+
expect(result.length).toBe(5);
|
|
240
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("1,2,3,4,1234");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("Or Selector", () => {
|
|
244
|
+
const tree = createNodeTree();
|
|
245
|
+
|
|
246
|
+
const selector = new Selector("[value='5'], [value='6']");
|
|
247
|
+
const result = selector.find([tree]);
|
|
248
|
+
|
|
249
|
+
expect(result.length).toBe(2);
|
|
250
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("5,6");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("Nested Or Selector", () => {
|
|
254
|
+
const tree = createNodeTree();
|
|
255
|
+
|
|
256
|
+
const selector = new Selector("parent > [value='5'], [value='6']");
|
|
257
|
+
const result = selector.find([tree]);
|
|
258
|
+
|
|
259
|
+
expect(result.length).toBe(2);
|
|
260
|
+
expect(result.map(n => n.toString()).join(",")).toEqual("5,6");
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { generateErrorMessage } from "../patterns/generate_error_message";
|
|
2
|
+
import { selectorParser } from "./selector_parser";
|
|
3
|
+
import { Node } from "../ast/Node";
|
|
4
|
+
|
|
5
|
+
const combinatorMap: Record<string, boolean> = {
|
|
6
|
+
"adjacent": true,
|
|
7
|
+
"after": true,
|
|
8
|
+
"descendant": true,
|
|
9
|
+
"direct-child": true
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const operatorMap: Record<string, boolean> = {
|
|
13
|
+
"equal": true,
|
|
14
|
+
"not-equal": true,
|
|
15
|
+
"starts-with": true,
|
|
16
|
+
"ends-with": true,
|
|
17
|
+
"contains": true,
|
|
18
|
+
"greater-than-or-equal": true,
|
|
19
|
+
"less-than-or-equal": true,
|
|
20
|
+
"greater-than": true,
|
|
21
|
+
"less-than": true
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class Selector {
|
|
25
|
+
private _selectedNodes: Node[];
|
|
26
|
+
private _selectorAst: Node;
|
|
27
|
+
private _combinator: string | null;
|
|
28
|
+
|
|
29
|
+
constructor(selector: string) {
|
|
30
|
+
this._selectedNodes = [];
|
|
31
|
+
this._combinator = null;
|
|
32
|
+
|
|
33
|
+
const result = selectorParser.exec(selector);
|
|
34
|
+
|
|
35
|
+
if (result.ast == null) {
|
|
36
|
+
const message = generateErrorMessage(selectorParser, result.cursor);
|
|
37
|
+
throw new Error(`[Invalid Selector] ${message}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this._selectorAst = result.ast;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
find(nodes: Node[]) {
|
|
44
|
+
this._selectedNodes = nodes;
|
|
45
|
+
|
|
46
|
+
const ast = this._selectorAst;
|
|
47
|
+
ast.walkUp((node) => {
|
|
48
|
+
this._process(node);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return this._selectedNodes;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
filter(nodes: Node[]) {
|
|
55
|
+
if (nodes.length < 1) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const nodeMap = new Map();
|
|
60
|
+
nodes.forEach(n => nodeMap.set(n, n));
|
|
61
|
+
|
|
62
|
+
this._selectedNodes = [nodes[0].findRoot()];
|
|
63
|
+
|
|
64
|
+
const ast = this._selectorAst;
|
|
65
|
+
ast.walkUp((node) => {
|
|
66
|
+
this._process(node);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return this._selectedNodes.filter(n => nodeMap.has(n));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
not(nodes: Node[]) {
|
|
73
|
+
if (nodes.length < 1) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this._selectedNodes = [nodes[0].findRoot()];
|
|
78
|
+
|
|
79
|
+
const ast = this._selectorAst;
|
|
80
|
+
ast.walkUp((node) => {
|
|
81
|
+
this._process(node);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const selectedNodeMap = new Map();
|
|
85
|
+
this._selectedNodes.forEach(n => selectedNodeMap.set(n, n));
|
|
86
|
+
|
|
87
|
+
return nodes.filter(n => !selectedNodeMap.has(n));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
parents(nodes: Node[]) {
|
|
91
|
+
if (nodes.length < 1) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this._selectedNodes = [nodes[0].findRoot()];
|
|
96
|
+
|
|
97
|
+
const ast = this._selectorAst;
|
|
98
|
+
ast.walkUp((node) => {
|
|
99
|
+
this._process(node);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const result = new Set<Node>();
|
|
103
|
+
const ancestorMap = new Map<Node, boolean>();
|
|
104
|
+
this._selectedNodes.forEach(n => ancestorMap.set(n, true));
|
|
105
|
+
|
|
106
|
+
nodes.forEach(n => {
|
|
107
|
+
const ancestor = n.findAncestor(a => ancestorMap.has(a));
|
|
108
|
+
if (ancestor != null) {
|
|
109
|
+
result.add(ancestor);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return Array.from(result);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private _process(ast: Node) {
|
|
117
|
+
const nodeName = ast.name;
|
|
118
|
+
|
|
119
|
+
if (nodeName === "wild-card") {
|
|
120
|
+
this._selectedNodes = this._processWildCard();
|
|
121
|
+
}
|
|
122
|
+
else if (nodeName === "or-selector") {
|
|
123
|
+
this._selectedNodes = this._processOrSelector(ast);
|
|
124
|
+
}
|
|
125
|
+
else if (nodeName === "name-selector" || (nodeName === "name" && (ast.parent == null || ast.parent.name === "selector-expression"))) {
|
|
126
|
+
this._selectedNodes = this._processNameSelector(ast);
|
|
127
|
+
}
|
|
128
|
+
else if (nodeName === "attribute-selector" && (ast.parent == null || ast.parent.name === "selector-expression")) {
|
|
129
|
+
this._selectedNodes = this._processAttributeSelector(ast);
|
|
130
|
+
}
|
|
131
|
+
else if (combinatorMap[nodeName]) {
|
|
132
|
+
this._combinator = nodeName;
|
|
133
|
+
}
|
|
134
|
+
else if (nodeName === "selector-expression") {
|
|
135
|
+
this._combinator = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private _processWildCard() {
|
|
140
|
+
return this._selectedNodes.map(n => {
|
|
141
|
+
return this._selectWithCombinator(n, () => true);
|
|
142
|
+
}).flat();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private _processOrSelector(ast: Node) {
|
|
146
|
+
const selectorNodes = ast.children.filter(n => n.name !== "comma");
|
|
147
|
+
const set: Set<Node> = new Set();
|
|
148
|
+
|
|
149
|
+
const selectors = selectorNodes.map(n => new Selector(n.toString()));
|
|
150
|
+
|
|
151
|
+
selectors.map(s => {
|
|
152
|
+
return s.find(this._selectedNodes.slice());
|
|
153
|
+
}).flat().forEach((node) => {
|
|
154
|
+
set.add(node);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return Array.from(set);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
private _processNameSelector(ast: Node) {
|
|
162
|
+
if (ast.children.length > 1) {
|
|
163
|
+
return this._selectedNodes.map(n => {
|
|
164
|
+
const name = ast.children[0].value;
|
|
165
|
+
|
|
166
|
+
return this._selectWithCombinator(n, (node: Node) => {
|
|
167
|
+
return node.name === name && this._isAttributeMatch(node, ast);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
}).flat();
|
|
171
|
+
} else {
|
|
172
|
+
return this._selectedNodes.map(n => {
|
|
173
|
+
return this._selectWithCombinator(n, (node: Node) => node.name === ast.value);
|
|
174
|
+
}).flat();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private _processAttributeSelector(ast: Node) {
|
|
179
|
+
return this._selectedNodes.map(n => {
|
|
180
|
+
return this._selectWithCombinator(n, (node: Node) => {
|
|
181
|
+
return this._isAttributeMatch(node, ast);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
}).flat();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private _selectWithCombinator(node: Node, predicate: (node: Node) => boolean) {
|
|
188
|
+
if (this._combinator === "adjacent") {
|
|
189
|
+
const sibling = node.nextSibling();
|
|
190
|
+
if (sibling == null) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (predicate(sibling)) {
|
|
195
|
+
return [sibling];
|
|
196
|
+
} else {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (this._combinator === "after") {
|
|
201
|
+
const parent = node.parent;
|
|
202
|
+
|
|
203
|
+
if (parent == null) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const index = parent.findChildIndex(node);
|
|
208
|
+
const after = parent.children.slice(index + 1);
|
|
209
|
+
return after.filter(predicate);
|
|
210
|
+
}
|
|
211
|
+
else if (this._combinator === "direct-child") {
|
|
212
|
+
return node.children.filter(predicate);
|
|
213
|
+
}
|
|
214
|
+
else if (this._combinator === "descendant" || this._combinator == null) {
|
|
215
|
+
return node.findAll(predicate);
|
|
216
|
+
} else {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private _isAttributeMatch(node: Node, ast: Node) {
|
|
222
|
+
const name = this._getAttributeName(ast);
|
|
223
|
+
const operator = this._getAttributeOperator(ast);
|
|
224
|
+
const value = this._getAttributeValue(ast);
|
|
225
|
+
const anyNode = node as any;
|
|
226
|
+
|
|
227
|
+
if (anyNode[name] == null) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (operator === "equal") {
|
|
232
|
+
return anyNode[name] === value;
|
|
233
|
+
} else if (operator === "not-equal") {
|
|
234
|
+
return anyNode[name] !== value;
|
|
235
|
+
} else if (operator === "starts-with") {
|
|
236
|
+
return anyNode[name].toString().startsWith(value);
|
|
237
|
+
} else if (operator === "ends-with") {
|
|
238
|
+
return anyNode[name].toString().endsWith(value);
|
|
239
|
+
} else if (operator === "contains") {
|
|
240
|
+
return anyNode[name].toString().includes(value);
|
|
241
|
+
} else if (operator === "greater-than-or-equal") {
|
|
242
|
+
return anyNode[name] >= value;
|
|
243
|
+
} else if (operator === "less-than-or-equal") {
|
|
244
|
+
return anyNode[name] <= value;
|
|
245
|
+
} else if (operator === "greater-than") {
|
|
246
|
+
return anyNode[name] > value;
|
|
247
|
+
} else if (operator === "less-than") {
|
|
248
|
+
return anyNode[name] < value;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private _getAttributeName(ast: Node) {
|
|
255
|
+
return (ast.find(n => n.name === "attribute-name") as Node).value;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private _getAttributeValue(ast: Node) {
|
|
259
|
+
let valueNode: Node | null = ast.find(n => n.name === "single-quote-string-literal");
|
|
260
|
+
|
|
261
|
+
if (valueNode != null) {
|
|
262
|
+
return valueNode.value.slice(1, -1);
|
|
263
|
+
} else {
|
|
264
|
+
valueNode = ast.find(n => n.name === "value");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (valueNode != null) {
|
|
268
|
+
return valueNode.value;
|
|
269
|
+
} else {
|
|
270
|
+
valueNode = ast.find(n => n.name === "number");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return (valueNode as Node).value;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private _getAttributeOperator(ast: Node) {
|
|
277
|
+
return (ast.find(n => operatorMap[n.name]) as Node).name;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
}
|
|
@@ -1,8 +1,49 @@
|
|
|
1
1
|
import { patterns } from "../grammar/patterns";
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { expression } = patterns`
|
|
4
|
+
number = /[+-]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?/
|
|
5
|
+
spaces = /\\s+/
|
|
6
|
+
single-quote-string-literal = /'(?:\\\\.|[^'\\\\])*'/
|
|
7
|
+
name = /[a-zA-Z_-]+[a-zA-Z0-9_-]*/
|
|
8
|
+
comma = /\\s*,\\s*/
|
|
9
|
+
wild-card = "*"
|
|
10
|
+
equal = "="
|
|
11
|
+
not-equal = "!="
|
|
12
|
+
starts-with = "^="
|
|
13
|
+
ends-with = "$="
|
|
14
|
+
contains = "*="
|
|
15
|
+
greater-than-or-equal = ">="
|
|
16
|
+
less-than-or-equal = "<="
|
|
17
|
+
greater-than = ">"
|
|
18
|
+
less-than = "<"
|
|
19
|
+
operators = equal |
|
|
20
|
+
not-equal |
|
|
21
|
+
starts-with |
|
|
22
|
+
ends-with |
|
|
23
|
+
contains |
|
|
24
|
+
greater-than-or-equal |
|
|
25
|
+
less-than-or-equal |
|
|
26
|
+
greater-than |
|
|
27
|
+
less-than
|
|
28
|
+
|
|
29
|
+
attribute-name = name
|
|
30
|
+
value = name
|
|
31
|
+
attribute-value = single-quote-string-literal | number | value
|
|
32
|
+
attribute-selector = "[" + spaces? + attribute-name + spaces? + operators + spaces? + attribute-value + "]"
|
|
4
33
|
|
|
5
|
-
|
|
34
|
+
adjacent = spaces? + "+" + spaces?
|
|
35
|
+
after = spaces? + "~" + spaces?
|
|
36
|
+
direct-child = spaces? + ">" + spaces?
|
|
37
|
+
descendant = spaces
|
|
6
38
|
|
|
39
|
+
combinators = adjacent | after | direct-child | descendant
|
|
40
|
+
name-selector = name-selector-expression + attribute-selector
|
|
41
|
+
name-selector-expression = name-selector | name
|
|
42
|
+
node-selector = wild-card | attribute-selector | name-selector-expression
|
|
43
|
+
or-selector = (node-selector, comma){2}
|
|
7
44
|
|
|
8
|
-
|
|
45
|
+
selector-expression = expression + combinators + expression
|
|
46
|
+
expression = selector-expression | or-selector | node-selector
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
export const selectorParser = expression;
|