diff --git a/docs/corelang.dj b/docs/corelang.dj index a31535ce..2a0d17f9 100644 --- a/docs/corelang.dj +++ b/docs/corelang.dj @@ -437,17 +437,38 @@ For example: ### Compound paths -For each of the filters in this section, -you can employ any atomic filter instead of the leading `.` (identity). -For example, you can write `explode[]` as short form for `explode | .[]`. - -You can also terminate any filter in this section with a `?`, +You can concatenate arbitrarily many path operators to an atomic filter. +Such a sequence of path operators is called a _compound path_. +For example, you can write +`explode.[]` as short form for `explode | .[]`. + +If the leading atomic filter is `.` (identity), you can omit it. +For example, the following filters are all equivalent: + +- `{a: [{b: 1}]} | . | .a | .[] | .b --> 1` +- `{a: [{b: 1}]} | . .a .[] .b --> 1` +- `{a: [{b: 1}]} | .a .[] .b --> 1` +- `{a: [{b: 1}]} .a .[] .b --> 1` + +You can omit the leading `.` of path operators that have the shape `.[...]`, +unless the path operator is the head of the compound path and +the leading atomic filter `.` has been omitted. +For example, the following filters are all equivalent: + +- `[[[1, 2, 3]]] | .[0] | .[] | .[:-1] --> [1, 2]` +- `[[[1, 2, 3]]] | .[0] .[] .[:-1] --> [1, 2]` +- `[[[1, 2, 3]]] | .[0] [] [:-1] --> [1, 2]` +- `[[[1, 2, 3]]] [0] [] [:-1] --> [1, 2]` + +You can also terminate any path operator in this section with a `?`, which silences errors from that operator. For example, `.[]` yields an error if the input is neither an array nor an object, but `.[]?` silences such errors, yielding no output instead. +Examples: -You can chain together an arbitrary number of these operators; -for example, `.[0][]?[:-1]` is the same as `.[0] | .[]? | .[:-1]`. +- `[1, {a: 2 }, {a: 3 }] | .[].a? --> 2 3` +- `[1, [2, 3 ], [4, 5 ]] | .[][]? --> 2 3 4 5` +- ` 1, {a: [2]}, {a: [3]} | .a?[] --> 2 3` ::: Advanced diff --git a/jaq-core/src/load/parse.rs b/jaq-core/src/load/parse.rs index 84559dfe..6a6c9464 100644 --- a/jaq-core/src/load/parse.rs +++ b/jaq-core/src/load/parse.rs @@ -651,12 +651,15 @@ impl<'s, 't> Parser<'s, 't> { fn path(&mut self) -> Result<'s, 't, Path>> { let mut path: Vec<_> = core::iter::from_fn(|| self.path_part_opt()).collect(); while let Some(key) = self.dot() { - let key = if key.is_empty() { - self.str_key()? + path.push(if key.is_empty() { + match self.path_part_opt() { + Some(part_opt) => part_opt, + None => (path::Part::Index(self.str_key()?), self.opt()), + } } else { - Term::from_str(key) - }; - path.push((path::Part::Index(key), self.opt())); + (path::Part::Index(Term::from_str(key)), self.opt()) + }); + path.extend(core::iter::from_fn(|| self.path_part_opt())); } Ok(Path(path))