Ejemplo n.º 1
0
        /// <summary>
        /// Entry point for `perlang`, with the possibility to override the console streams (stdin, stdout, stderr).
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        /// <param name="console">A custom `IConsole` implementation to use. May be null, in which case the standard
        /// streams of the calling process will be used.</param>
        /// <returns>Zero if the program executed successfully; non-zero otherwise.</returns>
        public static int MainWithCustomConsole(string[] args, IConsole console)
        {
            var versionOption         = new Option(new[] { "--version", "-v" }, "Show version information");
            var detailedVersionOption = new Option("-V", "Show detailed version information");
            var evalOption            = new Option <string>("-e", "Executes a single-line script")
            {
                AllowMultipleArgumentsPerToken = false, ArgumentHelpName = "script"
            };
            var printOption = new Option <string>("-p", "Parse a single-line script and output a human-readable version of the AST")
            {
                ArgumentHelpName = "script"
            };
            var noWarnAsErrorOption = new Option <string>("-Wno-error", "Treats specified warning as a warning instead of an error.")
            {
                ArgumentHelpName = "error"
            };

            var disabledWarningsAsErrorsList = new List <WarningType>();

            noWarnAsErrorOption.AddValidator(result =>
            {
                string warningName = result.GetValueOrDefault <string>();

                if (!WarningType.KnownWarning(warningName))
                {
                    return($"Unknown warning: {warningName}");
                }

                disabledWarningsAsErrorsList.Add(WarningType.Get(warningName));

                return(null);
            });

            // Note: options must be present in this list to be valid for the RootCommand.
            var options = new[]
            {
                versionOption,
                detailedVersionOption,
                evalOption,
                printOption,
                noWarnAsErrorOption
            };

            var scriptNameArgument = new Argument <string>
            {
                Name  = "script-name",
                Arity = ArgumentArity.ZeroOrOne,
            };

            scriptNameArgument.AddValidator(result =>
            {
                var tokens = result.Parent !.Tokens;

                if (tokens.Any(t => t.Type == System.CommandLine.Parsing.TokenType.Option && t.Value == evalOption.Name))
                {
                    return("<script-name> positional argument cannot be used together with the -e option");
                }

                return(null);
            });

            var rootCommand = new RootCommand
            {
                Description = "The Perlang Interpreter",

                Handler = CommandHandler.Create((ParseResult parseResult, IConsole console) =>
                {
                    if (parseResult.HasOption(versionOption))
                    {
                        console.Out.WriteLine(CommonConstants.InformationalVersion);
                        return(Task.FromResult(0));
                    }

                    if (parseResult.HasOption(detailedVersionOption))
                    {
                        console.Out.WriteLine($"Perlang {CommonConstants.InformationalVersion} (built from git commit {CommonConstants.GitCommit}) on .NET {Environment.Version}");
                        console.Out.WriteLine();
                        console.Out.WriteLine($"  Number of detected (v)CPUs: {Environment.ProcessorCount}");
                        console.Out.WriteLine($"  Running in 64-bit mode: {Environment.Is64BitProcess}");
                        console.Out.WriteLine($"  Operating system info: {Environment.OSVersion.VersionString}");
                        console.Out.WriteLine();

                        return(Task.FromResult(0));
                    }

                    if (parseResult.HasOption(evalOption))
                    {
                        // TODO: Workaround until we have a command-line-api package with https://github.com/dotnet/command-line-api/pull/1271 included.
                        OptionResult optionResult = parseResult.FindResultFor(evalOption);

                        string source = optionResult.Children
                                        .Where(c => c.Symbol.Name == evalOption.ArgumentHelpName)
                                        .Cast <ArgumentResult>()
                                        .First()
                                        .GetValueOrDefault <string>();

                        var program = new Program(
                            replMode: true,
                            standardOutputHandler: console.Out.WriteLine,
                            disabledWarningsAsErrors: disabledWarningsAsErrorsList
                            );

                        int result = program.Run(source, program.CompilerWarning);

                        return(Task.FromResult(result));
                    }
                    else if (parseResult.HasOption(printOption))
                    {
                        // TODO: Workaround until we have a command-line-api package with https://github.com/dotnet/command-line-api/pull/1271 included.
                        OptionResult optionResult = parseResult.FindResultFor(printOption);

                        string source = optionResult.Children
                                        .Where(c => c.Symbol.Name == evalOption.ArgumentHelpName)
                                        .Cast <ArgumentResult>()
                                        .First()
                                        .GetValueOrDefault <string>();

                        new Program(
                            replMode: true,
                            standardOutputHandler: console.Out.WriteLine,
                            disabledWarningsAsErrors: disabledWarningsAsErrorsList
                            ).ParseAndPrint(source);

                        return(Task.FromResult(0));
                    }
                    else if (parseResult.Tokens.Count == 0)
                    {
                        new Program(
                            replMode: true,
                            standardOutputHandler: console.Out.WriteLine,
                            disabledWarningsAsErrors: disabledWarningsAsErrorsList
                            ).RunPrompt();

                        return(Task.FromResult(0));
                    }
                    else
                    {
                        string scriptName = parseResult.ValueForArgument(scriptNameArgument);
                        int result;

                        if (parseResult.Tokens.Count == 1)
                        {
                            var program = new Program(
                                replMode: false,
                                standardOutputHandler: console.Out.WriteLine,
                                disabledWarningsAsErrors: disabledWarningsAsErrorsList
                                );

                            result = program.RunFile(scriptName);
                        }
                        else
                        {
                            // The first token available in RootCommandResult.Tokens at this point is the script name.
                            // All remaining arguments are passed to the program, which can use methods on the ARGV
                            // object to retrieve them.
                            var remainingArguments = parseResult.RootCommandResult.Tokens.Skip(1)
                                                     .Take(parseResult.Tokens.Count - 1)
                                                     .Select(r => r.Value);

                            var program = new Program(
                                replMode: false,
                                arguments: remainingArguments,
                                standardOutputHandler: console.Out.WriteLine,
                                disabledWarningsAsErrors: disabledWarningsAsErrorsList
                                );

                            result = program.RunFile(scriptName);
                        }

                        return(Task.FromResult(result));
                    }
                })
            };

            rootCommand.AddArgument(scriptNameArgument);

            rootCommand.AddValidator(result =>
            {
                if (result.HasOption(evalOption) && result.HasOption(printOption))
                {
                    return("Error: the -e and -p options are mutually exclusive");
                }

                if (result.HasOption(evalOption) && result.HasArgument(scriptNameArgument))
                {
                    return("Error: the -e option cannot be combined with the <script-name> argument");
                }

                return(null);
            });

            var scriptArguments = new Argument <string>
            {
                Name  = "args",
                Arity = ArgumentArity.ZeroOrMore
            };

            rootCommand.AddArgument(scriptArguments);

            foreach (Option option in options)
            {
                rootCommand.AddOption(option);
            }

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .Build()
                   .Invoke(args, console));
        }