Example #1
0
        public static ParserResult <T> Build <T>(
            Func <T> factory,
            Func <IEnumerable <string>, IEnumerable <OptionSpecification>, StatePair <IEnumerable <Token> > > tokenizer,
            IEnumerable <string> arguments,
            StringComparer nameComparer,
            CultureInfo parsingCulture)
        {
            var instance = factory();

            if (arguments.Any() && nameComparer.Equals("--help", arguments.First()))
            {
                return(ParserResult.Create(
                           ParserResultType.Options,
                           instance,
                           new[] { new HelpRequestedError() }));
            }

            var specProps = instance.GetType().GetSpecifications(pi => SpecificationProperty.Create(
                                                                     Specification.FromProperty(pi), pi, Maybe.Nothing <object>()));

            var optionSpecs = (from pt in specProps select pt.Specification)
                              .ThrowingValidate(SpecificationGuards.Lookup)
                              .OfType <OptionSpecification>();

            var tokenizerResult = tokenizer(arguments, optionSpecs);

            var tokens = tokenizerResult.Value;

            var partitions = TokenPartitioner.Partition(
                tokens,
                name => TypeLookup.GetDescriptorInfo(name, optionSpecs, nameComparer));

            var optionSpecProps = OptionMapper.MapValues(
                (from pt in specProps where pt.Specification.IsOption() select pt),
                partitions.Item1,
                (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture),
                nameComparer);

            var valueSpecProps = ValueMapper.MapValues(
                (from pt in specProps where pt.Specification.IsValue() select pt),
                partitions.Item2,
                (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture));

            var missingValueErrors = from token in partitions.Item3
                                     select new MissingValueOptionError(
                NameInfo.FromOptionSpecification(optionSpecs.Single(o => token.Text.MatchName(o.ShortName, o.LongName, nameComparer))));

            var specPropsWithValue = optionSpecProps.Value.Concat(valueSpecProps.Value);

            instance = instance
                       .SetProperties(specPropsWithValue,
                                      sp => sp.Value.IsJust(),
                                      sp => sp.Value.FromJust())
                       .SetProperties(specPropsWithValue,
                                      sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
                                      sp => sp.Specification.DefaultValue.FromJust())
                       .SetProperties(specPropsWithValue,
                                      sp => sp.Value.IsNothing() &&
                                      sp.Specification.ConversionType.ToDescriptor() == DescriptorType.Sequence &&
                                      sp.Specification.DefaultValue.MatchNothing(),
                                      sp => sp.Property.PropertyType.GetGenericArguments().Single().CreateEmptyArray());

            var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup)
                                   .OfType <Just <Error> >().Select(e => e.Value);

            return(ParserResult.Create(
                       ParserResultType.Options,
                       instance,
                       tokenizerResult.Errors
                       .Concat(missingValueErrors)
                       .Concat(optionSpecProps.Errors)
                       .Concat(valueSpecProps.Errors)
                       .Concat(validationErrors)));
        }
Example #2
0
        public static ParserResult <T> Build <T>(
            Type typeInfo,
            Maybe <Func <T> > factory,
            Func <IEnumerable <string>, IEnumerable <OptionSpecification>, Result <IEnumerable <Token>, Error> > tokenizer,
            IEnumerable <string> arguments,
            StringComparer nameComparer,
            bool ignoreValueCase,
            CultureInfo parsingCulture,
            IEnumerable <ErrorType> nonFatalErrors)
        {
            var isMutable = typeInfo.IsMutable();

            if (!isMutable && factory.IsJust())
            {
                throw new ArgumentException("Cannot use factor for immutable types.", "factory");
            }

            var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create(
                                                           Specification.FromProperty(pi), pi, Maybe.Nothing <object>())).Memorize();

            var specs = from pt in specProps select pt.Specification;

            var optionSpecs = specs
                              .ThrowingValidate(SpecificationGuards.Lookup)
                              .OfType <OptionSpecification>();

            Func <IEnumerable <Error>, ParserResult <T> > notParsed =
                errs => new NotParsed <T>(typeInfo.ToTypeInfo(), errs);

            Func <ParserResult <T> > buildUp = () =>
            {
                var tokenizerResult = tokenizer(arguments, optionSpecs);

                var tokens = tokenizerResult.SucceededWith();

                var partitions = TokenPartitioner.Partition(
                    tokens,
                    name => TypeLookup.FindTypeDescriptorAndSibling(name, optionSpecs, nameComparer));
                var optionsPartition = partitions.Item1;
                var valuesPartition  = partitions.Item2;
                var errorsPartition  = partitions.Item3;

                var optionSpecPropsResult =
                    OptionMapper.MapValues(
                        (from pt in specProps where pt.Specification.IsOption() select pt),
                        optionsPartition,
                        (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase),
                        nameComparer);

                var valueSpecPropsResult =
                    ValueMapper.MapValues(
                        (from pt in specProps where pt.Specification.IsValue() orderby((ValueSpecification)pt.Specification).Index select pt),
                        valuesPartition,
                        (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));

                var missingValueErrors = from token in errorsPartition
                                         select
                                         new MissingValueOptionError(
                    optionSpecs.Single(o => token.Text.MatchName(o.ShortName, o.LongName, nameComparer))
                    .FromOptionSpecification());

                var specPropsWithValue =
                    optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memorize();

                Func <T> buildMutable = () =>
                {
                    var mutable = factory.MapValueOrDefault(f => f(), (T)Activator.CreateInstance(typeInfo));
                    mutable =
                        mutable.SetProperties(specPropsWithValue, sp => sp.Value.IsJust(), sp => sp.Value.FromJustOrFail())
                        .SetProperties(
                            specPropsWithValue,
                            sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
                            sp => sp.Specification.DefaultValue.FromJustOrFail())
                        .SetProperties(
                            specPropsWithValue,
                            sp =>
                            sp.Value.IsNothing() && sp.Specification.TargetType == TargetType.Sequence &&
                            sp.Specification.DefaultValue.MatchNothing(),
                            sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray());
                    return(mutable);
                };

                Func <T> buildImmutable = () =>
                {
                    var ctor   = typeInfo.GetTypeInfo().GetConstructor((from sp in specProps select sp.Property.PropertyType).ToArray());
                    var values = (from prms in ctor.GetParameters()
                                  join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower()
                                  select
                                  sp.Value.GetValueOrDefault(
                                      sp.Specification.DefaultValue.GetValueOrDefault(
                                          sp.Specification.ConversionType.CreateDefaultForImmutable()))).ToArray();
                    var immutable = (T)ctor.Invoke(values);
                    return(immutable);
                };

                var instance = isMutable ? buildMutable() : buildImmutable();

                var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens));

                var allErrors =
                    tokenizerResult.SuccessfulMessages()
                    .Concat(missingValueErrors)
                    .Concat(optionSpecPropsResult.SuccessfulMessages())
                    .Concat(valueSpecPropsResult.SuccessfulMessages())
                    .Concat(validationErrors)
                    .Memorize();

                var warnings = from e in allErrors where nonFatalErrors.Contains(e.Tag) select e;

                return(allErrors.Except(warnings).ToParserResult(instance));
            };

            var preprocessorErrors = arguments.Any()
                ? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer))
                : Enumerable.Empty <Error>();

            var result = arguments.Any()
                ? preprocessorErrors.Any()
                    ? notParsed(preprocessorErrors)
                    : buildUp()
                : buildUp();

            return(result);
        }
        public static ParserResult <T> Build <T>(
            Maybe <Func <T> > factory,
            Func <IEnumerable <string>, IEnumerable <OptionSpecification>, Result <IEnumerable <Token>, Error> > tokenizer,
            IEnumerable <string> arguments,
            StringComparer nameComparer,
            bool ignoreValueCase,
            CultureInfo parsingCulture,
            bool autoHelp,
            bool autoVersion,
            IEnumerable <ErrorType> nonFatalErrors)
        {
            var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));

            var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create(
                                                           Specification.FromProperty(pi), pi, Maybe.Nothing <object>()))
                            .Memoize();

            var specs = from pt in specProps select pt.Specification;

            var optionSpecs = specs
                              .ThrowingValidate(SpecificationGuards.Lookup)
                              .OfType <OptionSpecification>()
                              .Memoize();

            Func <T> makeDefault = () =>
                                   typeof(T).IsMutable()
                    ? factory.MapValueOrDefault(f => f(), () => Activator.CreateInstance <T>())
                    : ReflectionHelper.CreateDefaultImmutableInstance <T>(
                (from p in specProps select p.Specification.ConversionType).ToArray());

            List <string> tempList = new List <string>(arguments);

            if (tempList.Count > 0)
            {
                if (tempList[0].StartsWith("-") || tempList[0].StartsWith("--"))//只处理第一个参数是-开头的
                {
                    tempList = new List <string>();
                    string name = string.Empty;
                    foreach (var item in arguments)
                    {
                        if (item.StartsWith("-") || item.StartsWith("--"))
                        {
                            if (!string.IsNullOrEmpty(name))
                            {
                                //上一次也是option
                                var tt = optionSpecs.Where(c => string.Format("-{0}", c.ShortName) == name || string.Format("--{0}", c.LongName) == name);
                                if (tt != null || tt.Count() > 0)
                                {
                                    tempList.Add(string.Empty);
                                }
                            }
                            name = item;
                        }
                        else
                        {
                            name = string.Empty;
                        }
                        tempList.Add(item);
                    }

                    if (!string.IsNullOrEmpty(name))
                    {
                        //上一次也是option
                        var tt = optionSpecs.Where(c => string.Format("-{0}", c.ShortName) == name || string.Format("--{0}", c.LongName) == name);
                        if (tt != null || tt.Count() > 0)
                        {
                            tempList.Add(string.Empty);
                        }
                    }
                }
                arguments = tempList;
            }

            Func <IEnumerable <Error>, ParserResult <T> > notParsed =
                errs => new NotParsed <T>(makeDefault().GetType().ToTypeInfo(), errs);

            var argumentsList = arguments.Memoize();
            Func <ParserResult <T> > buildUp = () =>
            {
                var tokenizerResult = tokenizer(argumentsList, optionSpecs);

                var tokens = tokenizerResult.SucceededWith().Memoize();

                var partitions = TokenPartitioner.Partition(
                    tokens,
                    name => TypeLookup.FindTypeDescriptorAndSibling(name, optionSpecs, nameComparer));
                var optionsPartition = partitions.Item1.Memoize();
                var valuesPartition  = partitions.Item2.Memoize();
                var errorsPartition  = partitions.Item3.Memoize();

                var optionSpecPropsResult =
                    OptionMapper.MapValues(
                        (from pt in specProps where pt.Specification.IsOption() select pt),
                        optionsPartition,
                        (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase),
                        nameComparer);

                var valueSpecPropsResult =
                    ValueMapper.MapValues(
                        (from pt in specProps where pt.Specification.IsValue() orderby((ValueSpecification)pt.Specification).Index select pt),
                        valuesPartition,
                        (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));

                var missingValueErrors = from token in errorsPartition
                                         select
                                         new MissingValueOptionError(
                    optionSpecs.Single(o => token.Text.MatchName(o.ShortName, o.LongName, nameComparer))
                    .FromOptionSpecification());

                var specPropsWithValue =
                    optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memoize();

                var setPropertyErrors = new List <Error>();

                //build the instance, determining if the type is mutable or not.
                T instance;
                if (typeInfo.IsMutable() == true)
                {
                    instance = BuildMutable(factory, specPropsWithValue, setPropertyErrors);
                }
                else
                {
                    instance = BuildImmutable(typeInfo, factory, specProps, specPropsWithValue, setPropertyErrors);
                }

                var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens));

                var allErrors =
                    tokenizerResult.SuccessMessages()
                    .Concat(missingValueErrors)
                    .Concat(optionSpecPropsResult.SuccessMessages())
                    .Concat(valueSpecPropsResult.SuccessMessages())
                    .Concat(validationErrors)
                    .Concat(setPropertyErrors)
                    .Memoize();

                var warnings = from e in allErrors where nonFatalErrors.Contains(e.Tag) select e;

                return(allErrors.Except(warnings).ToParserResult(instance));
            };

            var preprocessorErrors = (
                argumentsList.Any()
                    ? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion))
                    : Enumerable.Empty <Error>()
                ).Memoize();

            var result = argumentsList.Any()
                ? preprocessorErrors.Any()
                    ? notParsed(preprocessorErrors)
                    : buildUp()
                : buildUp();

            return(result);
        }
Example #4
0
        public static ParserResult <T> Build <T>(
            Maybe <Func <T> > factory,
            Func <IEnumerable <string>, IEnumerable <OptionSpecification>, StatePair <IEnumerable <Token> > > tokenizer,
            IEnumerable <string> arguments,
            StringComparer nameComparer,
            CultureInfo parsingCulture)
        {
            var typeInfo = factory.Return(f => f().GetType(), typeof(T));

            var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create(
                                                           Specification.FromProperty(pi), pi, Maybe.Nothing <object>()));

            var specs = from pt in specProps select pt.Specification;

            var optionSpecs = specs
                              .ThrowingValidate(SpecificationGuards.Lookup)
                              .OfType <OptionSpecification>();

            Func <T> makeDefault = () =>
                                   typeof(T).IsMutable()
                    ? factory.Return(f => f(), Activator.CreateInstance <T>())
                    : ReflectionHelper.CreateDefaultImmutableInstance <T>(
                (from p in specProps select p.Specification.ConversionType).ToArray());

            if (arguments.Any())
            {
                var preprocessorErrors = arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer));
                if (preprocessorErrors.Any())
                {
                    return(new NotParsed <T>(makeDefault(), preprocessorErrors));
                }
            }

            var tokenizerResult = tokenizer(arguments, optionSpecs);

            var tokens = tokenizerResult.Value;

            var partitions = TokenPartitioner.Partition(
                tokens,
                name => TypeLookup.FindTypeDescriptorAndSibling(name, optionSpecs, nameComparer));

            var optionSpecProps = OptionMapper.MapValues(
                (from pt in specProps where pt.Specification.IsOption() select pt),
                partitions.Options,
                (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture),
                nameComparer);

            var valueSpecProps = ValueMapper.MapValues(
                (from pt in specProps where pt.Specification.IsValue() select pt),
                partitions.Values,
                (vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture));

            var missingValueErrors = from token in partitions.Errors
                                     select new MissingValueOptionError(
                optionSpecs.Single(o => token.Text.MatchName(o.ShortName, o.LongName, nameComparer)).FromOptionSpecification());

            var specPropsWithValue = optionSpecProps.Value.Concat(valueSpecProps.Value);

            T instance;

            if (typeInfo.IsMutable())
            {
                instance = factory.Return(f => f(), Activator.CreateInstance <T>());
                instance = instance
                           .SetProperties(specPropsWithValue,
                                          sp => sp.Value.IsJust(),
                                          sp => sp.Value.FromJust())
                           .SetProperties(specPropsWithValue,
                                          sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
                                          sp => sp.Specification.DefaultValue.FromJust())
                           .SetProperties(specPropsWithValue,
                                          sp => sp.Value.IsNothing() &&
                                          sp.Specification.TargetType == TargetType.Sequence &&
                                          sp.Specification.DefaultValue.MatchNothing(),
                                          sp => sp.Property.PropertyType.GetGenericArguments().Single().CreateEmptyArray());
            }
            else
            {
                var ctor   = typeInfo.GetConstructor((from sp in specProps select sp.Property.PropertyType).ToArray());
                var values = (from prms in ctor.GetParameters()
                              join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower()
                              select sp.Value.Return(v => v,
                                                     sp.Specification.DefaultValue.Return(d => d,
                                                                                          sp.Specification.ConversionType.CreateDefaultForImmutable()))).ToArray();
                instance = (T)ctor.Invoke(values);
            }

            var validationErrors = specPropsWithValue.Validate(
                SpecificationPropertyRules.Lookup(tokens));

            var allErrors = tokenizerResult.Errors.Concat(missingValueErrors)
                            .Concat(optionSpecProps.Errors)
                            .Concat(valueSpecProps.Errors)
                            .Concat(validationErrors);

            if (allErrors.Any())
            {
                return(new NotParsed <T>(instance, allErrors));
            }
            return(new Parsed <T>(instance));
        }