#line 118 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
  typedef cstring = +char;
  type string = "::std::basic_string<char>"
    requires Cxx_headers::string,
    header '#include "flx_serialisers.hpp"',
    encoder "::flx::gc::generic::string_encoder",
    decoder "::flx::gc::generic::string_decoder"
  ;
  typedef strings = typesetof (string);
  
  class Str [T] {
    virtual fun str: T -> string;
  }
  
  class Repr [T with Str[T]] {
    virtual fun repr (t:T) : string => str t;
  }
  
  class Show [T] {
    inherit Str[T];
    inherit Repr[T];
  }
  
  
  #line 143 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
  instance[t in strings] Eq[t] {
    fun == : t * t -> bool = "$1==$2";
  }
  instance[t in strings] Tord[t] {
    fun < : t * t -> bool = "$1<$2";
  }
  
  open class String
  {
    inherit Eq[string];
  
    inherit Tord[string];
  
  #line 158 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun == (s:string, c:char) => len s == 1uz and s.[0] == c;
    fun == (c:char, s:string) => len s == 1uz and s.[0] == c;
    fun != (s:string, c:char) => len s != 1uz or s.[0] != c;
    fun != (c:char, s:string) => len s != 1uz or s.[0] != c;
  
  #line 165 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    proc  += : &string * string = "$1->append($2:assign);";
    proc  += : &string * +char = "$1->append($2:assign);";
    proc  += : &string * char = "*$1 += $2;";
  
  #line 171 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    // we need to cast to an int so that c++ won't complain
    fun len: string -> size = "$1.size()";
  
  #line 176 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun + : string * string -> string = "$1+$2";
    fun + : string * carray[char] -> string = "$1+$2";
    fun + : string * char -> string = "$1+$2";
    fun + : char * string -> string = "$1+$2";
    //fun + : string * int -> string = "$1+::flx::rtl::i18n::utf8($2:assign)" is add requires package "flx_i18n";
    fun + ( x:string,  y: int) => x + str y;
  
    // may be a bit risky!
    // IT WAS: interferes with "hello" + list ("world","blah"):
    // is this a string or a list of strings?
    //fun + [T with Str[T]] (x:string, y:T) => x + str y;
  
  #line 190 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun * : string * int -> string = "::flx::rtl::strutil::mul($1:assign,$2:assign)" requires package "flx_strutil";
    fun * : char * int -> string = "::std::string($2:assign,$1:assign)";
  
  #line 195 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun apply (x:string, y:string):string => x + y;
    fun apply (x:string, y:int):string => x + y;
  
  #line 201 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    ctor char (x:string) => x.[0];
  #line 204 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    ctor string (c:char) => ""+c;
    ctor string: +char = "::std::string($1:assign)";
    ctor string: +char  * !ints = "::std::string($1:assign,$2:assign)";
    fun utf8: int -> string = "::flx::rtl::i18n::utf8($1)" requires package "flx_i18n";
  
  #line 211 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun subscript: string * !ints -> char =
      "::flx::rtl::strutil::subscript($1:assign,$2:assign)" requires package "flx_strutil";
    fun copyfrom: string * !ints -> string =
      "::flx::rtl::strutil::substr($1:assign,$2:assign,$1:postfix.size())" requires package "flx_strutil";
    fun copyto: string * !ints -> string =
      "::flx::rtl::strutil::substr($1:assign,0,$2:assign)" requires package "flx_strutil";
    fun substring: string * !ints * !ints -> string =
      "::flx::rtl::strutil::substr($1:assign,$2:assign,$3:assign)" requires package "flx_strutil";
  
    fun subscript (x:string, s:slice[int]):string =>
      match s with
      | #Slice_all => substring (x, 0, x.len.int)
      | Slice_from (start) => copyfrom (x, start)
      | Slice_to (end) => copyto (x, end)
      | Slice_range (start, end) => substring (x, start, end)
      | Slice_one (index) => string x.[index]
      endmatch
    ;
  
    proc store: &string * !ints * char = "(*$1)[$2] = $3;";
  
  #line 234 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun map (f:char->char) (var x:string): string = {
      if len x > 0uz do
        for var i in 0uz upto (len x) - 1uz do
          store(&x, i, f x.[i]);
        done
      done
      return x;
    }
  
  #line 248 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    const stl_npos: size = "::std::string::npos";
  
    fun stl_find: string * string -> size = "$1.find($2)" is cast;
    fun stl_find: string * string * size -> size = "$1.find($2,$3)" is cast;
    fun stl_find: string * +char -> size = "$1.find($2)" is cast;
    fun stl_find: string * +char * size -> size = "$1.find($2,$3)" is cast;
    fun stl_find: string * char -> size = "$1.find($2)" is cast;
    fun stl_find: string * char * size -> size = "$1.find($2,$3)" is cast;
  
    fun find (s:string, e:string) : opt[size] => match stl_find (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find (s:string, e:string, i:size) : opt[size] => match stl_find (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find (s:string, e:+char) : opt[size] => match stl_find (s, e) with | i when i== stl_npos => None[size] | i => Some i endmatch;
    fun find (s:string, e:+char, i:size) : opt[size] => match stl_find (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find (s:string, e:char) : opt[size] => match stl_find (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find (s:string, e:char, i:size) : opt[size] => match stl_find (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
  
    fun stl_rfind: string * string -> size = "$1.rfind($2)";
    fun stl_rfind: string * string * size -> size = "$1.rfind($2,$3)";
    fun stl_rfind: string * +char-> size = "$1.rfind($2)";
    fun stl_rfind: string * +char * size -> size = "$1.rfind($2,$3)";
    fun stl_rfind: string * char -> size = "$1.rfind($2)";
    fun stl_rfind: string * char * size -> size = "$1.rfind($2,$3)";
  
    fun rfind (s:string, e:string) : opt[size] => match stl_rfind (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun rfind (s:string, e:string, i:size) : opt[size] => match stl_rfind (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun rfind (s:string, e:+char) : opt[size] => match stl_rfind (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun rfind (s:string, e:+char, i:size) : opt[size] => match stl_rfind (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun rfind (s:string, e:char) : opt[size] => match stl_rfind (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun rfind (s:string, e:char, i:size) : opt[size] => match stl_rfind (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
  
    fun stl_find_first_of: string * string -> size = "$1.find_first_of($2)";
    fun stl_find_first_of: string * string * size -> size = "$1.find_first_of($2,$3)";
    fun stl_find_first_of: string * +char -> size = "$1.find_first_of($2)";
    fun stl_find_first_of: string * +char * size -> size = "$1.find_first_of($2,$3)";
    fun stl_find_first_of: string * char -> size = "$1.find_first_of($2)";
    fun stl_find_first_of: string * char * size -> size = "$1.find_first_of($2,$3)";
  
    fun find_first_of (s:string, e:string) : opt[size] => match stl_find_first_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_of (s:string, e:string, i:size) : opt[size] => match stl_find_first_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_of (s:string, e:+char) : opt[size] => match stl_find_first_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_of (s:string, e:+char, i:size) : opt[size] => match stl_find_first_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_of (s:string, e:char) : opt[size] => match stl_find_first_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_of (s:string, e:char, i:size) : opt[size] => match stl_find_first_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
  
    fun stl_find_first_not_of: string * string -> size = "$1.find_first_not_of($2)";
    fun stl_find_first_not_of: string * string * size -> size = "$1.find_first_not_of($2,$3)";
    fun stl_find_first_not_of: string * +char -> size = "$1.find_first_not_of($2)";
    fun stl_find_first_not_of: string * +char * size -> size = "$1.find_first_not_of($2,$3)";
    fun stl_find_first_not_of: string * char -> size = "$1.find_first_not_of($2)";
    fun stl_find_first_not_of: string * char * size -> size = "$1.find_first_not_of($2,$3)";
  
    fun find_first_not_of (s:string, e:string) : opt[size] => match stl_find_first_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_not_of (s:string, e:string, i:size) : opt[size] => match stl_find_first_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_not_of (s:string, e:+char) : opt[size] => match stl_find_first_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_not_of (s:string, e:+char, i:size) : opt[size] => match stl_find_first_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_not_of (s:string, e:char) : opt[size] => match stl_find_first_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_first_not_of (s:string, e:char, i:size) : opt[size] => match stl_find_first_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
  
    fun stl_find_last_of: string * string -> size = "$1.find_last_of($2)";
    fun stl_find_last_of: string * string * size -> size = "$1.find_last_of($2,$3)";
    fun stl_find_last_of: string * +char -> size = "$1.find_last_of($2)";
    fun stl_find_last_of: string * +char * size -> size = "$1.find_last_of($2,$3)";
    fun stl_find_last_of: string * char -> size = "$1.find_last_of($2)";
    fun stl_find_last_of: string * char * size -> size = "$1.find_last_of($2,$3)";
  
    fun find_last_of (s:string, e:string) : opt[size] => match stl_find_last_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_of (s:string, e:string, i:size) : opt[size] => match stl_find_last_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_of (s:string, e:+char) : opt[size] => match stl_find_last_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_of (s:string, e:+char, i:size) : opt[size] => match stl_find_last_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_of (s:string, e:char) : opt[size] => match stl_find_last_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_of (s:string, e:char, i:size) : opt[size] => match stl_find_last_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
  
    fun stl_find_last_not_of: string * string -> size = "$1.find_last_not_of($2)";
    fun stl_find_last_not_of: string * string * size -> size = "$1.find_last_not_of($2,$3)";
    fun stl_find_last_not_of: string * +char -> size = "$1.find_last_not_of($2)";
    fun stl_find_last_not_of: string * +char * size -> size = "$1.find_last_not_of($2,$3)";
    fun stl_find_last_not_of: string * char -> size = "$1.find_last_not_of($2)";
    fun stl_find_last_not_of: string * char * size -> size = "$1.find_last_not_of($2,$3)";
  
    fun find_last_not_of (s:string, e:string) : opt[size] => match stl_find_last_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_not_of (s:string, e:string, i:size) : opt[size] => match stl_find_last_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_not_of (s:string, e:+char) : opt[size] => match stl_find_last_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_not_of (s:string, e:+char, i:size) : opt[size] => match stl_find_last_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_not_of (s:string, e:char) : opt[size] => match stl_find_last_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
    fun find_last_not_of (s:string, e:char, i:size) : opt[size] => match stl_find_last_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
  
  
  #line 337 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    instance Set[string,char] {
      fun \(\in\) (c:char, s:string) => stl_find (s,c) != stl_npos;
    }
  
  #line 343 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    instance Iterable[string, char] {
      gen iterator(var x:string) () = {
        for var i in 0 upto x.len.int - 1 do yield Some (x.[i]); done
        return None[char];
      }
    }
    inherit Streamable[string,char];
  
  #line 353 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun prefix(arg:string,key:string)=>
      arg.[to len key]==key
    ;
  
    fun suffix (arg:string,key:string)=>
      arg.[-key.len to]==key
    ;
  
  
    fun startswith (x:string) (e:string) : bool => prefix (x,e);
  
    // as above: slices are faster
    fun endswith (x:string) (e:string) : bool => suffix (x,e);
  
    fun startswith (x:string) (e:char) : bool => x.[0] == e;
    fun endswith (x:string) (e:char) : bool => x.[-1] == e;
  
  #line 372 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun ltrim (x:string) (e:string) : string =>
      if startswith x e then
        x.[e.len.int to]
      else
        x
      endif
    ;
  
    fun rtrim (x:string) (e:string) : string =>
      if endswith x e then
        x.[to x.len.int - e.len.int]
      else
        x
      endif
    ;
  
    fun trim (x:string) (e:string) : string => ltrim (rtrim x e) e;
  
  #line 392 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun lstrip (x:string, e:string) : string =
    {
      if len x > 0uz do
        for var i in 0uz upto len x - 1uz do
          var found = false;
          for var j in 0uz upto len e - 1uz do
            if x.[i] == e.[j] do
              found = true;
            done
          done
  
          if not found do
            return x.[i to];
          done
        done;
      done
      return '';
    }
  
    fun rstrip (x:string, e:string) : string =
    {
      if len x > 0uz do
        for var i in len x - 1uz downto 0uz do
          var found = false;
          for var j in 0uz upto len e - 1uz do
            if x.[i] == e.[j] do
              found = true;
            done
          done
  
          if not found do
            return x.[to i.int + 1];
          done
        done
      done
      return '';
    }
  
    fun strip (x:string, e:string) : string => lstrip(rstrip(x, e), e);
  
    fun lstrip (x:string) : string => lstrip(x, " \t\n\r\f\v");
    fun rstrip (x:string) : string => rstrip(x, " \t\n\r\f\v");
    fun strip (x:string) : string => lstrip$ rstrip x;
  
  #line 438 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun ljust(x:string, width:int) : string =>
      if x.len.int >= width
        then x
        else x + (' ' * (width - x.len.int))
      endif
    ;
  
    fun rjust(x:string, width:int) : string =>
      if x.len.int >= width
        then x
        else (' ' * (width - x.len.int)) + x
      endif
    ;
  
  #line 454 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun split (x:string, d:char): List::list[string] => List::rev (rev_split (x,d));
  
    fun rev_split (x:string, d:char): List::list[string] = {
      fun aux (x:string,y:List::list[string]) =>
        match find (x, d) with
        | #None => Cons (x, y)
        | Some n => aux$ x.[n+1uz to], List::Cons (x.[to n],y)
        endmatch
      ;
      return aux$ x, List::Empty[string];
    }
  
    fun split (x:string, d:string): List::list[string] => List::rev (rev_split (x,d));
  
    fun rev_split (x:string, d:string): List::list[string] = {
      fun aux (pos:size,y:List::list[string]) =>
        match stl_find_first_of (x, d, pos) with
        | $(stl_npos) => List::Cons (x.[pos to],y)
        | n => aux$ (n+1uz), List::Cons (x.[pos to n],y)
        endmatch
      ;
      return aux$ 0uz, List::Empty[string];
    }
  
    fun split (x:string, d:+char): List::list[string] => List::rev (rev_split (x,d));
  
    fun rev_split (x:string, d:+char): List::list[string] = {
      fun aux (x:string,y:List::list[string]) =>
        match find_first_of (x, d) with
        | #None => List::Cons (x, y)
        | Some n => aux$ x.[n+1uz to], List::Cons (x.[to n],y)
        endmatch
      ;
      return aux$ x, List::Empty[string];
    }
  
    fun split_first (x:string, d:string): opt[string*string] =>
      match find_first_of (x, d) with
      | #None => None[string*string]
      | Some n => Some (x.[to n],substring(x,n+1uz,(len x)))
      endmatch
    ;
  
  
    Split a string on whitespace but respecting
    double quotes, single quotes, and slosh escapes.
    // leading and trailing space is removed. Embedded
    // multiple spaces cause a single split.
    class RespectfulParser {
      union quote_action_t =
        | ignore-quote
        | keep-quote
        | drop-quote
      ;
      union dquote_action_t =
        | ignore-dquote
        | keep-dquote
        | drop-dquote
      ;
      union escape_action_t =
        | ignore-escape
        | keep-escape
        | drop-escape
      ;
      typedef action_t = (quote:quote_action_t, dquote:dquote_action_t, escape:escape_action_t);
  
      union mode_t = | copying | skipping | quote | dquote | escape-copying | escape-quote | escape-dquote;
      typedef state_t = (mode:mode_t, current:string, parsed: list[string] );
  
      noinline fun respectful_parse (action:action_t) (var state:state_t) (var s:string) : state_t =
      {
        var mode = state.mode;
        var current = state.current;
        var result = Empty[string];
  
        noinline proc handlecopying(ch:char) {
          if ch == char "'" do
            match action.quote with
            | #ignore-quote =>
              current += ch;
            | #keep-quote =>
              current += ch;
              mode = quote;
            | #drop-quote =>
              mode = quote;
            endmatch;
          elif ch == char '"' do
            match action.dquote with
            | #ignore-dquote =>
              current += ch;
            | #keep-dquote =>
              current += ch;
              mode = dquote;
            | #drop-dquote =>
              mode = dquote;
            endmatch;
          elif ch == char '\\' do
            match action.escape with
            | #ignore-escape =>
              current += ch;
            | #keep-escape =>
              current += ch;
              mode = escape-copying;
            | #drop-escape =>
              mode = escape-copying;
            endmatch;
          elif ord ch <= ' '.char.ord  do // can't happen if called from skipping
            result += current;
            current = "";
            mode = skipping;
          else
            current += ch;
            mode = copying;
          done
        }
  
        for ch in s do
          match mode with
          | #copying => handlecopying ch;
          | #quote =>
            if ch == char "'" do
              match action.quote with
              | #ignore-quote =>
                assert false;
                //current += ch;
              | #keep-quote =>
                current += ch;
                mode = copying;
              | #drop-quote =>
                mode = copying;
              endmatch;
            elif ch == char "\\" do
              match action.escape with
              | #ignore-escape =>
                current += ch;
              | #keep-escape =>
                current += ch;
                mode = escape-quote;
              | #drop-escape =>
                mode = escape-quote;
              endmatch;
            else
              current += ch;
            done
  
          | #dquote =>
            if ch == char '"' do
              match action.dquote with
              | #ignore-dquote =>
                assert false;
                //current += ch;
              | #keep-dquote =>
                current += ch;
                mode = copying;
              | #drop-dquote =>
                mode = copying;
              endmatch;
            elif ch == char "\\" do
              match action.escape with
              | #ignore-escape =>
                current += ch;
              | #keep-escape =>
                current += ch;
                mode = escape-dquote;
              | #drop-escape =>
                mode = escape-dquote;
              endmatch;
            else
              current += ch;
            done
  
          | #escape-copying =>
             current += ch;
             mode = copying;
  
          | #escape-quote =>
             current += ch;
             mode = quote;
  
          | #escape-dquote =>
             current += ch;
             mode = dquote;
  
          | #skipping =>
            if ord ch > ' '.char.ord  do
              handlecopying ch;
            done
          endmatch;
        done
        return (mode=mode, current=current, parsed=state.parsed + result);
      }
    }
  
    // simplified one shot parser.
    // ignores mismatched quotes and backslashes.
    fun respectful_split (action:RespectfulParser::action_t) (s:string) : list[string] =
    {
      var state = RespectfulParser::respectful_parse
        action
        (
          mode=RespectfulParser::skipping,
          current="",
          parsed=Empty[string]
        )
        s
      ;
      // ignore mismatched quotes and backslashes.
      match state.mode with
      | #skipping => ;
      | _ => state.parsed = state.parsed + state.current;
      endmatch;
      return state.parsed;
  
    }
  
    fun respectful_split (s:string) : list[string] =>
      respectful_split (
        quote=RespectfulParser::keep-quote,
        dquote=RespectfulParser::keep-dquote,
        escape=RespectfulParser::keep-escape
      )
      s
    ;
  
    // OO version of the parser.
    object respectfulParser (action:RespectfulParser::action_t) = {
      var state = (mode=RespectfulParser::skipping, current="", parsed=Empty[string]);
      method proc parse (s:string) {
        state = RespectfulParser::respectful_parse action state s;
      }
      method fun get_parsed () => state.parsed;
    }
  
  #line 689 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    // Note: pos, length!
    mutators
    proc erase: &string * size * size = "$1->erase($2,$3);";
    proc insert: &string * size * string = "$1->insert($2,$3);";
    proc replace: &string * size * size * string = "$1->replace($2,$3,$4);";
  
    functional
    fun erase: string * size * size -> string = "::std::string($1).erase($2,$3)";
    fun insert: string * size * string -> string = "::std::string($1).insert($2,$3)";
    fun replace: string * size * size * string -> string = "::std::string($1).replace($2,$3,$4)";
  
  
  #line 704 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun search_and_replace (x:string, var spos:size, s:string, r:string) : string =
    {
      val m = s.len;
      var o = x.[to spos];
      var n = (x,s,spos).stl_find;
      while n != stl_npos do
        o+=x.[spos to n]+r;
        spos = n+m;
        n = (x,s,spos).stl_find.size;
      done
      o+=x.[spos to];
      return o;
    }
    fun search_and_replace (x:string, s:string, r:string) : string => search_and_replace (x,0uz,s,r);
  
    fun search_and_replace (vs:list[string * string]) (var v:string) = {
      match k,b in vs do
        v = search_and_replace (v,k,b);
      done
      return v;
    }
  
  #line 729 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    // Replace \0 \1 \2 etc in s with text from v
    fun subst(s:string, v:varray[StringPiece]): string =
    {
    //println$ "Subst " + s +" with " + str v;
       enum mode_t {cp, ins};
       var b = "";
       var mode=cp;
       var j = 0;
       var count = 0;
       for var i in 0 upto s.len.int - 1 do
         match mode with
         | #cp =>
           if s.[i] == char "\\" do
             mode = ins;
             j=0; count = 0;
           else
            b += s.[i];
           done
         | #ins =>
           if s.[i] in "0123456789" do
             j = j * 10 + ord(s.[i]) - ord (char "0");
             ++count;
           else
             if count == 0 do
               b += "\\";
             elif j < v.len.int do
               b+= str v.stl_begin.j;
             done
             // adjacent insertion?
             if s.[i] == char "\\" do
               j=0; count=0;
             else
               mode = cp;
               b += s.[i];
             done
           done
         endmatch;
       done
       // run off end
       match mode with
       | #cp => ;
       | #ins =>
         if count == 0 do
           b += "\\";
         elif j < v.len.int do
           b+= str v.j;
         done
       endmatch;
       return b;
    }
    // Search for regex, replace by r with \0 \1 \2 etc replace by match groups.
    fun search_and_replace (x:string, var spos: size, re:Re2::RE2, r:string) : string =
    {
      var ngroups = re.NumberOfCapturingGroups + 1;
      var v = varray[StringPiece]$ (ngroups+1).size, StringPiece "";
      var o = x.[to spos];             // initial substring
      var sp = StringPiece(x);
      var base : +char = sp.data;      // base pointer of char array
      while Re2::Match(re, sp, spos.int, UNANCHORED, v.stl_begin, v.len.int) do
        var mpos = size(v.0.data - base);  // start of match
        o+= x.[spos to mpos];          // copy upto start of match
        o+= subst(r,v);                // copy replacement
        spos = mpos + v.0.len;       // advance over match
      done
      o+=x.[spos to];                  // rest of string
      return o;
    }
  #line 798 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun atoi: string -> int = "::std::atoi($1:postfix.c_str())"  requires Cxx_headers::cstdlib;
    fun atol: string -> long = "::std::atol($1:postfix.c_str())"  requires Cxx_headers::cstdlib;
    fun atoll: string -> long = "::std::atoll($1:postfix.c_str())"  requires Cxx_headers::cstdlib;
    fun atof: string -> double = "::std::atof($1:postfix.c_str())"  requires Cxx_headers::cstdlib;
  
  #line 805 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    proc reserve: &string * !ints = "$1->reserve($2);";
  
  #line 809 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    // Entirely unsafe because string could be an rvalue.
    fun _unsafe_cstr: string -> +char = "((char*)$1.c_str())" is atom;
  
    // partially unsafe because the string could be modified.
    fun stl_begin: &string -> +char = "((char*)$1->c_str())" is atom;
    fun stl_end: &string -> +char = "((char*)($1->c_str()+$1->size()))" is atom;
  
    // this operation is entirely safe because the char array
    // provided is copied (and garbage collected)
    fun cstr (x:string) : +char => (varray[char] x).stl_begin;
  
  #line 822 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    fun vsprintf[t]: +char  * t -> string =
      "::flx::rtl::strutil::flx_asprintf($1,$2)" requires package "flx_strutil"
    ;
  
    fun vsprintf[t]: string * t -> string =
      "::flx::rtl::strutil::flx_asprintf(const_cast<char*>($1.c_str()),$2)" requires package "flx_strutil"
    ;
  
  #line 832 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
    // Convert all characters to upper case
    fun toupper(s:string):string => map (toupper of char) s;
    // Convert all characters to lower case
    fun tolower(s:string):string => map (tolower of char) s;
  }
  
  
  #line 841 "/home/travis/build/felix-lang/felix/src/packages/strings.fdoc"
  
  instance Str[string] {
    fun str (s:string) : string => s;
  }
  
  instance Str[+char] {
    fun str: +char -> string = '::flx::rtl::strutil::atostr($1)' requires package "flx_strutil";
  }
  
  instance Repr[string] {
    fun repr (x:string) : string = {
      var o = "'";
      if len x > 0uz do
        for var i in 0uz upto (String::len x) - 1uz do
          o += repr x.[i];
        done
      done
      return o + "'";
    }
  }
  
  open[T in strings] Show[T];
  open Set[string,char];