private void AddStringSwitch(ref PathSplit psp, string value, string arg, ParseResult.Builder result)
        {
            CommandSwitch sw = FindSwitchIdentifiedBy(value);

            if (sw == null)
            {
                throw new InvalidInput("Invalid switch: " + value);
            }
            if (arg != null)
            {
                if (sw.arity != Arity.NoneOrOne)
                {
                    throw new InvalidInput($"Switch {value} has argument, which is not allowed for the given switch.");
                }
                result.AddSwitch(sw);

                if ((sw.arity == Arity.One || sw.arity == Arity.NoneOrOne) && result.AlreadyHasArgumentForLastSwitch())
                {
                    throw new InvalidInput($"Switch {value} can accept only one argument, but two are supplied.");
                }
                result.AddArgForLastSwitch(arg);
                psp = PathSplit.Maybe;
            }
            else
            {
                switch (sw.arity)
                {
                case Arity.None:                psp = PathSplit.Maybe; break;

                case Arity.One:                 psp = PathSplit.NotForOne; break;

                case Arity.NoneOrOne:   psp = PathSplit.Maybe; break;

                case Arity.Any:         psp = PathSplit.Not; break;

                default: throw new Termination("Should not reach this.");
                }
                result.AddSwitch(sw);
            }
        }
        /// <summary>
        /// Main parsing method
        /// </summary>
        /// <exception cref="Termination">Internal error</exception>
        /// <exception cref="InvalidInput">Input was not valid according to parsing rules</exception>
        public ParseResult Parse(string[] args)
        {
            var       result = new ParseResult.Builder();
            PathSplit psp    = PathSplit.Maybe;

            foreach (var arg in args)
            {
                switch (psp)
                {
                case PathSplit.Not:
                case PathSplit.Maybe: {
                    var(argType, value, swArg) = GetArgType(arg);

                    if (argType == ArgType.Separator)
                    {
                        psp = PathSplit.Yes;
                        break;
                    }
                    else if (argType == ArgType.SingleCharSwitch)
                    {
                        AddSingleCharSwitch(ref psp, value, swArg, result);
                        break;
                    }
                    else if (argType == ArgType.StringSwitch)
                    {
                        AddStringSwitch(ref psp, value, swArg, result);
                        break;
                    }
                    else if (argType == ArgType.Path)
                    {
                        if (psp == PathSplit.Not)
                        {
                            result.AddArgForLastSwitch(value);
                            // psp remains .Not.
                            break;
                        }
                        else if (psp == PathSplit.Maybe)
                        {
                            result.AddPath(value);
                            psp = PathSplit.Yes;
                            break;
                        }
                        throw new Termination("Should never reach this.");
                    }
                    throw new Termination("Should never reach this.");
                }

                case PathSplit.NotForOne: {
                    var(argType, value, swArg) = GetArgType(arg);

                    if (argType != ArgType.Path)
                    {
                        throw new InvalidInput("Expected path but got a switch at " + arg);
                    }
                    var lastSwitch = result.GetLastSwitch();
                    if (lastSwitch.arity == Arity.One && result.AlreadyHasArgumentForLastSwitch())
                    {
                        throw new InvalidInput($"Switch {value} can accept only one argument, but two are supplied.");
                    }

                    psp = PathSplit.Maybe;
                    result.AddArgForLastSwitch(arg);
                    break;
                }

                case PathSplit.Yes: {
                    result.AddPath(arg);
                    break;
                }

                default:  throw new Termination("Should never reach this.");
                }
            }
            return(result.Build());
        }
 private void AddSingleCharSwitch(ref PathSplit psp, string value, string arg, ParseResult.Builder result)
 {
     // Add all but last with null arg
     for (int i = 0; i < value.Length - 1; i++)
     {
         AddStringSwitch(ref psp, value[0].ToString(), null, result);
     }
     // Add last with actual arg
     AddStringSwitch(ref psp, value[value.Length - 1].ToString(), arg, result);
 }