*****************
XPath Expressions
*****************

The *Yangson* library includes a fairly complete implementation of
XPath parser and evaluator. It supports XPath 1.0 [XPath]_ with
extensions defined for YANG 1.1 [RFC7950]_, such as new XPath
functions, default namespace, and other features.

.. testsetup::

   import json
   import os
   from yangson import DataModel
   from yangson.schemadata import SchemaContext
   from yangson.xpathparser import XPathParser
   os.chdir("examples/ex4")

.. testcleanup::

   os.chdir("../..")

Doctest__ snippets in this section use the data model and instance
document from :ref:`sec-ex4`.

__ http://www.sphinx-doc.org/en/stable/ext/doctest.html

.. doctest::

   >>> dm = DataModel.from_file("yang-library-ex4.json",
   ... [".", "../../../yang-modules/ietf"])
   >>> with open("example-data.json") as infile:
   ...     ri = json.load(infile)
   >>> inst = dm.from_raw(ri)


Parser of XPath Expressions
===========================

.. module:: yangson.xpathparser
   :synopsis: Parser for XPath expressions

The :mod:`.xpathparser` module implements a parser for XPath 1.0
expressions with YANG 1.1 extensions.

The module defines the following classes:

* :class:`XPathParser`: Recursive-descent parser for XPath expressions.

.. class:: XPathParser(text: str, sctx: SchemaContext) -> Expr

   This class is a subclass of :class:~.parser.Parser`, and implements
   a recursive-descent parser for XPath expressions. Constructor
   argument *text* contains the textual form of an XPath expression
   (see also the :attr:`.Parser.input` attribute), and *sctx* initializes
   the value of the :attr:`sctx` instance attribute.

   .. rubric:: Instance Attributes

   .. attribute:: sctx

      :class:`~.schemadata.SchemaContext` that specifies the schema
      context in which the XPath expression is parsed.

   .. rubric:: Public Methods

   .. method:: parse() -> Expr

      Parse the input XPath expression and return a node of an XPath
      AST that can be evaluated.

      This method may raise the following exceptions:

      * :exc:`~.InvalidXPath` – if the input XPath expression is
        invalid.
      * :exc:`~.NotSupported` – if the input XPath expression contains a
        feature that isn't supported by the implementation, such as
        the ``preceding::`` axis.
      * other exceptions that are defined in the :mod:`.parser`
        module.

   .. doctest::

      >>> fref = inst["example-4-a:bag"]["example-4-b:fooref"]
      >>> xp = 'deref(.)/../../quux[2]/preceding-sibling::quux = 3.1415'
      >>> sctx = SchemaContext(dm.schema_data, 'example-4-b', ('example-4-b', ''))
      >>> xparser = XPathParser(xp, sctx)
      >>> cxp = xparser.parse()
      >>> cxp.__class__.__name__
      'EqualityExpr'

XPath Abstract Syntax Tree
==========================

.. module:: yangson.xpathast
   :synopsis: Abstract syntax tree for XPath expressions

The :mod:`.xpathast` module defines classes that allow for building
`abstract syntax trees`__ (AST) for XPath 1.0 expressions with
extensions introduced by YANG 1.1. Only the following class is
intended to be public:

__ https://en.wikipedia.org/wiki/Abstract_syntax_tree

* :class:`Expr`: XPath 1.0 expression with YANG 1.0 extensions.

.. class:: Expr

   An abstract superclass for nodes of the XPath abstract syntax
   tree. The methods of this class described below comprise the public
   API for compiled XPath expressions.

   .. rubric:: Public Methods

   .. method:: __str__() -> str

      Print a serialized string representation of the receiver. YANG
      module names are used as XML namespace prefixes. Note also that
      all numbers (including integers) are printed as floats, because
      the latter is the only numeric type defined in XPath 1.0.

      .. doctest::

	 >>> str(cxp)
	 'deref(.)/../../example-4-b:quux[2.0]/preceding-sibling::example-4-b:quux = 3.1415'

   .. method:: syntax_tree() -> str

      Print the abstract syntax tree of the receiver. This is mainly
      useful for debugging XPath expressions.

      .. doctest::

         >>> print(cxp.syntax_tree(), end='')
         EqualityExpr (=)
           PathExpr
             FilterExpr
               FuncDeref
                 Step (self None)
             LocationPath
               LocationPath
                 LocationPath
                   Step (parent None)
                   Step (parent None)
                 Step (child ('quux', 'example-4-b'))
                   -- Predicates:
                      Number (2.0)
               Step (preceding_sibling ('quux', 'example-4-b'))
           Number (3.1415)

   .. method:: evaluate(node: InstanceNode) -> XPathValue

      Evaluate the receiver and return the result, which can be a
      node-set, string, number or boolean. The *node* argument is an
      :class:`~.instance.InstanceNode` that is used as the context
      node for XPath evaluation.

      This method raises :exc:`~.XPathTypeError` if a subexpression
      evaluates to a value whose type is not allowed at a given
      place.

      .. doctest::

         >>> cxp.evaluate(fref)
         True
