diagrams-query

    Portable structural tree queries with a Tree-sitter-style syntax and a BPMN adapter

    Download zip
    Author
    Version
    0.4.0
    License
    Apache-2.0
    Last updated
    6 days ago
    Downloads
    14

    #moonrockz/diagrams-query

    A portable, bounded structural query language for immutable trees. The syntax borrows Tree-sitter node patterns, fields, alternations and captures. It is not Tree-sitter compatible: it has its own grammar, a fixed set of native predicates and domain semantics that a tree adapter declares. The root package imports only MoonBit core and runs on wasm, wasm-gc, js and native. The opt-in package moonrockz/diagrams-query/bpmn adapts the BPMN document tree of moonrockz/bpmn. The Python package diagrams.mbt uses the same compiler and evaluator through its native extension.

    #Language version 1

    query := rule+ rule := captured | '(' captured predicate+ ')' captured := node capture* node := '(' selector part* ')' | '[' captured+ ']' part := captured | field ':' captured | '!' field selector := qualified-kind | 'category.' category | '_' capture := '@' capture-name predicate := '(' predicate-name capture string* ')'

    • An identifier has ASCII letters, digits, _ and -, and starts with a letter or _. A qualified name is prefix.local; an unqualified name has no namespace. Capture names can also contain . after the first character.
    • Strings use JSON escapes and keep Unicode values. ; starts a comment to the end of the line. Whitespace separates tokens.
    • The namespace map of the compile call binds query prefixes to namespace URIs. Source-document prefixes have no effect. category is not a prefix.

    ConstructMeaning
    (prefix.kind)A node of that exact kind
    (category.name)A node of that adapter category
    (_)Any node
    (a (b))b is a direct containment child of a
    (a (b) (c))Distinct children b then c, in increasing positions; other siblings can be between them
    (a f: (b))A direct child b in field f
    (a !f)a has no child in field f
    [(a) (b)]Alternatives, tried in source order
    @nameCapture the node (for an alternation, the node that the chosen branch matched)

    #Native predicates

    Predicates are conjunctive. They run after all captures of a match are bound. A predicate whose capture is not bound by the chosen alternative is false.

    PredicateOperandsTrue when
    #has-attr?capture, attributeThe attribute is present
    #missing-attr?capture, attributeThe attribute is absent
    #attr-eq?capture, attribute, valueThe attribute is present and its decoded value equals value
    #attr-not-eq?capture, attribute, valueThe attribute is present and its value differs; absent is false
    #attr-in?capture, attribute, value+The attribute is present and its value is one of the values
    #text-eq?capture, valueThe node has adapter scalar text equal to value; no descendant text is joined

    An unqualified attribute string such as "name" has no namespace. A string such as "vendor:flag" uses the query namespace map. A missing attribute is different from an empty one. Queries never run host callbacks or external code; an unregistered predicate is a compile error.

    #Matching and order

    Candidate roots are the search root and its containment descendants, in preorder. At each root, rules are tried in source order. Successful child assignments are enumerated in lexicographic order of child positions, and alternatives in source order. Equal matches from different rules or branches stay distinct; nothing is de-duplicated. Captures of a match are reported in source order of their capture sites; one node can have several capture roles. Capture names are unique within each successful alternative and are scoped to their rule. Matching follows only the containment that the adapter declares. IDs, references, coordinates and labels never create child edges.

    #Unsupported features

    Version 1 refuses these features with a stable code and the query-source span. It never interprets them partially.

    FeatureCode
    Quantifiers *, +, ?query/unsupported-quantifier
    Anchors .query/unsupported-anchor
    Directives such as #set!query/unsupported-directive
    Regular expressions such as #match?query/unsupported-regex
    Anonymous string nodes such as "+"query/unsupported-anonymous-node
    Sibling groups such as ((a) (b))query/unsupported-grouping
    Other predicates such as #eq?query/unknown-predicate

    query_diagnostic_codes lists every compile and projection code. A diagnostic has a code, severity, message and, for query text, a half-open span. Offsets and columns count UTF-16 code units; lines and columns start at 1. CRLF, LF and CR each count as one line break. Forms built in code have no spans.

    ///|
    test "unsupported features fail with stable codes" {
    let schema = @query.Schema::new(
    id="example/tree",
    version=1,
    kinds=[@query.Name::new("", "box")],
    categories=["thing"],
    fields=[],
    )
    for case in [
    ("(box)*", "query/unsupported-quantifier"),
    ("(box . (box))", "query/unsupported-anchor"),
    ("((box) @b (#set! @b \"k\"))", "query/unsupported-directive"),
    ("((box) @b (#match? @b \"x\"))", "query/unsupported-regex"),
    ("(box \"+\")", "query/unsupported-anonymous-node"),
    ("((box) (box))", "query/unsupported-grouping"),
    ("((box) @b (#eq? @b \"x\"))", "query/unknown-predicate"),
    ] {
    let diagnostics = @query.compile(case.0, schema).unwrap_err()
    assert_eq(diagnostics[0].code, case.1)
    assert_true(diagnostics[0].span is Some(_))
    }
    }

    #Tree adapters and schemas

    An adapter implements QueryTree over integer nodes. It publishes a Schema: an identifier and version, the kind, category and field inventory, the reserved namespaces (whose names must be in the inventory), whether other namespaces are open extensions, the scalar tags and the extra projection fields. A plan records the schema identifier and version. A cursor over a tree with another schema fails with IncompatibleSchema before it searches.

    compile (query text) and compile_form (a typed QueryForm) use the same checks and limits and give equal plans. QueryPlan::normalized is the public normalized form, with expanded {uri}local names.

    ///|
    struct Shelf {
    kinds : Array[String]
    children : Array[Array[Int]]
    names : Array[String?]
    }

    ///|
    let shelf_schema : @query.Schema = @query.Schema::new(
    id="example/shelf",
    version=1,
    kinds=[@query.Name::new("", "shelf"), @query.Name::new("", "book")],
    categories=["item"],
    fields=[@query.Name::new("", "holds")],
    reserved_namespaces=[""],
    )

    ///|
    impl @query.QueryTree for Shelf with schema(_self) {
    shelf_schema
    }

    ///|
    impl @query.QueryTree for Shelf with root(_self) {
    0
    }

    ///|
    impl @query.QueryTree for Shelf with node_count(self) {
    self.kinds.length()
    }

    ///|
    impl @query.QueryTree for Shelf with kind(self, node) {
    Some(@query.Name::new("", self.kinds[node]))
    }

    ///|
    impl @query.QueryTree for Shelf with category(_self, _node) {
    "item"
    }

    ///|
    impl @query.QueryTree for Shelf with children(self, node) {
    self.children[node][:]
    }

    ///|
    impl @query.QueryTree for Shelf with field(_self, node) {
    if node == 0 {
    None
    } else {
    Some(@query.Name::new("", "holds"))
    }
    }

    ///|
    impl @query.QueryTree for Shelf with attribute(self, node, name) {
    if name == @query.Name::new("", "name") {
    self.names[node].map(text => @query.ScalarValue::{ tag: "text", text })
    } else {
    None
    }
    }

    ///|
    impl @query.QueryTree for Shelf with text(_self, _node) {
    None
    }

    ///|
    impl @query.QueryTree for Shelf with extra(_self, _node, _name) {
    None
    }

    ///|
    impl @query.QueryTree for Shelf with location(_self, _node) {
    None
    }

    ///|
    impl @query.QueryTree for Shelf with path(_self, _node) {
    []
    }

    ///|
    impl @query.QueryTree for Shelf with depth(_self, node) {
    if node == 0 {
    0
    } else {
    1
    }
    }

    ///|
    test "text and typed queries give the same plan and matches" {
    let shelf = Shelf::{
    kinds: ["shelf", "book", "book"],
    children: [[1, 2], [], []],
    names: [None, Some("Dune"), Some("")],
    }
    let text = "((shelf holds: (book) @b) @s (#attr-eq? @b \"name\" \"Dune\"))"
    let form = @query.QueryForm::new([
    @query.Rule::new(
    @query.Pattern::node(
    Kind(@query.QName::new("shelf")),
    parts=[
    Field(
    @query.QName::new("holds"),
    @query.Pattern::node(Kind(@query.QName::new("book")), captures=["b"]),
    ),
    ],
    captures=["s"],
    ),
    predicates=[@query.Predicate::attr_eq("b", "name", "Dune")],
    ),
    ])
    let a = @query.compile(text, shelf_schema).unwrap()
    let b = @query.compile_form(form, shelf_schema).unwrap()
    assert_eq(a.normalized(), b.normalized())
    let result = a.collect(shelf)
    assert_eq(result, b.collect(shelf))
    assert_eq(result.matches.length(), 1)
    assert_eq(result.matches[0].captures.map(c => (c.name, c.node)).iter().to_array(), [
    ("b", 1),
    ("s", 0),
    ])
    // A missing name and an empty name are different.
    let empty = @query.compile("((book) @b (#attr-eq? @b \"name\" \"\"))", shelf_schema)
    .unwrap()
    .collect(shelf)
    assert_eq(empty.matches.map(m => m.captures[0].node).iter().to_array(), [2])
    }

    #Execution, projection and limits

    QueryPlan::execute starts an independent QueryCursor. Plans are immutable and reusable. next_batch(n) returns Rows with up to n matches, or a terminal state: Complete, Stopped or Failed(reason). Rows never claims completion; when a terminal state is found after some rows, the next request returns it. stop and close end a running cursor as Stopped; close is idempotent. QueryPlan::collect runs a cursor to its end.

    A Projection asks for fields of each captured node in one operation: kind, category, text, location, path, depth, field, attr:<name> and the adapter's extra fields. A field that a node does not have is Absent, which differs from an empty string. An unknown field is an input failure. Projection does not change matching or capture order.

    BoundDefaultFailure
    CompileLimits.source_units65,536query/source-limit
    CompileLimits.patterns, pattern_nodes, predicates256, 4,096, 1,024query/pattern-limit
    CompileLimits.nesting (at most 1,000)32query/nesting-limit
    CompileLimits.captures256query/capture-limit
    ExecutionLimits.work10,000,000Failed(Limit(Work))
    ExecutionLimits.pending100,000Failed(Limit(Pending))
    ExecutionLimits.results1,000,000Failed(Limit(Results))
    ExecutionLimits.output_units100,000,000Failed(Limit(Output))

    Execution bounds are cumulative over all batches of a cursor; a new batch does not reset them. work counts candidate nodes, matching steps, child probes and predicate checks, so it bounds scans that find nothing. pending bounds open partial matches, which grow with ambiguous child assignments. Exactly results matches complete; one more fails. Matching uses heap stacks, so deep trees do not use the host stack; a 10,000-level tree runs on all targets.

    ///|
    test "batches, cumulative limits and truthful terminal states" {
    let shelf = Shelf::{
    kinds: ["shelf", "book", "book", "book"],
    children: [[1, 2, 3], [], [], []],
    names: [None, Some("a"), None, Some("b")],
    }
    let plan = @query.compile("(book) @b", shelf_schema).unwrap()
    let projection = @query.Projection::new(shelf_schema, [Attribute("name")]).unwrap()
    let cursor = plan.execute(shelf, projection~)
    guard cursor.next_batch(2) is Rows(first) else { fail("no rows") }
    assert_eq(first.length(), 2)
    assert_eq(first[1].captures[0].values[0], Absent)
    guard cursor.next_batch(2) is Rows(second) else { fail("no rows") }
    assert_eq(second.length(), 1)
    assert_true(cursor.next_batch(2) is Complete)
    // A work bound reached after a batch is a failure, not completion.
    let bounded = plan.execute(shelf, limits=@query.ExecutionLimits::new(work=5))
    guard bounded.next_batch(1) is Rows(_) else { fail("no rows") }
    assert_true(bounded.next_batch(10) is Failed(Limit(Work)))
    assert_true(bounded.next_batch(10) is Failed(Limit(Work)))
    }

    #Guidance

    • Compile a plan once and reuse it; each cursor has its own position and budgets.
    • Prefer a narrow candidate root and exact kinds over wildcards for large trees. Set work and pending for untrusted queries.
    • Request only the projection fields that you need; paths are materialized only when requested.
    • Stop or close a cursor when you have enough rows. A stopped or failed cursor is never complete, and the rows it delivered are not the full result.
    • Use the explicit graph operations of the domain library for references and reachability; tree queries do not follow them.

    See the BPMN adapter for the BPMN schema.

    QueryTree

    pub(open) trait QueryTree {
    fn schema(Self) -> Schema
    fn root(Self) -> Int
    fn node_count(Self) -> Int
    fn kind(Self, Int) -> Name?
    fn category(Self, Int) -> String
    fn children(Self, Int) -> ArrayView[Int]
    fn field(Self, Int) -> Name?
    fn attribute(Self, Int, Name) -> ScalarValue?
    fn text(Self, Int) -> String?
    fn extra(Self, Int, String) -> ScalarValue?
    fn location(Self, Int) -> QueryLocation?
    fn path(Self, Int) -> ReadOnlyArray[QueryPathStep]
    fn depth(Self, Int) -> Int
    }

    A read-only tree that the query evaluator can search.

    Nodes are integers from 0 to node_count() - 1. The evaluator follows only children, which is the containment declared by the adapter. It never follows references. An adapter must give the same answers for the same node for as long as a cursor uses it. All names and categories must be admitted by schema().

    CapturedNode

    pub struct CapturedNode {
    name : String
    node : Int
    values : ReadOnlyArray[Projected]
    } derive(Eq,
    Debug
    )

    A captured node with its projected values. node is the adapter node of the cursor's tree, so a capture is always qualified by that tree.

    CapturedNode::equal

    CapturedNode::not_equal

    fn CapturedNode::not_equal(x : CapturedNode, y : CapturedNode) -> Bool

    CompileLimits

    pub struct CompileLimits {
    source_units : Int
    patterns : Int
    pattern_nodes : Int
    nesting : Int
    captures : Int
    predicates : Int
    } derive(Eq,
    Debug
    )

    Compilation bounds. source_units counts UTF-16 code units of query text. patterns counts source rules, pattern_nodes node and alternation patterns, nesting the deepest pattern nesting and captures capture names. predicates counts predicates and their operands. nesting is at most 1000, which bounds the host stack that compilation uses.

    CompileLimits::equal

    CompileLimits::new

    fn CompileLimits::new(source_units? : Int, patterns? : Int, pattern_nodes? : Int, nesting? : Int, captures? : Int, predicates? : Int) -> CompileLimits

    CompileLimits::not_equal

    fn CompileLimits::not_equal(x : CompileLimits, y : CompileLimits) -> Bool

    CursorState

    pub(all) enum CursorState {
    Running
    Complete
    Stopped
    Failed(QueryFailure)
    } derive(Eq,
    Debug
    )

    CursorState::equal

    fn CursorState::equal(CursorState, CursorState) -> Bool

    CursorState::not_equal

    fn CursorState::not_equal(x : CursorState, y : CursorState) -> Bool

    ExecutionLimits

    pub struct ExecutionLimits {
    work : Int
    pending : Int
    results : Int
    output_units : Int
    } derive(Eq,
    Debug
    )

    Execution bounds, cumulative over all batches of one cursor. work counts inspected candidate nodes and matching steps, pending the open partial matches, results the delivered matches and output_units the projected output (UTF-16 code units of projected text, plus one for each value).

    ExecutionLimits::equal

    ExecutionLimits::new

    fn ExecutionLimits::new(work? : Int, pending? : Int, results? : Int, output_units? : Int) -> ExecutionLimits

    ExecutionLimits::not_equal

    fn ExecutionLimits::not_equal(x : ExecutionLimits, y : ExecutionLimits) -> Bool

    FieldRequest

    pub(all) enum FieldRequest {
    Kind
    Category
    Text
    Location
    Path
    Depth
    Field
    Attribute(String)
    Extra(String)
    } derive(Eq,
    Debug
    )

    A field that a projection reads from each captured node. The node identity is always part of a capture, so a projection with no fields returns identities alone.

    FieldRequest::equal

    FieldRequest::not_equal

    fn FieldRequest::not_equal(x : FieldRequest, y : FieldRequest) -> Bool

    FieldRequest::parse

    fn FieldRequest::parse(name : String) -> FieldRequest

    Parses a field name: kind, category, text, location, path, depth, field, attr:<attribute> or an adapter extra field such as id.

    Name

    pub struct Name {
    namespace_uri : String
    local_name : String
    } derive(Compare, Eq, Hash,
    Debug
    )

    An expanded name: a namespace URI and a local name. The empty URI means no namespace. Source prefixes are never part of a name.

    Name::clark

    fn Name::clark(self : Name) -> String

    Clark notation, {uri}local, or local with no namespace.

    Name::compare

    fn Name::compare(Name, Name) -> Int

    Name::equal

    fn Name::equal(Name, Name) -> Bool

    Name::hash

    fn Name::hash(self : Name) -> Int

    Name::hash_combine

    fn Name::hash_combine(Name, Hasher) -> Unit

    Name::new

    fn Name::new(namespace_uri : String, local_name : String) -> Name

    Name::not_equal

    fn Name::not_equal(x : Name, y : Name) -> Bool

    Name::op_ge

    fn Name::op_ge(x : Name, y : Name) -> Bool

    Name::op_gt

    fn Name::op_gt(x : Name, y : Name) -> Bool

    Name::op_le

    fn Name::op_le(x : Name, y : Name) -> Bool

    Name::op_lt

    fn Name::op_lt(x : Name, y : Name) -> Bool

    Name::to_repr

    Part

    pub(all) enum Part {
    Child(Pattern)
    Field(QName, Pattern)
    Absent(QName)
    } derive(Eq,
    Debug
    )

    A part of a node pattern. Child matches a direct child, Field a direct child in a named field, and Absent requires that the field has no child.

    Part::equal

    fn Part::equal(Part, Part) -> Bool

    Part::not_equal

    fn Part::not_equal(x : Part, y : Part) -> Bool

    Part::to_repr

    Pattern

    pub(all) enum Pattern {
    Node(Selector, ReadOnlyArray[Part], ReadOnlyArray[String])
    Alternation(ReadOnlyArray[Pattern], ReadOnlyArray[String])
    } derive(Eq,
    Debug
    )

    A structural pattern. Node has a selector, ordered parts and capture names. Alternation tries its branches in order; its captures bind the node that the selected branch matched.

    Pattern::alternation

    fn Pattern::alternation(branches : ArrayView[Pattern], captures? : ArrayView[String]) -> Pattern

    Pattern::equal

    fn Pattern::equal(Pattern, Pattern) -> Bool

    Pattern::node

    fn Pattern::node(selector : Selector, parts? : ArrayView[Part], captures? : ArrayView[String]) -> Pattern

    A node pattern.

    Pattern::not_equal

    fn Pattern::not_equal(x : Pattern, y : Pattern) -> Bool

    Pattern::to_repr

    Predicate

    pub struct Predicate {
    name : String
    capture : String
    operands : ReadOnlyArray[String]
    } derive(Eq,
    Debug
    )

    A native predicate. name is the predicate name without #, for example attr-eq?. operands are the strings after the capture.

    Predicate::attr_eq

    fn Predicate::attr_eq(capture : String, attribute : String, value : String) -> Predicate

    Predicate::attr_in

    fn Predicate::attr_in(capture : String, attribute : String, values : ArrayView[String]) -> Predicate

    Predicate::attr_not_eq

    fn Predicate::attr_not_eq(capture : String, attribute : String, value : String) -> Predicate

    Predicate::equal

    fn Predicate::equal(Predicate, Predicate) -> Bool

    Predicate::has_attr

    fn Predicate::has_attr(capture : String, attribute : String) -> Predicate

    Predicate::missing_attr

    fn Predicate::missing_attr(capture : String, attribute : String) -> Predicate

    Predicate::new

    fn Predicate::new(name : String, capture : String, operands? : ArrayView[String]) -> Predicate

    Predicate::not_equal

    fn Predicate::not_equal(x : Predicate, y : Predicate) -> Bool

    Predicate::text_eq

    fn Predicate::text_eq(capture : String, value : String) -> Predicate

    Projected

    pub(all) enum Projected {
    Absent
    Text(String)
    Scalar(ScalarValue)
    Name(Name)
    Int(Int)
    Location(QueryLocation)
    Path(ReadOnlyArray[QueryPathStep])
    } derive(Eq,
    Debug
    )

    A projected value. Absent means that the node has no such value; it is different from an empty string.

    Projected::equal

    fn Projected::equal(Projected, Projected) -> Bool

    Projected::not_equal

    fn Projected::not_equal(x : Projected, y : Projected) -> Bool

    Projection

    pub struct Projection {
    schema_id : String
    schema_version : Int
    fields : ReadOnlyArray[FieldRequest]
    // private fields
    }

    A checked field selection for one schema.

    Projection::identity

    fn Projection::identity(schema : Schema) -> Projection

    A projection with no fields: identities only.

    Projection::new

    fn Projection::new(schema : Schema, fields : ArrayView[FieldRequest], namespaces? : ArrayView[(String, String)]) -> Result[Projection, ReadOnlyArray[QueryDiagnostic]]

    Checks fields against schema. An unknown extra field, an unbound attribute prefix or too many fields is an input failure.

    Projection::to_repr

    QName

    pub struct QName {
    prefix : String?
    local_name : String
    } derive(Eq,
    Debug
    )

    An unresolved name of the query form. prefix is a query namespace prefix; None means no namespace.

    QName::equal

    fn QName::equal(QName, QName) -> Bool

    QName::new

    fn QName::new(local_name : String, prefix? : String) -> QName

    QName::not_equal

    fn QName::not_equal(x : QName, y : QName) -> Bool

    QName::to_repr

    QueryBatch

    pub(all) enum QueryBatch {
    Rows(ReadOnlyArray[QueryMatch])
    Complete
    Stopped
    Failed(QueryFailure)
    } derive(Eq,
    Debug
    )

    The result of one batch request. Rows is data and never claims completion; the next request gives the terminal state.

    QueryBatch::equal

    fn QueryBatch::equal(QueryBatch, QueryBatch) -> Bool

    QueryBatch::not_equal

    fn QueryBatch::not_equal(x : QueryBatch, y : QueryBatch) -> Bool

    QueryCursor

    pub struct QueryCursor[T] {
    // private fields
    }

    An execution of a plan over one tree. Cursors are independent: each has its own position and cumulative budgets. Matching uses explicit heap stacks, so deep trees do not use the host stack.

    QueryCursor::close

    fn[T] QueryCursor::close(self : QueryCursor[T]) -> Unit

    Releases the cursor's search state. Closing is idempotent; a running cursor becomes stopped.

    QueryCursor::delivered

    fn[T] QueryCursor::delivered(self : QueryCursor[T]) -> Int

    The matches delivered so far.

    QueryCursor::next_batch

    fn[T : QueryTree] QueryCursor::next_batch(self : QueryCursor[T], max : Int) -> QueryBatch

    Returns up to max matches (at least one is requested when max is smaller). When no match is left it returns the terminal state instead: Complete, Stopped or Failed. A terminal state found after some rows is returned by the next request, so Rows never implies completion.

    QueryCursor::state

    fn[T] QueryCursor::state(self : QueryCursor[T]) -> CursorState

    QueryCursor::stop

    fn[T] QueryCursor::stop(self : QueryCursor[T]) -> Unit

    Stops a running cursor. A stopped cursor does not claim completion.

    QueryCursor::to_repr

    QueryCursor::work

    fn[T] QueryCursor::work(self : QueryCursor[T]) -> Int

    The work units used so far.

    QueryDiagnostic

    pub struct QueryDiagnostic {
    code : String
    severity : Severity
    message : String
    span : QuerySpan?
    } derive(Eq,
    Debug
    )

    A compile or input diagnostic. span is present for query text and absent for forms built in code.

    QueryDiagnostic::equal

    QueryDiagnostic::not_equal

    fn QueryDiagnostic::not_equal(x : QueryDiagnostic, y : QueryDiagnostic) -> Bool

    QueryFailure

    pub(all) enum QueryFailure {
    Limit(QueryLimit)
    IncompatibleSchema(expected~ : String, actual~ : String)
    InvalidRoot
    InvalidLimits
    } derive(Eq,
    Debug
    )

    Why a cursor failed. A failure is terminal.

    QueryFailure::equal

    QueryFailure::not_equal

    fn QueryFailure::not_equal(x : QueryFailure, y : QueryFailure) -> Bool

    QueryForm

    pub struct QueryForm {
    rules : ReadOnlyArray[Rule]
    // private fields
    }

    The typed form of a query. parse gives the same form for query text, with source spans for diagnostics; forms built in code have no spans.
    impl Eq for QueryForm

    QueryForm::equal

    fn QueryForm::equal(a : QueryForm, b : QueryForm) -> Bool

    QueryForm::new

    fn QueryForm::new(rules : ArrayView[Rule]) -> QueryForm

    QueryForm::not_equal

    fn QueryForm::not_equal(x : QueryForm, y : QueryForm) -> Bool

    QueryForm::to_repr

    QueryLimit

    pub(all) enum QueryLimit {
    Work
    Pending
    Results
    Output
    } derive(Eq,
    Debug
    )

    The execution bound that stopped a cursor.

    QueryLimit::equal

    fn QueryLimit::equal(QueryLimit, QueryLimit) -> Bool

    QueryLimit::not_equal

    fn QueryLimit::not_equal(x : QueryLimit, y : QueryLimit) -> Bool

    QueryLocation

    pub(all) struct QueryLocation {
    source_id : String?
    start : QueryPosition
    end : QueryPosition
    } derive(Eq,
    Debug
    )

    A half-open source range with an optional source identifier.

    QueryLocation::equal

    QueryLocation::not_equal

    fn QueryLocation::not_equal(x : QueryLocation, y : QueryLocation) -> Bool

    QueryMatch

    pub struct QueryMatch {
    pattern : Int
    captures : ReadOnlyArray[CapturedNode]
    } derive(Eq,
    Debug
    )

    One successful match: the source pattern index and the bound captures in source order of their capture sites.

    QueryMatch::equal

    fn QueryMatch::equal(QueryMatch, QueryMatch) -> Bool

    QueryMatch::not_equal

    fn QueryMatch::not_equal(x : QueryMatch, y : QueryMatch) -> Bool

    QueryPathStep

    pub(all) struct QueryPathStep {
    field : Name
    index : Int
    position : Int
    } derive(Eq,
    Debug
    )

    One step of a document-relative path: the containing field, the index in that field and the position among all children of the parent.

    QueryPathStep::equal

    QueryPathStep::not_equal

    fn QueryPathStep::not_equal(x : QueryPathStep, y : QueryPathStep) -> Bool

    QueryPlan

    pub struct QueryPlan {
    language_version : Int
    schema_id : String
    schema_version : Int
    pattern_count : Int
    capture_names : ReadOnlyArray[String]
    // private fields
    }

    A checked, immutable and reusable query plan for one adapter schema.

    capture_names lists the capture sites in source order; a match reports each bound site with its name. normalized() is the public normalized representation: equal plans have equal normalized text.

    QueryPlan::collect

    fn[T : QueryTree] QueryPlan::collect(self : QueryPlan, tree : T, root? : Int, projection? : Projection, limits? : ExecutionLimits) -> QueryResult

    Runs an execution to its terminal state. On failure, matches holds the matches found before the failure.

    QueryPlan::execute

    fn[T : QueryTree] QueryPlan::execute(self : QueryPlan, tree : T, root? : Int, projection? : Projection, limits? : ExecutionLimits) -> QueryCursor[T]

    Starts an execution of the plan below root (the tree root by default), including root itself. An incompatible schema, an invalid root or invalid limits give a cursor that is already failed; the tree is not searched.

    QueryPlan::normalized

    fn QueryPlan::normalized(self : QueryPlan) -> String

    QueryPlan::to_repr

    QueryPosition

    pub(all) struct QueryPosition {
    offset : Int
    line : Int
    column : Int
    } derive(Eq,
    Debug
    )

    A position in source text. Offsets and columns count UTF-16 code units; lines and columns start at 1.

    QueryPosition::equal

    QueryPosition::not_equal

    fn QueryPosition::not_equal(x : QueryPosition, y : QueryPosition) -> Bool

    QueryResult

    pub struct QueryResult {
    matches : ReadOnlyArray[QueryMatch]
    outcome : CursorState
    } derive(Eq,
    Debug
    )

    All matches of an execution and its terminal state.

    QueryResult::equal

    fn QueryResult::equal(QueryResult, QueryResult) -> Bool

    QueryResult::not_equal

    fn QueryResult::not_equal(x : QueryResult, y : QueryResult) -> Bool

    QuerySpan

    pub struct QuerySpan {
    start : QueryPosition
    end : QueryPosition
    } derive(Eq,
    Debug
    )

    A half-open range of query source text.

    QuerySpan::equal

    fn QuerySpan::equal(QuerySpan, QuerySpan) -> Bool

    QuerySpan::not_equal

    fn QuerySpan::not_equal(x : QuerySpan, y : QuerySpan) -> Bool

    Rule

    pub struct Rule {
    pattern : Pattern
    predicates : ReadOnlyArray[Predicate]
    } derive(Eq,
    Debug
    )

    One source pattern with its conjunctive predicates.

    Rule::equal

    fn Rule::equal(Rule, Rule) -> Bool

    Rule::new

    fn Rule::new(pattern : Pattern, predicates? : ArrayView[Predicate]) -> Rule

    Rule::not_equal

    fn Rule::not_equal(x : Rule, y : Rule) -> Bool

    Rule::to_repr

    ScalarValue

    pub(all) struct ScalarValue {
    tag : String
    text : String
    } derive(Eq,
    Debug
    )

    A present attribute value. tag is one of the schema scalar tags and text is the decoded value that predicates compare.

    ScalarValue::equal

    fn ScalarValue::equal(ScalarValue, ScalarValue) -> Bool

    ScalarValue::not_equal

    fn ScalarValue::not_equal(x : ScalarValue, y : ScalarValue) -> Bool

    Schema

    pub struct Schema {
    id : String
    version : Int
    kinds : ReadOnlyArray[Name]
    categories : ReadOnlyArray[String]
    fields : ReadOnlyArray[Name]
    reserved_namespaces : ReadOnlyArray[String]
    open_extensions : Bool
    scalar_tags : ReadOnlyArray[String]
    extra_fields : ReadOnlyArray[String]
    // private fields
    }

    The published contract of a tree adapter.

    kinds, fields and categories are the inventory that queries can name. A kind or field in a namespace of reserved_namespaces must be in the inventory. A kind or field in another namespace is valid when open_extensions is true, also when no document contains it. With open_extensions false, every kind and field must be in the inventory. scalar_tags lists the tags of attribute values. extra_fields lists the adapter-specific projection fields, for example id.
    impl Eq for Schema

    Schema::admits_field

    fn Schema::admits_field(self : Schema, field : Name) -> Bool

    True when queries can name field under this schema.

    Schema::admits_kind

    fn Schema::admits_kind(self : Schema, kind : Name) -> Bool

    True when queries can name kind under this schema.

    Schema::compatible

    fn Schema::compatible(self : Schema, other : Schema) -> Bool

    True when a plan compiled for self can execute on a tree of other. Compatibility depends on the declared identifier and version, not on the contents of a document.

    Schema::equal

    fn Schema::equal(a : Schema, b : Schema) -> Bool

    Schema::has_category

    fn Schema::has_category(self : Schema, category : String) -> Bool

    Schema::new

    fn Schema::new(id~ : String, version~ : Int, kinds~ : ArrayView[Name], categories~ : ArrayView[String], fields~ : ArrayView[Name], reserved_namespaces? : ArrayView[String], open_extensions? : Bool, scalar_tags? : ArrayView[String], extra_fields? : ArrayView[String]) -> Schema

    Schema::not_equal

    fn Schema::not_equal(x : Schema, y : Schema) -> Bool

    Schema::to_repr

    Selector

    pub(all) enum Selector {
    Kind(QName)
    Category(String)
    Wildcard
    } derive(Eq,
    Debug
    )

    What a node pattern selects: an exact kind, a category or any node.

    Selector::equal

    fn Selector::equal(Selector, Selector) -> Bool

    Selector::not_equal

    fn Selector::not_equal(x : Selector, y : Selector) -> Bool

    Selector::to_repr

    Severity

    pub(all) enum Severity {
    Error
    Warning
    } derive(Eq,
    Debug
    )

    Severity::equal

    fn Severity::equal(Severity, Severity) -> Bool

    Severity::not_equal

    fn Severity::not_equal(x : Severity, y : Severity) -> Bool

    Severity::to_repr

    compile

    fn compile(source : String, schema : Schema, namespaces? : ArrayView[(String, String)], limits? : CompileLimits) -> Result[QueryPlan, ReadOnlyArray[QueryDiagnostic]]

    Parses and checks version 1 query text against schema.

    compile_form

    fn compile_form(form : QueryForm, schema : Schema, namespaces? : ArrayView[(String, String)], limits? : CompileLimits) -> Result[QueryPlan, ReadOnlyArray[QueryDiagnostic]]

    Checks a typed query form against schema and returns its plan. Query text uses the same checks through compile.

    is_capture_name

    fn is_capture_name(text : String) -> Bool

    True when text is a valid capture name (an identifier that may also contain dots after its first character).

    is_identifier

    fn is_identifier(text : String) -> Bool

    True when text is a version 1 identifier.

    language_version

    let language_version : Int

    The version of the query language that this package compiles.

    max_projection_fields

    let max_projection_fields : Int

    The most fields that one projection can request.

    parse

    fn parse(source : String, limits? : CompileLimits) -> Result[QueryForm, ReadOnlyArray[QueryDiagnostic]]

    Parses version 1 query text into its typed form, without schema checks. The form keeps source spans for the diagnostics of compile_form.

    query_diagnostic_codes

    let query_diagnostic_codes : ReadOnlyArray[String]

    Every diagnostic code that compilation and projection can give.