anchor.diff:    ].
   
   option_name_test_() ->
  -  [ ?_assertEqual("-h", option_name(#option{short="-h"}))
  +  [ ?_assertEqual("-h"    , option_name(#option{short="-h"}))
     , ?_assertEqual("--help", option_name(#option{short="-h", long="--help"}))
     , ?_assertEqual("--help", option_name(#option{long="--help"}))
     ].
  
positive.code:                   parse_args("-h arg -- -v", O))
    ].
  
  printable_usage(Doc) ->
    case re:split(Doc, "([Uu][Ss][Aa][Gg][Ee]:)", [{return, list}]) of
      UsageSplit when length(UsageSplit) < 3 ->
        throw("\"usage:\" (case-insensitive) not found.");
      UsageSplit when length(UsageSplit) > 3 ->
        throw("More than one \"usage:\" (case-insensitive)");
      [_|UsageSplit] ->
        L = re:split(lists:flatten(UsageSplit), "\\n\\s*\\n", [{return, list}]),
        string:strip(hd(L))
    end.
  
  formal_usage(PrintableUsage) ->
    %% Split and drop "usage:"
    [_Usage, ProgName|Args] = string:tokens(PrintableUsage, " \n"),
    F = fun (S) when (S) == ProgName -> ") | (";
            (S)                      -> S
        end,
    "( " ++ string:join(lists:map(F, Args), " ") ++ " )".
  
  printable_and_formal_usage_test_() ->
    Doc =
      "Usage: prog [-hv] ARG
              prog N M
  
       prog is a program.",
    [ ?_assertEqual("Usage: prog [-hv] ARG\n            prog N M",
                    printable_usage(Doc))
    , ?_assertEqual("( [-hv] ARG ) | ( N M )",
                    formal_usage(printable_usage(Doc)))
    , ?_assertEqual("uSaGe: prog ARG",
                    printable_usage("uSaGe: prog ARG\n\t \t\n bla"))
    ].
  
  
  parse_pattern(Source, Options) ->
    ok.
  
  %% docopt_any_options_test_() ->
  %%   Doc = "Usage: prog [options] A
  
  %%     -q  Be quiet
  %%     -v  Be verbose.",
  %%   [ ?_assertEqual([{"A", "arg"}, {"-v", false}, {"-q", false}],
  %%                   docopt(Doc, "arg"))
  %%   , ?_assertEqual([{"A", "arg"}, {"-v", true}, {"-q", false}],
  %%                   docopt(Doc, "-v arg"))
  %%   , ?_assertEqual([{"A", "arg"}, {"-v", false}, {"-q", true}],
  %%                   docopt(Doc, "-q arg"))
  %%   ].
  
  option_parse(Str) ->
    {Options, Desc} = partition(string:strip(Str), "  "),
    ParsedOpt = lists:foldl(fun([$-,$-|_] = S, Opt) -> Opt#option{long     = S};
                               ([$-|_]    = S, Opt) -> Opt#option{short    = S};
                               (_            , Opt) -> Opt#option{argcount = 1}
                            end, #option{}, string:tokens(Options, ",= ")),
    case ParsedOpt of
      #option{argcount = 1} -> ParsedOpt#option{value = default_value(Desc)};
      ParsedOpt             -> ParsedOpt
    end.
  
  default_value(Desc) ->
    {ok, Re} = re:compile("\\[default: (.*)\\]", [caseless]),
    case re:run(Desc, Re, [{capture, [1], list}]) of
      {match, [DefaultValue]} -> DefaultValue;
      nomatch                 -> false
    end.
  
  option_name(#option{long=undefined, short=Short}) -> Short;
  option_name(#option{long=Long})                   -> Long.
  
  partition(Str, Delim) ->
    case string:str(Str, Delim) of
      0 -> {Str, ""};
      I ->
        Left  = string:substr(Str, 1, I - 1),
        Right = string:substr(Str, I + length(Delim)),
        {Left, Right}
    end.
  
  partition_test_() ->
    [ ?_assertEqual({"foobar", ""}     , partition("foobar"      , "abc"))
  
negative.code:   [option_parse("-" ++ S) || S <- OptStrings].
  
  parse_args(Args, Options) ->
    State = #st{ tokens  = string:tokens(Args, " ")
               , options = Options
               , mode    = parse_args
               },
    parse_args_tokens(State).
  
  parse_args_tokens(#st{tokens=[]})                    -> [];
  parse_args_tokens(State0) ->
    case current(State0) of
      "--"           -> [#argument{value=Arg} || Arg <- tokens(State0)];
      [$-,$-|_]      ->
        {Opt, State} = parse_long(State0),
        Opt ++ parse_args_tokens(State);
      [$-|_] ->
        {Opts, State} = parse_shorts(State0),
        Opts ++ parse_args_tokens(State);
      _              ->
        [#argument{value=current(State0)}|parse_args_tokens(move(State0))]
    end.
  
  current(#st{tokens=[Current|_]}) -> Current;
  current(#st{tokens=[]})          -> undefined.
  move(#st{tokens=[_|Rest]}=St)    -> St#st{tokens=Rest}.
  tokens(#st{tokens=Tokens})       -> Tokens.
  rest(#st{tokens=[_|Rest]})       -> Rest.
  options(#st{options=Options})    -> Options.
  
  parse_long(State0) ->
    {Raw, Value} = partition(current(State0), "="),
    Opt =
      case [O || O <- options(State0), O#option.long == Raw] of
        [O] -> O;
        []  ->
          case [O || O <- options(State0), starts_with(O#option.long, Raw)] of
            []                         -> throw({Raw, "not recognized"});
            Opts when length(Opts) > 1 -> throw({Raw, "is not a unique prefix"});
            [O]                        -> O
          end
      end,
    Rest  = rest(State0),
    State = move(State0),
    case Opt#option.argcount of
      1 when Value == [],
             Rest  == [] -> throw({Raw, "requires an argument"});
      1 when Value == [] -> {[Opt#option{value = current(State)}], move(State)};
      1 when Value /= [] -> {[Opt#option{value = Value}], State};
      0 when Value /= [] -> throw({Raw, "must not have an argument"});
      0 when Value == [] -> {[Opt#option{value = true}], State}
    end.
  
  starts_with(Str, SubStr) when is_list(Str), is_list(SubStr) ->
    string:str(Str, SubStr) == 1;
  starts_with(_, _) -> false.
  
  parse_shorts(State) ->
    [$-|Tokens] = current(State),
    parse_shorts(Tokens, move(State), []).
  
  parse_shorts([], State, Acc) -> {lists:reverse(Acc), State};
  parse_shorts([H|T], State, Acc) ->
   case [O || O <- options(State), tl(O#option.short) == [H]] of
     []                       -> throw({[$-, H], "not recognized", State});
     Opt when length(Opt) > 1 -> throw({[$-, H], "specified ambiguously"});
     [Opt] when Opt#option.argcount == 0 ->
       parse_shorts(T, State, [Opt#option{value = true}|Acc]);
     [Opt] ->
       {Value, Rest} = get_value_shorts(H, T, State),
       {[Opt#option{value = Value}], Rest}
   end.
  
  get_value_shorts(H, [], [])     -> throw({H, "requires an argument"});
  get_value_shorts(_, [], State)  -> {current(State), move(State)};
  get_value_shorts(_, Arg, State) -> {Arg, State}.
  
  printable_usage(Doc) ->
    case re:split(Doc, "([Uu][Ss][Aa][Gg][Ee]:)", [{return, list}]) of
      UsageSplit when length(UsageSplit) < 3 ->
        throw("\"usage:\" (case-insensitive) not found.");
      UsageSplit when length(UsageSplit) > 3 ->
        throw("More than one \"usage:\" (case-insensitive)");
      [_|UsageSplit] ->
        L = re:split(lists:flatten(UsageSplit), "\\n\\s*\\n", [{return, list}]),
        string:strip(hd(L))
    end.
  
  formal_usage(PrintableUsage) ->
    %% Split and drop "usage:"
    [_Usage, ProgName|Args] = string:tokens(PrintableUsage, " \n"),
    F = fun (S) when S == ProgName -> ") | (";
            (S)                    -> S
        end,
    "( " ++ string:join(lists:map(F, Args), " ") ++ " )".
  
  parse_pattern(Source0, Options) ->
    %% Add spaces around []()| and ...
    Source = re:replace(Source0, "([\\[\\]\\(\\)\\|]|\\.\\.\\.)", " \\1 ",
                        [{return, list}, global]),
    State = #st{ tokens  = string:tokens(Source, " ")
               , options = Options
               , mode    = parse_pattern
               },
    {Result, _} = parse_expr(State),
    #required{children=Result}.
  
  % expr ::= seq ( '|' seq )* ;
  parse_expr(State0) ->
    {Seq, State} = parse_seq(State0),
    case current(State) of
      "|" -> parse_expr(move(State), [maybe_required_seq(Seq)]);
      _   -> {Seq, State}
    end.
  
  parse_expr(State0, Acc)                 ->
    ct:pal("in parse_expr: ~p, ~p", [tokens(State0), Acc]),
    {Seq, State} = parse_seq(State0),
    ct:pal("in parse_expr after parse_seq: ~p, ~p", [tokens(State), Seq]),
    case current(State) of
      "|" -> parse_expr(move(State), [maybe_required_seq(Seq)|Acc]);
      _   ->
        Result = lists:reverse([maybe_required_seq(Seq)|Acc]),
        case length(Result) > 1 of
          true  -> {[#either{children=Result}], State};
          false -> {Result, State} % Needed?
        end
     end.
  
  maybe_required_seq([Seq]) -> Seq;
  maybe_required_seq(Seq)   -> #required{children=Seq}.
  
  %% seq ::= ( atom [ '...' ] )* ;
  parse_seq(State) -> parse_seq(State, []).
  
  parse_seq(#st{tokens=[]}      = State, Acc) -> {lists:reverse(Acc), State};
  parse_seq(#st{tokens=["]"|_]} = State, Acc) -> {lists:reverse(Acc), State};
  parse_seq(#st{tokens=[")"|_]} = State, Acc) -> {lists:reverse(Acc), State};
  parse_seq(#st{tokens=["|"|_]} = State, Acc) -> {lists:reverse(Acc), State};
  parse_seq(State0, Acc) ->
    ct:pal("in parse seq: ~p, ~p", [tokens(State0), Acc]),
    {Atom, State} = parse_atom(State0),
    ct:pal("in parse seq after parse_atom: ~p, ~p, ~p", [Atom, tokens(State), Acc]),
    case current(State) of
      "..." -> parse_seq(move(State), [#one_or_more{children=Atom}|Acc]);
      _     -> parse_seq(State, Atom ++ Acc)
    end.
  
  %% atom ::= '(' expr ')' | '[' expr ']' | 'options'
  %%       | long | shorts | argument | command ;
  parse_atom(State) ->
    ct:pal("in parse atom: ~p", [tokens(State)]),
    case current(State) of
      "["       -> parse_optional(move(State));
      "("       -> parse_required(move(State));
      "options" -> {options(State), move(State)};
      [$-,$-|_] -> parse_long(State);
      [$-|_]    -> parse_shorts(State);
      Current   ->
        case is_arg(Current) of
          true  ->
            ct:pal("returning argument: ~p, ~p", [Current, tokens(State)]),
            {[#argument{name=Current}], move(State)};
          false -> {[#command{name=Current}], move(State)}
        end
    end.
  
  parse_optional(State0) ->
    ct:pal("parse optional ~p", [State0]),
    {Expr, State} = parse_expr(State0),
    ct:pal("parse optional after parse_expr ~p\n~p", [Expr, State]),
    case current(State) of
      "]" -> {[#optional{children=Expr}], move(State)};
      _   -> throw("Unmatched '['")
    end.
  
  parse_required(State0) ->
    ct:pal("parse required ~p", [tokens(State0)]),
    {Expr, State} = parse_expr(State0),
    ct:pal("parse required after parse_expr ~p, ~p", [tokens(State), Expr]),
    case current(State) of
      ")" -> {[#required{children=Expr}], move(State)};
      Res -> throw({"Unmatched '(':", Res})
    end.
  
  is_arg(S) ->
    (hd(S) == $< andalso lists:last(S) == $>) orelse string:to_upper(S) == S.
  
  %% docopt_any_options_test_() ->
  %%   Doc = "Usage: prog [options] A
  
  %%     -q  Be quiet
  %%     -v  Be verbose.",
  %%   [ ?_assertEqual([{"A", "arg"}, {"-v", false}, {"-q", false}],
  %%                   docopt(Doc, "arg"))
  %%   , ?_assertEqual([{"A", "arg"}, {"-v", true}, {"-q", false}],
  %%                   docopt(Doc, "-v arg"))
  %%   , ?_assertEqual([{"A", "arg"}, {"-v", false}, {"-q", true}],
  %%                   docopt(Doc, "-q arg"))
  %%   ].
  
  option_parse(Str) ->
    {Options, Desc} = partition(string:strip(Str), "  "),
    ParsedOpt = lists:foldl(fun([$-,$-|_] = S, Opt) -> Opt#option{long     = S};
                               ([$-|_]    = S, Opt) -> Opt#option{short    = S};
                               (_            , Opt) -> Opt#option{argcount = 1}
                            end, #option{}, string:tokens(Options, ",= ")),
    case ParsedOpt of
      #option{argcount = 1} -> ParsedOpt#option{value = default_value(Desc)};
      ParsedOpt             -> ParsedOpt
    end.
  
  default_value(Desc) ->
    {ok, Re} = re:compile("\\[default: (.*)\\]", [caseless]),
    case re:run(Desc, Re, [{capture, [1], list}]) of
      {match, [DefaultValue]} -> DefaultValue;
      nomatch                 -> false
    end.
  
  option_name(#option{long=undefined, short=Short}) -> Short;
  option_name(#option{long=Long})                   -> Long.
  
  partition(Str, Delim) ->
    case string:str(Str, Delim) of
      0 -> {Str, ""};
      I ->
        Left  = string:substr(Str, 1, I - 1),
        Right = string:substr(Str, I + length(Delim)),
        {Left, Right}
    end.
  
  %%%_* Tests ===================================================================
  
  parse_atom_test_() ->
    O = [ #option{short="-h"}
        , #option{short="-v", long="--verbose"}
        , #option{short="-f", long="--file", argcount=1}
        ],
    St = fun(Tokens) ->
             #st{ options = O
                , tokens  = Tokens
                , mode    = parse_pattern}
         end,
    [ ?_assertEqual({[#argument{name="FOO"}], St([])},
                    parse_atom(St(["FOO"])))
    , ?_assertEqual({[#argument{name="<foo>"}], St([])},
                    parse_atom(St(["<foo>"])))
    , ?_assertEqual({[#command{name="foo"}], St([])},
                    parse_atom(St(["foo"])))
    , ?_assertEqual({O, St([])},
                    parse_atom(St(["options"])))
    , ?_assertEqual({[#option{short="-v", long="--verbose", value=true}], St([])},
                    parse_atom(St(["--verbose"])))
    , ?_assertEqual({[#option{short="-h", value=true}], St([])},
                    parse_atom(St(["-h"])))
    , ?_assertEqual({[#required{children=[#argument{name="FOO"}]}], St([])},
                    parse_atom(St(["(", "FOO", ")"])))
    ].
  
  printable_and_formal_usage_test_() ->
    Doc =
      "Usage: prog [-hv] ARG
              prog N M
  
       prog is a program.",
    [ ?_assertEqual("Usage: prog [-hv] ARG\n            prog N M",
                    printable_usage(Doc))
    , ?_assertEqual("( [-hv] ARG ) | ( N M )",
                    formal_usage(printable_usage(Doc)))
    , ?_assertEqual("uSaGe: prog ARG",
                    printable_usage("uSaGe: prog ARG\n\t \t\n bla"))
    ].
  
  parse_args_test_() ->
    HelpOpt    = #option{short="-h", value=true},
  