// Returns the parsed command line. If ErrorDescription is non-null, then the parse failed. internal static CommandLineOptions ParseCommandLine(string[] args) { var result = new CommandLineOptions(); result.ParseCommandLineStrings(args); // Convert the language strings to language values. var languageTokenizer = new CommandlineTokenizer <Language>(Language.Ambiguous) .AddKeyword(Language.CSharp) .AddKeyword(Language.Cx, "cppcx") .AddKeyword(Language.Cx) .AddKeyword(Language.Cppwinrt) .AddKeyword(Language.Cppwinrt, "winrtcpp") .AddKeyword(Language.LottieYaml) .AddKeyword(Language.WinCompDgml, "dgml") .AddKeyword(Language.Stats); var languages = new List <Language>(); // Parse the language string. foreach (var languageString in result._languageStrings) { languageTokenizer.TryMatchKeyword(languageString, out var language); languages.Add(language); switch (language) { case Language.Unknown: result.ErrorDescription = $"Unrecognized language: {languageString}"; break; case Language.Ambiguous: result.ErrorDescription = $"Ambiguous language: {languageString}"; break; } } result.Languages = languages.Distinct().ToArray(); // Sort any additional interfaces and remove duplicates. var additionalInterfaces = result._additionalInterfaces.OrderBy(name => name).Distinct().ToArray(); result._additionalInterfaces.Clear(); result._additionalInterfaces.AddRange(additionalInterfaces); return(result); }
void ParseCommandLineStrings(string[] args) { // Define the keywords accepted on the command line. var tokenizer = new CommandlineTokenizer <Keyword>(ambiguousValue: Keyword.Ambiguous) .AddPrefixedKeyword(Keyword.AdditionalInterface) .AddPrefixedKeyword(Keyword.DisableCodeGenOptimizer) .AddPrefixedKeyword(Keyword.DisableTranslationOptimizer) .AddPrefixedKeyword(Keyword.GenerateColorBindings) .AddPrefixedKeyword(Keyword.GenerateDependencyObject) .AddPrefixedKeyword(Keyword.Help, "?") .AddPrefixedKeyword(Keyword.Help) .AddPrefixedKeyword(Keyword.InputFile) .AddPrefixedKeyword(Keyword.Interface) .AddPrefixedKeyword(Keyword.Language) .AddPrefixedKeyword(Keyword.MinimumUapVersion) .AddPrefixedKeyword(Keyword.Namespace) .AddPrefixedKeyword(Keyword.OutputFolder) .AddPrefixedKeyword(Keyword.Public) .AddPrefixedKeyword(Keyword.RootNamespace) .AddPrefixedKeyword(Keyword.Strict) .AddPrefixedKeyword(Keyword.TargetUapVersion) .AddPrefixedKeyword(Keyword.TestMode) .AddPrefixedKeyword(Keyword.WinUIVersion); // The last keyword recognized. This defines what the following parameter value is for, // or None if not expecting a parameter value. var previousKeyword = Keyword.None; foreach (var(keyword, arg) in tokenizer.Tokenize(args)) { var prev = previousKeyword; previousKeyword = Keyword.None; switch (prev) { case Keyword.None: // Expecting a keyword. switch (keyword) { case Keyword.Ambiguous: ErrorDescription = $"Ambiguous: \"{arg}\"."; return; case Keyword.None: ErrorDescription = $"Unexpected: \"{arg}\"."; return; case Keyword.GenerateColorBindings: GenerateColorBindings = true; break; case Keyword.GenerateDependencyObject: GenerateDependencyObject = true; break; case Keyword.Help: HelpRequested = true; return; case Keyword.Strict: StrictMode = true; break; case Keyword.TestMode: TestMode = true; break; case Keyword.DisableCodeGenOptimizer: DisableCodeGenOptimizer = true; break; case Keyword.DisableTranslationOptimizer: DisableTranslationOptimizer = true; break; case Keyword.Public: Public = true; break; // The following keywords require a parameter as the next token. case Keyword.AdditionalInterface: case Keyword.InputFile: case Keyword.Interface: case Keyword.Language: case Keyword.Namespace: case Keyword.OutputFolder: case Keyword.MinimumUapVersion: case Keyword.RootNamespace: case Keyword.TargetUapVersion: case Keyword.WinUIVersion: previousKeyword = keyword; break; default: // Should never get here. throw new InvalidOperationException(); } break; case Keyword.AdditionalInterface: _additionalInterfaces.Add(arg); previousKeyword = Keyword.None; break; case Keyword.InputFile: if (InputFile != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("Input"); return; } InputFile = arg; previousKeyword = Keyword.None; break; case Keyword.Language: _languageStrings.Add(arg); previousKeyword = Keyword.None; break; case Keyword.Namespace: if (Namespace != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("Namespace"); return; } Namespace = arg; previousKeyword = Keyword.None; break; case Keyword.OutputFolder: if (OutputFolder != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("Output folder"); return; } OutputFolder = arg; previousKeyword = Keyword.None; break; case Keyword.MinimumUapVersion: if (MinimumUapVersion != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("Minimum UAP version"); return; } { if (!uint.TryParse(arg, out var version)) { ErrorDescription = ArgumentMustBeAPositiveInteger("Minimum UAP version"); return; } MinimumUapVersion = version; } previousKeyword = Keyword.None; break; case Keyword.RootNamespace: if (RootNamespace != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("Output folder"); return; } RootNamespace = arg; previousKeyword = Keyword.None; break; case Keyword.TargetUapVersion: if (TargetUapVersion != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("Target UAP version"); return; } { if (!uint.TryParse(arg, out var version)) { ErrorDescription = ArgumentMustBeAPositiveInteger("Target UAP version"); return; } TargetUapVersion = version; } previousKeyword = Keyword.None; break; case Keyword.WinUIVersion: if (_winUIVersion != null) { ErrorDescription = ArgumentSpecifiedMoreThanOnce("WinUI version"); return; } { if (!Version.TryParse(arg, out var version)) { ErrorDescription = ArgumentMustBeAMajorAndMinorVerion("WinUI version"); return; } _winUIVersion = version; } previousKeyword = Keyword.None; break; default: // Should never get here. throw new InvalidOperationException(); } } // All tokens consumed. Ensure we are not waiting for the final parameter value. if (previousKeyword != Keyword.None) { ErrorDescription = "Missing value."; } }