Unicorn ECMAScript Interpreter
Reference Manual
XPath Extensions
Version 2.0 (Draft)
31 October 2024

Abstract

This document describes XPath extensions supported by Unicorn ECMAScript Interpreter (UESI).

Status of this document

This document forms part of the Unicorn ECMAScript Interpreter (UESI) Reference Manual.

Comments on this document may be sent to [email protected]

Table of contents

Appendices


1 Introduction

This document describes XPath extensions supported by Unicorn ECMAScript Interpreter (UESI).

2 Overview

XPath extensions provide built-in ECMAScript objects that support XPath expressions and node-sets. XPath extensions are built on the top of XML extensions and DOM extensions implemented by UESI.

NodeSet objects represent XPath node-sets.

Expr objects represent XPath expressions.

3 Conventions

Concepts and notation of the ECMA-262 Standard (ECMAScript Language Specification) are used in this document.

This document relies also on the concepts introduced in "Unicorn ECMAScript Interpreter Reference Manual. XML Extensions" and "Unicorn ECMAScript Interpreter Reference Manual. DOM Extensions".

4 XPath Objects

There are certain built-in XPath extension objects available whenever UESI begins execution of a program. One, the XPath object, is accessible as an initial property of the global object. Others are accessible as initial properties of the XPath object.

All XPath extension objects implement the semantics of native ECMAScript objects as specified in ECMA-262.

In every case, the length property of a built-in Function object described in this section has the attributes { DontEnum, DontDelete, ReadOnly } (and no others). Every other property described in this section has the attributes { DontEnum, DontDelete } (and no others) unless otherwise specified.

4.1 The XPath Object

The XPath object does not have a [[Construct]] property; it is not possible to use the XPath object as a constructor with the new operator.

The XPath object does not have a [[Call]] property; it is not possible to invoke the XPath object as a function.

The value of the internal [[Prototype]] property of the XPath object is the Object prototype. The value of the internal [[Class]] property of the XPath object is "XPath".

4.1.1 Constructor Properties of the XPath Object

4.2 NodeSet Objects

NodeSet objects represent XPath node-sets. Furthermore, each NodeSet instance provides also functionality of node-set iterator. For every NodeSet instance, current position and current node are available as object properties.

4.2.1 The NodeSet Constructor Called as a Function

Not supported in the current release of UESI. Reserved for the future.

4.2.1.1 XPath.NodeSet(...)

Not supported in the current release of UESI. Reserved for the future.

4.2.2 The NodeSet Constructor

When XPath.NodeSet is called as part of a new expression, it is a constructor: it initializes the newly created NodeSet instance.

4.2.2.1 new XPath.NodeSet([item1 [, item2 ...]])

If the NodeSet constructor is called without arguments, the empty node-set is assigned to the newly created NodeSet instance.

Otherwise let N be number of arguments. Let item[i] be i-th argument. For each item[i], 1 <= i <= N, create a node-set S[i] according to the following rules.

If item[i] is a NodeSet instance (its [[Class]] property is "XPath.NodeSet"), let S[i] be this node-set.

If item[i] is a DOM Node (it contains the DOM Node prototype in its prototype chain), let S[i] be a node-set that consists of this single node.

If item[i] is a DOM NodeList (it contains the DOM NodeList prototype in its prototype chain), let S[i] be a node-set that consists of all nodes that are contained in this node list.

If item[i] is a DOM NamedNodeMap (it contains the DOM NamedNodeMap prototype in its prototype chain), let S[i] be a node-set that consists of all nodes that are contained in this named node map.

Otherwise the runtime error is generated.

Let U be the union of node-sets S[i], 1 <= i <= N. Assign the node-set U to the newly created NodeSet instance.

If the result node-set is empty, set position property to 0 and current property to null.

Otherwise, set position property to 1 and current property to the DOM Node object that represents the first node in the node-set.

4.2.3 Properties of the NodeSet Constructor

The value of the internal [[Prototype]] property of the NodeSet constructor is the Function prototype object.

Besides the internal properties and the length property (whose value is 1), the NodeSet constructor has the following properties.

4.2.3.1 XPath.NodeSet.prototype

The initial value of the XPath.NodeSet.prototype is the NodeSet prototype object.

This property has the attributes { DontEnum, DontDelete, ReadOnly }.

4.2.4 Properties of the NodeSet Prototype Object

The NodeSet prototype object is itself a NodeSet object (its [[Class]] is "XPath.NodeSet"). It represents the empty node-set (contains no nodes).

The value of the internal [[Prototype]] property of the NodeSet prototype object is the Object prototype.

4.2.4.1 XPath.NodeSet.prototype.constructor

The initial value of XPath.NodeSet.prototype.constructor is the built-in NodeSet constructor.

4.2.4.2 XPath.NodeSet.prototype.reset()

This function resets the internal node-set iterator of this NodeSet instance.

If the result node-set is empty, set position property to 0 and current property to null.

Otherwise, set position property to 1 and current property to the DOM Node object that represents the first node in the node-set.

The function returns undefined value.

4.2.4.3 XPath.NodeSet.prototype.next()

This function selects advances the internal node-set iterator of this NodeSet instance.

If the result node-set is empty or the current node of the node-set is its last node, set position property to 0 and current property to null.

Otherwise, increase position property by 1 and set current property to the DOM Node object that represents the node in the node-set which is following the current node.

The function returns undefined value.

4.2.4.4 XPath.NodeSet.prototype.toString()

This function calculates and returns the XPath string value of the first node in the node-set specified by this NodeSet instance, or the empty string if the node-set is empty.

4.2.5 Properties of the NodeSet Instances

The value of the internal [[Prototype]] property of the NodeSet instance is the NodeSet prototype object.

The value of the internal [[Class]] property of the NodeSet instance is "XPath.NodeSet".

NodeSet instances inherit properties from their [[Prototype]] object, and also have the following propetrties.

4.2.5.1 current

This property references the DOM Node object that corresponds to the current node of the internal node-set iterator or null if the node-set is empty or iterator had reached end of the node-set.

This property has the attributes { DontDelete, DontEnum, ReadOnly }.

4.2.5.2 position

This number property contains the position number (started with 1) of the current node of the internal node-set iterator or 0 if the node-set is empty or iterator had reached end of the node-set.

This property has the attributes { DontDelete, DontEnum, ReadOnly }.

4.2.5.3 count

This number property contains number of nodes in the node-set associated with this NodeSet instance.

This property has the attributes { DontDelete, DontEnum, ReadOnly }.

4.3 Expr Objects

Expr objects represent XPath expressions.

4.3.1 The Expr Constructor Called as a Function

Not supported in the current release of UESI. Reserved for the future.

4.3.1.1 XPath.Expr(...)

Not supported in the current release of UESI. Reserved for the future.

4.3.2 The Expr Constructor

When XPath.Expr is called as part of a new expression, it is a constructor: it initializes the newly created Expr instance.

4.3.2.1 new XPath.Expr(source)

Let S be a string value computed by ToString( source ). If S is a valid XPath expression assign it to the newly created Expr instance. (For performance reasons, UESI compiles XPath expressions into internal code every time the new Expr instance is created). Otherwise, generate the runtime error.

4.3.3 Properties of the Expr Constructor

The value of the internal [[Prototype]] property of the Expr constructor is the Function prototype object.

Besides the internal properties and the length property (whose value is 1), the Expr constructor has the following properties.

4.3.3.1 XPath.Expr.prototype

The initial value of the XPath.Expr.prototype is the Expr prototype object.

This property has the attributes { DontEnum, DontDelete, ReadOnly }.

4.3.4 Properties of the Expr Prototype Object

The Expr prototype object is itself a Expr object (its [[Class]] is "XPath.Expr"). The XPath expression "" (one that evaluates to the empty string) is associated with the Expr prototype.

The value of the internal [[Prototype]] property of the Expr prototype object is the Object prototype.

4.3.4.1 XPath.Expr.prototype.constructor

The initial value of XPath.Expr.prototype.constructor is the built-in Expr constructor.

4.3.4.2 XPath.Expr.prototype.eval(nodeSet)

This function evaluates the XPath expression.

If nodeSet is a NodeSet instance (its [[Class]] property is "XPath.NodeSet"), let N be this node-set. The current position in the internal node-set iterator remains unchanged.

If nodeSet is a DOM Node (it contains the DOM Node prototype in its prototype chain), let N be a node-set that consists of this single node. Set the current position in the internal node-set iterator to 1.

If nodeSet is a DOM NodeList (it contains the DOM NodeList prototype in its prototype chain), let N be a node-set that consists of all nodes that are contained in this node list. Set the current position in the internal node-set iterator to 1 if the node-set is not empty or to 0 otherwise.

If nodeSet is a DOM NamedNodeMap (it contains the DOM NamedNodeMap prototype in its prototype chain), let N be a node-set that consists of all nodes that are contained in this named node map. Set the current position in the internal node-set iterator to 1 if the node-set is not empty or to 0 otherwise.

Otherwise the runtime error is generated.

If the node-set N is empty, generate the runtime error.

Otherwise, set the expression context as follows.

Set the context node to the current node of the internal iterator of N.

Set the context position to the current position of the internal iterator of N.

Set the context size to the number of nodes in N.

Set the variable bindings to the empty set.

Set the function library to XPath core function library.

Use namespace declarations in scope on the context node (i.e., the current node of the internal iterator of N) as the set of namespace declarations

Evaluate the XPath expression associated with this Expr instance using the expression context defined above. Let V be the result of evaluation.

If V is XPath boolean, return the equivalent ECMAScript boolean value.

If V is XPath number, return the equivalent ECMAScript number value.

If V is XPath string, return the equivalent ECMAScript string value.

Otherwise V is XPath node-set. Create a new NodeSet instance and assign this node-set to it. If the node-set is empty, set position property to 0 and current property to null. Otherwise, set position property to 1 and current property to the DOM Node object that represents the first node in the node-set. Return the newly created NodeSet object.

4.3.4.3 XPath.Expr.prototype.toString()

This function returns the string that contains a valid XPath expression equivalent to the XPath expression associated with this Expr instance.

4.3.5 Properties of the Expr Instances

The value of the internal [[Prototype]] property of the Expr instance is the Expr prototype object.

The value of the internal [[Class]] property of the Expr instance is "XPath.Expr".

Expr instances inherit properties from their [[Prototype]] object, and also have the following propetrties.

4.3.5.1 source

This property is a string value that contains a valid XPath expression equivalent to the XPath expression associated with this Expr instance.

This property has the attributes { DontDelete, DontEnum, ReadOnly }.


Appendices

A References

[ECMA-262] ECMA. Standard ECMA-262 2nd Edition - August 1998. ECMAScript Language Specification. See http://www.ecma.ch
[XML] World Wide Web Consortium. Extensible Markup Language (XML) 1.0. W3C Recommendation 10-February-1998 (REC-xml-19980210). See http://www.w3.org/TR/REC-xml
[DOM] World Wide Web Consortium. Document Object Model (DOM) Level 1 Specification Version 1.0. W3C Recommendation 1 October, 1998 (REC-DOM-Level-1-19981001). See http://www.w3.org/TR/REC-DOM-Level-1
[XPath] World Wide Web Consortium. XML Path Language (XPath) Version 1.0. W3C Recommendation 16 November 2024 (REC-xpath-19991116). See http://www.w3.org/TR/1999/REC-xpath-19991116
[XSLT] World Wide Web Consortium. XSL Transformations (XSLT) Version 1.0. W3C Recommendation 16 November 2024 (REC-xslt-19991116). See http://www.w3.org/TR/1999/REC-xslt-19991116
[SAX2] Megginson Technologies. SAX 2.0: The Simple API for XML. See http://www.megginson.com/SAX/index.html
[UESI-XML] Unicorn Enterprises SA. Unicorn ECMAScript Interpreter Reference Manual. XML Extensions. Version 2.0. 31 October 2000. See https://www.unicorn-enterprises.com/esxxml.htm
[UESI-DOM] Unicorn Enterprises SA. Unicorn ECMAScript Interpreter Reference Manual. DOM Extensions. Version 2.0. 31 October 2000. See https://www.unicorn-enterprises.com/esxdom.htm

B Examples

The following ECMAScript program tests various XPath expressions, using examples from XPath Recommendation. The pattern matching facility provided by XSLT extensions is demonstrated as well.

  var sourceURI = "file://localhost/d/demo/uesi/xpath01.xml";
  var d = parseDocument(sourceURI);
  var tests = new Array();
  addTest(tests, "chapter", "child::para");
  addTest(tests, "chapter", "para");
  addTest(tests, "chapter", "attribute::name");
  addTest(tests, "chapter", "@name");
  addTest(tests, "chapter", "attribute::*");
  addTest(tests, "chapter", "@*");
  addTest(tests, "chapter", "descendant::para");
  addTest(tests, "chapter", ".//para");
  addTest(tests, "figure", "ancestor::para");
  addTest(tests, "para", "ancestor-or-self::para");
  addTest(tests, "para", "descendant-or-self::para");
  addTest(tests, "para", "self::para");
  addTest(tests, "para", ".");
  addTest(tests, "book", "child::chapter/descendant::para");
  addTest(tests, "book", "chapter//para");
  addTest(tests, "book", "child::*/child::para");
  addTest(tests, "book", "*/para");
  addTest(tests, "chapter", "/descendant::para");
  addTest(tests, "chapter", "//para");
  addTest(tests, "book", "/descendant::para/child::figure");
  addTest(tests, "book", "//para/figure");
  addTest(tests, "book", "descendant::para/child::figure");
  addTest(tests, "chapter", "child::para[position()=1]");
  addTest(tests, "chapter", "para[1]");
  addTest(tests, "chapter", "child::para[position()=last()]");
  addTest(tests, "chapter", "para[last()]");
  addTest(tests, "chapter", "child::para[position()=last()-1]");
  addTest(tests, "chapter", "child::para[position()>1]");
  addTest(tests, "chapter", 
      "following-sibling::chapter[position()=1]");
  addTest(tests, "chapter", 
      "preceding-sibling::chapter[position()=1]");
  addTest(tests, "book", "/descendant::figure[position()=2]");
  addTest(tests, "book", 
      "/child::book/child::chapter[position()=3]/child::section[position()=2]");
  addTest(tests, "book", "/book/chapter[3]/section[2]");
  addTest(tests, "chapter", "child::para[attribute::type='warning']");
  addTest(tests, "chapter", "para[@type='warning']");
  addTest(tests, "chapter", "para[@type='warning']");
  addTest(tests, "chapter", 
      "child::para[attribute::type='warning'][position()=1]");
  addTest(tests, "chapter", "para[@type='warning'][1]");
  addTest(tests, "chapter", "para[1][@type='warning']");
  addTest(tests, "chapter", 
      "child::para[position()=1][attribute::type='warning']");
  addTest(tests, "book", 
      "child::chapter[child::title='Introduction']");
  addTest(tests, "book", "chapter[title='Introduction']");
  addTest(tests, "book", "child::chapter[child::title]");
  addTest(tests, "book", "chapter[title]");
  addTest(tests, "book", 
      "child::chapter[child::title[self::*='Introduction']]");
  addTest(tests, "book", "chapter[title[.='Introduction']]");
  addTest(tests, "book", 
      "child::*[self::chapter or self::appendix]");
  addTest(tests, "book", 
      "child::*[self::chapter or self::appendix][position()=last()]");
  addTest(tests, "book", "para[@name and @type]");
  addTest(tests, "para", "..");
  addTest(tests, "para", "../@name");
  addTest(tests, "book", 
      "descendant::chapter/descendant::para/descendant::figure");
  addTest(tests, "book", "..//chapter//para//figure");
  addTest(tests, "book", "descendant::chapter/descendant::para");
  addTest(tests, "book", "..//chapter//para");
  addTest(tests, "book", "descendant::para/ancestor::chapter");
  addTest(tests, "para", "preceding::*");
  addTest(tests, "para", "following::*");
  addTest(tests, "chapter", "preceding-sibling::*");
  addTest(tests, "chapter", "following-sibling::*");
  addTest(tests, "chapter", "descendant-or-self::*");
  runTests(tests, d);
  function parseDocument(URI) {
      var d = new DOM.Document;
      var wd = new DOM.Writer(d);
      var xr = new XML.Reader();
      xr.contentHandler = wd;
      xr.parse(URI);
      return d;
      }
  function addTest(tests, match, select) {
      var index = tests.length;
      var t = new Object();
      t.match = new XSLT.Pattern(match);
      t.select = new XPath.Expr(select);
      tests[index] = t;
      }
  function runTests(tests, d) {
      for (var i = 0; i < tests.length; i++) {
          var match = tests[i].match;
          var select = tests[i].select;
          print("Match: \""+match+"\", select: \""+select+"\"");
          walk(match, select, d);
          print("");
          }
      }
  function walk(match, select, node) {
      if (match.match(node))
          evalExpr(select, node);
      var type = node.nodeType;
      if (type == DOM.Node.ELEMENT_NODE) {
          var attrMap = node.attributes;
          var length = attrMap.length;
          for (var i = 0; i < length; i++)
              walk(match, select, attrMap.item(i));
          }
      if (type == DOM.Node.DOCUMENT_NODE || 
              type == DOM.Node.ELEMENT_NODE) {
          for (var child = node.firstChild; 
                  child != null; child = child.nextSibling)
              walk(match, select, child);
          }
      }
  function evalExpr(expr, node) {
      dumpNode("  Context: ", node);
    // note implicit type cast of Node to NodeSet:
      var nodeSet = expr.eval(node);
      while (nodeSet.current != null) {
          dumpNode("    Node: ", nodeSet.current);
          nodeSet.next();
          }
      }
  function dumpNode(prefix, node) {
      var type;
      var value;
      if (node.nodeType == DOM.Node.ELEMENT_NODE) {
          type = "element";
//
//    Variant 1: Use named node map
//
//          var attrMap = node.attributes;
//          var attr = attrMap.getNamedItem("name");
//          value = (attr != null) ? attr.nodeValue : null;
//
//
//    Variant 2: Call Element constructor as function 
//               to perform type cast
//
          var element = DOM.Element(node);
          value = element.getAttribute("name");
          }
      else if (node.nodeType == DOM.Node.ATTRIBUTE_NODE) {
          type = "attribute";
          value = node.nodeValue;
          }
      else {
          type = "other";
          value = null;
          }
      print(prefix+
          "type: "+type+
          ", name: "+node.nodeName+
          ", value: "+value);
      }

The following XML document can be used as input for this program.

<?xml version="1.0"?>
<book>
  <chapter name="1" id="id-1">
    <title>Preface</title>
    <title>Introduction</title>
    <para name="1.1">
      Text begin of paragraph 1.1
      <para name="1.1.1">
        Text of paragraph 1.1.1
        <figure name="fig-1.1.1-A"/>
      </para>
      <para name="1.1.2">
        Text of paragraph 1.1.2
      </para>
      Text end of paragraph 1.1
      <figure name="fig-1.1-Z"/>
    </para>
    <para name="1.2" type="warning">
      Text begin of paragraph 1.2
      <para name="1.2.1">
        Text of paragraph 1.2.1
      </para>
      <para name="1.2.2">
        Text of paragraph 1.2.2
      </para>
      Text end of paragraph 1.2
    </para>
  </chapter>
  <chapter name="2" id="id-2">
    <para name="2.1" type="warning">
      Text begin of paragraph 2.1
      <para name="2.1.1">
        Text of paragraph 2.1.1
      </para>
      <para name="2.1.2">
        Text of paragraph 2.1.2
      </para>
      Text end of paragraph 2.1
    </para>
    <para name="2.2">
      Text begin of paragraph 2.2
      <para name="2.2.1">
        Text of paragraph 2.2.1
      </para>
      <para name="2.2.2">
        Text of paragraph 2.2.2
      </para>
      Text end of paragraph 2.2
    </para>
  </chapter>
  <appendix name="appendix-1"/>
  <chapter name="3" id="id-3">
    <title>Introduction</title>
    <section name="section-1"/>
    <section name="section-2"/>
    <section name="section-3"/>
  </chapter>
  <appendix name="appendix-2"/>
</book>