Open
Conversation
…ance, in-place update The binding tree is now written to a grammar a reader can parse without guessing (a scope is an object, or an array whose first element is the own-bindings object -- always written -- followed by one list per repeated constraint or group, in schema order, empty when nothing matched; a list's iterations are uniform), and each iteration of a repeated shape-valued constraint carries "@node", the input node it matched. A non-repeated nested shape merges into the scope that matched it. shex.js's stored bindings still compare equal once @node is stripped, and its two older layouts are still read. The materializer no longer flattens the tree into frames read by a forward-only cursor. It evaluates the output schema over the scope tree: a constraint reads its variable from the scope it is evaluated at or the nearest ancestor binding it, and reading marks nothing, so a parent's binding can be read in every item; a repetition iterates the deepest input list its body's variables are bound at, skipping items whose body fails, honouring min and max; a body reading from two unrelated lists is refused by name; OneOf, ShapeOr, optionals and extensions yield alternatives combined by product and pruned to max_accepts, the one that read the most distinct bindings winning. A nested schema now maps to itself, sibling lists transpose, a parent's binding read at the parent and in each item no longer mixes items, and the round-trip law holds on every example; the expected graphs are unchanged. ThreadedMaterializer keeps its name and results. Node identity: a shape-valued constraint with a %Map variable names its node; %Map:{ id(arg, ...) %} keys it -- variables, @node, or an IRI template <...{v:x}...> percent-encoded as R2RML does -- so equal keys merge and id(@node) maps a graph onto itself node for node; other nodes are minted from the root, constraint, depth and scope, so runs agree and materializing the same root twice adds nothing. A value a Map code produces must satisfy the constraint's value expression; a plain literal whose lexical form fits the datatype is retyped, anything else fails the constraint like an unbound variable. analyse(input_schema, output_schema) and shexmap --check decide before any data whether a pair maps coherently: unrelated lists, "which one?" reads, unbound and unused variables, double binding sites, id() misuse. Every triple carries its provenance (constraint, input scope and node, bindings and statics read, how the object arose); Bindings.matched is the subgraph the input schema selected; ThreadedMaterializer.update, and shexmap --into, replace what the output schema holds at a root and leave the rest alone. A differential test runs every example through shex.js's extension-map when SHEXJS_EXTENSION_MAP is set. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The recorded bindings are now what shex.js (branch shexmap-scopes, 4d53c6e1) writes: the scope layout with @node, one list per repeated expression. The example test compares the whole tree against them, blank-node labels aside, instead of only the frames. The differential driver reads shex.js's bindingTree() when the checkout has it, hands @node over as written, and the EXTENDS example no longer diverges. normalize() keeps a scope's own bindings as a frame when its lists are all empty, as shex.js's normalizeBindingTree does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ShExMap (
pyshex.shexmap) maps RDF between two ShEx schemas whose triple constraints share%Map:{ … %}variables. This PR replaces the materializer's frame-cursor model, ported from shex.js, with materialization by iteration scopes, and adds what that makes possible.Why
With cardinality above one, flattening the binding tree into frames and reading them with a forward-only cursor could not preserve the input's structure: a nested schema did not map to itself, a list of
(a, b)pairs could not be transposed into a list ofas and a list ofbs, and reading a parent's binding a second time inside an item stepped the cursor and mixed items. Those are properties of the model, not bugs in the search (the design notes trace them to the nested-relational result thatnest ∘ unnestis the identity only with keys at every level).What changes
@node, the input node it matched. shex.js's stored bindings still compare equal modulo@node, and its two older layouts are still read.max_accepts; the one reading the most distinct bindings wins.ThreadedMaterializerkeeps its name and results. Every example's expected graph is unchanged; the round-trip law (materialize with the input schema, bind again) holds on all of them.%Mapvariable on a shape-valued constraint names its node;%Map:{ id(arg, …) %}keys it (variables,@node, or an IRI template<…{v:x}…>percent-encoded as R2RML does), so equal keys merge; unkeyed nodes are minted from root, constraint, depth and scope, so runs agree.analyse(input_schema, output_schema)andshexmap --checkdecide before any data whether a pair maps coherently.Bindings.matchedis the subgraph the input schema selected;ThreadedMaterializer.update/materialize(replace=True)/shexmap --intoreplace what the output schema holds at a root and leave the rest alone.@shexjs/extension-mapwhenSHEXJS_EXTENSION_MAPpoints at a built checkout (13 of 14 agree; shex.js binds nothing through EXTENDS).The design notes behind this (problem analysis, literature, the normative draft, per-phase status) are kept outside the repo for now; the corresponding shex.js changes are being prepared separately.
🤖 Generated with Claude Code