[xslt] Any interest in an alternative syntax for XSLT?



    [Background: I work writing software for routers (JUNOS), which uses
    an XML-based API to allow extensive access to our configuration
    database and operational command output.  We've used libxslt
    for web-oriented transformations (XML->HTML) in our product line,
    and have recently used XSLT to allow customer-specific constraints
    to be applied to the configuration database.]

When we exposed our XSLT scripting ability to customers, we found them
quite hostile to xslt syntax.  I looked around for alternatives, but
didn't find anything that really fit with libxslt, my needs, and my
customers' preferences.

The result is a Perl/C-ish alternative syntax that I call "slax"
(Stylesheet Language Alternative Syntax).  (Yes, my boss made me remove
the original leading "X" ;^)

The code includes a slax reader and a slax writer, so code can easily
be converted between XSLT and slax.  The slax reader builds the same
structures/etc as the XML parser.  xsltSetLoaderFunc() is used to hook
into the normal parsing flow.  Documentation is attached.

I'd love to contribute this code back to the libxslt sources, if
you are interested.

Thanks,
 Phil



* SLAX -- An Alternative Syntax for XSLT

XSLT is a commonly used transformation language for XML.  It is a
simple, powerful, declarative language that uses XPath expressions to
inspect an XML input document and can generate XML output based on
that input hierarchy.  It's a great tool for handling XML.

Except for the syntax.

XSLT uses an XML-based syntax, and while XML is great for
machine-to-machine communication, it is quite painful for humans to
write, especially when writing programs.  The occasional benefits of
having an XSLT stylesheet be an XML document are outweighed by the
ongoing issues of dealing with the syntax.

There have been a number of attempts to make an alternative syntax for
XSLT, nothing revolutionary, but just some syntactic sugar to reduce
the pain felt by normal people writing XSLT scripts.  None of these
alternatives seems to have really gathered much of a following.  SLAX
is another attempt in this same direction, with a more C-like syntax.

* Overview

SLAX has a simple syntax which follows the style of C and Perl.
Programming constructs and XPath expressions are moved from XML
elements and attributes to first class language constructs.  The
overabundance of angle brackets and quotes is replaced by the
parentheses and curly braces which programmers have enjoyed since the
advent of "C".

SLAX allows you to:

- Use if/then/else instead of <xsl:choose> and <xsl:if>
- Put test expressions in parentheses
- Use "==" to test equality (to avoid building dangerous habits)
- Use curly braces to show containment, reducing the noise from close
  tags
- Perform concatenation using the "_" operator (lifted from perl6)
- Write text strings using simple quotes instead of <xsl:text>
- Simplify invoking named templates with a syntax resembling a
  function call
- Simplify defining named template with a syntax resembling a
  function definition
- Simplify namespace declarations
- Reduce the noise level in your script, allowing the important parts
  of your code to increase their signal
- Write readable scripts
- Feel like a programmer again ;^)

The benefits of SLAX are particularly strong for new developers, since
it puts familiar constructs in familiar syntax, allowing them to
concentrate in the new topics introduced by XSLT.

* Peeking under the hood

SLAX is purely syntactic sugar.  The underlaying constructs are
completely native to XSLT.  Nothing is added to the XSLT engine.  The
SLAX parser parses an input document and builds an XML tree identical
to one produced when the XML parser reads an XSLT document.

SLAX can be viewed as a pre-processor for XSLT, turning SLAX
constructs (like if/then/else) into the equivalent XSLT constructs
(like <xsl:choose> and <xsl:if>) before the real XSLT transformation
engine gets invoked.

SLAX uses the xsltSetLoaderFunc() in libxslt to tell libxslt to use a
different function when loading script documents.  When
xsltParseStylesheetDoc() loads a script, it then calls the SLAX loader
code, which reads the script, constructs an XML document (xmlDoc)
which mimics the document the normal XML parser would have created for
a functionally equivalent script.  After this document is returned to
the existing libxslt code, normal XSLT processing continues, unaffected
by the fact that the script was written in SLAX.

The "build-as-if" nature of SLAX makes is trivial to write a
SLAX-to-XSLT convertor, which reads in SLAX and emit XSLT.  In
addition, an XSLT-to-SLAX convertor is also available, which
emits an XSLT document in SLAX format.  These make a great learning
aid, as well as allowing conversion of SLAX scripts to XSLT for
environments where libxslt is not available.

* Statements

This section lists the SLAX statements, with brief examples followed
by their XSLT equivalents.  The utility of SLAX will hopefully be
appearent, as will the simple transformation that SLAX parsing code is
performing.

** if/then/else

SLAX supports an "if" statement that uses a C-like syntax.  The
expressions that appear in parentheses are extended form of XPath
expressions, which support the double equal sign ("==") in place of
XPath's single equal sign ("=").  This allows C programmers to avoid
slipping into dangerous habits.

    if (this && that || the/other[one]) {
        /* SLAX has a simple "if" statement */
    } else if (yet[more == "fun"]) {
        /* ... and it has "else if" */
    } else {
        /* ... and "else" */
    }

Depending on the presence of the "else" clause, an "if" statement can
be transformed into either an <xsl:if> element or an <xsl:choose>
element.

    if (starts-with(name, "fe-")) {
        if (mtu < 1500) {
           /* Deal with fast ethernet interfaces with low MTUs */
        }
    } else {
        if (mtu > 8096) {
           /* Deal with non-fe interfaces with high MTUs */
        }
    }

The XSLT equivalent:

    <xsl:choose>
      <xsl:when select="starts-with(name, 'fe-')">
        <xsl:if test="mtu &lt; 1500">
          <!-- Deal with fast ethernet interfaces with low MTUs -->
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="mtu &gt; 8096">
          <!-- Deal with non-fe interfaces with high MTUs -->
        </xsl:if>
      </xsl:otherwise>
    </xsl:choose>

** Elements

SLAX elements are written with in a C-like syntax, with only the open
tag.  The contents of the tag appear immediately following the open
tag.  These contents can either be a simple expression, or a more
complex expression placed inside braces:

  <top> {
      <one>;
      <two> {
          <three>;
          <four>;
          <five> <six>;
      }
  }

This is equivalent to:

  <top>
    <one/>
    <two>
      <three/>
      <four/>
      <five>
        <six/>
      </five>
    </two>
  </top>

Programmers are used to using braces, indentations, and editor support
to delineate blocks of data.  Using these nesting techniques and
removing the close tag reduces the clutter and increases the clarity
of code.

** Expressions

XPath expressions can appear as either the contents of an XML element
or as the contents of a "expr" (expression) statement.  In either
case, the value is translated in an <xsl:text> or <xsl:value-of>
element.

Strings are encoded using quotes (single or double) in a way that will
feel natural to C programmers.  The concatenation operator is
underscore ("_"), which is the new concatenation operator for Perl 6.
(The use of "+" or "." would have created ambiguities in the SLAX
language.)

    <top> {
        <one> "test";
        <two> "The answer is " _ results/answer _ ".";
        <three> results/count _ " attempts made by " _ results/user;
        <four> {
            expr results/count _ " attempts made by " _ results/user;
        }
        <five> {
            expr results/count;
            expr " attempts made by ";
            expr results/user;
        }
        <six> results/message;
    }

The equivalent XSLT:

    <top>
      <one><xsl:text>test</xsl:text></one>
      <two><xsl:value-of select='concat("The answer is ", 
                                   results/answer, ".")'/></two>;
      <three><xsl:value-of select='concat(results/count,
                         " attempts made by ", , results/user)'/></three>
      <four><xsl:value-of select='concat(results/count,
                         " attempts made by ", , results/user)'/></four>
      <five>
        <xsl:value-of select="results/count"/>
        <xsl:text> attempts made by </xsl:text>
        <xsl:value-of select="results/user"/>
      </five>
      <six><xsl:value-of select='results/message'/></six>
    </top>

In this example, the contents of the <three> and <four> element are
identical, and the <five> element's contents are nearly identical,
differing only in the use of the XPath concat() function.

** Variables and Parameters

Variable and parameter syntax uses the "var" and "param" statements to
declare variables and parameters, respectively.  SLAX declarations
differ from XSLT in that the variable name contains the dollar sign
even in the declaration, which is unlike the "name" attribute of
<xsl:variable> and <xsl:parameter>.  This was done to enhance the
consistency of the language.

    param $fido;
    var $bone;

The XSLT equivalent:

    <xsl:parameter name="fido"/>
    <xsl:variable name="bone"/>

An initial value can be given by following the variable name with an
equals sign and an expression.

    param $dot = .;
    var $location = $dot/@location;
    var $message = "We are in " _ $location _ " now.";

The XSLT equivalent:

    <xsl:parameter name="dot" select="."/>
    <xsl:variable name="location" select="$dot/location"/>
    <xsl:variable name="message" select="concat('We are in ',
                                           $location, ' now.')"/>

Again, these are the same constucts as XSLT, but packaged in a more
readable, maintainable syntax.

** The "match" statement

Basic match templates are specified using the "match" statement,
followed by an expression specifying when the template should be
allowed.  This is followed by a block of statements enclosed in a set
of braces.

    match configuration {
        <error> {
	    <message> "System is named " _ system/host-name;
        }
    }

** Applying Templates

Match templates are applied using the "apply-templates" statement.
The statement accepts an optional XPath expression, which is
equivalent to the "select" in an <xsl:apply-templates> element.

    match configuration {
        apply-template system/host-name;
    }

    match host-name {
        <hello> .;
    }

The XSLT equivalent:

    <xsl:template match="configuration">
      <xsl:apply-templates select="system/host-name"/>
    </xsl:template>

    <xsl:template match="host-name">
      <hello>
        <xsl:value-of select="."/>
      </hello>
    </xsl:template>

** Template Parameters

Parameters may be passed to match templates using the "with"
statement.  The "with" statement consists of the keyword "with" and
the name of the parameter, optionally followed by an equals sign ("=")
and a value expression.  If no value is given, the current value of
that variable or parameter is passed, giving a simple shorthand for
passing parameters if common names are used.

    match configuration {
        var $domain = domain-name;
        apply-template system/host-name {
            with $message = "Invalid host-name";
            with $domain;
        }
    }

    match host-name {
        param $message = "Error";
        param $domain;
        <hello> $message _ ":: " _ . _ " (" _ $domain _ ")";
    }

The XSLT equivalent:

    <xsl:template match="configuration">
      <xsl:apply-templates select="system/host-name">
        <xsl:with-param name="message" select="'Invalid host-name'"/>
        <xsl:with-param name="domain" select="$domain"/>
      </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="host-name">
      <xsl:param name="message" select="'Error'"/>
      <xsl:param name="domain"/>
      <hello>
        <xsl:value-of select="concat($message, ':: ', ., 
                                    ' (', $domain, ')')"/>
      </hello>
    </xsl:template>

** The "mode" Statement

The "mode" attribute of the <xsl:template> element is available
using the "mode" statement.  This statement can appear inside a
"match" statement and inside an "apply-templates" statement.

    match * {
        mode "one";
        <one> .;
    }

    match * {
        mode "two";
        <two> string-length(.);
    }

    match / {
        apply-templates version {
            mode "one";
        }
        apply-templates version {
            mode "two";
        }
    }

The XSLT equivalent:

    <xsl:template match="*" mode="one">
      <one>
        <xsl:value-of select="."/>
      </one>
    </xsl:template>

    <xsl:template match="*" mode="two">
      <two>
        <xsl:value-of select="string-length(.)"/>
      </two>
    </xsl:template>

    <xsl:template match="/">
      <xsl:apply-template select="version" mode="one"/>
      <xsl:apply-template select="version" mode="two"/>
    </xsl:template>


** The "priority" Statement

The "priority" statement mimics the "priority" attribute of the
<xsl:template> element.  It may appear inside a "match" statement.

    match * {
        priority 10;
        <output> .;
    }

The XSLT equivalent:


    <xsl:template match="*" priority="10">
      <output>
        <xsl:value-of select="."/>
      </output>
    </xsl:template>

** Named Templates

Named templates are defined using their name and parameters and
invoked using the "call" statement.  The template definition consists
of the template name, a set of parameters, and a braces-delineated
block of code.  Parameter declarations can be either inline, with the
parameter name and optionally an equals sign ("=") and a value
expression.  Additional parameters can be declared inside the block
using the "param" statement.

The template is invoked using the "call" statement, which consists of
the "call" keyword followed by a set of parameter bindings.  These
binding are a comma-separated list of parameter names, optionally
followed by an equal sign and a value expression.  If the value is not
given, the current value of that variable or parameter is passed,
giving a simple shorthand for passing parameters if common names are
used.  Additional template parameters can be supplied inside the block
using the "with" statement.

    match configuration {
        var $name-servers = name-servers/name;
        call temp:ting();
        call temp:ting($name-servers, $size = count($name-servers));
        call temp:ting() {
            with $name-servers;
            with $size = count($name-servers);
        }

    temp:ting($name-servers, $size = 0) {
        <output> "template called with size " _ $size;
    }

The XSLT equivalent:

    <xsl:template match="configuration">
      <xsl:variable name="name-servers" select="name-servers/name"/>
      <xsl:call-template name="temp:ting"/>
      <xsl:call-template name="temp:ting">
        <xsl:with-param name="name-servers" select="$name-servers"/>
        <xsl:with-param name="size" select="count($name-servers)"/>
      </xsl:call-template>
      <xsl:call-template name="temp:ting">
        <xsl:with-param name="name-servers" select="$name-servers"/>
        <xsl:with-param name="size" select="count($name-servers)"/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template name="temp:ting">
      <xsl:param name="name-servers"/>
      <xsl:param name="size" select="0"/>
      <output>
        <xsl:value-of select="concat('template called with size ', $size)"/>
      </output>
    </xsl:template>

** The "for-each" Statement

The "for-each" statement mimics functionality of the <xsl:for-each>
element.  The statement consists of the "for-each" keyword, the
parentheses-delimited select expression, and a block.

    for-each ($inventory/chassis/chassis-module
              /chassis-sub-module[part-number == '750-000610']) {
        <message> "Down rev PIC in " _ ../name _ ", "
                     _ name _ ": " _ description;
    }

The XSLT equivalent:


    <xsl:for-each select="$inventory/chassis/chassis-module
              /chassis-sub-module[part-number == '750-000610']">
        <message>
            <xsl:value-of select="concat('Down rev PIC in ', ../name,
                                  ', ', name, ': ', description)"/>
        </message>
    </xsl:for-each>

** "version"

    version 1.0;

All SLAX stylesheets must begin with a "version" statement, which
gives the version number for the SLAX language.  This is currently
fixed at "1.0" and will increase as the language evolves.

SLAX version 1.0 implies XML version 1.0 and XSLT version 1.1.

In addition, the "xsl" namespace is implicitly defined (as
'xmlns:xsl="http://www.w3.org/1999/XSL/Transform";').

** The "ns" Statement

Namespace definitions are supplied using the "ns" statement.  This
consists of either the "ns" keyword, a prefix string, an equal sign
and a namespace URI or the "ns" keyword and a namespace URI.  The
second form defines the default namespace.

    ns junos = "http://www.juniper.net/junos/";;

The "ns" statement may appear either following the "version" statement
at the beginning of the stylesheet or at the beginning of any block.

    ns a = "http://example.com/1";;
    ns "http://example.com/global";;
    ns b = "http://example.com/2";;

    match / {
        ns c = "http://example.com/3";;
        <top> {
            ns a = "http://example.com/4";;
            apply-templates commit-script-input/configuration;
        }
    }

When appearing at the beginning of the stylesheet, the ns statement
may include either the "exclude" or "extension" keywords.  These
keywords instruct the parser to add the namespace prefix to the
"exclude-result-prefixes" or "extension-element-prefixes" attribute.

    ns exclude foo = "http://example.com/foo";;
    ns extension jcs = "http://xml.juniper.net/jcs";;

The XSLT equivalent:

    <xsl:stylesheet xmlns:foo="http://example.com/foo";
                    xmlns:jcs="http://xml.juniper.net/jcs";
                    exclude-result-prefixes="foo"
                    extension-element-prefixes="jcs">
        <!-- ... -->
    </xsl:stylesheet>

** The "copy-of" Statement

The "copy-of" statement mimics the functionality of the <xsl:copy-of>
element.

    copy-of configuration/protocols/bgp;

The XSLT equivalent:

   <xsl:copy-of select="configuration/protocols/bgp"/>

** The "apply-imports" Statement

The "apply-imports" statement mimics the <xsl:apply-imports> element,
allowing the script to invoke any imported templates.

    apply-imports;

The XSLT equivalent:

   <xsl:apply-imports/>

** The "include" and "import" Statements

The "include" and "import" statements mimic the <xsl:include> and
<xsl:import> elements.

    include "foo.slax";
    import "goo.xsl";

The XSLT equivalent:

    <xsl:include href="foo.slax"/>
    <xsl:import href="goo.xsl"/>

Import and include documents do _not_ need to be in written in SLAX.
SLAX can import and include normal XSLT documents.

** The "comment" Statement

The "comment" statement mimics the <xsl:comment> element, allowing XML
comments to be generated.

    comment "Added by user " _ $user _ " on " _ $date;

The XSLT equivalent:

    <xsl:comment>
      <xsl:value-of select='concat("Added by user ", $user, " on ", $date)'/>
    </xsl:comment>

** The "preserve-space" Statement

The "preserve-space" statement mimics the <xsl:preserve-space>
element, instructing the XSLT processor that certain elements
should have internal whitespace retained.

    preserve-space foo goo moo;

The XSLT equivalent:

    <xsl:preserve-space elements="foo goo moo"/>

** The "strip-space" Statement

The "strip-space" statement mimics the <xsl:strip-space>
element, instructing the XSLT processor that certain elements
should have internal whitespace removed.

    strip-space foo goo moo;

The XSLT equivalent:

    <xsl:strip-space elements="foo goo moo"/>

** Comments

Comments in SLAX are entered in the traditional C style, beginning the
"/*" and ending with "*/".  These comments are preserved in the
in-memory XML tree representation of the SLAX script.  This allows
comments to be preserved if the tree is emitted using the normal
XML output renderer.

    /*
     * This is a comment.
     */

The XSLT equivalent:

    <!-- /*
     * This is a comment
     */ -->

** Other XSLT Elements

A number of rarely used XSLT elements are not directly translated into
SLAX statements.  This is driven by two factors: (a) they aren't worth
the time (rarely used) and (b) the user will need to refer to the XSLT
spec (or the Kay book) for the syntax, so why make it any harder on
them. 

These elements can be encoded directly as normal SLAX elements:

    match * {
        for-each (configuration/interfaces/unit) {
            <xsl:sort some-obnoxious-option="some-obnoxious-value">;
        }
    }

** Example Stylesheets

** Status of the Software

The SLAX software contains both a reader and a writer.  The reader
turns a SLAX source file into an XSLT tree (xmlDocPtr) using the
xsltSetLoaderFunc hook.

The writer turns an XSLT tree (xmlDocPtr) into a file containing
SLAX statements.

The software is currently working, but is not set up for "configure"
use.  I need to get it working so the installer can say
"--enable-slax".  When that's done, I'll make this available as
a patch file.

** BNF for SLAX

This is the BNF for SLAX, derived from slaxparser.y.  Note that most
of the second half is verbatim from the XPath and XSLT RECs.

stylesheet ::=
	version_stmt ns_list preamble_list template_list

version_stmt ::=
	K_VERSION version_number L_EOS

version_number ::=
	T_NUMBER L_DOT T_NUMBER

preamble_list ::=
	empty
	| preamble_list preamble_stmt

preamble_stmt ::=
	param_decl
	| var_decl
	| import_stmt
	| include_stmt
	| strip_space_stmt
	| preserve_space_stmt
	| element_stmt

ns_list ::=
	empty
	| ns_list ns_decl

ns_decl ::=
	K_NS ns_options T_BARE L_EQUALS T_QUOTED L_EOS
	| K_NS T_QUOTED L_EOS

ns_options ::=
	empty
	| K_EXCLUDE
	| K_EXTENSION

import_stmt ::=
	K_IMPORT T_QUOTED L_EOS

include_stmt ::=
	K_INCLUDE T_QUOTED L_EOS

strip_space_stmt ::=
	K_STRIP_SPACE element_list L_EOS

element_list ::=
	q_name
	| element_list q_name

preserve_space_stmt ::=
	K_PRESERVE_SPACE element_list L_EOS

param_decl ::=
	K_PARAM T_VAR L_EOS
	| K_PARAM T_VAR L_EQUALS initial_value

var_decl ::=
	K_VAR T_VAR L_EQUALS initial_value

initial_value ::=
	xpath_value L_EOS
	| block
	| element_stmt

template_list ::=
	empty
	| template_list template_stmt

template_stmt ::=
	match_template
	| named_template

match_template ::=
	K_MATCH xs_pattern L_OBRACE match_block_contents L_CBRACE

match_block_contents ::=
	match_preamble_list block_preamble_list block_statement_list

match_preamble_list ::=
	empty
	| match_preamble_list match_block_preamble_stmt

match_block_preamble_stmt ::=
	mode_stmt
	| priority_stmt

mode_stmt ::=
	K_MODE T_QUOTED L_EOS

priority_stmt ::=
	K_PRIORITY T_NUMBER L_EOS

named_template ::=
	template_name named_template_arguments block

named_template_arguments ::=
	empty
	| L_OPAREN named_template_argument_list L_CPAREN

named_template_argument_list ::=
	empty
	| named_template_argument_list_not_empty

named_template_argument_list_not_empty ::=
	named_template_argument_decl
	| named_template_argument_list_not_empty
		    L_COMMA named_template_argument_decl

named_template_argument_decl ::=
	T_VAR
	| T_VAR L_EQUALS xpath_expr

block ::=
	L_OBRACE block_contents L_CBRACE

block_contents ::=
	block_preamble_list block_statement_list

block_preamble_list ::=
	empty
	| block_preamble_list block_preamble_stmt

block_preamble_stmt ::=
	ns_decl

block_statement_list ::=
	empty
	| block_statement_list block_statement

block_statement ::=
	apply_imports_stmt
	| apply_templates_stmt
	| call_stmt
	| copy_of_stmt
	| element_stmt
	| for_each_stmt
	| if_stmt
	| param_decl
	| var_decl
	| expr_stmt
	| L_EOS

apply_imports_stmt ::=
	K_APPLY_IMPORTS L_EOS

apply_templates_stmt ::=
	K_APPLY_TEMPLATES xpath_expr_optional apply_template_arguments

xpath_expr_optional ::=
	empty
	| xpath_expr

expr_stmt ::=
	K_EXPR xpath_stmt

xpath_stmt ::=
	xpath_value L_EOS

call_stmt ::=
	K_CALL template_name call_arguments

q_name ::=
	T_BARE

template_name ::=
	q_name
	| T_FUNCTION_NAME

apply_template_arguments ::=
	template_arguments_braces_style

call_arguments ::=
	template_arguments_parens_style template_arguments_braces_style

template_arguments_parens_style ::=
	empty
	| L_OPAREN template_argument_list L_CPAREN

template_argument_list ::=
	empty
	| template_argument_list_not_empty

template_argument_list_not_empty ::=
	template_argument_member
	| template_argument_list_not_empty L_COMMA template_argument_member

template_argument_member ::=
	T_VAR
	| T_VAR L_EQUALS xpath_value

template_arguments_braces_style ::=
	L_EOS
	| L_OBRACE template_argument_braces_list L_CBRACE

template_argument_braces_list ::=
	template_argument_braces_list_not_empty

template_argument_braces_list_not_empty ::=
	template_argument_braces_member
	| template_argument_braces_list_not_empty
			template_argument_braces_member

template_argument_braces_member ::=
	K_WITH T_VAR L_EOS
	| K_WITH T_VAR L_EQUALS initial_value
	| mode_stmt
	| element_stmt
	;

copy_of_stmt ::=
	K_COPY_OF xpath_expr L_EOS

element ::=
	L_LESS q_name attribute_list L_GRTR

element_stmt ::=
	element L_EOS
	| element block
	| element xpath_stmt

attribute_list ::=
	empty
	| attribute_list attribute

attribute ::=
	q_name L_EQUALS xpath_full_value

for_each_stmt ::=
	K_FOR_EACH L_OPAREN xpath_expr L_CPAREN block

if_stmt ::=
	K_IF L_OPAREN xpath_expr L_CPAREN block elsif_stmt_list else_stmt

elsif_stmt_list ::=
	empty
	| elsif_stmt_list elsif_stmt

elsif_stmt ::=
	K_ELSE K_IF L_OPAREN xpath_expr L_CPAREN block

else_stmt ::=
	empty
	| K_ELSE block

and_operator ::=
	K_AND
	| L_DAMPER

or_operator ::=
	K_OR
	| L_DVBAR

equals_operator ::=
	L_EQUALS
	| L_DEQUALS

xpath_value ::=
	xpath_value_one
	| xpath_value L_UNDERSCORE xpath_value_one

xpath_value_one ::=
	xp_expr

xpath_expr ::=
	xp_expr

xpath_full_value ::=
	xpath_full_value_one
	| xpath_full_value L_UNDERSCORE xpath_full_value_one

xpath_full_value_one ::=
	xp_full_path_expr

xs_pattern ::=
	xs_location_path_pattern
	| xs_pattern L_VBAR xs_location_path_pattern

xs_location_path_pattern ::=
	L_SLASH xs_relative_path_pattern_optional
	| xs_id_key_pattern xs_single_or_double_slash
	    xs_relative_path_pattern_optional
	| xs_dslash_optional xs_relative_path_pattern
	;

xs_dslash_optional ::=
	empty
	| L_DSLASH

xs_id_key_pattern ::=
	K_ID L_OPAREN q_name L_CPAREN
	| K_KEY L_OPAREN q_name L_COMMA q_name L_CPAREN

xs_relative_path_pattern_optional ::=
	empty
	| xs_relative_path_pattern

xs_relative_path_pattern ::=
	xs_step_pattern 	
	| xs_relative_path_pattern xs_single_or_double_slash xs_step_pattern

xs_single_or_double_slash ::=
	L_SLASH
	| L_DSLASH

xs_step_pattern ::=
	xp_axis_specifier_optional xp_node_test xs_predicate_list

xs_predicate_list ::=
	empty
	| xs_predicate_list xp_predicate

xp_location_path ::=
	xp_relative_location_path
	| xp_absolute_location_path

xp_absolute_location_path ::=
	L_SLASH xp_relative_location_path_optional
	| xp_abbreviated_absolute_location_path

xp_full_location_path ::=
	xp_relative_location_path
	| xp_full_absolute_location_path

xp_full_absolute_location_path ::=
	L_SLASH xp_relative_location_path
	| xp_abbreviated_absolute_location_path

xp_relative_location_path_optional ::=
	empty
	| xp_relative_location_path

xp_relative_location_path ::=
	xp_step
	| xp_relative_location_path L_SLASH xp_step
	| xp_abbreviated_relative_location_path

xp_step ::=
	xp_axis_specifier_optional xp_node_test xp_predicate_optional
	| xp_abbreviated_step

xp_axis_specifier_optional ::=
	empty
	| xp_abbreviated_axis_specifier

xp_predicate_optional ::=
	empty
	| xp_predicate

xp_predicate ::=
	L_OBRACK xp_predicate_expr L_CBRACK

xp_predicate_expr ::=
	xp_expr

xp_abbreviated_absolute_location_path ::=
	L_DSLASH xp_relative_location_path

xp_abbreviated_relative_location_path ::=
	xp_relative_location_path L_DSLASH xp_step

xp_abbreviated_step ::=
	L_DOT
	| L_DOTDOT

xp_abbreviated_axis_specifier ::=
	L_AT

xp_expr ::=
	xp_or_expr

xp_primary_expr ::=
	L_OPAREN xp_expr L_CPAREN
	| xp_variable_reference
	| xp_literal
	| xp_number
	| xp_function_call

xp_function_call ::=
	T_FUNCTION_NAME L_OPAREN xp_argument_list_optional L_CPAREN

xp_argument_list_optional ::=
	empty
	| xp_argument_list

xp_argument_list ::=
	xp_argument
	| xp_argument_list L_COMMA xp_argument

xp_argument ::=
	xp_expr

xp_union_expr ::=
	xp_path_expr
	| xp_union_expr L_VBAR xp_path_expr

xp_path_expr ::=
	xp_location_path
	| xp_filter_expr
	| xp_filter_expr L_SLASH xp_relative_location_path
	| xp_filter_expr L_DSLASH xp_relative_location_path

xp_full_path_expr ::=
	xp_full_location_path
	| xp_filter_expr
	| xp_filter_expr L_SLASH xp_relative_location_path
	| xp_filter_expr L_DSLASH xp_relative_location_path

xp_filter_expr ::=
	xp_primary_expr
	| xp_filter_expr xp_predicate

xp_or_expr ::=
	xp_and_expr
	| xp_or_expr or_operator xp_and_expr

xp_and_expr ::=
	xp_equality_expr
	| xp_and_expr and_operator xp_equality_expr

xp_equality_expr ::=
	xp_relational_expr
	| xp_equality_expr equals_operator xp_relational_expr
	| xp_equality_expr L_NOTEQUALS xp_relational_expr

xp_relational_expr ::=
	xp_additive_expr
	| xp_relational_expr L_LESS xp_additive_expr
	| xp_relational_expr L_GRTR xp_additive_expr
	| xp_relational_expr L_LESSEQ xp_additive_expr
	| xp_relational_expr L_GRTREQ xp_additive_expr

xp_additive_expr ::=
	xp_multiplicative_expr
	| xp_additive_expr L_PLUS xp_multiplicative_expr
	| xp_additive_expr L_MINUS xp_multiplicative_expr

xp_multiplicative_expr ::=
	xp_unary_expr
	| xp_multiplicative_expr L_STAR xp_unary_expr
	| xp_multiplicative_expr K_DIV xp_unary_expr
	| xp_multiplicative_expr K_MOD xp_unary_expr

xp_unary_expr ::=
	xp_union_expr
	| L_MINUS xp_unary_expr

xp_literal ::=
	T_QUOTED

xp_number ::=
	T_NUMBER
	| T_NUMBER L_DOT T_NUMBER

xp_variable_reference ::=
	T_VAR

xp_node_test ::=
	xp_name_test	
	| xp_node_type L_OPAREN L_CPAREN
	| K_PROCESSING_INSTRUCTION L_OPAREN xp_literal L_CPAREN

xp_name_test ::=
	q_name
	| L_ASTERISK

xp_node_type ::=
	K_COMMENT
	| K_TEXT
	| K_NODE


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]