The SLAX Language

The SLAX Scripting Language P. Shafer
Juniper Networks
February 20, 2012

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:

  • Use if/then/else instead of <xsl:choose> and <xsl:if> elements
  • Put test expressions in parentheses
  • Use "==" to test equality (to avoid building dangerous habits)
  • Use curly braces to show containment instead of closing tags
  • Perform concatenation using the "_" operator (lifted from perl6)
  • Perform simple logic using the "?:" operator
  • Write text strings using simple quotes instead of the <xsl:text> element
  • Simplify invoking named templates with a syntax resembling a function call
  • Simplify defining named template with a syntax resembling a function definition
  • Simplify namespace declarations
  • Reduce the clutter in scripts, allowing the important parts to be more obvious to the reader
  • Write more readable scripts

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.

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.

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

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

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

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

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

1.2.3.3 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 $name = 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.

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

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

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

1.2.3.7 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;
            }
        }
    }
	    

1.2.3.8 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";
	    
        <out> $cur;
        if ($cur < $max) {
            call count($cur = $cur + 1, $max);
        }
    }
	    

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

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

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:

  • Paths to elements
    • Select nodes based on node names
      • Example: chapter
    • Selects child nodes based on parent node names
      • Example: doc/chapter/section/paragraph
    • Selects child nodes under at any depth
      • Example: doc//paragraph
    • Selects parent nodes relative to the current node
      • Example: ../../chapter
    • Selects attributes using "@" followed by attribute name
      • Example: ../../chapter/@number
  • Predicate tests
    • Selects nodes for which the expression in the square brackets evaluates to "true" (with boolean() type conversion)
      • Example chapter[@number == 1]
    • Can be applied to any path member
      • Example: chapter[@number == 1]/section[@number == 2]
    • Use a number to select the <n>th node from a node set
      • Example: chapter[1]
    • Multiple predicate tests can be specified (ANDed together)
      • Example: chapter[@number > 15][section/@number > 15]
  • References to variables or parameters
    • Variable and parameter names start with "$"
      • Example: $this
    • Variables can hold expressions, node sets, etc
      • Example: $this/paragraph
  • Literal strings
    • SLAX accepts single or double quotes
      • Example: "test"
    • Character escaping is allowed
      • Example: '\tthat\'s good\n\tnow what?\n'
  • Calls to functions
    • Function calls can be used in expressions or predicate tests
      • Example: chapter[count(section) > 15]
    • Allows calls to pre-defined or user-defined functions
      • Example: chapter[substring-before(title, "ne") == "O"]

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.

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

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

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>
	    

2.2.1 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>;
        <two> {
            <three>;
            <four>;
            <five> {
                <six>;
            }
        }
    }
	    

This is equivalent to:

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

2.2.2  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. Node values are selected from the current context.

    element name {
        element "from-" _ address;
    }
	    

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>
	    

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

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

2.4.1  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; }
    }
	    

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

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

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

2.5.3  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

2.5.4  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

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.

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

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

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

2.8.2  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

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

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.

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

3.1.2  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);
	    

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

3.1.2.2  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";
        }
    } 
	    

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

3.2.1  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;
        }
    }
	    

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

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

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

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

3.2.6  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 [ '=' optional-value ] ';'
	    

For 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;
    }
	    

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

3.2.7  The "with" Statement

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

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

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

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>
	    

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.

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.

4.1.1  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' variable-name '=' initial-value ';'
	    

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

These same constraints apply to parameters as well.

4.1.1.1  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 use of it in JUNOS), we've added mutable variables, which 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:

  • Emit as output
  • Conversion to a string
  • Conversion to a proper node-set

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

4.2.1  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.2.2  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.

4.3 Control Statements

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

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

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

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

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

4.3.5  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";
        }
    }
	    

XSLT equivalent:

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

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:

  • Arguments are passed by position, not name
  • Return values _can_ be objects (not RTFs)
  • Can be used in expressions
  • Can be resolved dynamically (using EXSLT's dyn:evaluate())

This section describes how functions are defined.

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

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

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.

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.

5.5.1  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 ';'
	    

The message-expression is an XPath expression that is emitted as output, typically on the standard error file descriptor.

    if (not(valid)) {
        message "The " _ name() _ " node is not valid";
    }
	    

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

5.5.2  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 ';'
	    

The message-expression is an XPath expression that is emitted as output, like the "message" statement. The script then stops any further processing.

5.5.3  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 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".

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

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

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

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

5.7.2  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.8  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.

6.1 SLAX External Functions

6.1.1 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, "@");
	    

6.1.2 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);
	    

6.1.3 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> .;
            }
        }
	    

6.1.4 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>;
        }
	    

6.1.5 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 := {
            <format> "base64";
            <encoding> "ascii";
            <non-xml> "#";
        }
        var $data2 = slax:document($url, $options);
	    
Option Description
<non-xml> Skip field if value has not changed
<encoding> Prepend TAG if string is not empty
<format> "base64" for BASE64-encoded data

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.

6.1.6 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 == '&']");
	    

6.1.7 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");
	    

6.1.8 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("# ");
	    

6.1.9 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: ");
	    

6.1.10 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: ");
	    

6.1.11 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";
        }
	    

6.1.12 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);
            }
        }
	    

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

6.1.14 slax:sleep

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

Sleep for a given time period.

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

6.1.16 slax:sysctl

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

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

6.1.17 slax:string-to-xml

    SYNTAX::
        node-set slax:string-to-xml(string+)
	    

Return parsed XML of concatenated arguments.

6.1.18 slax:syslog

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

Syslog the concatenation of set of arguments.

6.1.19 slax:xml-to-string

    SYNTAX::
        string slax:xml-to-string(node-set+)
	    

Return stringified XML hierarchies.


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:

   http://code.google.com/p/libslax
	    

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
     --run OR -r: run a SLAX script (the default mode)
     --slax-to-xslt OR -x: turn SLAX into XSLT
     --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
     --help OR -h: display this help message
     --html OR -H: Parse input data as HTML
     --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
     --lib <dir> OR -L <dir>: search dir for extension libraries
     --name <file> OR -n <file>: read the script from the given file
     --no-randomize: do not initialize the random number generator
     --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
     --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: http://libslax.googlecode.com
	    

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.

7.2.1  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
	    

7.2.1.1 "#!" 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.

7.2.2 Command Line Options

Command line options to slaxproc can be divided into two types. Mode options affect the

7.2.2.1 Modes

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

7.2.2.2 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.
--help OR -h
Displays this help message and exits.
--html OR -H
Parse input data using the HTML parser, which differs from XML.
--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.
--lib <dir> OR -L <dir>
Add a directory to the list of directories searched for extension libraries.
--name <file> OR -n <file>
Read the SLAX script from the given file.
--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.
--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.
--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
	    

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

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:

  • Line -- line number of the source file
  • Hits -- number of times this line was executed
  • User -- the number of microseconds if "user" time spent processing this line
  • U/Hit -- average number of microseconds per hit
  • System -- the number of microseconds if "system" time spent processing this line
  • S/Hit -- average number of microseconds per hit
  • Source -- Source code line

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 show how much time is spent during code execution, but can also show which lines are being executed, which can help debug scripts where the execution does not match expectations.

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. The extension library should be placed in the directory named with the --with-extension-dir options (aka SLAX_EXTDIR) or in a directory named by a --lib/-L argument.

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 inversion (NOT) of a bit string
bit:mask(count, len?) Return a len bits with the low count set
bit:to-int(bs) Return integer value of bit string
bit:from-int(str) Return bit string of integer value
bit:to-hex(bs) Return hex value of a bit string
bit:from-hex(str) 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.

8.2.1 curl Elements

Curl operations are directed using a set of elements, passed to the curl extension functions. This section gives details on the elements supported.

8.2.1.1 <url>

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

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

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

8.2.1.3 <username>

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

    SYNTAX::
      <username> "username";
	    

8.2.1.4 <password>

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

    SYNTAX::
      <password> "password";
	    

8.2.1.5 <content‑type>

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

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

8.2.1.6 <contents>

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

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

8.2.1.7 <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
text None
xml Result is parsed as XML

8.2.1.8 <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";
	    

8.2.1.9 <local>

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

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

8.2.1.10 <from>

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

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

8.2.1.11 <subject>

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

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

8.2.1.12 <upload>

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

    SYNTAX::
      <upload>;
	    

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

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

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

8.2.1.16 <secure>

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

    SYNTAX::
      <secure>;
	    

8.2.1.17 <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";
	    

8.2.1.18 <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";
	    

8.2.1.19 <header>

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

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

8.2.1.20 <param>

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

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

8.2.2 curl:perform

The "curl:perform" extension function performs simple transfers.

8.2.3 curl:open

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

8.2.4 curl:set

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

8.2.5 curl:single

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

8.2.6 curl:close

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


Author's Address

Phil ShaferShaferPhilJuniper NetworksEMail: phil@juniper.net

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.

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

B.1 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.