1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | export function* trace<T = any>(expr: string, val: unknown, path: string): IterableIterator<[string, T]> {
|
10 | if (expr) {
|
11 | const [loc, ...rest] = expr.split(";");
|
12 | const x = rest.join(";");
|
13 |
|
14 | if (val !== null && typeof val === 'object' && loc in val) {
|
15 | yield* trace(x, (<any>val)[loc], path + ";" + loc);
|
16 | }
|
17 | else if (loc === "*") {
|
18 | for (const [m, _l, v, p] of walk(loc, val, path)) {
|
19 | yield* trace(m + ";" + x, v, p)
|
20 | }
|
21 | }
|
22 | else if (loc === "..") {
|
23 | yield* trace(x, val, path);
|
24 | for (const [m, _l, v, p] of walk(loc, val, path)) {
|
25 | if (typeof (<any>v)[m] === "object")
|
26 | yield* trace("..;" + x, (<any>v)[m], p + ";" + m);
|
27 | }
|
28 | }
|
29 | else if (/,/.test(loc)) {
|
30 | for (let s = loc.split(/'?,'?/), i = 0, n = s.length; i < n; i++)
|
31 | yield* trace(s[i] + ";" + x, val, path);
|
32 | }
|
33 | else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) {
|
34 | yield* slice(loc, x, val, path);
|
35 | }
|
36 | }
|
37 | else yield [path, val as T]
|
38 | }
|
39 |
|
40 | function* slice<T>(loc: string, expr: string, val: unknown, path: string): IterableIterator<[string, T]> {
|
41 | if (val instanceof Array) {
|
42 | const len = val.length;
|
43 | let start = 0, end = len, step = 1;
|
44 | loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, (_$0, $1, $2, $3) => {
|
45 | start = parseInt($1 || start);
|
46 | end = parseInt($2 || end);
|
47 | step = parseInt($3 || step);
|
48 | return ''
|
49 | });
|
50 | start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start);
|
51 | end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end);
|
52 | for (let i = start; i < end; i += step)
|
53 | yield* trace(i + ";" + expr, val, path);
|
54 | }
|
55 | }
|
56 |
|
57 | function* walk(loc: string, val: unknown, path: string) {
|
58 | if (val instanceof Array) {
|
59 | for (let i = 0, n = val.length; i < n; i++)
|
60 | if (i in val)
|
61 | yield [i, loc, val, path] as const
|
62 | }
|
63 | else if (typeof val === "object") {
|
64 | for (const m in val)
|
65 | if (val.hasOwnProperty(m))
|
66 | yield [m, loc, val, path] as const
|
67 | }
|
68 | }
|
69 |
|
70 | export function normalize(expr: string) {
|
71 | const subX: string[] = [];
|
72 | if (!expr.startsWith('$')) expr = '$' + expr
|
73 | return expr
|
74 | .replace(/[\['](\??\(.*?\))[\]']/g, (_$0, $1) => { return "[#" + (subX.push($1) - 1) + "]"; })
|
75 | .replace(/'?\.'?|\['?/g, ";")
|
76 | .replace(/;;;|;;/g, ";..;")
|
77 | .replace(/;$|'?\]|'$/g, "")
|
78 | .replace(/#([0-9]+)/g, (_$0, $1) => { return subX[$1]; });
|
79 | }
|
80 |
|
81 |
|
82 | export function match(expr: string, path: string): boolean {
|
83 | if (expr && path) {
|
84 | const [loc, ...restLoc] = expr.split(";");
|
85 | const [val, ...restVal] = path.split(";");
|
86 | const exprRest = restLoc.join(";");
|
87 | const pathRest = restVal.join(';')
|
88 |
|
89 | if (loc === val) {
|
90 | return match(exprRest, pathRest)
|
91 | }
|
92 | else if (loc === "*") {
|
93 | return match(exprRest, pathRest)
|
94 | }
|
95 | else if (loc === "..") {
|
96 | return match(exprRest, path) || match("..;" + exprRest, pathRest);
|
97 | }
|
98 | else if (/,/.test(loc)) {
|
99 | if (loc.split(/'?,'?/).some(v => v === val)) return match(exprRest, pathRest)
|
100 | else return false
|
101 | }
|
102 | else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) {
|
103 | let start = 0, end = Number.MAX_SAFE_INTEGER, step = 1;
|
104 | loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, (_$0, $1, $2, $3) => {
|
105 | start = parseInt($1 || start);
|
106 | end = parseInt($2 || end);
|
107 | step = parseInt($3 || step);
|
108 | return ''
|
109 | });
|
110 | const idx = Number(val)
|
111 | if (start < 0 || end < 0 || step < 0)
|
112 | throw TypeError('Negative numbers not supported. Can\'t know length ahead of time when stream parsing');
|
113 | if (idx >= start && idx < end && start + idx % step === 0) return match(exprRest, pathRest)
|
114 | else return false
|
115 | }
|
116 | }
|
117 | else if (!expr && !path) return true
|
118 | return false;
|
119 | }
|