#line 69 "/home/travis/build/felix-lang/felix/src/packages/program.fdoc"
  
  open class CmdOpt
  {
    // Convert key/value pairs represented like -I path
    // into form --include=path
    noinline fun cvt-key-arg (keys: list[string * string]) (x:list[string]) =
    {
       var out = Empty[string];
       var inp = x.iterator;
       for word in inp do
         match keys.find word with
         | #None => out = Cons (word,out);
         | Some prefix =>
           match inp() with
           | #None => println$ "Error, expected argument to option " + word;
           | Some arg => out = Cons (prefix+"=" + arg, out);
           endmatch;
         endmatch;
       done
       return rev out;
    }
  
    // Parse key value pairs represented by --key=value.
    // Allows multiple values to be given.
    // Stores reversed list of values.
    // Returns ordered list of non-handled elements.
    var rekv = RE2 "(--.*)=(.*)";
    noinline gen parse-key-multi-value (keys:list[string]) (d:strdict[list[string]]) (x:list[string]) =
    {
      var out = Empty[string];
      var va = varray[StringPiece] (StringPiece "", StringPiece "", StringPiece "");
      for opt in x do
        if Match (rekv, StringPiece (opt), 0,ANCHOR_BOTH, va.stl_begin, va.len.int) do
          var key = va . 1 . string;
          if key in keys do
            val value= va . 2 . string ;
            val nuval = Cons (value, d.get_dflt (key,Empty[string]));
            d.add key nuval;
          else
            out = Cons (opt, out);
          done
        else
          out = Cons (opt, out);
        done
      done
      return rev out;
    }
  
    // Parse key value pairs represented by --key=value.
    // Keys must be unique.
    // Stores reversed list of values.
    // Returns ordered list of non-handled elements.
    noinline gen parse-key-single-value (keys:list[string]) (d:strdict[string]) (x:list[string]) =
    {
      var out = Empty[string];
      var va = varray[StringPiece] (StringPiece "", StringPiece "", StringPiece "");
      for opt in x do
        if Match (rekv, StringPiece (opt), 0,ANCHOR_BOTH, va.stl_begin, va.len.int) do
          var key = va . 1 . string;
          if key in keys do
            val value= va . 2 . string ;
            match d.get key with
            | #None => d.add key value;
            | _ => println$ "Duplicate option '" + opt +"'";
            endmatch;
          else
            println$ "Invalid option '" + opt+"'";
          done
        else
          out = Cons (opt, out);
        done
      done
      return rev out;
    }
  
  
    // Parse keys given by --key.
    // Allows multiple values.
    // Stores count of occurences.
    var rek = RE2 "(--.*)";
    noinline gen parse-key (keys:list[string]) (d:strdict[int]) (x:list[string]) =
    {
      var out = Empty[string];
      var va = varray[StringPiece] (StringPiece "", StringPiece "");
      for opt in x do
        if Match (rek, StringPiece (opt), 0,ANCHOR_BOTH, va.stl_begin, va.len.int) do
          var key = va . 1 . string;
          if key in keys do
            val nuval =d.get_dflt (key,0) + 1;
            d.add key nuval;
          else
            println$ "Invalid option '" + opt+"'";
          done
        else
          out = Cons (opt, out);
        done
      done
      return rev out;
    }
  
    // Parse keys given by -abcd
    // Allows multiple values.
    // Stores count of occurences.
    // Replaces option letter with specified long option key.
    // Returns ordered list of non-handled elements.
    var resw = RE2 "(-.*)";
    noinline gen parse-switches (switchmap: list[char * string] ) (d:strdict[int]) (x:list[string]) =
    {
      var out = Empty[string];
      var va = varray[StringPiece] (StringPiece "", StringPiece "");
      for opt in x do
        if Match (resw, StringPiece (opt), 0,ANCHOR_BOTH, va.stl_begin, va.len.int) do
          var switches = va . 1 . string . [1 to];
          for switch in switches do
            match switchmap.find switch with
            | #None =>
              println$ "Invalid option " + opt + " char '" + str switch+"'";
            | Some key=>
              val nuval = d.get_dflt (key,0) + 1;
              d.add key nuval;
            endmatch;
          done
        else
          out = Cons (opt, out);
        done
      done
      return rev out;
    }
  
    typedef cmdspec_t = (
      split-key-value-spec: list[string * string],
      multi-valued-keys-spec: list[string],
      single-valued-keys-spec: list[string],
      switches-spec: list[string],
      short-switch-map-spec: list[char * string]
    );
  
    typedef cmdopt-parse-result_t = (
       multi-valued-keys : strdict[list[string]],
       single-valued-keys : strdict[string],
       switches : strdict[int],
       positional : list[string]
    );
  
    ctor cmdopt-parse-result_t () =>
    (
      multi-valued-keys = strdict[list[string]](),
      single-valued-keys = strdict[string](),
      switches = strdict[int](),
      positional = Empty[string]
    );
  
    noinline gen parse-cmdline (spec:cmdspec_t) (x:list[string]) : cmdopt-parse-result_t = {
      var result = cmdopt-parse-result_t ();
      var nonk = cvt-key-arg spec.split-key-value-spec x;
      nonk = parse-key-multi-value spec.multi-valued-keys-spec result.multi-valued-keys nonk;
      nonk = parse-key-single-value spec.single-valued-keys-spec result.single-valued-keys nonk;
      nonk = parse-key spec.switches-spec result.switches nonk;
      result.positional = parse-switches spec.short-switch-map-spec result.switches nonk;
      return result;
    }
  }