The SLAX Scripting Language: An Alternate Syntax for XSLT
slax-manual

Abstract

SLAX is an alternate syntax for XSLT, the W3C standard XML-to-XML transformation. XSLT is a powerful language, but uses an XML-based syntax that is painful to read and write. SLAX uses a syntax modeled after PERL and C which promotes the basic concepts of XSLT into first class language constructs. The result is scripts that are easier to develop and maintain.

This documentation covers the SLAX language, beginning with an overview and a reference section listing all SLAX statements. SLAX has a number of built-in functions which are also covered.

An implementation of the SLAX is available in an open source project called libslax. Built on top of libxslt and libxml2, libslax parses SLAX files and executes them, and can convert between SLAX and XSLT. A debugger and profiler are included. libslax was originally developed as part of the JUNOS Operating System by Juniper Networks and is released under a BSD license. See the "Copyright" file for details.

Table of Contents


1_ 
SLAX (Overview)

XSLT is a commonly used transformation language for XML. It is a 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 simple but powerful tool for handling XML, but the syntax is problematic for developers.

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

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. XML angle brackets and quotes is replaced by parentheses and curly braces which are familiar delimiters for programmers.

SLAX allows you to:

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.

Section Contents:

1.1 
Peeking under the hood

SLAX is purely syntactic sugar. The underlaying constructs are completely native to XSLT. All SLAX constructs can be represented in XSLT. 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.

The current distribution for libslax is built on libxslt. 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 emits XSLT using the normal libxslt file writing mechanism. 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.

1.2 
Composite Technologies

SLAX builds on three existing technologies, XML, XPath, and XSLT.

XML is a set of encoding rules that allow simplified parsing of documents, which gives a simple way to represent hierarchical data sets in text. XML parsers can be used to encode any type of content, and their reuse has breed a set of high-quality implementations that can be easily incorporated into any software project. Having a single encoding helps removing parsing errors, increase code reuse, simplify developer interaction, reduce development costs, and reduce errors.

XPath is an expression language that allows the specification of sets of nodes within an XML document. The language is simultaneously powerfully expressive in terms of the nodes that can be selected, but also simple and readable. It follows the path of making simple things simple, while making hard things possible.

XSLT is a transformation language that turns XML input document into an XML output document. The basic model is that an XSLT engine (or "processor") reads a script (or stylesheet) and an XML document. The XSLT engine uses the instructions in the script to process the XML document by traversing the document's hierarchy. The script indicates what portion of the tree should be traversed, how it should be inspected, and what XML should be generated at each point.

We'll examine each of these technologies in more detail in the following sections.

Section Contents:

XML Concepts

XML is a set of encoding rules that allow simplified parsing of documents, which gives a simple way to represent hierarchical data sets in text. There are six basic constructs in XML:

Open tags: <foo>
Starts a level of hierarchy. An open tag and it's matching close tag constitute an XML element. An element can contain additional elements, data, or both.
Close tags: </foo>
Ends a level of hierarchy. There must be a close tag for every open tag.
Empty tags: <foo/>
Equivalent to <foo></foo>. Functions as a shorthand.
Text: <tag>data</tag>
Any additional text is data.
Attributes: <tag attribute="value"/>
Attributes are used to encode name=value pairs inside the open or empty tag. A specific attribute can appear only once per tag.
Namespaces: <prefix:tag xmlns:prefix="URI"/>
Defines the scope of a tag, as indicated by the prefix. This allows the same tag used in different namespaces to be unique. The URI is not dereferenced, but provides a unique name for the namespace. The "xmlns:" attribute maps the prefix to the given URI.

In addition, comments can be added in XML using the delimiters "<!‑‑" and "‑‑>". For example:

    <!-- This is a comment -->
	    

From these simple concepts, hierarchical documents are constructed, such as the following JUNOS configuration file:

    <configuration
           xmlns:junos="http://xml.juniper.net/junos/7.1I0/junos">
      <system>
        <host-name>my-router</host-name> 
        <domain-name>example.net</domain-name> 
        <time-zone>America/New_York</time-zone>
        <name-server>
          <name>10.4.1.40</name>
        </name-server>
        <accounting inactive="inactive">
          <events>login</events>
          <events>change-log</events>
          <events>interactive-commands</events>
          <destination>
            <tacplus>
              <server>
                <name>10.1.1.1</name>
              </server>
              <server>
                <name>10.2.2.2</name>
              </server> 
            </tacplus>
          </destination> 
        </accounting>
        <syslog>
          <archive>
            <size>64k</size>
            <files>5</files>
            <world-readable/>
          </archive>
          <host>
            <name>lhs</name>
            <contents>
              <name>any</name>
              <alert/>
            </contents>
            <explicit-priority/>
          </host>
        </syslog>
      </system>
      <routing-options>
        <static>
          <route>
            <name>0.0.0.0/0</name>
            <next-hop>10.10.1.1</next-hop>
            <retain/>
          </route>
        </static>
        <autonomous-system inactive="inactive">
          <as-number>42</as-number>
          <loops>9</loops>
        </autonomous-system>
      </routing-options>
    </configuration>
	    

See also http://www.w3c.org/TR/REC-xml.

XPath Concepts

The XPath expression language allows selection of arbitrary nodes from with an XML document. XSLT uses the XPath standard to specify and locate elements in the input document's XML hierarchy. XPath's powerful expression syntax allows complex criteria for selecting portions of the XML input document.

XPath views every piece of the document hierarchy as a node, including element nodes, text nodes, and attribute nodes.

An XPath expression can include a path of XML node names, which select child nodes based on their ancestors' names. Each member of the path must match an existing node, and only nodes that match all path members will be included in the results. The members of the path are separated by the slash character ('/').

For example, the following expression selects all <paragraph> elements that are parented by a <section> element, which in turn must be parented by a <chapter> element, which must be parented by a <doc> element.

    doc/chapter/section/paragraph
	    

XSLT views nodes as being arranged in certain "axes". The "ancestor" axis points from a node up through it's series of parent nodes. The "child" axis points through the list of an element node's direct child nodes. The "attribute" axis points through the list of an element node's set of attributes. The "following‑sibling" axis points through the nodes which follow a node but are under the same parent, while the "proceeding‑sibling" axis points through the nodes that appear before a node and are under the parent. The "descendant" axis contains all the descendents of a node. There are numerous other axes which are not detailed here.

When referring to an axis, use the axis name followed by two colons followed by the element name (which may include an optional prefix and it's trailing colon).

There are two axis aliases that make a convenient shorthand when writing expressions. The "@" alias refers to the attribute axis, allowing either "attribute::number" or "@number" to refer to the "number" attribute. The "//" alias refers to the "descendant‑or‑self" axis, so "doc//paragraph" will find all <paragraph> elements that have a <doc> element as an ancestor.

See also .....

Each XPath expression is evaluated from a particular node, which is referred to as the "context node" (or simply "context"). XSLT changes the context as the document's hierarchy is traversed, and XPath expressions are evaluated from that particular context node.

XPath expression contain two types of syntaxes, a path syntax and a predicate syntax. Path syntax allows traversal along one of the axis in the document's hierarchy from the current context node.

accounting-options
selects an element node named "accounting‑options" that is a child of the current context
server/name
selects an element node named "name" that is parented by an element named "server" that is a child of the current context
/configuration/system/domain-name
selects an element node named "domain‑name" that is parented by a "system" element that is parented by a "configuration" element that is the root element of the document.
parent::system/host-name
selects an element node name "host‑name" that is parented by an element named "system" that is the parent of the current context node. The "parent::" axis can be abbreviated as "..".

A predicate is a boolean test must be satisfied for a node to be selected. Each path member of the XPath expression can have zero or more predicates specified, and the expression will only select nodes for which all the predicates are true.

The predicate syntax allows tests to be performed at each member of the path syntax, and only nodes that pass the test are selected. A predicate appears inside square brackets ("[]") after a path member.

server[name == '10.1.1.1']
selects a "server" element that is a child of the current context and has a "name" element whose value is '10.1.1.1'
*[@inactive]
selects any node ('*' matches any node) that is a child of the current context that has an attribute ('@' selects node from the "attribute" axis) named "inactive"
route[starts-with(next-hop, '10.10.')]
selects a "route" element that is a child of the current context and has a "next‑hop" element whose value starts with the string "10.10."

The "starts‑with" function is one of many functions that are built into XPath. XPath also supports relational tests, equality tests, boolean operations, and many more features not listed here.

SLAX defines a concatenation operator "_" that concatenates its two arguments using the XPath "concat()" function. The following two lines are equivalent:

    expr "Today is " _ $date _ ", " _ $user _ "!!";
    expr concat("Today is ", $date, ", ", $user, "!!");
	    

The SLAX statement "expr" evaluates an XPath expression and inserts its value into the output tree.

XPath is fully described in the W3C's specification, http://w3c.org/TR/xpath.

XSLT Concepts

This section contains some overview material, intended as both overview and introduction. Careful reading of the specification or any of the numerous books on XSLT will certainly be helpful before using commit scripts, but the information here should provide a starting point.

XSLT is a language for transforming one XML document into another XML document. The basic model is that an XSLT engine (or processor) reads a script (or stylesheet) and an XML document. The XSLT engine uses the instructions in the script to process the XML document by traversing the document's hierarchy. The script indicates what portion of the tree should be traversed, how it should be inspected, and what XML should be generated at each point.

                        +-------------+
                        |  XSLT       |
                        |   Script    |
                        +-------------+
                               |
        +-----------+          |           +-----------+
        |XML Input  |          v           |XML Output |
        |  Document |   +-------------+    |  Document |
        |     i     |-->|  XSLT       |--->|     o     |
        |    /|\    |   |   Engine    |    |    / \    |
        |   i i i   |   +-------------+    |   o   o   |
        |  /| |  \  |                      |  /|\  |   |
        | i i i   i |                      | o o o o   |
        +-----------+                      +-----------+
	    

XSLT has seven basic concepts:

  • XPath -- expression syntax for selecting node from the input document
  • Templates -- maps input hierarchies to instructions that handle them
  • Parameters -- a mechanism for passing arguments to templates
  • Variables -- defines read-only references to nodes
  • Programming Constructs -- how to define logic in XSLT
  • Recursion -- templates that call themselves to facilitate looping
  • Context (Dot) -- the node currently be inspected in the input document

XPath has be discussed above. The other concepts are discussed in the following sections. These sections are meant to be introductory in nature, and later sections will provide additional details and complete specifications.

Section Contents:

Templates

An XSLT script consists of a series of template definitions. There are two types of templates, named and unnamed. Named templates operate like functions in traditional programming languages, although with a verbose syntax. Parameters can be passed into named templates, and can be declared with default values.

    template my-template ($a, $b = 'false', $c = name) {
      /* ... body of the template goes here ... */
    }
	    

A template named "my‑template" is defined, with three parameters, one of which defaults to the string "false", and one of which defaults to the contents of the element node named "name" that is a child of the current context node. If the template is called without values for these parameters, the default values will be used. If no select attribute is given for a parameter, it defaults to an empty value.

    call my-template($c = other-name);
	    

In this example, the template "my‑template" is called with the parameter "c" containing the contents of the element node named "other‑name" that is a child of the current context node.

The parameter value can contain any XPath expression. If no nodes are selected, the parameter is empty.

Unnamed Templates

Unnamed templates are something altogether different. Each unnamed template contains a select attribute, specifying the criteria for nodes upon which that template should be invoked.

    match route[starts-with(next-hop, '10.10.')] {
      /* ... body of the template goes here ... */
    }
	    

By default, when XSLT processes a document, it will recursively traverse the entire document hierarchy, inspecting each node, looking for a template that matches the current node. When a matching template is found, the contents of that template are evaluated.

The <xsl:apply‑templates> element can be used inside a template to continue XSLT's default, hierarchical traversal of nodes. If the <xsl:apply‑templates> element is used with a "select" attribute, only nodes matching the XPath expression are traversed. If no nodes are selected, nothing is traversed and nothing happens. Without a "select" attributes, all children of the context node are traversed. In the following example, a <route> element is processed. First all the nodes containing a "changed" attribute are processed under a <new> element. Then all children are processed under an <all> element. The particular processing depends on the other templates in the script and how they are applied.

    match route {
        <new> {
            apply-templates *[@changed];
        }
        <all> {
            apply-templates;
        }
    }
	    

Named templates can also use the "match" statement to perform dual roles, so the template can be used via "apply‑templates" or be calling it explicitly.

Parameters

Parameters can be passed to either named or unnamed templates using either parameter lists or the "with" statement. Inside the template, parameters must be declared with a "param" statement and can then be referenced using their name prefixed by the dollar sign.

    /*
     * This template matches on "/", the root of the XML document.
     * It then generates an element named "outer", and instructs
     * the XSLT engine to recursively apply templates to only the
     * subtree only "configuration/system".  A parameter called
     * "host" is passed to any templates that are processed.
     */
    match / {
        <outer> {
            apply-templates configuration/system {
                with $host = configuration/system/host-name;
            }
        }
    }

    /*
     * This template matches the "system" element, which is the top
     * of the subtree selected above.  The "host" parameter is
     * declared with no default value.  An "inner" element is
     * generated, which contains the value of the host parameter.
     */
    match system {
        param $host;
        <inner> $host;
    }
	    

Parameters can be declares with default values by using the "select" attribute to specify the desired default. If the template is invoked without the parameter, the XPath expression is evaluated and the results are assigned to the parameter.

    match system {
      call report-changed($changed = @changed || ../@changed);
    }

    template report-changed($dot = ., $changed = $dot/@changed) {
      /* ... */
    }
	    

The second template declares two parameters, $dot which defaults to the current node, and $changed, which defaults to the "changed" attribute of the node $dot. This allows the caller to either use a different source for the "changed" attribute, use the "changed" attribute but relative to a different node that the current one, or use the default of the "changed" attribute on the current node.

Variables

XSLT allows the definition of both local and global variables, but the value of variables can only be set when the variable is defined. After that point, they are read only.

A variable is defined using the "var" statement.

    template emit-syslog ($device, $date, $user) {
        var $message = "Device " _ $device _ " was changed on "
                       _ $date _ " by user '" _ $user _ "'";
        <syslog> {
            <message> $message;
        }
    }
	    

Although this example used an XSL variable, the above example could have used an XSL parameter for $message, allowing users to pass in their own message.

Mutable Variables

SLAX adds the ability to assign new values to variables and to append to node sets.

    mvar $count = 10;
    if (this < that) {
        set $count = that;
    }

    mvar $errors;
    if ($count < 2) {
        append $errors += <error> {
            <location> location;
            <message> "Not good, dude.";
        }
    }
	    

Mutable variables can be used like normal variables, including use in XPath expressions.

Character Encoding

SLAX supports a C-like escaping mechanism for encoding characters. The following escapes are available:

Escape Meaning
"\n" Newline (0x0a)
"\r" Return (0x0d)
"\t" Tab (0x09)
"\xXX" Hex-based character number
"\u+XXXX" UTF-8 4-byte hex value
"\u-XXXXXX" UTF-8 6-byte hex value
"\\" The backslash character

Other character encodings based on '\' may be added at a later time.

Programming Constructs

XSLT has a number of traditional programming constructs:

    if (xpath-expression) {
        /* Code here is evaluated if the expression is true */
    }

    if (xpath-expression) {
        /*
         * xsl:choose is similar to a switch statement, but
         * the "test" expression can vary among "when" statements.
         */

    } else if (another-xpath-expression) {
        /*
         * xsl:when is the case of the switch statement.
         * Any number of "when" statements may appear.
         */

    } else {
        /* xsl:otherwise is the 'default' of the switch statement */
    }

    for-each (xpath-expression) {
        /*
         * Code here is evaluated for each node that matches 
         * the xpath expression.  The context is moved to the
         * node during each pass.
         */
    }

    for $item (items) {
        /* 
         * Code here is evaluated with the variable $item set
         * to each node that matches the xpath expression.
         * The context is not moved.
         */
    }

    for $i (1 ... 20) {
        /*
         * Code here is evaluated with the variable $i moving
         * thru a sequence of values between 1 and 20.  The
         * context is not changed.
         */
    }

    while ($count < 10) {
        /*
         * Code here is evaluated until the XPath expression is
         * false.  Note that this is normally only useful with
         * mutable variables.
         */
    }
	    

XSLT is a declarative language, mixing language statements (in the "xsl" namespace) with output elements in other namespaces. For example, the following snippet makes a <source> element containing the value of the "changed" attribute.

    if (@changed) {
        <source> {
            <notify> name();
            if (@changed == "changed") {
                <changed>;

            } else {
                <status> $changed;
            }
        }
    }
	    

Recursion

XSLT depends on recursion as a looping mechanism. Recursion occurs when a section of code calls itself, either directly or indirectly. Both named and unnamed templates can recurse, and different templates can mutually recurse, with one calling another that in turn calls the first.

Care must be taken to prevent infinite recursion. The XSLT engine used by JUNOS limits the maximum recursion, to avoid excessive consumption of system resources. If this limit is reached, the commit script fails and the commit is stopped.

In the following example, an unnamed template matches on a <count> element. It then calls the "count‑to‑max" template, passing the value of that element as "max". The "count‑to‑max" template starts by declaring both the "max" and "cur" parameters, which default to one. Then the current value of "$cur" is emitted in an <out> element. Finally, if "$cur" is less than "$max", the "count‑to‑max" template recursively invokes itself, passing "$cur + 1" as "cur". This recursive pass then output the next number and recurses again until "$cur" equals "$max".

    match count {
        call count-to-max($max = count);
    }

    template count-to-max ($cur = "1", $max = "1") {
        param $cur = "1";
        param $max = "1";

        expr "count: " _ $cur;
        if ($cur < $max) {
            call count($cur = $cur + 1, $max);
        }
    }
	    

Context (Dot)

As mentioned earlier, the current context node changes as the apply-templates logic traverses the document hierarchy and as an <xsl:for‑each> iterates through a set of nodes that match an XPath expression. All relative node references are relative to the current context node. This node is abbreviated "." (read: dot) and can be referred to in XPath expressions, allowing explicit references to the current node.

    match system {
        var $system = .;

        for-each (name-server/name[starts-with(., "10.")]) {
            <tag> .;
            if (. == "10.1.1.1") {
                <match> $system/host-name;
            }
        }
    }
	    

This example contains four uses for ".". The "system" node is saved in the "system" variable for use inside the "for‑each", where the value of "." will have changed. The "for‑each"'s "select" expression uses "." to mean the value of the "name" element. "." is then used to pull the value of the "name" element into the <tag> element. The <xsl:if> test then uses ".", also to reference the value of the current context node.

Additional Resources

The XPath specification is on the W3C web site: (http://www.w3c.org/TR/xpath) and the XSLT specification is on the W3C web site (http://www.w3c.org/TR/xslt).

Books and tutorials on XSLT abound, helping programmers learn the technology. XSLT processors (programs that run XSLT scripts) are available from both commercial and open-source developers, allowing users to play with XSLT offline. IDEs with extended debuggers also exist. XSLT is common enough that piecing together a simple script is easy, as is finding help and advice.

This document 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.


2_ 
Building Content with SLAX

SLAX (and XSLT) is a "declarative" language, meaning that a SLAX script will describe the output that should be generated, not give imperative instructions about how to generate that output. This is quite different than traditional procedural programming, in both content and style.

As a SLAX script executes, it uses the description contained in rules and templates to generate a "result tree", which is a hierarchy of XML output nodes. Some logic statements and conditional processing statements are intermixed with the output description to allow flexibility in the output generated.

We'll start by examining how SLAX generated these output nodes.

Section Contents:

2.1 
Expressions

SLAX makes extensive use of the XPath expression language. Expressions are used to select nodes, specify conditions, and to generate output content. Expressions contain five constructs:

SLAX follows XPath syntax, with the following additions:

  1. "&&" may be used in place of the "and" operator
  2. "||" may be used in place of the "or" operator
  3. "==" may be used in place of the "=" operator
  4. "!" may be used in place of the "not()" operator
  5. "_" is the concatenation operator ("x" _ "y" === concat("x", "y"))
  6. "?:" is converted into choose/when/otherwise elements

The first four additions are meant to prevent programmers from learning habits in SLAX that will negatively affect their ability to program in other languages. The last two additions are for convenience.

When referring to XPath expressions in this document, we mean this extended syntax.

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.)

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:

    <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>
	    

XPath expression can be added to the result tree using the "expr" and "uexpr" statements.

Section Contents:

Using Elements as Function Arguments

Beginning with SLAX-1.2, elements may be used directly as function arguments. Arguments can be either a single element or a block of SLAX code, placed inside braces.

    var $a = my:function(<elt>, <max> 15);
    var $b = my:test({
        <min> 5;
        <max> 15;
        if ($step) {
            <step> $step;
        }
    });
    var $c = my:write(<content> {
        <document> "total.txt";
        <size> $file/size;
        if (node[@type == "full]) {
            <full>;
        }
    });
	    

expr

The "expr" statement adds an XPath expression to the result tree. The expression is the argument to the statement:

    expr "Test: ";
    expr substring-before(name, ".");
    expr status;
	    

uexpr

The "uexpr" behaves identically to the "expr" statement, except that the contents are not escaped. Normally characters like "<", ">", and "&" are escaped into proper XML (as "&lt;", "&gt;", and "&amp;", respectively), but uexpr avoids this escaping mechanism.

    uexpr "<:-&>";
	    

See also disable-output-escaping in the XSLT specification.

2.2 
Elements

Elements are the primary encoding mechanism of XML, and can be combined and arranged to encoding complex hierarchical constructs.

The XML encoding uses three tags: the start tag, the end tag, and the empty tag. The start tag consists of the less than character ('<'), the element name, a set of optional attributes (discussed later), and the greater than character ('>'). This is followed by the contents of the XML element, which may include additional elements. The end tag consists of the less than character ('<'), the slash character ('/'), the element name, and the greater than character ('>'). If an element has no content, an empty tag can be used in place of an open and close tags. The empty tag consists of the less than character ('<'), the element name, a set of optional attributes (discussed later), the slash character ('/'), and the greater than character ('>').

    <doc>
        <chapter>
            <section>
                <paragraph>A brief introduction</paragraph>
            </section>
       </chapter>
       <index/>
    </doc>
	    

Section Contents:

XML Elements

In SLAX, XML elements are encoded using braces instead of the closing tag. The element is given, wrapped in chevrons as in XML, but it then followed by an open brace, the elements contents, and a close brace. If the element is empty, a semi-colon can be used to signify an empty element. If the element contains only a single XPath expression, that expression can be used in place of the braces and the element is ended with a single semi-colon.

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.

The following SLAX is equivalent to the above XML data:

    <doc> {
        <chapter>
            <section> {
                <paragraph> "A brief introduction";
            }
        }
        <index>;
    }
	    

Programmers are accustomed 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.

    <top> {
        <one> 1;
        <two> {
            <three> 3;
            <four> 4;
            <five> {
                <six> 6;
            }
        }
    }
	    

This is equivalent to:

    <top>
        <one>1</one>
        <two>
            <three>3</three>
            <four>4</four>
            <five>
                <six>6</six>
            </five>
        </two>
    </top>
	    

The "element" Statement

The name of an element can be specified by an XPath expression, using the "element" statement. The contents of the element must be placed inside a set of braces.

In this example, the value of the "name" node (rather than the literal string "name") will be used to create an XML element, whose contents are an empty element with a name of "from‑" concatenated with the value of the address node and an emply element whose name is the value of the variable "$my‑var" (refer to Section 4.1 for more information about variables). Node values are selected from the current context.

    element name {
        element "from-" _ address;
        element $my-var;
    }
	    

JSON Elements

XML elements can also be specified using a JSON-like style, where quoted strings name the element, followed by a colon and the contents of the element. The contents can be any of the following:

Type Examples
String "test", "when in the course", "42"
Number 42, 1.5, 2e45.5
boolean true, false
null null
Array [ 1, 2, 3, 4, 5 ]
Object { "this": { "that": "the other" } }

Hierarchical nesting is done using objects:

    "top" : {
        "one": 1,
        "two": {
            "three": 3,
            "four": 4,
            "five": {
                "six: 6
            }
        }
    }
	    

This would generate XML equivalent to the examples in Section 2.2.1.

Section Contents:

Attributes for JSON Elements

Since there is a mismatch between the data encoding capabilities in XML and JSON, a set of attributes can be used to control the exact rendering of JSON data.

Attribute Value Description
name string A name to use in place of the element name
type string An indication of the desired encoding

The value of the "type" attribute must be one of the subset listed below. This allows SLAX to distinguish between '{ "value": "5" }' and '{ "value": 5 }' to control the encoding of the value as a string or a number. Similarly the caller can control whether "null" is a string or the special null value. If the type attribute is missing, the element is assumed to be a simple field.

Type Description
array Contains a JSON array
false Contains the JSON "false" value
null Contains the JSON "null" value
member Is a member of an array
number Contains a number value
true Contains the JSON "true" value

In this example, the type attribute is used to indicate the encoding of JSON in XML.

    JSON:
      "name": "Skip Tracer",
      "location": "The city that never sleeps", 
      "age": 5,
      "real": false, 
      "cases": null, 

    XML:
      <name>Skip Tracer</name>
      <location>The city that never sleeps</location>
      <age type="number">5</age>
      <real type="false">false</real>
      <cases type="null">null</cases>
	    

Encoding JSON Arrays in XML

The mismatch of capabilities between JSON and XML also requires the use of alternative encodings for arrays. The <member> element is used to contain member (type="member" of an array (of type="array").

In this example, alternative encodings are used for array values. Also the "type" attribute is used to hold the JSON type:

    "book" : {
        "topics": [ "colors", "fish", "spots", "stars", "cars" ],
        "pages": 63,
        "counts": [
            { "red": 1 },
            { "blue": 1 },
            { "sad": "some" },
            { "glad": "some" },
        ]
    }
	    

The equivalent in SLAX elements would be:

    <book> {
        <topics type="array"> {
            <member type="member"> "colors";
            <member type="member"> "fish";
            <member type="member"> "spots";
            <member type="member"> "stars";
            <member type="member"> "cars";
        }
        <pages type="number"> "63";
        <counts type="array"> {
            <member type="member"> {
                <red type="number"> "1";
            }
            <member type="member"> {
                <blue type="number"> "1";
            }
            <member type="member"> {
                <sad> "some";
            }
            <member type="member"> {
                <glad> "some";
            }
        }
    }
	    

The XML equivalent is shown below:

    <book>
      <topics type="array">
        <member type="member">colors</member>
        <member type="member">fish</member>
        <member type="member">spots</member>
        <member type="member">stars</member>
        <member type="member">cars</member>
      </topics>
      <pages type="number">63</pages>
      <counts type="array">
        <member type="member">
          <red type="number">1</red>
        </member>
        <member type="member">
          <blue type="number">1</blue>
        </member>
        <member type="member">
          <sad>some</sad>
        </member>
        <member type="member">
          <glad>some</glad>
        </member>
      </counts>
    </book>
	    

Encoding Invalid JSON Names

In addition, JSON allows values for the names which an invalid in XML. When this occurs, an element named "element" is used to hold the content with the "name" attribute containing the real name:

    JSON style:
        "": {
            "<>": "<>"
        }

    SLAX style:
        <element name=""> {
            <element name="<>"> "<>";
        }

    XML content:
        <element name="">
            <element name="&lt;&gt;">&lt;&gt;</element>
        </element>
	    

2.3 
Attributes

XML allows a set attribute value to be specified on an open or empty tag. These attributes are an unordered set of name/value pairs. The XML encoding uses the attribute name, an equals sign ('='), and the value of the attribute in quotes.

    <chapter number="1" title="Introduction" ref="intro">
        <section number="1">
            <!-- .... content ... -->
        </section>
    </chapter>
	    

Section Contents:

XML Attributes

SLAX uses an identical encoding scheme for attributes:

    <chapter number="1" title="Introduction" ref="intro"> {
        <section number="1"> {
            /* .... content ... */
        }
    }
	    

In addition, XSLT introduces a means of assigning attribute values, called "attribute value templates" (AVT). SLAX encodes these as XPath values for attributes:

Attributes on elements follow the style of XML, with the attribute name, an equals sign, and the value of the attribute.

    <element attr1="one" attr2="two">;
	    

In this example, the attributes are assigned values using XPath values. The "tag" node value is assigned to the "ref" attribute, while the "title" attribute is assigned the value of the "$title" variable.

    <chapter number=position() title=$title ref=tag> {
        <section number=position()> {
            /* .... content ... */
        }
    }
	    

Where XSLT allow attribute value templates using curly braces, SLAX uses the normal expression syntax. Attribute values can be any XPath expression, including quoted strings, parameters, variables, and numbers, as well as the SLAX concatenation operator ("_").

    <location state=$location/state
              zip=$location/zip5 _ "-" _ $location/zip4>;
	    

The XSLT equivalent:

    <location state="{$location/state}"
              zip="{concat($location/zip5, "-", $location/zip4}"/>
	    

Note that curly braces placed inside quote strings are not interpreted as attribute value templates, but as real braces and are escaped when translated into XSLT.

    <avt sign="{here}">;
	    

The XSLT equivalent:

    <avt sign="{{here}}"/>
	    

The "attribute" Statement

The name of an attribute can be specified by an XPath expression, using the "attribute" statement. The contents of the attribute must be placed inside a set of braces.

In this example, the value of the "name" node (rather than the literal string "name") will be used to create an XML attribute, whose contents are an empty element with a name of "from‑" concatenated with the value of the address node. Node values are selected from the current context.

    attribute name {
        expr "from-" _ address;
    }
	    

2.4 
Attribute Sets

Attribute sets can be used to define sets of attributes that can be used together. An attribute set contains a set of "attribute" statements that define the names and values of attributes. These attributes can then be referred to as a collection rather than repeat the contents in the script.

faq: Do people really use this?

My guess (which could be wildly wrong) is that this is a rarely used feature that are created to cover the condition that a template cannot return a set of attributes. That's my guess anyway.

[faq]

Section Contents:

The "attribute‑set" Statement

The "attribute‑set" statement defines a set of attributes that can be used repeatedly. The argument is the name of the attribute set, and the contents are a set of attribute statements.

    attribute-set table-attributes {
        attribute "order" { expr "0"; }
        attribute "cellpadding" { expr $cellpadding; }
        attribute "cellspacing" { expr $cellspacing; }
    }
	    

The "use‑attribute‑sets" Statement

The "use‑attribute‑sets" statement adds the attributes from a given set to the current element.

    <table> {
        use-attribute-sets table-attributes;
    }
	    

The use-attribute-sets statement can be used under the "element", "copy‑node", and "attribute‑sets" statements, as well as under a normal element.

2.5 
Namespaces

Namespaces map URI strings to prefixes that are used to indicate the administrative domain of specific XML elements. The syntax and semantic constraints for the element <size> will be distinct depending on the namespace under which it is defined.

The URI is a string that uniquely identifies the namespace. Here are some examples:

Example namespaces
http://xml.juniper.net/junos
http://www.w3.org/1999/XSL/Format

The prefix is a string pre-pended to the local element name with a colon. Prefixes map to namespaces and are used as "shorthand" for the underlaying namespace.

Example Prefix Example Element Usage
junos <junos:name/>
example <example:name/>
fo <fo:name/>
svg <svg:name/>

Section Contents:

The "ns" Statement

The "ns" statement defines a mapping from a prefix to a URI namespace identifier. Namespaces must be defined prior to their use.

By default, elements are in the "null" namespace, but the "ns" statement can be used to change the namespace for unprefixed elements.

The syntax of the "ns" statement is:

    ns [<prefix> <options> = ] <uri>;
	    

If a prefix is the value prefixed to element names to indicate their namespace should be that of the given URI. If no prefix is given, the given URI will be applied to all elements that do not include a prefix. The values and meanings of <options> are detailed below.

    ns junos =  "http://xml.juniper.net/junos";
    ns example = "http://www.example.com/example-one";
    ns fo = "http://www.w3.org/1999/XSL/Format";
    ns svg = "http://www.w3.org/Graphics/SVG/SVG-19990812.dtd";
	    

In this example, a default namespace is defined, as well as a namespace mapped to the "test" prefix:

    ns "http://example.com/main";
    ns test = "http://example.com/test";
	    

The XML equivalent is:

    <some-element xmlns="http://example.com/main"
            xmlns:test="http://example.com/test"/>
	    

See also http://www.w3.org/TR/xml#namespaces.

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 "extension" Statement

The "extension" statement instructs the processing engine that extenstion namespaces, which will cause elements in that namespace to be given special handling by the engine.

For libslax, the extension keyword instructs the library to search for an extension library associated with the namespace. If found, the extension library is loaded and initialized so the script can use functions and elements defined within that library.

    ns pref extension = "http://some.example.com";
	    

See also http://www.w3.org/TR/xslt#extension-element.

The "exclude" Statement

The "exclude" statement prevents the namespace from appearing in the final result tree, effectively blocking it from output.

    ns xslax extension = "http://libslax.org/slax";
	    

See also http://www.w3.org/TR/xslt#literal-result-element.

Reserved Prefixes

The XML specification reserved all prefixes and attributes beginning with the characters "xml". In addition, SLAX reserves all prefixes and attributes that begin with the characters "slax". These reservations help to future proof against changes and enhancements to the language.

Default Namespaces for Prefixes

When a prefix is used without a corresponding "ns" statement in scope, SLAX will refer to a set of default namespaces. If the prefix has a default namespace, that namespace will be automatically mapped to the prefix.

The following table lists the default set of prefixes installed with the libslax software distribution:

Prefix Source URI
bit libslax http://xml.libslax.org/bit
curl libslax http://xml.libslax.org/curl
db libslax http://xml.libslax.org/db
exsl exslt http://exslt.org/common
crypto exslt http://exslt.org/crypto
math exslt http://exslt.org/math
set exslt http://exslt.org/sets
func exslt http://exslt.org/functions
str exslt http://exslt.org/strings
date exslt http://exslt.org/dates-and-times
dyn exslt http://exslt.org/dynamic
saxon libxslt http://icl.com/saxon
os libslax http://xml.libslax.org/os
xutil libslax http://xml.libslax.org/xutil

The libslax extension libraries are documented in this document under the Section 8 section. The exslt extension libraries are documented at http://exslt.org and the libxslt extension library is documented at http://xmlsoft.org.

When using the "slaxproc" tool with the --format or --slax-to-xslt command line options, the namespace will be properly displayed:

    % cat /tmp/foo.slax
    version 1.1;

    match / {
        <top> {
            expr date:time();
        }
    }
    % slaxproc --format /tmp/foo.slax
    version 1.1;

    ns date extension = "http://exslt.org/dates-and-times";

    match / {
        <top> date:time();
    }
	    

2.6 
Processing Instructions

An XML processing instruction is a mechanism to convey application-specific information inside an XML document. The application can detect processing instructions and change behaviour accordingly.

Section Contents:

The "processing‑instruction" Statement

The "processing‑instruction" statement adds a processing instruction to the result tree. The argument to the statement is that name of the processing instruction and the contents of the statement (within braces) is the value of that instruction.

    processing-instruction "my-app" {
        expr "my-value";
    }
	    

Both the argument and the value may be XPath expressions.

2.7 
Comments

Comments are information for the user or author. They are not formal content and should not be inspected or parsed. They can be discarded without affecting the content of the XML.

    <!-- This is an XML comment -->
	    

SLAX comments are distinct from XML comments. SLAX comments appear as part of the SLAX script, and are not part of either the input or output XML documents. SLAX comments follow the C/Perl style of a "/*" followed by the comment, terminated by "*/".

    /* This is a SLAX comment */
	    

XML comments may not appear inside a SLAX script.

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
     */ -->
	    

Section Contents:

The "comment" Statement

The "comment" statement adds an XML comment to the result tree. The argument is an XPath expression containing the comment to be added:

    comment "This script was run by " _ $user _ " on " _ $date;
	    

See also comment

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>
	    

2.8 
Copying Content

On many occasions, parts of the input XML document will be copied to the output XML document. Such copies can be deep or shallow, meaning that the entire node hierarchy is copied or just the node itself. SLAX contains two distinct statements for these two styles of copying.

Section Contents:

The "copy‑of" Statement

The "copy‑of" statement performs a deep copy of a given set of nodes. The argument to the statement is an XPath expression specifying which nodes should be copied.

    copy-of $top/my/stuff;
    copy-of .;
	    

See also copy-node (Section 2.8.2).

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 "copy‑node" Statement

The "copy‑node" statement performs a shallow copy of the specific node to the result tree, along with any namespace nodes, but none other child nodes (including attribute nodes) are copied. The contents of the statement are a template specifying what should be inserted into the new node.

    copy-node {
        <that> "one";
    }
	    

See also copy-of (Section 2.8.1).

2.9 
Formatting

This section contains information about statements that control formatting of output. See also the Section 5.6 and Section 5.4.

Section Contents:

The "number" Statement

The "number" statement inserts a generated number into the result tree. This statement has two distinct forms. When used with an argument, the statement inserts the number given by that XPath expression, and a set of optional statements can be used to specify the formatting to be used for that number.

When used without an argument, the number is generated based on position of the current node in the source document, and a set of optional statements can be used to specify formatting.

The formatting statements are given in the following table:

Statement Value Description
format see below Style of numbering
letter-value Not implemented in libxslt
grouping-separator character Used between groups of digits
grouping-size number Number of digits in a group

The value of the "format" statement gives the style of numbering, as

Value Style
"1" 1 2 3 4
"01" 01 02 03 04
"a" a b c d
"A" A B C D
"i" i ii iii iv
"I" I II III IV

The selection statements used when the number statement has no argument are given in the following table:

Statement Value Description
count XPath What to count
from XPath Where to start counting from
level See below How to count

The "level" statement indicates how to count tags:

Value Behavior
single Count from first ancestor node
multiple Count from any ancestor node
any Count from any node

In the following example, the value of $this is formatted with three digits of output and the number of "section" elements before the current context value is emitted.

    number $this {
        format "001";
    }
    number {
        count section;
    }
	    

See also the format-number() XPath function.


3_ 
Templates

A SLAX script consists of a set of templates. There are two types of templates, match templates and named templates. This section discusses each of these types.

Section Contents:

3.1 
Named Templates

In addition to the template processing, templates can be given explicit names and called directly, allowing a programming style that follows more traditional procedural languages. Named templates are called like function, returning their XML output nodes to the caller, where they can be merged into the caller's XML output tree.

Section Contents:

The "template" Statement

Named templates are defined using their name and parameters and invoked using the "call" statement. The template definition consists of the "template" keyword, 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.

The "call" Statement

Named templates accept parameters by name, rather than by position. This means the caller needs to indicate the name of the desired parameter when passing a value:

    call test($arg1 = 1, $arg4 = 4, $arg2 = 2);
	    

As a convenience, arguments with the same local name as the argument name can be passed directly:

    var $message = "test " _ $name _ " is " _ $status;
    call emit-message($message)
	    

This is identical to "call emit-message($message = $message)", but is simpler and the use of common names increased readability.

The call statement can be used as the initial value of a variable or parameter, without using an enclosing set of braces:

    var $a = call test($a = 1);
	    

Section Contents:

Using the "with" Statement with "call"

In addition, template arguments can be passed using the "with" statement. This statement is placed inside braces after the "call" statement, and gives the parameter name and the value:

    call test {
        with $arg1 = 1;
        with $arg4 = 4;
        with $arg2 = 2;
    }
	    
note::

Note that in SLAX parameter names are always prefixed with the dollar sign ("$"). This is not true for XSLT, where the dollar sign is not used when defining variables.

[note:]

The "with" statement is described in more detail below (Section 3.2.7).

Using Elements As Argument Values

Beginning with SLAX-1.2, elements may be used directly as argument values for templates. Arguments can be either a single element or a block of SLAX code, placed inside braces.

    call my-template($a = <elt>, $b = <max> 15);
    call test($params = {
        <min> 5;
        <max> 15;
        if ($step) {
            <step> $step;
        }
    }
    call write($content = <content> {
        <document> "total.txt";
        <size> $file/size;
        if (node[@type == "full]) {
            <full>;
        }
    });
	    

The "match" Statement

While rarely used, XSLT allows named template to be used as match templates. This is done using the "match" statement after the "template" statement. For more information on "match" statements, see Section 3.2.1.

    SYNTAX::
        'template' template-name 'match' xpath-expression '{'
            template-contents
        '}'

    template test match paragraph ($message) {
        <message> $message;
    }

    match / {
        call test($message = "called as named template");
        apply-templates {
            with $message = "called as match template";
        }
    } 
	    

Example with XSLT Translation

This section includes a short example, allow with the XSLT into which the script translates.

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

    template my:thing($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="my:thing"/>
      <xsl:call-template name="my:thing">
        <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="my:thing">
        <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="my:thing">
      <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>
	    

3.2 
Match Templates

The processing model for SLAX is identical to that of XSLT. A set of XML input nodes are processed to generate a set of XML output nodes. This processing begins at the top of the XML input document and proceeds recursively through the entire document, using rules defined in the SLAX script, rules imported from other scripts, and a set of default rules defined by the XSLT specification.

Each rule defines the matching criteria that controls when the rule is applied, followed by a template for the creation of the XML output nodes. The processing engine inspects each node, finds the appropriate rule that matches that node, executes the template associated with the rules, builds the XML output nodes, and merges those nodes with the XML output nodes from other rules to build the XML output nodes.

Section Contents:

The "match" Statement

The "match" statement defines a match template, with its matching criteria and its template. The keyword "match" is followed by an XPath expression that selects the nodes on which this template should be executed. This is followed by a set of curly braces containing the template.

    SYNTAX::
        'match' xpath-expression '{'
            template-contents
        '}'
	    

The template consists of SLAX code, whose statements are defined later in this document.

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

The "apply‑templates" Statement

The "apply‑templates" statement instructs the processing engine to apply the set of templates given in the script to a set of nodes. The statement takes as its argument an XPath expression that selects a set of nodes to use. If no expression is given, the current node is used.

The set of XML input nodes is processed according to the set of templates, and the XML output nodes are given to the context in which the apply-templates statement was issued.

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>
	    

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 "mode" Statement

The "mode" statement allows the apply-template to choice a distinct set of rules to use during processing. The argument to the "mode" statement is a text string that identifies the mode for both the template and the template processing. Templates processing will only select templates that match the current mode value. If no mode statement is given with an "apply‑templates" invocation, then the current mode remains in effect.

In this example, template processing is invoked twice, first for mode "content" and then for mode "index".

    match doc {
        apply-templates {
            mode "content";
        }
        apply-template {
            mode "index";
        }
    }

    match paragraph {
        mode "content";
        ... template ...
    }

    match paragraph {
        mode "index";
        ... template ...
    }
	    

See also http://www.w3.org/TR/xslt#modes.

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 sets the priority of the template, which is used as part of the conflict resolution when more that one template matches a source node. The highest priority rule is chosen. The argument to the "priority" statement is a real number (positive or negative).

In this example, the template is given a high priority:

    match paragraph {
        priority 100;
        ...
    }
	    

See also http://www.w3.org/TR/xslt#conflict.

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>
	    

The "param" Statement

Template can accept parameters from their callers, and scripts can accept parameters from the calling environment (typically the command line). The "param" statement declares a parameter, along with an optional default value.

    SYNTAX::
        'param' parameter-name ';'
        'param' parameter-name '=' optional-value ';'
        'param' parameter-name '=' '{' value-block '}'

    EXAMPLE::
        param $address = "10.1.2.3";
        param $count = 25;

        templace count {
            /*
             * This defines a local parameter $count and sets
             * its value to that of the global parameter $count.
             */
            param $count = $count;
        }
	    

Like variables, parameters are immutable. Once created, their value cannot be changed. See Section 4.1 for a discussion on dealing with immutable values.

Template parameters can also be defined in a C style following the template name:

    template area ($width = 10, $length = 10, $scale = 1) {
        <area> $width * $length * $scale;
    }
	    

The "with" Statement

The "with" statement supplies a value for a given parameter.

    call area {
        with $length = 2;
        with $width = 100;
        with $max;
    }
	    

Parameter values may also be passed using a C/perl style, but since arguments in SLAX (and XSLT) are passed by name, the parameter names are also given:

    call area($width = 100, $length = 2, $max);
	    

If a parameter is not supplied with a value, the current value of that parameter variable (in the current context) is used, meaning that

    with $max;
	    

is equivalent to:

    with $max = $max;
	    

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 (in the current scope) 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>
	    

3.3 
The "main" Template

The XSLT programming model used with SLAX calls for a traversal of the input data hierarchy. Since the first step is typically the match of the top of the hierarchy and the creation of the top-level tag of the output hierarchy. The "main" statement allows both of these objectives. Two forms of the statement are supported, with and without the output tag. Without an output element, the "main" template is equivalent to "match /". The token "main" is followed by a block of statements within a set of braces:

    main {
        <top> {
            <answer> 42;
        }
    }
	    

The "main" template can also be used with a top-level output element following the "main" token. The element can include attributes.

    main <top> {
        <answer> 42;
    }
	    

Both of the preceding examples are equivalent to the following XSLT:

    <xsl:template match="/">
        <top>
            <answer>42</answer>
        </top>
    </xsl:template>
	    

4_ 
Programming Constructs

This section covers the programming constructs in SLAX. SLAX tries to use language paradigms and constructs from traditional languages (primarily C and Perl) to aid the user in readability and familiarity. But SLAX is built on top of XSLT, and the declarative nature of XSLT means that many of its constructs are visible.

Section Contents:

4.1 
Variables

Variables in SLAX are immutable. Once defined, the value of a variable cannot be changed. This is a major change from traditional programming language, and requires a somewhat different approach to programming. There are two tools to help combat this view: recursion and complex variable assignments.

Recursion allows new values for variables to be set, based on the parameters passed in on the recursive call.

    template count ($cur = 1, $max = 10) {
        if ($cur < $max) {
            <output> $cur;
            call count($cur = $cur + 1, $max);
        }
    }
	    

Complex variable assignments allow the use of programming constructs inside the variable definition:

    var $message = {
        if ($red > 40) {
            expr "Invalid red value";
        } else if ($blue > 80) {
            expr "Invalid red value";
        } else if ($green > 10) {
            expr "Invalid green value";
        }
    }
	    

This requires different approaches to problems. For example, instead of appending to a message as you loop thru a list of nodes, you would need to make the message initial value contain a "for‑each" loop that emits parts of the message:

    var $message = {
        <output> "acceptable colors: ";
        for-each (color) {
            if (red <= 40 && blue <= 80 && green < 10) {
                <output> "    " _ name _ 
                   " (" _ red _ "," _ green _ "," _ blue _ ")";
            }
        }
    }
	    

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.

Section Contents:

The "var" Statement

Variable are defined using the "var" statement, which accept a variable name and an initial value. Variable names (and parameter names) always begin with a dollar sign ("$").

    SYNTAX::
        'var' name '=' initial-value ';'
        'var' name '=' '{' initial-value-block '}'
	    

The initial value can be an XPath expresion, an element, or a block of statements (enclosed in braces) that emit a set of nodes.

    var $x = 4;
    var $y = <price> cost div weight;
    var $z = {
        expr "this: ";
        copy-of $this;
    }
	    

The "mvar" Statement

In XSLT, all variables are immutable, meaning that once created, their value cannot be changed. This creates a distinct programming environment which is challenging to new programmers. Immutable variables allow various optimizations and advanced streaming functionality.

Given the use case and scenarios for libslax (especially our usage patterns in JUNOS), we've added mutable variables, which are variables that can be changed. The "set" statement allows a new value to be assigned to a variable and the "append" statement allows the value to be extended, with new data appended to it.

    SYNTAX::
        'mvar' variable-name '=' initial-value ';'
        'set' variable-name '=' new-value ';'
        'append' variable-name '+=' new-content ';'
	    

The mvar is typically a node set, and appended adds the new objects to the nodeset.

    mvar $test;

    set $test = <block> "start here";

    for $item (list) {
        append $test += <item> {
            <name> $item/name;
            <size> $item/size;
        }
    }
	    

4.2 
Result Tree Fragments

The most annoying "features" of XSLT is the concept of "Result Tree Fragments" (aka RTF). These fragments are produced with nodes are created that are not directly emitted as output. The main source is variable or parameter definitions that have complex content.

    var $x = {
        <color> {
            <name> "cornflower blue";
            <red> 100;
            <green> 149;
            <blue> 237;
        }
    }
	    

Only three operations can be performed on an RTF:

In this example, an RTF is generated, and then each of the three valid operations is performed:

    var $rtf = <rtf> {
        <rats> "bad";
    }
    if ($rtf == "bad") { /* Converts the RTF into a string */
        copy-of $rtf;  /* Emits the RTF to the output tree */

        /* Convert RTF to a node set (see discussion below) */
        var $node-set = ext:node-set($rtf);
    }
	    

Any XPath operation performed against an RTF will result in an "Invalid type" error.

In truth, the only interesting thing to do with an RTF is to convert it to a node set, which is not a standard XPath/XSLT operation. Most scripts will use the extension function "ext:node‑set()" (which is specific to libxslt) or "exslt:node‑set()" (which is in the EXSLT extension library; see http://exslt.org for additional information).

    ns ext = "http://xmlsoft.org/XSLT/namespace";
    ...
        var $alist = ext:node-set($alist-raw);
	    

This must be done when a variable or paramter has a complex initial value:

    var $this-raw = <this> {
        <that>;
        <the-other>;
    }
    var $this = ext:node-set($this-raw);
	    

Fortunately for SLAX programmers, the ":=" operator does away with these conversion issues, as the following section details.

Section Contents:

The ":=" Operator

The ":=" operator is designed to hide the conversion of RTFs to node sets from the programmer. It is used in assigning initial values to variables and parameters.

    var $this := <this> {
        <that> "one";
        <the-other> "one";
    }
    if ($this/that == "one") {
        <output> "not an invalid type error";
    }
	    

Calling named templates can also produce RTFs, since the "call" statement would be considered complex variable content. But using the ":=" operator removes this problem:

    var $output := call matching-color($match = "corn");
	    

Behind the scenes, SLAX is performing the ext:node-set() call but the details are hidden from the user.

4.3 
Control Statements

This section gives details and examples for each of the control statements in SLAX.

Section Contents:

The "if" and "else" Statements

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>
	    

The "for‑each" Statement

The "for‑each" statement iterates through the members of a node set, evaluating the contents of the statement with the context set to each node.

    SYNTAX::
        'for-each' '(' xpath-expression ')' '{'
            contents
        '}'
	    

The XPath expression is evaluated into a set of nodes, and then each node is considered as the "context" node, the contents of the "for‑each" statement are evaluated.

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

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>
	    

The "for" Statement

In addition to the standard XSLT "for‑each" statement, SLAX incorporates a "for" statement that allows iteration through a node set without changing the context (".").

    SYNTAX::
        'for' variable-name '(' xpath-expression ')' '{'
            contents
        '}'
	    

The variable is assigned each member of the node-set selected by the expression in sequence, and the contents are then evaluated.

    for $item (item-list) {
        <item> $item;
    }
	    

Internally, this is translated into normal XSLT constructs involving a pair of nested for-each loops, one to iterate and one to put the context back to the previous setting. This allows the script writer to ignore the context change.

The "while" Statement

The "while" statement allows a block of code to be repeated until a condition is no longer true. This construct is only useful when combined with mutable variables (Section 4.1.2).

    SYNTAX::
        'while' '(' xpath-expression ')' '{'
            contents
        '}'
	    

The xpath-expression is cast to a boolean type and if true, the contents are evaluated. The context is not changed. This loop continues until the expression is no longer true. Care must be taken to avoid infinite loops.

    mvar $seen;
    mvar $count = 1;
    while (not($seen)) {
        if (item[$count]/value) {
            set $seen = true();
        }
        set $count = $count + 1;
    }
	    

The "sort" Statement

The "for‑each" normally considers nodes in document order, but the "sort" statement indicates the specific order the programmer needs.

The "sort" statement takes an expression argument that is used as the key, as well as substatements that alter the normal sort behavior.

Statement Values
language Not implemented in libxslt
data-type "text", "number", or qname
order "ascending" or "descending"
case-order "upper-first" or "lower-first"

Multiple "sort" statements can be used to given secondary sorting keys.

    for-each (author) {
        sort name/last;
        sort name/first;
        sort age {
            order "descending";
        }
        copy-of .;
    }
	    

XSLT equivalent:

    <xsl:for-each select="author">
      <xsl:sort select="name/last"/>
      <xsl:sort select="name/first"/>
      <xsl:sort select="age" order="descending"/>
      <xsl:copy-of select="."/>
    </xsl:for-each>
	    

The "..." Operator

Often a loop is required to iterator through a range of integer values, such a 1 to 10. SLAX introduces the "..." operator to generate sequences of such numbers:

    for $i (1 ... 10) {
        <player number=$i>;
    }
	    

The operator translates into an XPath function that generates the sequence as a node set, which contains a node for each value. The "for" and "for‑each" statements can be used to iterate thru the nodes in a sequence.

    for-each ($min ... $max) {
        message "Value: " _ .;
    }
	    

The "?:" Operator

The "?:" operator allows simple logic tests to be coded with the familiar C and Perl operator.

    var $x = ($a > 10) ? $b : $c;
    var $y = $action ?: "display";
	    

The generated XSLT uses an <xsl:choose> element:

    <xsl:variable name="slax-ternary-1">
      <xsl:choose>
        <xsl:when test="($a &gt; 10)">
          <xsl:copy-of select="$b"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy-of select="$c"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="x" select="slax:value($slax-ternary-1)"/>
	    

The use of slax:value() make the "?:" operator non-standard, in that it requires a non-standard extension function. Use of the "?:" should be limited to environments where this function is available.

The use of <xsl:copy‑of> means that attributes cannot be used in a ?: expression, directly or indirectly.

    /* These are examples of invalid use of attributes */
    var $direct = $test ? @broken : will-not-work;
    var $attrib = @some-attribute;
    var $indirect = $test ? $attrib : wont-work-either;
	    

4.4 
Functions

Functions are one of the coolest extensions defined by EXSLT. They allow a script to define an extension function that is available in XPath expressions. Functions have several advantages over templates:

This section describes how functions are defined.

Section Contents:

The "function" Statement

The "function" statement defines a function that can be called in XPath expressions.

    SYNTAX::
        'function' function-name '(' argument-list ')' '{'
            function-template
        '}'
	    

The argument-list is a comma separated list of parameter names, which will be positionally assigned based on the function call. Trailing arguments can have default values, in a similar way to templates. If there are fewer parameters in the invocation than in the definition, then the default values will be used for any trailing arguments. If is an error for the function to be invoke with more arguments than are defined.

    function size ($width, $length, $scale = 1) {
        ...
    }
	    

Function parameters can also be defined using the "param" statement.

The "result" Statement

The "result" statement defines a value or template used as the return value of the function.

    SYNTAX::
        'result' value ';'
        'result' element
        'result' '{'
            result-template
        '}'
	    

The value can be a simple XPath expression, an XML element, or a set of instructions that emit the value to be returned.

    function size ($width, $length, $scale = 1) {
        result $width * $length * $scale;
    }

    function box-parts ($width, $height, $depth, $scale = 1) {
        result <box> {
            <part count=2> size($width, $depth);
            <part count=2> size($width, $height);
            <part count=2> size($depth, $height);
        }
    }

    function ark () {
        result {
            <ark> {
                expr box-parts(2.5, 1.5, 1.5);
            }
        }
    }
	    

5_ 
Additional SLAX Statements

This section contains statements and constructs that don't easily fit into other categories.

Section Contents:

5.1 
The "#!" Line

If the first line of a SLAX script begins with the characters "#!", the rest of that line is ignored. This allows scripts to be invoked directly by name on a unix command line. Additional details are available at the following site:

http://en.wikipedia.org/wiki/Shebang_%28Unix%29

(Personally, I've never heard it pronounced "shebang" before; we call it "pound‑bang" around here, just as we say "pound define" rather than "hash define".)

See Section 7.2.1 for details about the slaxproc utility argument handling and the "#!" line.

5.2 
Combining Scripts ("include" and "import")

Scripts files can use multiple source files, allowing library files to be shared among a number of source files. The "import" and "include" statements incorporate the templates and functions from one file into another, allowing them to be used implicitly or explicitly by the XSLT engine. Files need not be SLAX files, but can be traditional XSLT files.

A script may include another script using the "include" statement. Content from included files is conceptually inserted into the script at the point where the "include" statement is used. The "import" statement works similarly, but the contents of an imported file are given a lower priority (for expression matching and template or function redefinition). Conceptually, imported files are inserted at the top of the script, in the order in which they are given in the source files.

See also http://www.w3.org/TR/xslt#section-Combining-Stylesheets.

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.

5.3 
The "key" Statement

The "key" statement defines a key for use by the "key()" XPath function. Keys allow a script to locate nodes in a document that are referenced by other nodes. Each key definition indicates the nodes on which the key is defined and the value of the key. The key() function can then be used to locate the appropriate node.

    SYNTAX::
        'key' key-name '{'
            'match' match-expression ';'
            'value value-expression ';'
        '}'
	    

The "key‑name" uniquely identifies the key within the script, and is passed as the first argument to the key() function. The "match" statement gives an XPath expression selecting the set of nodes on which the keys are found, and the "value" statement gives an XPath expression for the value of the key.

    key func {
        match prototype;
        value @name;
    }
	    

See also http://www.w3.org/TR/xslt#key.

5.4 
The "decimal‑format" Statement

The "decimal‑format" statement defines formatting parameters for use by the format-number() XPath function.

    SYNTAX::
        'decimal-format' format-name ';'
        'decimal-format' format-name '{'
            'decimal-separator' data_value ';'
            'grouping-separator' data_value ';'
            'infinity' data_value ';'
            'minus-sign' data_value ';'
            'nan' data_value ';'
            'percent' data_value ';'
            'per-mille' data_value ';'
            'zero-digit' data_value ';'
            'digit' data_value ';'
            'pattern-separator' data_value ';'
        '}'
	    

The format-name is the name passed as the third argument to the format-number() XPath function. The statements under the "decimal‑format" statement follow the meaning of their counterparts under the <xsl:decimal‑format> element, as detailed in the reference below.

    decimal-format us {
        decimal-separator ".";
        grouping-separator ",";
    }
    decimal-format eu {
        decimal-separator ",";
        grouping-separator ".";
    }

    match / {
        <top> {
            <data> format-number(24535.2, "###.###,00", "eu");
            <data> format-number(24535.2, "###,###.00", "us");
        }
    }
	    

See also http://www.w3.org/TR/xslt#format-number.

5.5 
Messages

Typical scripts work by generating XML content as a result tree, but occasionally a script may need to make explicit, immediate output. The statements in this section allow for such output.

Section Contents:

The "message" Statement

The "message" statement allows output to be generated immediately, without waiting until the script generates its final result tree.

    SYNTAX::
        'message' message-expression ';'
        'message' '{'
            block-statements
        '}'
	    

The message-expression is an XPath expression that is emitted as output, typically on the standard error file descriptor. Alternatively, a block of statements can be used to generate the content of the message. The output of the block will be converted to a string using the normal XSLT rules.

    if (not(valid)) {
        message name() _ " invalid";
    } else if (failed) {
        message {
            expr "Failed";
            if ($count > 1) {
                expr ", again!";
            }
        }
    }
	    

The XSLT is as follows:

    <xsl:choose>
      <xsl:when test="not(valid)">
        <xsl:message>
          <xsl:value-of
              select="concat(name(), &quot; invalid&quot;)"/>
        </xsl:message>
      </xsl:when>
      <xsl:when test="failed">
        <xsl:message>
          <xsl:text>Failed</xsl:text>
          <xsl:if test="$count &gt; 1">
            <xsl:text>, again!</xsl:text>
          </xsl:if>
        </xsl:message>
      </xsl:when>
    </xsl:choose>
	    

See also http://www.w3.org/TR/xslt#message.

The "terminate" Statement

The "terminate" statement can be used to deliver a message to the user and then exit the script.

    SYNTAX::
        'terminate' message-expression ';'
        'terminate' '{'
            block-statements
        '}'
	    

The "terminate" statement mimics Section 5.5.1. The message-expression is an XPath expression that is emitted as output, like the "message" statement. Alternatively, a block of statements can be used to generate the content of the message. The output of the block will be converted to a string using the normal XSLT rules.

After emitting the message, the script stops any further processing.

The "trace" Statement

Trace output is vital to writing, debugging, and maintaining scripts. SLAX introduces a trace facility that will record XPath expressions or template contents in a trace file. If tracing is not enabled, then the trace template is not evaluated and no trace output is generated. The enabling of tracing and the naming of trace files is not covered here, since it is typically a feature of the environment in which a SLAX script is called. For example, the "slaxproc" command uses the "‑t" and "-T file" to enable tracing.

    SYNTAX::
        'trace' trace-message ';'
        'trace' '{'
            trace-template
        '}'
	    

The trace-message is an XPath expression that is written to the trace file. The trace-template is executed and the results are written to the trace file.

    trace "max " _ $max _ "; min " _ $min;
    trace {
        <max> $max;
        <min> $min;
    }
    trace {
        if ($my-trace-flag) {
            expr "max " _ $max _ "; min " _ $min;
            copy-of options;
        }
    }
	    

5.6 
The "output‑method" Statement

The "output‑method" statement defines the output style to be used when outputing the result tree.

    SYNTAX::
        'output-method' [style] '{'
            'version' data_value ';'
            'encoding' data_value ';'
            'omit-xml-declaration' data_value ';'
            'standalone' data_value ';'
            'doctype-public' data_value ';'
            'doctype-system' data_value ';'
            'cdata-section-elements' cdata_section_element_list ';'
            'indent' data_value ';'
            'media-type' data_value ';'
        '}'
	    

The style can be "xml", "html", or "text" (without quotes).

The substatements of "output‑method" correspond to the attributes of the XSLT <xsl:output> element.

Statement Description
version Version number of output method
encoding Character set
omit-xml-declaration (yes/no) Omit initial XML DECL
standalone (yes/no) Emit standalone declaration
doctype-public Public identifier in DTD
doctype-system System identifier in DTD
cdata-section-elements List of elements to use CDATA
indent (yes/no) Emit pretty indentation
media-type MIME type for document

See also http://www.w3.org/TR/xslt#output.

5.7 
The "fallback" Statement

The "fallback" statement directs the XSLT engine to perform a block of code when an extension element is invoked which is not supported.

    SYNTAX::
        'fallback' '{' statements '}'

    EXAMPLE::
        if ($working) {
            <some:fancy> "thing";
            fallback {
                message "nothing fancy, please";
            }
        }
	    

5.8 
Whitespace Handling

SLAX includes a means of retaining or removing text nodes that contain only whitespace. Whitespace for XML is the space, tab, newline or carriage return characters.

Section Contents:

The "strip‑space" Statement

The "strip‑space" statement tells the engine to discard the given elements if they contain only whitespace.

    SYNTAX::
        'strip-space' list-of-element-names ';'
	    

The list-of-element-names is a space separated list of element names that should have their contents discarded if they contain only whitespace.

    strip-space section paragraph bullet;
	    

See also http://www.w3.org/TR/xslt#strip.

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"/>
	    

The "preserve‑space" Statement

The "preserve‑space" statement works similar to the "strip‑space" statement, but with the opposite result.

    SYNTAX::
        'preserve-space' list-of-element-names ';'
	    

The list-of-element-names is a space separated list of element names that should have their contents retained even if they contain only whitespace.

    preserve-space art picture line;
	    

See also http://www.w3.org/TR/xslt#strip.

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"/>
	    

5.9 
The "version" Statement

The "version" statement contains the current version of the SLAX language, allowing scripts and interpreters to progress independently. Old engines will not understand new constructs and should stop with an error when a version number that is unknown to them is seen. New engines should accept any previous language version number, so allow old scripts to run on new engines.

    SYNTAX::
        'version' version-number ';'
	    

The version-number should be either "1.1" or "1.0". The current version is "1.1" and newly developed scripts should use this version number.

    version 1.1;
	    

All SLAX stylesheets must begin with a "version" statement, which gives the version number for the SLAX language. This is currently fixed at "1.1" and will increase as the language evolves. Version 1.1 is completely backward compatible with version 1.0, but adds additional functionality that may cause issues for implementations of SLAX 1.0.

SLAX version 1.1 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"').


6_ 
SLAX Functions

SLAX includes a number of utility functions, both for internal and external use. This section documents the external functions.

Section Contents:

6.1 
SLAX External Functions

SLAX defines a set of built-in extension functions. When one of these functions is referenced, the SLAX namespace is automatically added to the script.

Section Contents:

slax:base64-decode

Use the slax:base64-decode function to decode BASE64 encoded data. BASE64 is means of encoding arbitrary data into a radix-64 format that can be more easily transmitted, typically via STMP or HTTP.

The argument is a string of BASE64 encoded data and an option string which is used to replace any non-XML control characters, if any, in the decoded string. If this argument is an empty string, then non-xml characters will be removed. The decoded data is returned to the caller.

    SYNTAX::
        string slax:base64-decode(string [,control])

    EXAMPLE::
        var $real-data = slax:base64-decode($encoded-data, "@");
	    

slax:base64-encode

Use the slax:base64-encode function to encode a string of data in the BASE64 encoding format. BASE64 is means of encoding arbitrary data into a radix-64 format that can be more easily transmitted, typically via STMP or HTTP.

The argument is a string of data, and the encoded data is returned.

    SYNTAX::
        string slax:base64-encode(string)

    EXAMPLE::
        var $encoded-data = slax:base64-encode($real-data);
	    

slax:break-lines

Use the slax:break-lines function to break multi-line text content into multiple elements, each containing a single line of text.

    SYNTAX::
        node-set slax:break-lines(node-set)

    EXAMPLE::
        var $lines = slax:break-lines(.//p);
        for-each ($lines) {
            if (start-with("pfe:", .)) {
                <output> .;
            }
        }
	    

For historical reasons, this function is also available using the name "slax:break_lines" (with an underscore instead of a dash). Scripts should however avoid using this name.

slax:dampen

Use the slax:dampen() function to limit the rate of occurrence of a named event. If slax:dampen() is called more than "max" times in "time‑period" minutes, it will return "true", allowing the caller to react to this condition.

    SYNTAX::
        boolean slax:dampen(name, max, time-period)

    EXAMPLE::
        if (slax:dampen("reset", 2, 10)) {
            message "reset avoided";
        } else {
            <reset>;
        }
	    

slax:document

Use the slax:document() function to read a data from a file or URL. The data can be encoded in any character set (defaulting to "utf‑8") and can be BASE64 encoded. In addition, non-XML control characters, if any, can be replaced.

    SYNTAX::
        string slax:document(url [, options])

    EXAMPLE::
        var $data = slax:document($url);

        var $options := {
            <encoding> "ascii";
            <format> "base64";
            <non-xml> "#";
        }
        var $data2 = slax:document($url, $options);
	    
Option Description
<encoding> Character encoding scheme ("utf-8")
<format> "base64" for BASE64-encoded data
<non-xml> Replace non-xml characters with this string

If the <non‑xml> value is an empty string, then non-xml characters will be removed, otherwise they will be replaced with the given string.

slax:evaluate

Use the slax:evaluate() function to evaluate a SLAX expression. This permits expressions using the extended syntax that SLAX provides in addition to what is allowed in XPath (see Section 2.1). The results of the expression are returned.

    SYNTAX::
        object slax:evaluate(expression)

    EXAMPLE::
        var $result = slax:evaluate("expr[name == '&']");
	    

slax:first-of

Use the slax:first-of() function to find the first value present in a test of arguments. The first non-empty or non-zero length string will be returned.

    SYNTAX::
        object slax:first-of(object+)

    EXAMPLE::
        var $title = slax:first-of(@title, $title, "Unknown");
	    

slax:get-command

Use the slax:get-command() function to return an input string provided by the user in response to a given prompt string. If the "readline" (or "libedit") library was found at install time, the returned value is entered in the readline history, and will be available via the readline history keystrokes (Control-P and Control-N).

    SYNTAX::
        string slax:get-command(prompt)

    EXAMPLE::
        var $response = slax:get-command("# ");
	    

slax:get-input

Use the slax:get-input() function to return an input string provided by the user in response to a given prompt string.

    SYNTAX::
        string slax:get-input(prompt)

    EXAMPLE::
        var $response = slax:get-input("Enter peer address: ");
	    

slax:get-secret

Use the slax:get-secret() function to return an input string provided by the user in response to a given prompt string. Any text entered by the user will not be displayed or echoed back to the user, making this function suitable for obtaining secret information such as passwords.

    SYNTAX::
        string slax:get-secret(prompt)

    EXAMPLE::
        var $response = slax:get-secret("Enter password: ");
	    

slax:is-empty

Use the slax:is-empty() function to determine if a node-set or RTF is truly empty.

    SYNTAX::
        boolean slax:is-empty(object)

    EXAMPLE::
        if (slax:is-empty($result)) {
            message "missing result";
        }
	    

slax:printf

Use the slax:printf() function to format text in the manner of the standard "C" "printf" function (printf(3)). The normal printf format values are honored, as are a number of "%j" extensions.

Format Description
"%jcs" Capitalize first letter
"%jt{TAG}s" Prepend TAG if string is not empty
"%j1s" Skip field if value has not changed
    SYNTAX::
        string slax:printf(format, string*)

    EXAMPLE::
        for-each (article) {
            for-each (author) {
                message  slax:printf("%8j1s%8s%8jcj1s %jt{b:}s",
                                    ../title, name, dept, born);
            }
        }
	    

slax:regex

    SYNTAX::
        node-set slax:regex(pattern, string, opts?)
	    

Match a regex, returning a node set of the full string matched plus any parenthesized matches. Options include "b", "i", "n", "^", and "$", for boolean results, ICASE, NEWLINE, NOTBOL, and NOTEOL.

slax:sleep

    SYNTAX::
        void slax:sleep(seconds, milliseconds)
	    

Sleep for a given time period.

slax:split

    SYNTAX::
        node-set slax:split(pattern, string, limit)
	    

Break a string into a set of elements, up to the limit times, at the pattern.

slax:sysctl

    SYNTAX::
        string slax:sysctl(name, format)
	    

Retrieve a sysctl variable. Format is "i" or "s".

slax:syslog

    SYNTAX::
        void slax:syslog(priority, string+)
	    

Syslog the concatenation of set of arguments.


7_ 
The libslax Distribution

SLAX is available as an open-source project with the "New BSD" license. Current releases, source code, documentation, and support materials can be downloaded from:

https://github.com/Juniper/libslax

Section Contents:

7.1 
The libslax Library

The core of the distribution is the libslax library, which incorporates a SLAX parser to read SLAX files, a SLAX writer to write SLAX files, a debugger, a profiler, and a commandline tool.

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.

To support SLAX in your application, link with libslax and call the libslax initializer function:

    #include <libslax/slax.h>

    slaxEnable(1);
	    

7.2 
slaxproc: The SLAX Processor

The SLAX software distribution contains a library (libslax) and a command line tool (slaxproc). The command line tool can be used to convert between XSLT and SLAX syntax, as well as run stylesheets and check syntax.

 Usage: slaxproc [mode] [options] [script] [files]
  Modes:
    --check OR -c: check syntax and content for a SLAX script
    --format OR -F: format (pretty print) a SLAX script
    --json-to-xml: Turn JSON data into XML
    --run OR -r: run a SLAX script (the default mode)
    --show-select: show XPath selection from the input document
    --show-variable: show contents of a global variable
    --slax-to-xslt OR -x: turn SLAX into XSLT
    --xml-to-json: turn XML into JSON
    --xpath <xpath> OR -X <xpath>: select XPath data from input
    --xslt-to-slax OR -s: turn XSLT into SLAX

   Options:
    --debug OR -d: enable the SLAX/XSLT debugger
    --empty OR -E: give an empty document for input
    --exslt OR -e: enable the EXSLT library
    --expression <expr>: convert an expression
    --help OR -h: display this help message
    --html OR -H: Parse input data as HTML
    --ignore-arguments: Do not process any further arguments
    --include <dir> OR -I <dir>: search dir for includes/imports
    --indent OR -g: indent output ala output-method/indent
    --input <file> OR -i <file>: take input from the given file
    --json-tagging: tag json-style input with the 'json' attribute
    --keep-text: mini-templates should not discard text
    --lib <dir> OR -L <dir>: search dir for extension libraries
    --log <file>: use given log file
    --mini-template <code> OR -m <code>: wrap template code in script
    --name <file> OR -n <file>: read the script from the given file
    --no-randomize: do not initialize the random number generator
    --no-tty: Do not use tty for sdb and other input needs
    --no-json-types: do not insert 'type' attribute for --json-to-xml
    --output <file> OR -o <file>: make output into the given file
    --param <name> <value> OR -a <name> <value>: pass parameters
    --partial OR -p: allow partial SLAX input to --slax-to-xslt
    --slax-output OR -S: emit SLAX-style XML output
    --trace <file> OR -t <file>: write trace data to a file
    --verbose OR -v: enable debugging output (slaxLog())
    --version OR -V: show version information (and exit)
    --write-version <version> OR -w <version>: write in version

  Project libslax home page: https://github.com/Juniper/libslax
	    

Files can be referenced positionally (where the script file first) or by using the "‑n", "‑i", and "‑o" options.

To use slaxproc to convert a SLAX file to XSLT:

    $ slaxproc mine.slax new.xsl
	    

To convert an XSLT file to SLAX:

    $ slaxproc existing.xsl new.slax
	    

To run a script:

    $ slaxproc mine.slax infile.xml outfile.xml
	    

Use the "‑g" option to produce good-looking output by enabling indenting (aka "pretty‑printing") of output. In this example, since the output filename is not given, the output is written to the standard output stream (stdout):

    $ slaxproc -g mine.slax infile.xml
	    

Use the "‑p" flag to perform conversion of SLAX and XML formats for partial data files. This can be used as a filter inside an editor to convert a region from one format to the other:

    $ cat in.xml | slaxproc -s -p > out.slax
	    

Use the "‑w" option to restrict the output of slaxproc to 1.0 features.

Section Contents:

Argument Handling

slaxproc accept the script name, input name, and output name in two ways. You can say:

    slaxproc script.slax input.xml output.xml
	    

using positional arguments. This way allows slaxproc to be plug compatible with xsltproc.

The other way is to give explicit option values using "‑n", "‑i", and "‑o". The above command line can be given as:

   slaxproc -i input.xml -n script.slax -o output.xml
	    

These options can be in any order and can be intermixed with other arguments. If none of the values are given, they can still be parsed positionally. In this example, the script name is positional but the input and output file names are positional.

   slaxproc -i input.xml -o output.xml -g -v script.slax
	    

Section Contents:

"#!" Support

SLAX supports the "#!" unix scripting mechanism, allowing the first line of a script to begin with the characters '#' and '!' followed by a path to the executable that runs the script and a set of command line arguments. For SLAX scripts, this might be something like:

    #!/usr/bin/slaxproc -n
	    

or:

    #!/opt/local/bin/slaxproc -n
	    

The operating system will add the name of the scripts and any command line arguments to the command line that follows the "#!". Adding the "‑n" option (as shown above) allows additional arguments to be passed in on the command line. Flexible argument parsing allows aliases and

needs. For example if a script begins with:

    #!/usr/bin/slaxproc -E -n 
	    

then additional slaxproc arguments can be given:

    $ that-script -g output.xml
	    

and the resulting command should be:

    /usr/bin/slaxproc -E -n /path/to/that-script -g output.xml
	    

The -E option tells slaxproc to use an empty input document, removing the need for the "‑i" option or a positional argument.

If the input or output arguments have the value "‑" (or is not given), the standard input or standard output file will be used. This allows slaxproc to be used as a traditional unix filter.

Command Line Options

Command line options to slaxproc can be divided into two types. Mode options control the operation of slaxproc, and are mutually exclusive. Behavioral options tailor the behavior of slaxproc in minor ways.

Section Contents:

Modes Options

--check OR -c
Perform syntax and content check for a SLAX script, reporting any errors detected. This mode is useful for off-box syntax checks for scripts before installing or uploading them.
--format OR -F
Format (aka "pretty print") a SLAX script, correcting indentation and spacing to the style preferred by the author (that is, me).
--json-to-xml
Transform JSON input into XML, using the conventions defined in Section 2.2.3.
--run OR -r
Run a SLAX script. The script name, input file name, and output file name can be provided via command line options and/or using positional arguments as described in Section 7.2.1. Input defaults to standard input and output defaults to standard output. "‑r" is the default mode for slaxproc.
--show-select
Show an XPath selection from the input document. Used to extract selections from a script out for external consumption. This allows the consumer to avoid a SLAX parser, but still have visibility into the contents of the script.
--show-variable
Show contents of a global variable. Used to extract static variable contents for external consumption. This allows the consumer of the data to avoid a SLAX parser, but still have access to the static contents of global variables, such as the $arguments variable.
--slax-to-xslt OR -x
Convert a SLAX script into XSLT format. The script name and output file name can be provided via command line options and/or using positional arguments as described in Section 7.2.1.
--xml-to-json
Transform XML input into JSON, using the conventions defined in Section 2.2.3.
--xpath <xpath> OR -X <xpath>
Select data matching an XPath data from input document. This allows slaxproc to operate as a filter.
--xslt-to-slax OR -s
Convert a XSLT script into SLAX format. The script name and output file name can be provided via command line options and/or using positional arguments as described in Section 7.2.1.

Behavioral Options

--debug OR -d
Enable the SLAX/XSLT debugger. See Section 7.3 for complete details on the operation of the debugger.
--empty OR -E
Provide an empty document as the input data set. This is useful for scripts that do not expect or need meaningful input.
--exslt OR -e
Enables the EXSLT library, which provides a set of standard extension functions. See exslt.org for more information.
--expression <expr>
Converts a SLAX expression to an XPATH one, or vice versa, depending on the presence of --slax-to-xslt and --xslt-to-slax.
--help OR -h
Displays this help message and exits.
--html OR -H
Parse input data using the HTML parser, which differs from XML.
--ignore-arguments
Do not process any further arguments. This can be combined with "#!" to allow distinct styles of argument parsing.
--include <dir> OR -I <dir>
Add a directory to the list of directories searched for include and /import files. The environment variable SLAXPATH can be set to a list of search directories, separated by colons.
--indent OR -g
Indent output to make it good looking. This option is identical to the behavior triggered by "output-method { indent 'true'; }".
--input <file> OR -i <file>
Use the given file for input.
--json-tagging
Tag JSON elements as they are parsing into XML with the 'json' attribute. This allows the --format mode to transform them back into JSON format.
--keep-text
When building a script from mini-templates, do not add a template to discard normal text. By default XSLT will display unmatched text data. This option preserves this default behavior instead of replacing it with the more typically desirable discard action.
--lib <dir> OR -L <dir>
Add a directory to the list of directories searched for extension libraries.
--log <file>
Write log data to the given file.
--mini-template <code> or -m <code>
Allows a simple script to be passed in via the command line using one of more "‑m" options. The argument to "‑m" is typically a template, such as a named or match template
--name <file> OR -n <file>
Read the SLAX script from the given file.
--no-json-types
Do not generate the 'type' attribute in the XML generated by --json-to-xml. This type is needed to 'round‑trip' data back into JSON, but is not needed for simple XML output.
--no-randomize
Do not initialize the random number generator. This is useful if you want the script to return identical data for a series of invocation, which is typically only used during testing.
--no-tty
Do not use tty for sdb and other tty-related input needs.
--output <file> OR -o <file>
Write output into the given file.
--param <name> <value> OR -a <name> <value>
Pass a parameter to the script using the name/value pair provided. Note that all parameters are string parameters, so normal quoting rules apply.
--partial OR -p
Allow the input data to contain a partial SLAX script, which can be used with the "‑‑slax‑to‑xslt" to perform partial transformations.
--slax-output OR -S
Write the result using SLAX-style XML (braces, etc)
--trace <file> OR -t <file>
Write trace data to the given file.
--verbose OR -v
Adds very verbose internal debugging output to the trace data output, including calls to the slaxLog() function.
--version OR -V
Show version information and exit.
--write-version <version> OR -w <version>
Write in the given version number on the output file for "‑x" or "‑s" output. This can be also be used to limit the conversion to avoid SLAX 1.1 feature (using "-w 1.0").

7.3 
The SLAX Debugger (sdb)

The SLAX distribution includes a debugger called "sdb", which can be accessed via the "slaxproc" command using the "‑d" option. The debugger resembles "gdb" command syntax and operation.

  (sdb) help
  List of commands:
    break [loc]     Add a breakpoint at [file:]line or template
    callflow [val]  Enable call flow tracing
    continue [loc]  Continue running the script
    delete [num]    Delete all (or one) breakpoints
    finish          Finish the current template
    help            Show this help message
    info            Showing info about the script being debugged
    list [loc]      List contents of the current script
    next            Execute the over instruction, stepping over calls
    over            Execute the current instruction hierarchy
    print <xpath>   Print the value of an XPath expression
    profile [val]   Turn profiler on or off
    reload          Reload the script contents
    run             Restart the script
    step            Execute the next instruction, stepping into calls
    where           Show the backtrace of template calls
    quit            Quit debugger
	    

The "info" command can display the following information:

  (sdb) info help
  List of commands:
    info breakpoints  Display current breakpoints
    info profile [brief]  Report profiling information
	    

Many of these commands follow their "gdb" counterparts, to the extent possible.

The location for the "break", "continue", and "list" commands can be either a line number of the current file, a filename and a line number, separated by a colon, or the name of a template.

  (sdb) b 14
  Breakpoint 1 at file ../tests/core/test-empty-21.slax, line 14
  (sdb) b 19
  Breakpoint 2 at file ../tests/core/test-empty-21.slax, line 19
  (sdb) b three
  Breakpoint 3 at file ../tests/core/test-empty-21.slax, line 24
  (sdb) info br
  List of breakpoints:
      #1 template one at ../tests/core/test-empty-21.slax:14
      #2 template two at ../tests/core/test-empty-21.slax:19
      #3 template three at ../tests/core/test-empty-21.slax:24
  (sdb) 
	    

Information on the profiler is in the next section (Section 7.4).

7.4 
The SLAX Profiler

The SLAX debugger includes a profiler which can report information about the activity and performance of a script. The profiler is automatically enabled when the debugger is started, and tracks script execution until the script terminates. At any point, profiling information can be displayed or cleared, and the profiler can be temporarily disabled or enabled.

Use the "profile" command to access the profiler:

  (sdb) help profile
  List of commands:
    profile clear   Clear  profiling information
    profile off     Disable profiling
    profile on      Enable profiling
    profile report [brief]  Report profiling information
  (sdb) 
	    

The profile report includes the following information:

The "brief" option instructs sdb to avoid showing lines that were not hit, since there is no valid information for them. Without the "brief" option, dashes are displayed.

In the following example, the source code data is heavily truncated (with "....") to allow the material to fit on this page. sdb would not truncate these lines:

  (sdb) run
  <?xml version="1.0"?>
  <message>Down rev PIC in Fruvenator, Fru-Master 3000</message>
  Script exited normally.
  (sdb) profile report
   Line   Hits   User    U/Hit  System    S/Hit Source
      1      -      -        -       -        - version 1.0;
      2      -      -        -       -        - 
      3      2      4     2.00       8     4.00 match / {
      4      1     25    25.00      13    13.00     var ....
      5      -      -        -       -        - 
      6      -      -        -       -        -     for-each....
      7      1     45    45.00      10    10.00          ..
      8      1     12    12.00       5     5.00         <message>
      9      1     45    45.00      15    15.00          ....
     10      -      -        -       -        -     }
     11      -      -        -       -        - }
  Total      6    131               51   Total
  (sdb) pro rep b
   Line   Hits   User    U/Hit  System    S/Hit Source
      3      2      4     2.00       8     4.00 match / {
      4      1     25    25.00      13    13.00     var  ....
      7      1     45    45.00      10    10.00          ....
      8      1     12    12.00       5     5.00      <message>
      9      1     45    45.00      15    15.00          ....
  Total      6    131               51   Total
  (sdb) 
	    

This information not only shows how much time is spent during code execution, but also shows which lines are being executed, which can help debug scripts where the execution does not match expectations.

The profiling is not "Monte Carlo", or clock based, but is based on trace data generated as each SLAX instruction is executed, giving more precise data.

7.5 
callflow

The "callflow" command enables the printing of informational data when levels of the script are entered and exited. The lines are simple, but reference the instruction, filename, and line number of the frame:

  callflow: 0: enter <xsl:template> in match / at empty-15.slax:5
  callflow: 1: enter <xsl:variable> at empty-15.slax:13
  callflow: 1: exit <xsl:variable> at empty-15.slax:13
  callflow: 1: enter <xsl:variable> at empty-15.slax:20
  callflow: 1: exit <xsl:variable> at empty-15.slax:20
  callflow: 0: exit <xsl:template> in match / at empty-15.slax:5
	    

8_ 
Extension Libraries in libslax

libslax supports a means of dynamically loading extension libraries. After a script is parsed, any extension prefixes are determined, along with their namespaces. Each namespace are then URL-escaped and a ".ext" suffix is appended to generate a filename for the extension library that supports that namespace.

Extension libraries should be placed in the directory named that is either:

Section Contents:

8.1 
The "bit" Extension Library

The "bit" extension library has functions that interpret a string as a series of bit, allowing arbitrary length bit arrays and operations on those arrays.

Function and Arguments Description
bit:and(b1, b2) Return AND of two bit strings
bit:or(b1, b2) Return OR of two bit strings
bit:nand(b1, b2) Return NAND of two bit strings
bit:nor(b1, b2) Return NOR of two bit strings
bit:xor(b1, b2) Return XOR of two bit strings
bit:xnor(b1, b2) Return XNOR of two bit strings
bit:not(b1) Return NOT of a bit string
bit:clear(b, off) Clear a bit within a bit string
bit:compare(b1, b2) Compare two bit strings
bit:set(b, off) Set a bit within a bit string
bit:mask(count, len?) Return len bits set on
bit:to-int(bs) Return integer value of bit string
bit:from-int(val, len?) Return bit string of integer value
bit:to-hex(bs) Return hex value of a bit string
bit:from-hex(str, len?) Return bit string of hex value

8.2 
The "curl" Extension Library

curl and libcurl are software components that allow access to a number of protocols, include http, https, smtp, ftp, and scp. They are open source and available from the web site http://curl.haxx.se/libcurl/. Please refer to that site for additional information.

Section Contents:

curl Elements

Curl operations are directed using a set of elements, passed to the curl extension functions. These elements closely mimic the options used by the native C libcurl API via libcurl's curl_easy_setopt() function), as documented on the following web page:

http://curl.haxx.se/libcurl/c/curl_easy_setopt.html

Once these values are set, a call to curl_easy_perform() performs the requested transfer.

In the SLAX curl extension library, these options are represented as individual elements. This means the <url> element is mapped to the "CURLOPT_URL" option, the <method> option is mapped to the CURLOPT_CUSTOMREQUEST option, and so forth.

These elements can be used in three ways:

  • The curl:single() extension function allows a set of options to be used in a single transfer operation with no persistent connection handle.
  • The curl:perform() extension function allows a set of options to be used with a persistent connection handle. The handle is returned from the curl:open() extension function and can be closed with the curl:close() extension function.
  • The curl:set() extension function can be used to record a set of options with a connection handle and keep those options active for the lifetime of the connection.

For example, if the script needs to transfer a number of files, it can record the <username> and <password> options and avoid repeating them in every curl:perform() call.

This section gives details on the elements supported. The functions themselves are documented in the next section (Section 8.2.2).

Section Contents:

<cc>

The <cc> element gives a "Cc" address for "email" (SMTP) requests. For multiple address, use multiple <cc> elements.

    SYNTAX::
      <cc> "cc-user@email.examplecom";
	    

<connect‑timeout>

The <connect‑timeout> element gives the number of seconds before a connection attempt is considered to have failed.

    SYNTAX::
      <connect-timeout> 10;
	    

<contents>

The <contents> element gives the contents to be transfered.

    SYNTAX::
      <contents> "multi-\nline\ncontents\n";
	    

<content‑type>

The <content‑type> element gives the MIME type for the transfer payload.

    SYNTAX::
      <content-type> "mime/type";
	    

<errors>

The <errors> element controls how HTML and XML parsing errors are handled. The default (display them on stderr) is often not attractive. Instead errors can be ignored, logged, or recorded, based on the contents of the <errors> element:

Value Special behavior
default Errors are displayed on stderr
ignore Errors are discarded
log Errors are logged (via slaxLog())
record Errors are recorded

When <errors> is set to "record", all errors appear in a string under the <errors> element in the XML node (as returned by e.g. curl:perform). If no errors are generated, the <errors> element will not be present, allowing it to be used as a test for errors:

    var $opt = {
        <url> $url;
        <format> "html";
        <errors> "record";
    }
    var $res = curl:single($opts);
    if ($res/errors) {
        terminate "failure: " _ $res/errors;
    }
	    

<fail‑on‑error>

The <fail‑on‑error> element indicates that the transfer should fail if any errors where detected, including insignificant ones.

    SYNTAX::
      <fail-on-error>;
	    

<format>

The <format> element gives the expected format of the returned results, allowing the curl extension to automatically make the content available in the native format.

<format> "xml";

Format name Special behavior
html Result is parsed as HTML
name Result is name=value pairs
text None
url-encoded Result is n1=v1&n2=v2 pairs
xml Result is parsed as XML

The "name" encoding is used for name=value pairs separated by newlines, where the url-encoded encoding is used when the name=value pairs are separated by "&".

The parsed data is returned in the <data> element, using <name> elements. In the following example results, <format> was set to "url‑encoded":

    <results>
      <url>https://api.example.com/request_token</url>
      <curl-success/>
      <raw-headers>HTTP/1.1 200 OK&#xD;
    Server: XXXX&#xD;
    Date: Tue, 18 Jun 2013 18:56:31 GMT&#xD;
    Content-Type: application/x-www-form-urlencoded&#xD;
    Transfer-Encoding: chunked&#xD;
    Connection: keep-alive&#xD;
    x-server-response-time: 69&#xD;
    x-example-request-id: 123456&#xD;
    pragma: no-cache&#xD;
    cache-control: no-cache&#xD;
    x-http-protocol: None&#xD;
    x-frame-options: SAMEORIGIN&#xD;
    X-RequestId: 12345&#xD;
    &#xD;
    </raw-headers>
      <headers>
        <version>HTTP/1.1</version>
        <code>200</code>
        <message>OK</message>
        <header name="Server">XXXXX</header>
        <header name="Date">Tue, 18 Jun 2013 18:56:31 GMT</header>
        <header name="Content-Type"
               >application/x-www-form-urlencoded</header>
        <header name="Transfer-Encoding">chunked</header>
        <header name="Connection">keep-alive</header>
        <header name="x-server-response-time">69</header>
        <header name="x-example-request-id">123456</header>
        <header name="pragma">no-cache</header>
        <header name="cache-control">no-cache</header>
        <header name="x-http-protocol">None</header>
        <header name="x-frame-options">SAMEORIGIN</header>
        <header name="X-RequestId">12345</header>
      </headers>
      <raw-data>oauth_token_secret=s&amp;oauth_token=t</raw-data>
      <data format="url-encoded">
        <name name="oauth_token_secret">s</name>
        <name name="oauth_token">t</name>
      </data>
    </results>
	    

<from>

The <from> element gives the "From" address to use for "email" (SMTP) requests.

    SYNTAX::
      <from> "source-user@email.example.com";
	    

<header>

The <header> element gives additional header fields for the request.

    SYNTAX::
      <header name="name"> "value";
	    

<insecure>

The <insecure> element indicates a willingness to tolerate insecure communications operations. In particular, it will allow SSL Certs without checking the common name.

    SYNTAX::
      <insecure>;
	    

<local>

The <local> element gives the name to use as the local hostname for "email" (SMTP) requests .

    SYNTAX::
      <local> "local host name";
	    

<method>

The <method> element sets the method used to transfer data. This controls the HTTP request type, as well as triggering other transfer mechanisms.

    SYNTAX::
      <method> $method;
	    

Method names are listed in the table below.

Method Description
get HTTP GET or FTP GET operation
post HTTP POST operation
delete HTTP DELETE operation
head HTTP HEAD operation
email SMTP email send operation
put HTTP PUT operation
upload HTTP POST or FTP PUT operation

The "get" method is the default.

<param>

The <param> element gives additional parameter values for the request. These parameters are typically encoded into the URL.

    SYNTAX::
      <param name="x"> "y";
	    

<password>

The <password> element sets the user's password for the transfer.

    SYNTAX::
      <password> "password";
	    

<secure>

The <secure> element requests the use of the "secure" sibling of many protocols, including HTTPS and FTPS.

    SYNTAX::
      <secure>;
	    

<server>

The <server> element gives the outgoing SMTP server name. At present, MX records are not handled, but that will be fixed shortly.

    SYNTAX::
      <server> "email-server.example.com";
	    

<subject>

The <subject> element gives the "Subject" field for "email" (SMTP) requests.

    SYNTAX::
      <subject> "email subject string";
	    

<timeout>

The <timeout> element gives the number of seconds before an open connection is considered to have failed.

    SYNTAX::
      <timeout> 10;
	    

<to>

The <to> element gives a "To" address for "email" (SMTP) requests. For multiple address, use multiple <to> elements.

    SYNTAX::
      <to> "to-user@email.examplecom";
	    

<upload>

The <upload> element indicates this is a file upload request.

    SYNTAX::
      <upload>;
	    

<url>

The <url> element sets the base URL for the request.

    SYNTAX::
      <url> "target-url";
	    

<username>

The <username> element sets the user name to use for the transfer.

    SYNTAX::
      <username> "username";
	    

<verbose>

The <verbose> element requests an insanely detailed level of debug information that can be useful when debugging requests. The curl extension will display detailed information about the operations and communication of the curl transfer.

    SYNTAX::
      <verbose>;
	    

curl Extension Functions

The curl namespace defines a set of extension functions. This section describes those functions.

Section Contents:

curl:perform

The "curl:perform" extension function performs simple transfers using a persistent connection handle, as provided by curl:open (Section 8.2.2.2).

The arguments are the connection handle and a set of option elements as listed in Section 8.2.1. The returned object is an XML hierarchy containing the results of the transfer.

    SYNTAX::
        object curl:perform(handle, options)
	    

The returned object may contain the following elements:

Element Contents
url The requested URL
curl-success Indicates sucess (empty)
raw-headers Raw header fields from the reply
raw-data Raw data from the reply
error Contains error message text, if any
header Parsed header fields
data Parsed data

The <header> element can contain the following elements:

Element Contents
code HTTP reply code
version HTTP reply version string
message HTTP reply message
field HTTP reply fields (with @name and value)

The following is an example of the <header> element, with header fields parsed into <field> elements.

      <header>
        <version>HTTP/1.1</version>
        <code>404</code>
        <message>Not Found</message>
        <field name="Content-Type">text/html</field>
        <field name="Content-Length">345</field>
        <field name="Date">Mon, 08 Aug 2011 03:40:21 GMT</field>
        <field name="Server">lighttpd/1.4.28 juisebox</field>
      </header>
	    

curl:open

The "curl:open" extension function opens a connection to a remote server, allowing multiple operations over a single connection.

    SYNTAX::
        handle curl:open();
	    

The returned handle can be passed to curl:perform() or curl:close().

curl:set

The "curl:set" extension function records a set of parameters that will persist for the lifespan of a connection.

    SYNTAX::
        void cur:set(handle, options);
	    

curl:set() sets options on the handle, so they don't need to be repeated on each curl:perform() call.

    var $curl = curl:open();
    var $global-options = {
        <method> "post";
        <header name="id"> "phil";
        <header name="location"> "raleigh";
    }

    expr curl:set($curl, $global-options);

    call some-other-template($curl);
	    

curl:single

The "curl:single" extension function performs transfer operations without using a persistent connection.

    SYNTAX::
        object curl:single(options);
	    

The returned object is identical in structure to the one returned by curl:perform. Refer to Section 8.2.2.1 for additional information.

curl:close

The "curl:close" extension function closes an open connection. Further operations cannot be performed over the connection.

    SYNTAX::
        void curl:close(handle);
	    

Examples

This section contains a set of example scripts that use the curl extension to perform simple gets, google authorization, and send email.

Section Contents:

Simple GET

This script gets a vanilla web page, but just to be interesting, includes a header field for the HTTP header and a parameter that is incorporated into the requested URL.

    version 1.1;

    ns curl extension = "http://xml.libslax.org/curl";

    param $url = "http://www.juniper.net";

    match / {
        <op-script-results> {
            var $options = {
                <header name="client"> "slaxproc";
                <param name="smokey"> "bandit";
            }

            var $results = curl:single($url, $options);
            message "completed: " _ $results/headers/message;
            <curl> {
                copy-of $results;
            }
        }
    }
	    

Google Auth

This script take a username and password and uses the google login services to translate them into an "Authorization" string.

    version 1.1;

    ns curl extension = "http://xml.libslax.org/curl";

    param $url = "https://www.google.com/accounts/ClientLogin";
    param $username;
    param $password;

    var $auth-params := {
        <url> $url;
        <method> "post";
        <insecure>;
        <param name="Email"> $username;
        <param name="Passwd"> $password;
        <param name="accountType"> "GOOGLE";
        <param name="service"> "wise";
        <param name="source"> "test-app";
    }

    match / {
        var $curl = curl:open();

        var $auth-cred = curl:perform($curl, $auth-params);

        <options> {
            for-each(slax:break-lines( $auth-cred/raw-data )) {
                if(starts-with(.,"Auth")) { 
                    <header name="GData-Version"> "3.0";
                    <header name="Authorization"> "GoogleLogin " _ .;
                }
            }
        }

        expr curl:close($curl);
    }
	    

Email

This script sends an email via a server provided as a parameter.

    version 1.1;

    ns curl extension = "http://xml.libslax.org/curl";

    param $server;

    match / {
        <out> {
            var $info = {
                <method> "email";
                <server> $server;
                <from> "muffin@example.com";
                <to> "phil@example.net";
                <subject> "Testing...";
                <contents> "Hello,
    This is an email.  But you know that.

    Thanks,
     Phil
    ";
            }

            var $res = curl:single($info);
            <res> {
                copy-of $res;
            }
        }
    }
	    

8.3 
The "db" Extension Library

The db extension library provides a way to store/retrieve/manipulate/delete data stored in a backend database from SLAX scripts

Section Contents:

db Elements

Following are the elements that can be used in options and data to db extension functions

Section Contents:

<engine>

Used to specify the backend database engine that is used to access/store data

    <engine> "sqlite";
	    

sqlite is the currently supported backend engine. <engine> element can also take mysql/mongodb as values once the adapters for corresponding database engines are made available

<database>

Used to specify the name of the database to operate on

    <database> "test.db";
	    

<collection>

This is used to specify the data collection on which to perform operations. This corresponds to a database table in SQL world.

    <collection> "employee";
	    

<fields>

This is used to specify meta data about various fields that a collection contain using <field> elements

    <fields> {
        <field> {
            ...
            ...
        }
        <field> {
            ...
            ...
        }
    }
	    

<field>

Contains meta data about each field in collection. This corresponds to column definition in SQL world and is used when creating a collection.

    <field> {
        <name> "name";
        <type> "integer";
        <primary>;        
    }
	    

<field> can contain following elements

Element Description
name Name of the field
type Type of this field. Can take integer/text/real as values
primary Field is primary key
unique Field value are unique
notnull Field value must be specified
auto-increment Field value is auto incremented; must be integer type
default Value field will have if not specified by the user

<instance>

Represents a single instance from collection. Contains fields and their corresponding values in that record. Used when inserting/manipulating data in the datastore. Example,

    <instance> {
        <id> 5;
        <name> "John";
    }
	    

<instances>

Used to hold multiple instances.

    <instances> {
        <instance> {
            ...
            ...
        }
        <instance> {
            ...
            ...
        }
    }
	    

<condition>

Used to specify a condition that must to be satisfied when operating with data instances from datastore. This forms the condition used with WHERE clause when operating with SQL datastore

<condition> will contain following mandatory elements

Element Description
selector Name of the field to apply this condition
operator Operator for this condition. Can take one of comparison
or logical operators (<, >, <=, >=, =, LIKE, IN, NOT)
value Value to be used with operator on this field

Example,

    <condition> {
        <selector> "id;
        <operator> "in";
        <value> "(1, 10)";
    }
	    

<conditions>

This is used to specify multiple conditions with <and> or <or> as parent node. For example,

    <conditions> {
        <and> {
            <condition> {
                ... /* c1 */
            }
            <condition> {
                ... /* c2 */
            }
            <or> {
                <condition> {
                    ... /* c3 */
                }
                <condition> {
                    ... /* c4 */
                }
            }
        }
    }
	    

Above condition set gets translated to "c1 AND c2 AND c3 OR c4"

<retrieve>

This is used to specify only the fields that should appear as part of result set when querying the datastore. For example,

    <retrieve> {
        <id>;
        <name>;
    }
	    

<sort>

Used to specify the fields and order by which the result set must be sorted before returning. List of fields can be specified using <by> and order using <order>. "asc" and "desc", corresponds to ascending and descending order are the only valid values for order, which defaults to asc.

    <sort> {
        <by> "id";
        <by> "age";
        <order> "desc";
    }
	    

<limit>

Used to limit the number of instances that a result can contain.

    <limit> 10;
	    

Used to skip over a specified number of instances in the result set before returning to the user.

    <skip> 5;
	    

<result>

This is the output node from most of the extension functions. This contains <status> and may contain one or more <instance>s.

<status> can take one of the following values

Value Description
<done> Operation ran to completion. Usually means no more
data to return
<ok> Operation is successful. Returned with insert/update
functions
<data> Data is available in the result as <instance> elements
<error> Error has occurred. Error message will be the value of
this element

db Extension Functions

The db extension functions require the following ns statement:

    ns db extension = "http://xml.libslax.org/db";
	    

Section Contents:

db:open()

Used to open a database connection using provided options. Options contain backend engine and database name. For example,

    var $options = {
        <engine> "sqlite";
        <database> "test.db";
    }

    var $handle = db:open($options);
	    

db:open() returns database handle (a session cookie), that can be used to perform further operations on this opened connection.

If sqlcipher is available, db:open() also takes an optional <access> config where in we can specify the key that can be used to encrypt/decrypt the database. In such a case, options will look as below

    var $options = {
        <engine> "sqlite";
        <database> "test.db";
        <access> {
            <key> "testKey";
        }
    }
	    

If the user provides a rekey in access, database key will be changed to the valued mentioned in rekey

    var $options = {
        <engine> "sqlite";
        <database> "test.db";
        <access> {
            <key> "testKey";
            <rekey> "newTestKey";
        }
    }
	    

Above options when used in with db:open() will change test.db key from "testKey" to "newTestKey"

db:create()

Used to create a collection using opened database handle and information about the fields that this collection contains. For example,

    var $schema = {
        <collection> "employee";
        <fields> {
            <field> {
                <name> "id";
                <type> "integer";
                <primary>;
            }
            <field> {
                <name> "name";
                <type> "text";
                <default> "John";
            }
        }
    }

    var $result = db:create($handle. $schema);
	    

db:create() returns result node which contains status of the operation. Status is returned as ok in case of success and has error with message if it fails

db:insert()

Used to insert data into a collection using a handle and input data. For example, to insert one instance into a collection, we use

    var $data = {
        <collection> "employee";
        <instance> {
            <id> 3;
            <name> "Joey";
        }
    }

    var $result = db:insert($handle, $data);
	    

To insert a batch of instances, we use

    var $data = {
        <collection> "employee";
        <instances> {
            <instance> {
                <id> 1;
                <name> "Rachael";
            }
            <instance> {
                <id> 2;
                <name> "Chandler";
            }
            <instance> {
                <id> 3;
                <name> "Monica";
            }
        }
    }

    var $result = db:insert($handle, $data);
	    

db:insert() returns a result node and includes result of operation in status. Status is returned as ok in case of success and error with message in case of failure.

db:update()

Used to update a set of instances matching given conditions with a new provided instance. Takes database handle as first argument and input as second

    var $input = {
        <collection> "employee";
        <conditions> {
            <and> {
                <condition> {
                    <selector> "id";
                    <operator> ">";
                    <value> "2";
                }
                <condition> {
                    <selector> "name";
                    <operator> "like";
                    <value> "Monica";
                }
            }
        }
        <update> {
            <name> "Ross";
        }
    }

    var $result = db:update($handle, $input);
	    

Above example will update all the employee instances whose id is greater than 2 with names Monica, to Ross. db:update() returns result set like create/insert with status of the operation.

db:delete()

Used to delete instances matching given conditions. For example,

    var $input = {
        <collection "employee";
        <conditions> {
            <condition> {
                <selector> "id";
                <operator> ">";
                <value> "4";
            }
        }
        <limit> 10;
    }

    var $result = db:delete($handle, $input);
	    

Above operation deletes up to 10 employee instances whose id is greater than 4. db:delete() returns result set with status similar to insert/update/create operations

db:find()

This call returns a cursor for instances matching given conditions. For example,

    var $query = {
        <collection> "employee";
        <sort> {
            <by> "id";
            <order> "desc";
        }
        <retrieve> {
            <name>;
        }
        <conditions> {
            <condition> {
                <selector> "id";
                <operator> ">";
                <value> "5";
            }
        }
        <limit> 10;
        <skip> 5;
    }

    var $cursor = db:find($handle, $query);
	    

Above example returns a cursor to result set containing names first 10 employees skipping first 5 whose id is more than 5 sorted in descending order. We will have to use db:fetch() call to retrieve each of these result instances.

db:fetch()

This function call is used to fetch result instance using cursor returned from find/query call.

    var $result = db:fetch($cursor);
	    

Returned result contains status and <instance> with fields and values if available. Status will be returned as <data> if instance is available. It will be <done> if the query ran to completion and no more data is available to be read.

db:fetch() also takes an optional second argument that can be used to specify additional data. This can be useful when fetching on the cursor returned from custom query using db:query().

db:find-and-fetch()

This function call is used to find and read all the instances in one step. Usage is as below

    var $result = db:find-and-fetch($handle, $query);
	    

Returned result contains all the available instances from the query and status will be <data>. status will be <error> with message in case of errors

db:query()

This function can be used to run custom queries. For example,

    var $queryString = "INSERT INTO employee (id, name) "
                       _ "VALUES (@id, @name)";
    var $cursor = db:query($queryString);

    var $input = {
        <id> 11;
        <name> "Phoebe";
    }

    var $result = db:fetch($cursor, $input);
	    

Above example runs a custom query and receives cursor from db:query() and used db:fetch() to insert data into employee collection using the previous cursor.

db:close()

Used to close the database connection and free all the structures associated with previous operations performed on this handle

    var $result = db:close($handle);
	    

8.4 
The "xutil" Extension Library

The xutil extension library provides a number of XML- and XSLT-related utility functions.

Section Contents:

"xutil" Extension Functions

The "xutil" extension functions require the following ns statement:

    ns xutil extension = "http://xml.libslax.org/xutil";
	    

Section Contents:

xutil:max-call-depth()

SLAX and XSLT use recursion as a programming tool for iteration, but unlimited recursion can lead to disaster. To avoid this, the libxslt engine limits the depth of recursive calls to 3,000. This limit should be find for almost all uses, but it the value is not suitable, it can be adjusted using the xutil:max-call-depth() function.

If invoked without an argument, the function returns the current value. If a number is passed as the argument, that number is used as the new max call depth limit.

    EXAMPLE::
        var $limit = xutil:max-call-depth();
        expr xutil:max-call-depth($limit * 2);
	    

xutil:string-to-xml()

The xutil:string-to-xml() function turns a string containing XML data into the native representation of that data.

    EXAMPLE::
        var $data = "<doc><title>fred</title></doc>";
        var $xml = xutil:string-to-xml($data);
        message "title is " _ $xml/title;
	    

Multiple strings can be passed, in which case, they will be concatenated before the XML conversion:

    EXAMPLE::
        var $xml2 = xutil:string-to-xml("<top>", $content, "</top");
	    

xutil:xml-to-string()

The xutil:xml-to-string() function turns XML content into a string. This is different than the normal XPath stringification, which discards open and close tag. xml-to-string will encode tags as part of the string.

    EXAMPLE::
        var $xml = <dog> "red";
        var $str = xutil:xml-to-string($xml);
        /* str is now the string "<dog>red</dog>" */
	    

xutil:json-to-xml()

The xutil:json-to-xml() function turns a string containing JSON data into the native representation of that data in XML.

    EXAMPLE::
        var $data = "[ { "a" : 4, "name": "fish"}, 4, 5]";
        var $xml = xutil:json-to-xml($data);
        message "title is " _ $xml/json/name;
	    

An optional second parameter contains a node set of the following optional elements:

Element Value Description
types "no" Do not encode type information
root string Name of root node to be returned ("json")
        var $options = {
            <root> "my-top";
            <types> "no";
        }
        var $xml = xutil:json-to-xml($data, $options);
	    

The XML returned from json-to-xml() is decorated with attributes (including the "type" and "name" attributes) which allow the data to be converted back into JSON using xml-to-json(). Refer to that function for additional information.

For details on the JSON to XML encoding, refer to Section 2.2.3.1, Section 2.2.3.2, and Section 2.2.3.3.

xutil:xml-to-json()

The xutil:xml-to-json() function turns XML content into a string of JSON data. This is different than the normal XPath stringification, which discards open and close tag. xml-to-json will encode tags as JSON objects inside a string

    EXAMPLE::
        var $xml = <json> {
            <color> "red";
        }
        var $str = xutil:xml-to-json($xml);
        /* str is now the string '{ "color": "red" }' */
	    

An optional second parameter contains a node set of the following optional elements:

Element Value Description
pretty empty Add newlines and indentation to output
quotes "optional" Avoid quotes for names

For details on the JSON to XML encoding, refer to Section 2.2.3.1, Section 2.2.3.2, and Section 2.2.3.3.

8.5 
The "os" Extension Library

The "os" extension library provides a set of functions to invoke operating system-related operations on the local host. Note that these are _not_ run on the remote target, but on the machine where the script is being executed.

Section Contents:

"os" Extension Functions

The "os" extension functions require the following ns statement:

    ns os extension = "http://xml.libslax.org/os";
	    

Section Contents:

Error Nodes

The return value of many os:* functions consists of a set of zero or more error nodes. Each node may contain an <error> element, which in turn may contain the following elements:

Element Description
errno Error message based on errno
path The path that triggered the error
message The error message

In addition, the <errno> element contains a "code" attribute which holds the tag for the errno value, if known.

    <error>
      <errno code="ENOENT">No such file or directory</errno>
      <message>unknown group: phil</message>
    </error>
	    

os:chmod

The os:chmod function changes the permissions of one or more files, allowing or preventing different sets of users from accessing those files.

The first argument is a mode specification similar to the chmod command, with either an octal number to set the permissions to, or an expression consisting of one or more of the letters 'u', 'g', 'o', and 'a' (for user, group, other, and all) followed by '+' or '‑' (for setting or clearing) and one or more of the letters 'r', 'w', and 'x' (for read, write, and execute). The expression "ug+rw" would give read and write permission to the user and group which own the file or directory.

The second and subsequent arguments can be either a path name string or a nodeset of <file>, <directory>, and <wildcard> elements, with the former two contain path for files and directories, and the latter contains shell/glob-style wildcard patterns. The following patterns are permitted:

  • '*' matches zero or more characters
  • '?' matches any single character
  • '[...]' matches any of the enclosed characters
  • '{...}' matches any of the enclosed comma-separated sequences

For example, <wildcard> "*.txt" matches all text files.

    SYNTAX::
        void os:chmod(mode, files, ...);

    EXAMPLE::
        $res = os:chmod("g+w", "file1", {
            <file> "test.test";
            <directory> "dir";
            <wildcard> "*.c";
        });
	    

If successful, nothing is returned. On failure, a set of error nodes is returned. See Section 8.5.1.1 for details.

os:chown

The os:chown function changes for owning user and group for one or more files.

The first argument is the target name and/or group, in either symbolic or numeric format:

    SYNTAX::
        [ <user> ]? [ ':' <group> ]
    EXAMPLE:
        phil
        phil:eng
        :eng
        :12
        0:0
	    

The second and subsequent arguments can be either a path name string or a nodeset of <file>, <directory>, and <wildcard> elements. See Section 8.5.1.2 for details.

    SYNTAX::
        void os:chown(owner, files, ...);

    EXAMPLE::
        $res = os:chown(":eng", "file1", {
            <file> "test.test";
            <directory> "dir";
            <wildcard> "*.c";
        });
	    

If successful, nothing is returned. On failure, a set of error nodes is returned. See Section 8.5.1.1 for details.

os:exit-code

The os:exit-code function sets the exit code for the process running the script. This can be used to indicate an error to the caller. The argument to the function is the exit code value, in numeric form.

    SYNTAX::
        void os:exit-code(number);

    EXAMPLE::
        expr os:exit-code(1);
	    

os:mkdir

The os:mkdir function makes directories, similar to the "mkdir" command or the POSIX "mkdir" library function. These are two arguments; the first is name of the directory to be made and the second is an node-set containing options to be used during the operation. The options can include the values in the following table.

Element Description
create Error if the last element of the path exists
mode Octal value of directory permissions
path Create intermediate directories as required
    SYNTAX::
        node-set os:mkdir(path [, options]);

    EXAMPLE::
        var $res = os:mkdir("/tmp/foo");
        var $opts = {
            <mode> "0700";
            <path>;
            <create>;
        }
        var $res2 = os:mkdir("/tmp/foo/a/b/c/d/e/f", $opts);
	    

If the value for <mode> is a string, it will be converted to an integer using the default numeric base of 8 (octal), so '<mode> "644"' will work, but '<mode> 644' will see 644 as a number with base 10 (decimal), which will result in undesirable results since 644 base 10 is 01204 base 8.

If the value of <mode> is not a valid mode integer value, it will be ignored.

The return value of os:mkdir is a node-set which may contain an <error> element is an error occurred. This element may contain the following elements:

Element Description
errno Error message based on errno
path The path that triggered the error
message The error message

In addition, the <errno> element contains a "code" attribute which holds the tag for the errno value, if known.

If successful, nothing is returned. On failure, a set of error nodes is returned. See Section 8.5.1.1 for details.

os:stat

The os:stat function returns information about files and directories, similar to the POSIX stat() function, returning a node-set of <entry> elements containing details about each file.

The arguments to the os:stat() function are either strings or node-sets. os:stat() allows any number of arguments. If the argument is a string, it is used as a path specification and information on matching files is returned. The path specification can include glob-style wildcards (e.g. test*.c, *.slax). If the argument is a node-set, then the node can contains the following elements:

Element Description
brief Only summary information is emitted
depth The number of subdirectory levels to descend
hidden Return information on hidden files
name File specification (same as string argument)
recurse Show all subdirectories

Note the <name> element functions identically to the string argument details given above.

    var $files = os:stat("/etc/m*");
    var $options = {
        <hidden>;
        <depth> 3;
    }
    var $logs = os:stat("/var/log/*txt", $options);
    for-each ($logs) {
        message name _ " is a " _ type;
    }
	    

The return value is a node-set of <entry> elements. Each entry contains the following elements:

Element Description Brief
name Path to the entry Y
type Type of file (see below) Y
executable Present if entry is executable Y
symlink Present if entry is a symbolic link Y
symlink-target Contents of the symbolic link N
permissions Permissions for the entry N
owner Name of the owning user N
group Name of the owning group N
links Number of hard links to this entry N
size Number of bytes used by the entry N
date Time and date of last modification N
entry Directory contents N

Only elements tagged "Y" are emitted when the <brief> option is used.

The <type> element contains one of the following:

Value Description
directory Directory containing additional files
file Regular file
character Character-oriented device (tty)
block Block-oriented device (disk)
link Symbolic link
socket AF_UNIX Socket
fifo Named pipe (First-in/first-out)
unknown Other/unknown file type

In some cases, attributes are used to attach useful information to elements. The following table lists these attributes and values.

Element Attribute Description
permissions mode Octal value for the mode
owner uid Numeric value of the user's uid
group gid Numeric value of the group's gid
date date Seconds since Jan 1, 1970

os:user-info

The os:user-info helps know the details of user running the script.

    var $userinfo = os:user-info();
	    

This function returns a user element with following information

Element Description
class User class (if availalbe)
dir User home directory
gecos User information
name Username
shell User shell program

This functions returns empty when error


Author's Address

Phil ShaferJuniper NetworksEMail:

A_ 
Example Stylesheets

This section contains a few examples, converted from the libxslt test/ directory. The XSLT form can be found in the libxslt source code. They were converted using the "slaxproc" tool.

Section Contents:

A.1 
general/itemschoose.xsl

    version 1.1;

    ns fo = "http://www.w3.org/1999/XSL/Format";

    strip-space itemlist;
    match doc {
        <doc> {
            apply-templates;
        }
    }

    match orderedlist/listitem {
        <fo:list-item indent-start="2pi"> {
            <fo:list-item-label> {
                var $level = count(ancestor::orderedlist) mod 3;

                if ($level=1) {
                    <number format="i">;

                } else if ($level=2) {
                    <number format="a">;

                } else {
                    <number format="1">;
                }
                expr ". ";
            }
            <fo:list-item-body> {
                apply-templates;
            }
        }
    }
	    

A.2 
REC2/svg.xsl

    version 1.1;

    ns "http://www.w3.org/Graphics/SVG/SVG-19990812.dtd";

    output-method xml {
        indent "yes";
         media-type "image/svg";
    }

    match / {
        <svg width="3in" height="3in"> {
            <g style="stroke: #000000"> {
                /* draw the axes */
                <line x1="0" x2="150" y1="150" y2="150">;
                <line x1="0" x2="0" y1="0" y2="150">;
                <text x="0" y="10"> "Revenue";
                <text x="150" y="165"> "Division";

                for-each (sales/division) {

                    /* define some useful variables */
                    /* the bar's x position */
                    var $pos = (position()*40)-30;

                    /* the bar's height */
                    var $height = revenue*10;

                    /* the rectangle */
                    <rect x=$pos y=150 - $height 
                          width="20" height=$height>;

                    /* the text label */
                    <text x=$pos y="165"> @id;

                    /* the bar value */
                    <text x=$pos y=145 - $height> revenue;
                }
            }
        }
    }
	    

A.3 
XSLTMark/metric.xsl

    version 1.1;

    output-method html {
        encoding "utf-8";
    }

    match measurement {
        var $m = {
            if (@fromunit == 'km') {
                expr . * 1000;

            } else if (@fromunit == 'm') {
                expr .;

            } else if (@fromunit == 'cm') {
                expr . * 0.01;

            } else if (@fromunit == 'mm') {
                expr . * 0.001;
            }
        }
        <measurement unit=@tounit> {
            if (@tounit == 'mi') {
                expr 0.00062137 * $m;

            } else if (@tounit == 'yd') {
                expr 1.09361 * $m;

            } else if (@tounit == 'ft') {
                expr 3.2808 * $m;

            } else if (@tounit == 'in') {
                expr 39.37 * $m;
            }
        }
    }
	    

B_ 
Additional Notes

This section contains additional notes regarding SLAX, libslax, its history, problems, and future.

Section Contents:

B.1 
Language Notes

This section includes discussion of SLAX language issues.

Section Contents:

Type Promotion for XPath Expressions

XPath expressions use a style of type promotion that coerces values into the particular type needed for the expression. For example, if a predicate refers to a node, then that predicate is true if the node exists. The value of the node is not considered, just it's existence.

For example, the expression "chapter[section]" selects all chapters that have a section element as a child.

Similarly, if a predicate uses a function that needs a string, the argument is converted to a string value by concatenating all the text values of that node and all that node's child elements.

For example, the expression "chapter[starts-with(section, 'A')]" will inspect all <chapter> elements, convert their <section> elements to strings, and select those whose string value starts with 'A'. This may be an expensive operation.

The Ternary Conditional Operator

As the last touch to SLAX-1.1, I've added the ternary operator from C, allowing expressions like:

    var $a = $b ? $c : $d;
    var $e = $f ?: $g;
	    

The caveat is that this uses an extension function slax:value() which may not be available in all XSLT environments. Coders must consider whether should a restriction deems this operator unusable. Portability considerations are identical to mutable variables (mvars).

Mutable Variables

XSLT has immutable variables. This was done to support various optimizations and advanced streaming functionality. But it remains one of the most painful parts of XSLT. We use SLAX in JUNOS and provide the ability to perform XML-based RPCs to local and remote JUNOS boxes. One RPC allows the script to store and retrieve values in an SNMP MIB (the jnxUtility MIB). We have users using this to "fake" mutable variables, so for our environment, any theoretical arguments against the value of mutable variables are lost. They are happening, and the question becomes whether we want to force script writers into mental anguish to allow them.

Yes, exactly. That was an apologetical defense of the following code, which implements mutable variables. Dio, abbi pieta della mia anima.

The rest of this section contacts mind-numbing comments on the implementation and inner working of mutable variables.

For the typical scriptor, the important implications are:

  • Non-standard feature: mutable variables are not available outside the libslax environment. This will significantly affect the portability of your scripts. Avoid mutable variables if you want to use your scripts in other XSLT implementations or without libslax.
  • Memory Overhead: Due to the lifespan of XML elements and RTFs inside libxslt, mutable variables must retain copies of their previous values (when non-scalar values are used) to avoid dangling references. This means that heavy use of mutable variables will significantly affect memory overhead, until the mutable variables fall out of scope.
  • Axis Implications: Since values for mutable variables are copied (see above), the operations of axes will be affected. This is a relatively minor issue, but should be noted.

Let's consider the memory issues associated with mutable variables. libxslt gives two ways to track memory WRT garbage collection:

contexts
as RTF/RVT (type XPATH_XSLT_TREE)
variables
strings (simple; forget I even mentioned them) or node sets (type XPATH_NODESET)
via the nodesetval field
does not track nodes, but references nodes in other trees

The key is that by having node sets refer to nodes "in situ" where they reside on other documents, the idea of refering to nodes in the input document is preserved. Node sets don't require an additional memory hook or a reference count.

The key functions here are xmlXPathNewValueTree() and xmlXPathNewNodeSet(). Both return a fresh xmlXPathObject, but xmlXPathNewValueTree will set the xmlXPathObject's "boolval" to 1, which tells xmlXPathFreeObject() to free the nodes contained in a nodeset, not just the nodeTab that holds the references.

Also note that if one of the nodes in the node set is a document (type XML_DOCUMENT_NODE) then xmlFreeDoc() is called to free the document. For RTFs, the only member of the nodeset is the root of the document, so freeing that node will free the entire document.

All this works well for immutable objects and RTFs, but does not allow my mutable variables to work cleanly. This is quite annoying.

I need to allow a variable to hold a nodeset, a document, or a scalar value, without caring about the previous value. But I need to hold on to the previous values to allow others to refer to them without dangling references. Dangling References

Consider the following input document:

    <top>
        <x1/>
        <x2/> 
        <x3/>
    </top>
	    

The following code makes a nodeset (type XPATH_NODESET) whose nodeTab array points into the input document:

    var $x = top/*[starts-with("x", name())];
	    

The following code make an RTF/RVT (type XPATH_XSLT_TREE), whose "fake" document contains a root element (type XML_DOCUMENT_NODE) that contains the "top" element node.

    var $y = <top> {
        <x1>;
        <x2>;
        <x3>;
    }
	    

The following code makes a nodeset (type XPATH_NODESET) that refers to nodes in the "fake" document under $y:

    var $z = $y/*[starts-with("x", name())];
	    

Now consider the following code:

    mvar $z = $y/*[starts-with("x", name())];
    var $a = $z[1];
    if ($a) {
        set $z = <rvt> "a";  /* RVT */
        var $b = $z[1];      /* refers to nodes in "fake" $y doc */
        set $z = <next> "b"; /* RVT */
        var $c = $z[1];      /* refers to node in <next> RVT */
        <a> $a;
        <b> $b;
        <c> $c;
    }
	    

In this chunk of code, the changing value of $z cannot change the nodes recorded as the values of $a, $b, or $c. Since I can't count on the context or variable memory garbage collections, my only choice is to roll my own. This is quite annoying.

The only means of retaining arbitrary previous values of a mutable variable is to have a complete history of previous values.

The "overhead" for an mvar must contain all previous values for the mvar, so references to the node in the mvar (from other variables) don't become dangling when those values are freed. This is not true for scalar values that do not set the nodesetval field.

Yes, this is pretty much as ugly as it sounds. After a variable has been made, it cannot be changed without being risking impacting existing references to it.

So a mutable variable needs to make two things, a real variable, whose value can be munged at will, and a hook to handle memory management.

The Rules

  • Assigning a scalar value to an mvar just sets the variables value (var->value).
  • Assigning a non-scalar value to an mvar means making deep copy, keeping this copy in "overhead".

But where does the "overhead" live?

In classic SLAX style, the overhead is kept in a shadow variable. The shadow variable (svar) holds an RTF/RVT that contains all the nodes ever assigned to the variable, a living history of all values of the variable.

We don't need to record scalar values, so:

    mvar $x = 4;
	    

becomes:

    <xsl:variable name="slax-x"/>
    <xsl:variable name="x" select="4"/>
	    

But for RTFs, the content must be preserved, so:

    mvar $x = <next> "one";
	    

becomes:

    <xsl:variable name="slax-x">
        <next>one</next>
    </xsl:variable>
    <xsl:variable name="x" select="slax:mvar-init($slax-x)"/>
	    

where slax:mvar-init() is an extension function that returns the value of another variable, either as a straight value or as a nodeset.

If an mvar is only ever assigned scalar values, the svar will not be touched. When a non-scalar value is assigned to an mvar, the content is copied to the svar and the mvar is given a nodeset that refers to the content inside the svar. Appending to a mvar means adding that content to the svar and then appending the node pointers to the mvar.

If the mvar has a scalar value, appending discards that value. If the appended value is a scalar value, then the value is simply assigned to the mvar. This will be hopelessly confusing, but there's little that can be done, since appending to an RTF to a number or a number to an RTF makes little sense. We will raise an error for this condition, to let the scriptor know what's going on. Memory

When the mvar is freed, its "boolval" is zero, so the nodes are not touched but the nodesetval/nodeTab are freed. When the svar is freed, its "boolval" is non-zero, so xmlXPathFreeObject will free the nodes referenced in the nodesetval's nodeTab. The only node there will be the root document of a "fake" RTF document, which will contain all the historical values of the mvar. In short, the normal libxslt memory management will wipe up after us. Implications

The chief implications are:

  • memory utilization -- mvar assignments are very sticky and only released when the mvar (and its svar) go out of scope
  • axis -- since the document that contains the mvar contents is a living document, code cannot depend on an axis staying unchanged. I'm not sure of what this means yet, but following::foo is a nodeset that may change over time, though it won't change once fetched (e.g. into a specific variable).

B.2 
Historical Notes

This section discusses some historical issues with SLAX and libslax.

Section Contents:

Why on earth did you make SLAX?

I have worked with XSLT for over ten years, as part of my work for Juniper Networks. Beginning in 2001, we made an XML API for our line of routers so that any command that can be issued at the command line (CLI) can be issued as an XML RPC and the response received in XML. This work was the foundation for the IETF NETCONF protocol (RFC6241) (see also RFC6244).

Internally, we used this API with XSLT to make our Junoscope network management platform, and were happy working with XSLT using multiple XSLT implementations.

In the 2005-2006 timeframe, we started developing on-box script capabilities using XSLT. I like the niche and properties of XSLT, but the syntax makes development and maintenance problematic. First class constructs are buried in attributes, with unreadable encodings. Customers objections were fairly strong, and they asked for a more perl-like syntax. SLAX was our answer.

SLAX simplifies the syntax of XSLT, making an encoding that makes scripts more readable, maintainable, and helps the reader to see what's going on. XML escaping is replaced by unix/perl/c-style escaping. Control elements like <xsl:if> are replaced with the familiar "if" statement. Minor details are more transparent.

The majority of our scripts are simple, following the pattern:

    if (find/something/bad) {
        call error($message = "found something bad");
    }
	    

The integration of XPath into familiar control statements make the script writers job fairly trivial.

At the same time, using XSLT constrains our scripting environment and limits what scripts can and cannot do. We do not need to worry about system access, processes, connections, sockets, or other features that are easily available in perl or other scripting languages. The scripts emit XML that instructs our environment on what actions to take, so those actions can be controlled.

So SLAX meets our needs. I hope making this an open source projects allows it to be useful to a broader community.

Why the name conflict?

The SLAX language is named for "eXtensible Stylesheet Language Alternate syntaX". Juniper started development on SLAX as part of the on-box scripting features in the 2004/2005 time frame. The name "SLAX" was adopted after the Juniper management requested that we remove the leading "X" from the original internal name.

Section Contents:

What about the SLAX linux distro?

At about this same time, the "SLAX" linux distro was named, but not being involved in the linux world (we're a FreeBSD house), we were not aware of this name conflict for many years.

When we were made aware of the name conflict, we consulted with various parts of the Juniper family, and no one was interested in changing the language name. We repeated this procedure as we were publishing this open source version, but again, no one was interested in doing the internal and external work to change the language name, since the name conflict was considered minor and not an issue for our customers.

B.3 
Developers Notes

This section contains notes for developers wanting to work on or near libslax.

Section Contents:

Dynamic Extension Libraries

libslax provides a means of dynamically loading extension libraries based on the contents of the "extension‑element‑prefixes" attribute. During initialization, a parsed SLAX document is inspected for both the "extension‑element‑prefix" attribute on the <stylesheet> element, and the "xsl:extension‑element‑prefix" on arbitrary tags.

The prefixes found in the document are translated into namespace URIs, which are then escaped using the URL-escaping algorithm that turns unacceptable characters into three character strings like "%2F".

An extension of ".ext" is appended to the URL-escaped URI and this file is searched but in ${SLAX_EXTDIR} and any directories given via the "‑‑lib/‑L" argument.

When the extension library is found, we dlopen() it and look for the magic symbol "slaxDynLibInit". This function is called with two arguments, the API version number and a struct that contains information about the extension library.

    /*
     * This structure is an interface between the libslax main code
     * and the code in the extension library.  This structure should
     * only be extended by additions to the end.
     */
    typedef struct slax_dyn_arg_s {
        unsigned da_version;   /* Version of the caller */
        void *da_handle;       /* Handle from dlopen() */
        void *da_custom;       /* Memory hook for the extension */
        char *da_uri;          /* URI */
        slax_function_table_t *da_functions; /* Functions */
        slax_element_table_t *da_elements;   /* Elements */
    } slax_dyn_arg_t;
	    

The da_functions and da_elements allow the library to register and unregister there functions and elements.

At cleanup time, in addition to removing functions and elements, a search is made for the library to find the symbol "slaxDynLibClean". If the symbol is found, it is called also.

The file slaxdyn.h defines some macros for helping define extension libraries.

Default Prefixes for Extension Libraries

When a default prefix is used with an extension library, the prefix is mapped to the extension using a symbolic link ("ln -s"). The source of the symlink should be the prefix with a ".prefix" appended and should be located in the extension directory ("${SLAX_EXTDIR}"). The target of the symlink should be the namespace URI with the escaping and ".ext" extension as described above.

See the Makefile.am file in any of the libslax extension directories for examples of creating the appropriate symlink.

Example

Here's a quick example:

    slax_function_table_t slaxBitTable[] = {
        { "and", extBitAnd },
        { "or", extBitOr },
        { "nand", extBitNand },
        { "nor", extBitNor },
        { "xor", extBitXor },
        { "xnor", extBitXnor },
        { "not", extBitNot },
        { NULL, NULL },
    };

    SLAX_DYN_FUNC(slaxDynLibInit)
    {
        /* Fill in our function table */
        arg->da_functions = slaxBitTable;

        return SLAX_DYN_VERSION;
    }
	    

B.4 
External Documentation

My documentation style tends to be man-page-like, rather than tutorial-ish. But folks at Juniper Networks have made some outrageously great documentation and it's available on the Juniper website. Many thanks for Curtis Call, Jeremy Schulman, and others for doing this work.

This Week: Junos Automation Reference for SLAX 1.0 does not cover any of the new SLAX-1.1 material, but is an incredible reference book.

  www.juniper.net/us/en/community/junos/
      training-certification/day-one/
      automation-series/junos-automation-slax/
	    

This Week: Mastering Junos Automation Programming covers many introductory tasks with a tutorial style:

  www.juniper.net/us/en/community/junos/
      training-certification/day-one/automation-series/
      mastering-junos-automation/
	    

Script Library contains a set of scripts that will help get you started:

https://www.juniper.net/us/en/community/junos/script-automation/

Note these require Juniper-specific permissions, JNet logins, and other hurdles. Apologies for the inconvenience.

News! The Day One Guides are available for <$2 (some free) on the itunes store! Search under books for "juniper".