parse
Parses an expression such as $.items[*].sku or $["odd name"].
The same grammar app.oreshkov.rabosh.variant.VariantPath.parse accepts, with [*] in place of a numeric index and .. for a descendant. A numeric index is rejected rather than silently collapsed: $.items[0] means something this type cannot represent, and quietly widening it to $.items[*] would answer a question the caller did not ask.
.. is read greedily and the name after it carries no dot, which is what keeps the two spellings apart: $..sku is CatalogStep.AnyDescendant then a field, $.a.sku is two fields, and $...sku is neither and fails. $.. alone is every node in the document, root included — the shape an elemMatch over a subtree of unknown depth needs. Two .. in a row are refused, because the second selects nothing the first does not and there would be no expression for it to round-trip through.
A field name that is not [A-Za-z0-9_]+ requires the bracket form, and the example that matters is not an odd one. $.@type does not parse — the dot form takes an identifier — so a protobuf-JSON corpus, where @type is on every message, is written $["@type"] throughout. In Kotlin the readable spelling is a raw string:
CatalogPath.parse("""$["@type"]""")
CatalogPath.parse("""$.players[*]["@type"]""")Inside the quotes a backslash escapes the next character literally, so $["a\nb"] is the three-character name anb and not a, newline, b. That is self-consistent and it is deliberately not RFC 9535 §2.7's escaping — see app.oreshkov.rabosh.variant.VariantPath.toNormalizedPath for the interchange spelling, which is the one to hand to something outside the engine.
Throws
with the offending position, for malformed input.