Preserves Path schema

Preserves Path is a language for selecting and filtering portions of a Preserves value. It has an associated schema describing the various kinds of Path expressions as abstract syntax.

The schema source below is taken from path/path.prs in the Preserves source code repository.

Preserves Path expressions come in several flavours: selectors, steps (axes and filters), and predicates. Each is described below along with its abstract syntax definitions.

Selectors and Steps

Selectors are a sequence of steps, applied one after the other to the currently-selected value. Each step transforms an input into zero or more outputs. A step is an axis or a filter.

Selector = [Step ...] .
Step = Axis / Filter .

Axes: selecting portions of the input

Each axis step generally selects some sub-portion or -portions of the current document. An axis may also have a secondary filtering effect: for example, label only applies to Records, and will yield an empty result set when applied to any other kind of input.

Axis =
/ <values>          # yields the immediate subvalues of the input nonrecursively
/ <descendants>     # recurses through all descendant subvalues of the input
/ <at @key any>     # extracts a subvalue named by the given key, if any
/ <label>           # extracts a Record's label, if any
/ <keys>            # extracts all keys (for subvalues) of the input, nonrecursively
/ <length>          # extracts the length/size of the input, if any
/ <annotations>     # extracts all annotations attached to the input
/ <embedded>        # moves into the representation of an embedded value, if any
/ <parse @module [symbol ...] @name symbol>   # parses using Preserves Schema
/ <unparse @module [symbol ...] @name symbol> # unparses using Preserves Schema
.

The parse and unparse variants name Schema definitions, to be resolved by the eventual surrounding context in which the expression will be executed. A parse axis parses the input using a Schema definition; if the parse succeeds, the axis moves into the parse result. Similarly, unparse expects an abstract parse result, transforming it back into a concrete value according to the Schema definition.

Filters: rejecting inputs

Each filter step generally applies some test to the current document as a whole, either emitting it unchanged (with exceptions, detailed below) or emitting no outputs at all.

Filter =
/ <nop>                                 # Always emit the input
/ <compare @op Comparison @literal any> # Emit iff the comparison holds
/ <regex @regex string>                 # Emit iff input is String and regex matches
/ <test @pred Predicate>                # Apply complex predicate
/ <real>                                # Emit iff input is Double, or Integer
/ <int>                                 # TRUNCATE and emit iff Double or Integer
/ <kind @kind ValueKind>                # Emit iff input kind matches
.

Complex predicates

The complex predicates in a test filter are built up from logical connectives over selectors. A Selector predicate evaluates to true whenever, applied to its input, it results in a non-empty output set.

Predicate =
/ Selector
/ <not @pred Predicate>
/ <or @preds [Predicate ...]>
/ <and @preds [Predicate ...]>
.

Comparison against a literal

Each compare filter includes a Comparison and a literal value to compare the input against. For example, <compare eq 3> only produces an output if the input is equal (according to the Preserves semantic model) to 3.

Comparison = =eq / =ne / =lt / =ge / =gt / =le .

NB. For inequalities (lt/ge/gt/le), comparison between values of different kinds is undefined in the current draft specification.

Filtering by value kind

Each kind filter selects only values from one of the kinds of Preserves value:

ValueKind =
/ =Boolean / =Double / =SignedInteger / =String / =ByteString / =Symbol
/ =Record / =Sequence / =Set / =Dictionary
/ =Embedded
.