XQuery/XPath parser in Javascript

XForms is evaluating most XPath expressions in forms repeatedly. So, it is much more efficient, especially in browsers, to parse them once and preserve some compiled form for repeated evaluations.

Previously, XSLTForms was using XSLT to parse XPath 1.0 expressions into Javascript code to create objects with a .run() method. Performance was good enough but with some unexpected delays for very long node names in XML instances…

This XPath parser has been rewritten in Javascript to be executed just-in-time when an expression is to be evaluated for the first time. Again, the XSLT stylesheet is lighter and the blank page wait is reduced.

Because the previous parser was targeting XPath 1.0, the new one has been extended to support XQuery 3.1 and generate XQueryX [XQueryX] stored in Javascript arrays (each element or attribute as an array of name and value).

For example,

concat('Hello ', PersonGivenName, '. We hope you like XForms!')

Becomes

[Fleur.XQueryX.functionCallExpr,[
  [Fleur.XQueryX.functionName,['concat']],
  [Fleur.XQueryX.arguments,[
    [Fleur.XQueryX.stringConstantExpr,[[Fleur.XQueryX.value,['Hello ']]]],
    [Fleur.XQueryX.pathExpr,[
      [Fleur.XQueryX.stepExpr,[
        [Fleur.XQueryX.xpathAxis,['child']],
        [Fleur.XQueryX.nameTest,['PersonGivenName']]
      ]]
    ]],
    [Fleur.XQueryX.stringConstantExpr,[[Fleur.XQueryX.value,['. We hope you like XForms!']]]]
  ]]
]]

Which can be serialized in XQueryX as

<xqx:functionCallExpr>
  <xqx:functionName>concat</xqx:functionName>
  <xqx:arguments>
    <xqx:stringConstantExpr>
      <xqx:value>Hello </xqx:value>
    </xqx:stringConstantExpr>
    <xqx:pathExpr>
      <xqx:stepExpr>
        <xqx:xpathAxis>child</xqx:xpathAxis>
        <xqx:nameTest>PersonGivenName</xqx:nameTest>
      </xqx:stepExpr>
    </xqx:pathExpr>
    <xqx:stringConstantExpr>
      <xqx:value>. We hope you like XForms!</xqx:value>
    </xqx:stringConstantExpr>
  </xqx:arguments>
</xqx:functionCallExpr>

On Node.js, the XQueryX structure is, then, the input for a new XQuery engine, named Fleur [Fleur], which also includes a DOM3 engine. It has to run asynchronous calls on functions such as fn:doc(), http:send-request(), file:write(), prof:sleep(),…

Because async/await feature was not yet being supported in Node.js and old browsers, current version Fleur is still struggling with callbacks hell, call stack overflow. Using this version from XSLTForms would have needed a massive reorganization of sources: a converter from XQueryX to old XPath 1.0 objects of XSLTForms has, temporarily, been added to remove the previous XPath 1.0 parser written in XSLT 1.0.

The fleur() Javascript function has been added to be used within the Console in the browser debugger to evaluate XPath expressions.

For example,

fleur("instance()")

Returns

"<data xmlns=\"\"><PersonGivenName/></data>"

Because rendering in Javascript console is rather limited, an XForms console has been created (accessible with F1 key). Currently limited to expression evaluation, it will be enriched to render, with HTML, Model Item Properties (type, relevant, required, valid, read-only,...) bound to XForms instance data nodes.

With Node.js easily allowing to run an http server, Fleur is also a nice XQuery Web server for XSLTForms to generate inline XML instances, manipulate files, REST APIs,…