@loancrate/json-selector 2.1.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/src/access.ts CHANGED
@@ -23,18 +23,18 @@ import { visitJsonSelector } from "./visitor";
23
23
 
24
24
  export interface UnboundAccessor {
25
25
  readonly selector: JsonSelector;
26
- isValidContext(context: unknown): boolean;
27
- get(context: unknown): unknown;
28
- set(context: unknown, value: unknown): void;
29
- delete(context: unknown): void;
26
+ isValidContext(context: unknown, rootContext?: unknown): boolean;
27
+ get(context: unknown, rootContext?: unknown): unknown;
28
+ set(value: unknown, context: unknown, rootContext?: unknown): void;
29
+ delete(context: unknown, rootContext?: unknown): void;
30
30
  }
31
31
 
32
32
  abstract class BaseAccessor implements UnboundAccessor {
33
33
  constructor(readonly selector: JsonSelector) {}
34
- abstract isValidContext(context: unknown): boolean;
35
- abstract get(context: unknown): unknown;
36
- abstract set(context: unknown, value: unknown): void;
37
- abstract delete(context: unknown): void;
34
+ abstract isValidContext(context: unknown, rootContext?: unknown): boolean;
35
+ abstract get(context: unknown, rootContext?: unknown): unknown;
36
+ abstract set(value: unknown, context: unknown, rootContext?: unknown): void;
37
+ abstract delete(context: unknown, rootContext?: unknown): void;
38
38
  }
39
39
 
40
40
  abstract class ReadOnlyAccessor extends BaseAccessor {
@@ -74,6 +74,15 @@ class ContextAccessor extends ReadOnlyAccessor {
74
74
  return context;
75
75
  }
76
76
  }
77
+ class RootContextAccessor extends ReadOnlyAccessor {
78
+ constructor() {
79
+ super({ type: "root" });
80
+ }
81
+
82
+ get(context: unknown, rootContext = context): unknown {
83
+ return rootContext;
84
+ }
85
+ }
77
86
 
78
87
  export function makeJsonSelectorAccessor(
79
88
  selector: JsonSelector
@@ -84,6 +93,9 @@ export function makeJsonSelectorAccessor(
84
93
  current() {
85
94
  return new ContextAccessor();
86
95
  },
96
+ root() {
97
+ return new RootContextAccessor();
98
+ },
87
99
  literal(selector) {
88
100
  return new ConstantAccessor(selector, selector.value);
89
101
  },
@@ -99,7 +111,7 @@ export function makeJsonSelectorAccessor(
99
111
  get(context: unknown) {
100
112
  return getField(context, id);
101
113
  }
102
- set(context: unknown, value: unknown) {
114
+ set(value: unknown, context: unknown) {
103
115
  if (isObject(context)) {
104
116
  context[id] = value;
105
117
  }
@@ -119,20 +131,20 @@ export function makeJsonSelectorAccessor(
119
131
  constructor() {
120
132
  super(selector);
121
133
  }
122
- isValidContext(context: unknown) {
123
- return isObject(base.get(context));
134
+ isValidContext(context: unknown, rootContext = context) {
135
+ return isObject(base.get(context, rootContext));
124
136
  }
125
- get(context: unknown) {
126
- return getField(base.get(context), field);
137
+ get(context: unknown, rootContext = context) {
138
+ return getField(base.get(context, rootContext), field);
127
139
  }
128
- set(context: unknown, value: unknown) {
129
- const obj = base.get(context);
140
+ set(value: unknown, context: unknown, rootContext = context) {
141
+ const obj = base.get(context, rootContext);
130
142
  if (isObject(obj)) {
131
143
  obj[field] = value;
132
144
  }
133
145
  }
134
- delete(context: unknown) {
135
- const obj = base.get(context);
146
+ delete(context: unknown, rootContext = context) {
147
+ const obj = base.get(context, rootContext);
136
148
  if (isObject(obj)) {
137
149
  delete obj[field];
138
150
  }
@@ -147,20 +159,20 @@ export function makeJsonSelectorAccessor(
147
159
  constructor() {
148
160
  super(selector);
149
161
  }
150
- isValidContext(context: unknown) {
151
- return isArray(base.get(context));
162
+ isValidContext(context: unknown, rootContext = context) {
163
+ return isArray(base.get(context, rootContext));
152
164
  }
153
- get(context: unknown) {
154
- return getIndex(base.get(context), index);
165
+ get(context: unknown, rootContext = context) {
166
+ return getIndex(base.get(context, rootContext), index);
155
167
  }
156
- set(context: unknown, value: unknown) {
157
- const arr = base.get(context);
168
+ set(value: unknown, context: unknown, rootContext = context) {
169
+ const arr = base.get(context, rootContext);
158
170
  if (isArray(arr)) {
159
171
  arr[index] = value;
160
172
  }
161
173
  }
162
- delete(context: unknown) {
163
- const arr = base.get(context);
174
+ delete(context: unknown, rootContext = context) {
175
+ const arr = base.get(context, rootContext);
164
176
  if (isArray(arr)) {
165
177
  arr.splice(index, 1);
166
178
  }
@@ -175,14 +187,14 @@ export function makeJsonSelectorAccessor(
175
187
  constructor() {
176
188
  super(selector);
177
189
  }
178
- isValidContext(context: unknown) {
179
- return isArray(base.get(context));
190
+ isValidContext(context: unknown, rootContext = context) {
191
+ return isArray(base.get(context, rootContext));
180
192
  }
181
- get(context: unknown) {
182
- return findId(base.get(context), id);
193
+ get(context: unknown, rootContext = context) {
194
+ return findId(base.get(context, rootContext), id);
183
195
  }
184
- set(context: unknown, value: unknown) {
185
- const arr = base.get(context);
196
+ set(value: unknown, context: unknown, rootContext = context) {
197
+ const arr = base.get(context, rootContext);
186
198
  if (isArray(arr)) {
187
199
  const index = findIdIndex(arr, id);
188
200
  if (index >= 0) {
@@ -190,8 +202,8 @@ export function makeJsonSelectorAccessor(
190
202
  }
191
203
  }
192
204
  }
193
- delete(context: unknown) {
194
- const arr = base.get(context);
205
+ delete(context: unknown, rootContext = context) {
206
+ const arr = base.get(context, rootContext);
195
207
  if (isArray(arr)) {
196
208
  const index = findIdIndex(arr, id);
197
209
  if (index >= 0) {
@@ -210,30 +222,30 @@ export function makeJsonSelectorAccessor(
210
222
  constructor() {
211
223
  super(selector);
212
224
  }
213
- isValidContext(context: unknown) {
214
- return isArray(base.get(context));
225
+ isValidContext(context: unknown, rootContext = context) {
226
+ return isArray(base.get(context, rootContext));
215
227
  }
216
- get(context: unknown) {
217
- return project(base.get(context), projection);
228
+ get(context: unknown, rootContext = context) {
229
+ return project(base.get(context, rootContext), projection, context);
218
230
  }
219
- set(context: unknown, value: unknown) {
220
- const arr = base.get(context);
231
+ set(value: unknown, context: unknown, rootContext = context) {
232
+ const arr = base.get(context, rootContext);
221
233
  if (isArray(arr)) {
222
234
  if (proj) {
223
235
  for (const element of arr) {
224
- proj.set(element, value);
236
+ proj.set(value, element, rootContext);
225
237
  }
226
238
  } else {
227
239
  replaceArray(arr, asArray(value));
228
240
  }
229
241
  }
230
242
  }
231
- delete(context: unknown) {
232
- const arr = base.get(context);
243
+ delete(context: unknown, rootContext = context) {
244
+ const arr = base.get(context, rootContext);
233
245
  if (isArray(arr)) {
234
246
  if (proj) {
235
247
  for (const element of arr) {
236
- proj.delete(element);
248
+ proj.delete(element, rootContext);
237
249
  }
238
250
  } else {
239
251
  arr.length = 0;
@@ -250,25 +262,27 @@ export function makeJsonSelectorAccessor(
250
262
  constructor() {
251
263
  super(selector);
252
264
  }
253
- isValidContext(context: unknown) {
254
- return isArray(base.get(context));
265
+ isValidContext(context: unknown, rootContext = context) {
266
+ return isArray(base.get(context, rootContext));
255
267
  }
256
- get(context: unknown) {
257
- return filter(base.get(context), condition);
268
+ get(context: unknown, rootContext = context) {
269
+ return filter(base.get(context, rootContext), condition, context);
258
270
  }
259
- set(context: unknown, value: unknown) {
260
- const arr = base.get(context);
271
+ set(value: unknown, context: unknown, rootContext = context) {
272
+ const arr = base.get(context, rootContext);
261
273
  if (isArray(arr)) {
262
274
  replaceArray(
263
275
  arr,
264
- invertedFilter(arr, condition).concat(asArray(value))
276
+ invertedFilter(arr, condition, rootContext).concat(
277
+ asArray(value)
278
+ )
265
279
  );
266
280
  }
267
281
  }
268
- delete(context: unknown) {
269
- const arr = base.get(context);
282
+ delete(context: unknown, rootContext = context) {
283
+ const arr = base.get(context, rootContext);
270
284
  if (isArray(arr)) {
271
- replaceArray(arr, invertedFilter(arr, condition));
285
+ replaceArray(arr, invertedFilter(arr, condition, rootContext));
272
286
  }
273
287
  }
274
288
  };
@@ -281,14 +295,14 @@ export function makeJsonSelectorAccessor(
281
295
  constructor() {
282
296
  super(selector);
283
297
  }
284
- isValidContext(context: unknown) {
285
- return isArray(base.get(context));
298
+ isValidContext(context: unknown, rootContext = context) {
299
+ return isArray(base.get(context, rootContext));
286
300
  }
287
- get(context: unknown) {
288
- return slice(base.get(context), start, end, step);
301
+ get(context: unknown, rootContext = context) {
302
+ return slice(base.get(context, rootContext), start, end, step);
289
303
  }
290
- set(context: unknown, value: unknown) {
291
- const arr = base.get(context);
304
+ set(value: unknown, context: unknown, rootContext = context) {
305
+ const arr = base.get(context, rootContext);
292
306
  if (isArray(arr)) {
293
307
  replaceArray(
294
308
  arr,
@@ -296,8 +310,8 @@ export function makeJsonSelectorAccessor(
296
310
  );
297
311
  }
298
312
  }
299
- delete(context: unknown) {
300
- const arr = base.get(context);
313
+ delete(context: unknown, rootContext = context) {
314
+ const arr = base.get(context, rootContext);
301
315
  if (isArray(arr)) {
302
316
  replaceArray(arr, invertedSlice(arr, start, end, step));
303
317
  }
@@ -312,20 +326,20 @@ export function makeJsonSelectorAccessor(
312
326
  constructor() {
313
327
  super(selector);
314
328
  }
315
- isValidContext(context: unknown) {
316
- return isArray(base.get(context));
329
+ isValidContext(context: unknown, rootContext = context) {
330
+ return isArray(base.get(context, rootContext));
317
331
  }
318
- get(context: unknown) {
319
- return flatten(base.get(context));
332
+ get(context: unknown, rootContext = context) {
333
+ return flatten(base.get(context, rootContext));
320
334
  }
321
- set(context: unknown, value: unknown) {
322
- const arr = base.get(context);
335
+ set(value: unknown, context: unknown, rootContext = context) {
336
+ const arr = base.get(context, rootContext);
323
337
  if (isArray(arr)) {
324
338
  replaceArray(arr, asArray(value));
325
339
  }
326
340
  }
327
- delete(context: unknown) {
328
- const arr = base.get(context);
341
+ delete(context: unknown, rootContext = context) {
342
+ const arr = base.get(context, rootContext);
329
343
  if (isArray(arr)) {
330
344
  arr.length = 0;
331
345
  }
@@ -340,8 +354,8 @@ export function makeJsonSelectorAccessor(
340
354
  constructor() {
341
355
  super(selector);
342
356
  }
343
- get(context: unknown) {
344
- return isFalseOrEmpty(base.get(context));
357
+ get(context: unknown, rootContext = context) {
358
+ return isFalseOrEmpty(base.get(context, rootContext));
345
359
  }
346
360
  };
347
361
  return new Accessor();
@@ -354,9 +368,9 @@ export function makeJsonSelectorAccessor(
354
368
  constructor() {
355
369
  super(selector);
356
370
  }
357
- get(context: unknown) {
358
- const lv = la.get(context);
359
- const rv = ra.get(context);
371
+ get(context: unknown, rootContext = context) {
372
+ const lv = la.get(context, rootContext);
373
+ const rv = ra.get(context, rootContext);
360
374
  return compare(lv, rv, operator);
361
375
  }
362
376
  };
@@ -370,9 +384,9 @@ export function makeJsonSelectorAccessor(
370
384
  constructor() {
371
385
  super(selector);
372
386
  }
373
- get(context: unknown) {
374
- const lv = la.get(context);
375
- return isFalseOrEmpty(lv) ? lv : ra.get(context);
387
+ get(context: unknown, rootContext = context) {
388
+ const lv = la.get(context, rootContext);
389
+ return isFalseOrEmpty(lv) ? lv : ra.get(context, rootContext);
376
390
  }
377
391
  };
378
392
  return new Accessor();
@@ -385,9 +399,9 @@ export function makeJsonSelectorAccessor(
385
399
  constructor() {
386
400
  super(selector);
387
401
  }
388
- get(context: unknown) {
389
- const lv = la.get(context);
390
- return !isFalseOrEmpty(lv) ? lv : ra.get(context);
402
+ get(context: unknown, rootContext = context) {
403
+ const lv = la.get(context, rootContext);
404
+ return !isFalseOrEmpty(lv) ? lv : ra.get(context, rootContext);
391
405
  }
392
406
  };
393
407
  return new Accessor();
@@ -400,17 +414,17 @@ export function makeJsonSelectorAccessor(
400
414
  constructor() {
401
415
  super(selector);
402
416
  }
403
- isValidContext(context: unknown) {
404
- return ra.isValidContext(la.get(context));
417
+ isValidContext(context: unknown, rootContext = context) {
418
+ return ra.isValidContext(la.get(context, rootContext), rootContext);
405
419
  }
406
- get(context: unknown) {
407
- return ra.get(la.get(context));
420
+ get(context: unknown, rootContext = context) {
421
+ return ra.get(la.get(context, rootContext), rootContext);
408
422
  }
409
- set(context: unknown, value: unknown) {
410
- ra.set(la.get(context), value);
423
+ set(value: unknown, context: unknown, rootContext = context) {
424
+ ra.set(value, la.get(context, rootContext), rootContext);
411
425
  }
412
- delete(context: unknown) {
413
- ra.delete(la.get(context));
426
+ delete(context: unknown, rootContext = context) {
427
+ ra.delete(la.get(context, rootContext), rootContext);
414
428
  }
415
429
  };
416
430
  return new Accessor();
@@ -431,31 +445,37 @@ export interface Accessor<T> {
431
445
 
432
446
  export function bindJsonSelectorAccessor(
433
447
  unbound: UnboundAccessor,
434
- context: unknown
448
+ context: unknown,
449
+ rootContext = context
435
450
  ): Accessor<unknown> {
436
451
  const { selector } = unbound;
437
- const valid = unbound.isValidContext(context);
452
+ const valid = unbound.isValidContext(context, rootContext);
438
453
  return {
439
454
  selector,
440
455
  valid,
441
456
  path: formatJsonSelector(selector),
442
457
  get() {
443
- return unbound.get(context);
458
+ return unbound.get(context, rootContext);
444
459
  },
445
- set(v: unknown) {
446
- unbound.set(context, v);
460
+ set(value: unknown) {
461
+ unbound.set(value, context, rootContext);
447
462
  },
448
463
  delete() {
449
- unbound.delete(context);
464
+ unbound.delete(context, rootContext);
450
465
  },
451
466
  };
452
467
  }
453
468
 
454
469
  export function accessWithJsonSelector(
455
470
  selector: JsonSelector,
456
- context: unknown
471
+ context: unknown,
472
+ rootContext = context
457
473
  ): Accessor<unknown> {
458
- return bindJsonSelectorAccessor(makeJsonSelectorAccessor(selector), context);
474
+ return bindJsonSelectorAccessor(
475
+ makeJsonSelectorAccessor(selector),
476
+ context,
477
+ rootContext
478
+ );
459
479
  }
460
480
 
461
481
  function replaceArray(
@@ -467,9 +487,13 @@ function replaceArray(
467
487
  return target;
468
488
  }
469
489
 
470
- function invertedFilter(value: unknown[], condition: JsonSelector): unknown[] {
490
+ function invertedFilter(
491
+ value: unknown[],
492
+ condition: JsonSelector,
493
+ rootContext: unknown
494
+ ): unknown[] {
471
495
  return value.filter((e) =>
472
- isFalseOrEmpty(evaluateJsonSelector(condition, e))
496
+ isFalseOrEmpty(evaluateJsonSelector(condition, e, rootContext))
473
497
  );
474
498
  }
475
499
 
package/src/ast.ts CHANGED
@@ -6,6 +6,10 @@ export interface JsonSelectorCurrent {
6
6
  type: "current";
7
7
  }
8
8
 
9
+ export interface JsonSelectorRoot {
10
+ type: "root";
11
+ }
12
+
9
13
  export interface JsonSelectorLiteral {
10
14
  type: "literal";
11
15
  value: JsonValue;
@@ -93,6 +97,7 @@ export interface JsonSelectorPipe {
93
97
 
94
98
  export type JsonSelector =
95
99
  | JsonSelectorCurrent
100
+ | JsonSelectorRoot
96
101
  | JsonSelectorLiteral
97
102
  | JsonSelectorIdentifier
98
103
  | JsonSelectorFieldAccess
package/src/evaluate.ts CHANGED
@@ -5,7 +5,8 @@ import { visitJsonSelector } from "./visitor";
5
5
 
6
6
  export function evaluateJsonSelector(
7
7
  selector: JsonSelector,
8
- context: unknown
8
+ context: unknown,
9
+ rootContext = context
9
10
  ): unknown {
10
11
  return visitJsonSelector<unknown, unknown>(
11
12
  selector,
@@ -13,6 +14,9 @@ export function evaluateJsonSelector(
13
14
  current() {
14
15
  return context;
15
16
  },
17
+ root() {
18
+ return rootContext;
19
+ },
16
20
  literal({ value }) {
17
21
  return value;
18
22
  },
@@ -20,49 +24,76 @@ export function evaluateJsonSelector(
20
24
  return getField(context, id);
21
25
  },
22
26
  fieldAccess({ expression, field }) {
23
- return getField(evaluateJsonSelector(expression, context), field);
27
+ return getField(
28
+ evaluateJsonSelector(expression, context, rootContext),
29
+ field
30
+ );
24
31
  },
25
32
  indexAccess({ expression, index }) {
26
- return getIndex(evaluateJsonSelector(expression, context), index);
33
+ return getIndex(
34
+ evaluateJsonSelector(expression, context, rootContext),
35
+ index
36
+ );
27
37
  },
28
38
  idAccess({ expression, id }) {
29
- return findId(evaluateJsonSelector(expression, context), id);
39
+ return findId(
40
+ evaluateJsonSelector(expression, context, rootContext),
41
+ id
42
+ );
30
43
  },
31
44
  project({ expression, projection }) {
32
- return project(evaluateJsonSelector(expression, context), projection);
45
+ return project(
46
+ evaluateJsonSelector(expression, context, rootContext),
47
+ projection,
48
+ rootContext
49
+ );
33
50
  },
34
51
  filter({ expression, condition }) {
35
- return filter(evaluateJsonSelector(expression, context), condition);
52
+ return filter(
53
+ evaluateJsonSelector(expression, context, rootContext),
54
+ condition,
55
+ rootContext
56
+ );
36
57
  },
37
58
  slice({ expression, start, end, step }) {
38
59
  return slice(
39
- evaluateJsonSelector(expression, context),
60
+ evaluateJsonSelector(expression, context, rootContext),
40
61
  start,
41
62
  end,
42
63
  step
43
64
  );
44
65
  },
45
66
  flatten({ expression }) {
46
- return flatten(evaluateJsonSelector(expression, context));
67
+ return flatten(evaluateJsonSelector(expression, context, rootContext));
47
68
  },
48
69
  not({ expression }) {
49
- return isFalseOrEmpty(evaluateJsonSelector(expression, context));
70
+ return isFalseOrEmpty(
71
+ evaluateJsonSelector(expression, context, rootContext)
72
+ );
50
73
  },
51
74
  compare({ lhs, rhs, operator }) {
52
- const lv = evaluateJsonSelector(lhs, context);
53
- const rv = evaluateJsonSelector(rhs, context);
75
+ const lv = evaluateJsonSelector(lhs, context, rootContext);
76
+ const rv = evaluateJsonSelector(rhs, context, rootContext);
54
77
  return compare(lv, rv, operator);
55
78
  },
56
79
  and({ lhs, rhs }) {
57
- const lv = evaluateJsonSelector(lhs, context);
58
- return isFalseOrEmpty(lv) ? lv : evaluateJsonSelector(rhs, context);
80
+ const lv = evaluateJsonSelector(lhs, context, rootContext);
81
+ return isFalseOrEmpty(lv)
82
+ ? lv
83
+ : evaluateJsonSelector(rhs, context, rootContext);
59
84
  },
60
85
  or({ lhs, rhs }) {
61
- const lv = evaluateJsonSelector(lhs, context);
62
- return !isFalseOrEmpty(lv) ? lv : evaluateJsonSelector(rhs, context);
86
+ const lv = evaluateJsonSelector(lhs, context, rootContext);
87
+ return !isFalseOrEmpty(lv)
88
+ ? lv
89
+ : evaluateJsonSelector(rhs, context, rootContext);
63
90
  },
64
91
  pipe({ lhs, rhs }) {
65
- return evaluateJsonSelector(rhs, evaluateJsonSelector(lhs, context));
92
+ return evaluateJsonSelector(
93
+ rhs,
94
+ evaluateJsonSelector(lhs, context, rootContext),
95
+ rootContext
96
+ );
66
97
  },
67
98
  },
68
99
  context
@@ -71,15 +102,18 @@ export function evaluateJsonSelector(
71
102
 
72
103
  export function project(
73
104
  value: unknown[],
74
- projection: JsonSelector | undefined
105
+ projection: JsonSelector | undefined,
106
+ rootContext: unknown
75
107
  ): unknown[];
76
108
  export function project(
77
109
  value: unknown,
78
- projection: JsonSelector | undefined
110
+ projection: JsonSelector | undefined,
111
+ rootContext: unknown
79
112
  ): unknown[] | null;
80
113
  export function project(
81
114
  value: unknown,
82
- projection: JsonSelector | undefined
115
+ projection: JsonSelector | undefined,
116
+ rootContext: unknown
83
117
  ): unknown[] | null {
84
118
  if (!isArray(value)) {
85
119
  return null;
@@ -88,25 +122,31 @@ export function project(
88
122
  return value;
89
123
  }
90
124
  const result = value
91
- .map((e) => evaluateJsonSelector(projection, e))
125
+ .map((e) => evaluateJsonSelector(projection, e, rootContext))
92
126
  .filter((e) => e != null);
93
127
  return result;
94
128
  }
95
129
 
96
- export function filter(value: unknown[], condition: JsonSelector): unknown[];
130
+ export function filter(
131
+ value: unknown[],
132
+ condition: JsonSelector,
133
+ rootContext: unknown
134
+ ): unknown[];
97
135
  export function filter(
98
136
  value: unknown,
99
- condition: JsonSelector
137
+ condition: JsonSelector,
138
+ rootContext: unknown
100
139
  ): unknown[] | null;
101
140
  export function filter(
102
141
  value: unknown,
103
- condition: JsonSelector
142
+ condition: JsonSelector,
143
+ rootContext: unknown
104
144
  ): unknown[] | null {
105
145
  if (!isArray(value)) {
106
146
  return null;
107
147
  }
108
148
  const result = value.filter(
109
- (e) => !isFalseOrEmpty(evaluateJsonSelector(condition, e))
149
+ (e) => !isFalseOrEmpty(evaluateJsonSelector(condition, e, rootContext))
110
150
  );
111
151
  return result;
112
152
  }
package/src/format.ts CHANGED
@@ -63,6 +63,9 @@ export function formatJsonSelector(
63
63
  current() {
64
64
  return !options.currentImplied ? "@" : "";
65
65
  },
66
+ root() {
67
+ return "$";
68
+ },
66
69
  literal({ value }) {
67
70
  return formatLiteral(value);
68
71
  },
package/src/grammar.pegjs CHANGED
@@ -97,7 +97,15 @@ projection =
97
97
  / ws "[" ws @slice ws "]"
98
98
 
99
99
  slice = start:number? ws ":" ws end:number? ws ":"? ws step:number?
100
- { return (expression) => ({ type: "slice", expression, start, end, step }); }
100
+ {
101
+ return (expression) => ({
102
+ type: "slice",
103
+ expression,
104
+ start: start ?? undefined,
105
+ end: end ?? undefined,
106
+ step: step ?? undefined,
107
+ });
108
+ }
101
109
 
102
110
  index_expression = lhs:index_lhs rhs:index_rhs* { return reduceProjection(lhs, rhs); }
103
111
 
@@ -117,6 +125,7 @@ member_expression = lhs:primary_expression rhs:dot_rhs* { return reduceProjectio
117
125
  primary_expression =
118
126
  id:identifier { return { type: "identifier", id }; }
119
127
  / "@" { return { type: "current" }; }
128
+ / "$" { return { type: "root" }; }
120
129
  / literal
121
130
  / value:raw_string { return { type: "literal", value }; }
122
131
  / "(" @selector ")"
package/src/visitor.ts CHANGED
@@ -14,11 +14,13 @@ import {
14
14
  JsonSelectorOr,
15
15
  JsonSelectorPipe,
16
16
  JsonSelectorProject,
17
+ JsonSelectorRoot,
17
18
  JsonSelectorSlice,
18
19
  } from "./ast";
19
20
 
20
21
  export interface Visitor<R, C> {
21
22
  current(node: JsonSelectorCurrent, context: C): R;
23
+ root(node: JsonSelectorRoot, context: C): R;
22
24
  literal(node: JsonSelectorLiteral, context: C): R;
23
25
  identifier(node: JsonSelectorIdentifier, context: C): R;
24
26
  fieldAccess(node: JsonSelectorFieldAccess, context: C): R;
@@ -43,6 +45,8 @@ export function visitJsonSelector<R, C>(
43
45
  switch (selector.type) {
44
46
  case "current":
45
47
  return visitor.current(selector, context);
48
+ case "root":
49
+ return visitor.root(selector, context);
46
50
  case "literal":
47
51
  return visitor.literal(selector, context);
48
52
  case "identifier":