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.

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;

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

XSLT Equivalent

The following is the XSLT equivalent of the above examples:

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

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

SLAX is using the same constucts as XSLT, but packaged in a more readable, maintainable syntax.

The var Statement

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

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

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

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

The mvar Statement

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

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

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

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

mvar $test;

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

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

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 http://exslt.org for additional information):

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

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

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

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

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.

Control Statements

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

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

XSLT Equivalent

The following is the XSLT equivalent of the above example:

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

The for-each Statement

The for-each statement iterates through the members of a node set, evaluating the contents of the statement with the context set to each node:

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

The XPath expression is evaluated into a set of nodes, and then each node is considered as the “context” node, the contents of the for-each statement are evaluated:

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

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

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

XSLT Equivalent

The following is the XSLT equivalent of the above example:

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

The for Statement

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

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

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

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

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

The while Statement

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

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

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

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

The sort Statement

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

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

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

Multiple sort statements can be used to given secondary sorting keys:

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

XSLT Equivalent

The following is the XSLT equivalent of the above example:

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

The “…” Operator

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

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

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

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

The “?:” Operator

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

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

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

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

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

XSLT Equivalent

The generated XSLT uses an <xsl:choose> element. The following is the equivalent of the first example above:

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

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.

The function Statement

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

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

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

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

Function parameters can also be defined using the param statement.

The result Statement

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

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

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

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

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

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