Changes since XSH 1.x

This section briefly describes differences between XSH2 and previous XSH 1.x releases. The list should not be considered complete. Some syntax variations or amendments in the semantics of various commands may not be documented in this section, neither are various improvements in the XSH interpreter.

Changes in XSH2

  1. In XSH2, subroutines can be called without a call. They can be redefined and undefined. The command call can still be used, but it's use only makes sense in indirect calls, where subroutine's name is computed from an expression.

    def foo $param1 $param2 { 
      # param1 and $param2 are lexical (a.k.a. my)
      ls $param1; 
      echo $param2 
    }
    foo (//chapter)[1] (//chapter)[1]/title
    
    def inc $param1 { return ($param1 + 1) }
    $two := inc 1;
    
  2. XSH2 uses variables of the form $variable for all kinds of objects, including node-sets (which, if evaluated as Perl expressions, preserve node order). Node-list variables of XSH 1.x have been deprecated.

    $var = //foo/bar;                 # node set
    $var = "hallo world";             # string
    $var = xsh:new-element("foo");    # node object
    $var = { ['a','b','c'] };         # Perl array reference
    $var = {{ 'a'=>'A', 'b'=>'B' }};  # Perl hash reference
    
  3. XSH2 allows variables to be used in XPath just as they are used in XSLT:

    $var = //foo/bar;
    ls //baz[ . = $var[@test=1]/any ]
    

    Variable interpolation is still available in XSH2 via ${var}, but it's importance is diminished compared to XSH 1.x, because the XPath engine now evaluates variables directly. Interpolation can still be used for things like "XPath-macros":

    $filter = "[ . = $var[@test=1]/any ]";
    ls //baz${filter};
    
  4. XSH2 equally supports XPath and Perl expressions (written in braces { ... }). Unfortunately, Perl expressions can't be embedded in XPath expressions, but one can still use variables as an agent:

    perl { use MIME::Base64 };
    my $encoded = { encode_base64('open sesame') }
    ls //secret-cave[string(password) = $encoded]
    

    We can, however, use Perl-only expressions complemented with auto-conversion to do things like:

    copy { encode_base64('Pe do mellon a minno!') } replace //secret-cave/password/text();
    
  5. Commands return values (see := assignment, or &{ } expressions).

    $moved_paras := xmove //para replace .;
    $chapter := wrap chapter $moved_paras;
    ls $chapter;
    
    # or just
    
    ls &{ wrap chapter &{ xmove //para replace . } };
    
  6. XSH2 deprecates "string" expressions of XSH 1.x. However, for convenience, some XSH2 commands interpret name-like XPath expressions on certain argument positions as strings (mostly commands that expect file-name or node-name arguments):

    insert element my_document into .;
    insert text "foo" into my_document;
    
    $doc := open my_document;         # opens file named "my_document"
    $doc := open "my_document";       # same
    $doc := open (my_document);       # opens file named "foo"
    $doc := open string(my_document); # same
    
  7. In XSH2, XML documents have no ID. They are referred to using variables (which fits in well with the unified variable concept):

    $doc1 := open "foo1.xml";
    $doc2 := open "foo2.xml";
    ls ($doc1//para|$doc2//para);
    cd $doc1;
    ls id('intro');             # finds ID intro in the current document ($doc1)
    ls xsh:id2($doc2, 'intro'); # finds ID intro in $doc2
    
  8. XSH2 commands have options and flags instead of many optional (positional) arguments. Options/flags usually have both long forms (like --flag) and equivalent short forms (like :f) (colon is borrowed from Scheme, because dash is reserved for XPath minus).

    $doc := open --format html "version1.html";
    save --file "version2.xml" $doc;
    
    ls --fold /;
    ls :f /;
    ls --depth 1 /;
    ls :d 1 /;
    
    # all the same:
    $sorted = sort --key @name --locale --descending //user;
    $sorted = sort :l:d:k@name //user;
    $sorted = sort --key @name --compare { use locale; $b cmp $a } //user;
    
    validate --relaxng --file "test.rng" $mydoc;
    validate --public "-//OASIS//DTD DocBook XML V4.1.2//EN" $mydoc;
    validate --yesno $mydoc;
    
  9. Finally, eval is no longer an alias for perl in XSH2, but instead evaluates strings containing XSH2 commands (so eval $string now practically works like old ugly perl { xsh($string) }). See the documentation for eval for a handy usage example (no more PHP, XSTL and XPathScript :-)).

Examples

Example 12. Open command has changed.

XSH1:
foo = file.xml;
or
foo = "file.xml";
XSH2:
$foo := open file.xml;        # file.xml is a bareword in file-name context
or
$foo := open "file.xml";      # "file.xml" is a XPath string
or
$foo := open {"file.xml"};    # "file.xml" is a Perl string
or
$foo = xsh:open("file.xml");  # righthand side is an XPath extension function

Example 13. XSH2 commands have options

XSH1:
open HTML FILE foo2 = "file.html";
XSH2:
$foo2 := open --format html "file.html";

Example 14. documents

XSH1:
foo = file.xml;
ls foo:(//bar|//baz);
XSH2:
$foo := open file.xml;
ls ($foo//bar|$foo//baz);

Example 15. variable interpretation

XSH1:
$family = "Arial";
ls //font[@family="$family"];   # interpolation
or
ls //font[@family="${family}"]; # interpolation
XSH2:
$family = "Arial";
ls //font[@family=$family];     # evaluation by XPath engine
or
ls //font[@family="${family}"]; # interpolation

Example 16. adding new nodes

XSH1:
insert attribute "foo=bar" into /scratch;
XSH2:
insert attribute "foo=bar" into /scratch;
or
copy xsh:new-attribute("foo","bar") into /scratch;

Example 17. foreach with perl expression

XSH1:
foreach { glob('*.xml') } {
  open doc = $__;
  ...
}
XSH2:
foreach { glob('*.xml') } {
  my $doc := open .;
  ...
} 

Example 18. foreach (perl expression) with variable

XSH2:
foreach my $filename in { glob('*.xml') } {
  my $doc := open $filename;
  ...
} 

Example 19. sorting nodes

XSH1:
%list = //player;
sort @best_score { $a <=> $b } %list;
copy %list into .;
XSH2:
$list := sort --numeric --key @best_score //player;
copy { $list } into .;
or
copy &{ sort --numeric --key @best_score //player } into .;
or (using short options)
copy &{ sort :n :k @best_score //player } into .;