Variables

In XSH2, like in Perl and XPath, variable names are are prefixed with a dollar sign ($). Variables can contain arbitrary Perl Scalar (string, number, array reference, hash reference or an object reference). XPath objects are transparently mapped to Perl objects via XML::LibXML objects. Values can be assigned to variables either by simple assignments of the form $variable = expression, where the right hand side is an expression, or by command assignments of the form $variable := command, where the right hand side is a XSH2 command, or by capturing the output of some command with a variable redirection of the following form:

command |> $variable;

XSH2 expressions are evaluated either by XPath engine or by Perl (the latter only happens if the entire expression is enclosed with braces {...}), and both Perl and XPath can access all XSH2 variables transparently (Perl expressions may even assign to them).

A simple simple expression consisting of a variable name (e.g. $variable) is always evaluated by the XPath engine and the result is the content of the variable as it appears to the XPath data model. Since in XPath object cannot be void (undefined), XPath engine complains, if the value of the variable is undefined. On the other hand, expressions like {$variable} are evaluated by Perl, which results in the value of the variable as seen by Perl.

Variables can also be used as macros for complicated XPath expressions. Any occurrence of a substring of the form ${variable} in an XPath expression is interpolated to the value of $variable (if $variable contains an object rather than a string or number, then the object is cast to string first) before the entire expression is evaluated. So, for example, if ${variable} contains string "chapter[title]" (without the quotes), then the XPath expression //sect1/${variable}/para interpolates to //sect1/chapter[title]/para prior to evaluation.

To display the current value of a variable, use either print or (in case of a global variables - the distinction is discussed below) the command variables:

xsh> $b="my_document";
xsh> $file="${b}s.xml";
xsh> $f := open $file;
xsh> ls //$b[count(descendant::para)>10]
xsh> print $b
my_document
xsh> variables
...
$b='my_document';
...
$file='my_documents.xml';
...

Variables can also serve as containers for documents and can be used to store lists of nodes that result from evaluating an XPath expression (a.k.a. XPath node-sets). This is especially useful when a sequence of commands is to be performed on some fixed set of nodes and repetitive evaluation of the same XPath expression would be lengthy. XPath node-sets are represented by XML::LibXML::NodeList Perl objects (which is simply a array reference blessed to the above class, which provides some simple operator overloading). In XPath, by a node-set by definition can only contain a single copy of each node and the nodes within a node-set are processed in the same order as they appear in the XML document. Having XPath node-sets represented by a list gives us the advantage of having the possibility to process the list in a different order than the one implied by the document (which is what happens if a variable containing a node-list is evaluated by Perl rather than XPath), see an example below.

xsh> $creatures = //creature[@status='alive']
# process creatures in the document order: 
xsh> foreach $creature print @name;
# process creatures in the reverse document order: 
xsh> foreach { reverse @$creature } print @name;
# append some more nodes to a node-list (using a variant of
# a simple assignment)
xsh> $creatures += //creature[@status='dead'];
# again, we can process creatures in order implied by the document:
xsh> foreach $creature print @name;
# but we can also process first living and then dead creatures,
# since this is how they are listed in $creature
xsh> foreach {$creature} print @name;
# same as the above is
xsh> foreach {@$creature} print @name;

XSH2 variables are either globally or lexically scoped. Global variables need not to be declared (they can be directly assigned to), whereas lexical variables must be declared using the command my. Global variable assignment may also be made temporal for the enclosing block, using local.

$var1 = "foo";           # a global variable requires no declaration
local $var1 $var2 $var3; # localizes global variables
$var1 = "bar";           # assignment to a localized variable is temporary
local $var4 = "foo";     # localized assignment
my $var1 $var $var3;     # declares lexical variables
my $var1 = "foo";        # lexical variable declaration with assignment

Lexical variables are only defined in the scope of current block or subroutine. There is no way to refer to a lexical variable form outside of the block it was declared in, nor from within a nested subroutine call. Of course, lexical variables can be referred to from nested blocks or Perl expressions (where they behave just like Perl's lexical variables).

On the other hand, global or localized XSH2 variables are just Perl Scalar variables belonging to the XML::XSH2::Map namespace, which is also the default namespace for any Perl code evaluated from XSH2 (so there's no need to use this prefix explicitly in Perl expressions, unless of course there is a lexical variable in the current scope with the same).

Localizing a variable using the local keyword makes all assignments to it occurring in the enclosing block temporary. The variable itself remains global, only its original value is restored at the end of the block that localized it.

In all above cases, it is possible to arbitrarily intermix XSH2 and Perl assignments:

xsh> ls //chapter[1]/title
<title>Introduction</title>
xsh> $a=string(//chapter[1]/title)
xsh> perl { $b="CHAPTER 1: ".uc($a); }
xsh> print $b
CHAPTER 1: INTRODUCTION

Although all XSH2 variables are in fact Perl Scalars, it is still possible to store Perl Array or Hash value to a XSH2 variable via reference. The following example demonstrates using Perl Hashes to collect and print some simple racial statistics about the population of Middle-Earth:

my $races;
foreach a:/middle-earth/creature { 
  my $race=string(@race);
  perl { $races->{$race}++ };
}
print "Middle-Earth Population (race/number of creatures)"
print { map "$_/$races->{$_}\n" keys(%$races); };

Related topics

assign

variable assignment

document

specifying documents

expression

expression argument type

local

temporarily assign new value to a variable

subroutine

name of a sub-routine

$variable

xpath

XPath expression