stringops evaluator

## stringops

### Description

Implements *nix shell-inspired string ops.

### Simple operations

Substring property foo from index start for length len:

    ${foo:start:len}

Evaluate to "defaultfoo" if foo not set:

    ${foo:-defaultfoo}

Default unset property foo to "defaultfoo" and return:

    ${foo:=defaultfoo}

Fail build if property foo unset:

    ${foo:?optional error message}

Evaluate to "if-foo" if property foo set:

    ${foo:+if-foo}

### Delete/Replace

The patterns used for deletion and replacement are *nix-style; i.e. Ant patternset syntax without ** support. File separators are transparently merged. Characters used in the expressions themselves can be specified by backslash escaping.

Delete shortest leading match:

          <!-- given ${x}=foo/bar/baz.ext -->
          ${x#*/} <!-- yields bar/baz.ext -->

Delete longest leading match:

          <!-- given ${x}=foo/bar/baz.ext -->
          ${x##*/} <!-- yields baz.ext -->

Delete shortest trailing match:

          <!-- given ${x}=foo/bar/baz.ext -->
          ${x%/*} <!-- yields foo/bar -->

Delete longest trailing match:

          <!-- given ${x}=foo/bar/baz.ext -->
          ${x%%/*} <!-- yields foo -->

Delete extension (using delete shortest trailing match):

          <!-- given ${x}=foo/bar/baz.ext -->
          ${x%.*} <!-- yields foo/bar/baz -->

Extract extension (using delete longest leading match):

          <!-- given ${x}=foo/bar/baz.ext -->
          ${x##*.} <!-- yields ext -->

Replace first occurrence of a pattern:

          <!-- given ${path}=org/apache/ant/props -->
          ${path/\//.} <!-- yields org.apache/ant/props -->

Replace all occurrences of a pattern:

          <!-- given ${path}=org/apache/ant/props -->
          ${path//\//.} <!-- yields org.apache.ant.props -->