Skip to content

Commit

Permalink
Move selector parsers to subpackage. More consts. More docs. Distinct…
Browse files Browse the repository at this point in the history
… parse vs parse+compile functions.

Also switch to dag-json rather than plain json, because selectors may include links now (in the condition clauses).
  • Loading branch information
warpfork committed Aug 10, 2021
1 parent d527acb commit 3eb2c31
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 35 deletions.
35 changes: 0 additions & 35 deletions traversal/selector/helpers.go

This file was deleted.

94 changes: 94 additions & 0 deletions traversal/selector/parse/selector_parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
selectorparse package contains some helpful functions for parsing the serial form of Selectors.
Some common selectors are also exported as pre-compiled variables,
both for convenience of use and to be readable as examples.
*/
package selectorparse

import (
"strings"

"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagjson"
"github.com/ipld/go-ipld-prime/node/basic"
"github.com/ipld/go-ipld-prime/traversal/selector"
)

// ParseJSONSelector accepts a string of json which will be parsed as a selector,
// and returns an ipld.Node of the parsed Data Model.
// The returned ipld.Node is suitable to hand to `selector.CompileSelector`,
// or, could be composed programmatically with other Data Model selector clauses
// and then compiled later.
//
// The selector will be checked for compileability, and an error returned if it is not.
func ParseJSONSelector(jsonStr string) (ipld.Node, error) {
nb := basicnode.Prototype.Any.NewBuilder()
if err := dagjson.Decode(nb, strings.NewReader(jsonStr)); err != nil {
return nil, err
}
// Compile it, because that's where all of our error checking is right now.
// ... but throw that result away, because the point of this method is to return nodes that you can compose further.
// Ideally, we'd have just used Schemas for this check,
// which would be cheaper than running the full compile,
// and also more correct (because it would let us parse incomplete phrases that won't compile alone),
// but that's not currently how the Selectors code is implemented. Future work!
n := nb.Build()
if _, err := selector.CompileSelector(n); err != nil {
return nil, err
}
return n, nil
}

// ParseJSONSelector accepts a string of json which will be parsed as a selector,
// and returns a compiled and ready-to-run Selector.
//
// ParseJSONSelector is functionally equivalent to combining ParseJSONSelector and CompileSelector into one step.
func ParseAndCompileJSONSelector(jsonStr string) (selector.Selector, error) {
nb := basicnode.Prototype.Any.NewBuilder()
if err := dagjson.Decode(nb, strings.NewReader(jsonStr)); err != nil {
return nil, err
}
if s, err := selector.CompileSelector(nb.Build()); err != nil {
return nil, err
} else {
return s, nil
}
}

func must(s selector.Selector, e error) selector.Selector {
if e != nil {
panic(e)
}
return s
}

// CommonSelector_MatchPoint is a selector that matches exactly one thing: the first node it touches.
// It doesn't walk anywhere at all.
//
// This is not a very useful selector, but is an example of how selectors can be written.
var CommonSelector_MatchPoint = must(ParseAndCompileJSONSelector(`{".":{}}`))

// CommonSelector_MatchChildren will examine the node it is applied to,
// walk to each of its children, and match the children.
// It does not recurse.
// Note that the root node itself is visited (necessarily!) but it is not "matched".
var CommonSelector_MatchChildren = must(ParseAndCompileJSONSelector(`{"a":{">":{".":{}}}}`))

// CommonSelector_ExploreAllRecursively is a selector that walks over a graph of data,
// recursively, without limit (!) until it reaches every part of the graph.
// (This is safe to assume will halt eventually, because in IPLD, we work with DAGs --
// although it still may be a bad idea to do this in practice,
// because you could accidentally do this on terabytes of linked data, and that would still take a while!)
//
// It does not actually _match_ anything at all.
// That means if you're intercepting block loads (e.g. you're looking at calls to LinkSystem.StorageReadOpener), you'll see them;
// and if you're using `traversal.AdvVisitFn`, you'll still hear about nodes visited during the exploration;
// however, if you're using just `traversal.VisitFn`, nothing is considered "matched", so that callback will never be called.
var CommonSelector_ExploreAllRecursively = must(ParseAndCompileJSONSelector(`{"R":{"l":{"none":{}},":>":{"a":{">":{"@":{}}}}}}`))

// CommonSelector_MatchAllRecursively is like CommonSelector_ExploreAllRecursively, but also matching everything it touches.
// The first thing inside the recursion is an ExploreUnion clause (which means the selection continues with multiple logical paths);
// the first thing inside that union clause is a Matcher clause;
// the second thing inside that union is the ExploreAll clause, which gets us deeper, and then that contains the ExploreRecursiveEdge.
var CommonSelector_MatchAllRecursively = must(ParseAndCompileJSONSelector(`{"R":{"l":{"none":{}},":>":{"|":[{".":{}},{"a":{">":{"@":{}}}}]}}}`))
3 changes: 3 additions & 0 deletions traversal/selector/parse/selector_parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package selectorparse

// nothing -- this file just makes sure the vars get initialized, which is a defacto test.

0 comments on commit 3eb2c31

Please sign in to comment.