From 7ad6f3a3d36c8ad16fe7751a2e3c012ef9a3c334 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Fri, 16 Aug 2024 14:39:13 -0700 Subject: [PATCH] Add a repl script handling framework --- .../CommandLine/ReplOptions.cs | 74 ++++++++++++++++++- MultiversalDiplomacy/Script/IScriptHandler.cs | 21 ++++++ .../Script/ReplScriptHandler.cs | 16 ++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 MultiversalDiplomacy/Script/IScriptHandler.cs create mode 100644 MultiversalDiplomacy/Script/ReplScriptHandler.cs diff --git a/MultiversalDiplomacy/CommandLine/ReplOptions.cs b/MultiversalDiplomacy/CommandLine/ReplOptions.cs index d675ff0..2ed1668 100644 --- a/MultiversalDiplomacy/CommandLine/ReplOptions.cs +++ b/MultiversalDiplomacy/CommandLine/ReplOptions.cs @@ -1,12 +1,84 @@ using CommandLine; +using MultiversalDiplomacy.Script; + namespace MultiversalDiplomacy.CommandLine; [Verb("repl", HelpText = "Begin an interactive 5dplomacy session.")] public class ReplOptions { + [Option('i', "input", HelpText = "Begin the repl session by executing the commands in this file.")] + public string? InputFile { get; set; } + + [Option('o', "output", HelpText = "Echo the repl session to this file. Specify a directory to autogenerate a filename.")] + public string? OutputFile { get; set; } + public static void Execute(ReplOptions args) { - throw new NotImplementedException(); + IEnumerable? inputFileLines = null; + if (args.InputFile is not null) { + var fullPath = Path.GetFullPath(args.InputFile); + inputFileLines = File.ReadAllLines(fullPath); + Console.WriteLine($"Reading from {fullPath}"); + } + + // Create a writer to the output file, if specified. + StreamWriter? outputWriter = null; + if (args.OutputFile is not null) + { + string fullPath = Path.GetFullPath(args.OutputFile); + string outputPath = Directory.Exists(fullPath) + ? Path.Combine(fullPath, $"{DateTime.UtcNow:yyyyMMddHHmmss}.log") + : fullPath; + Console.WriteLine($"Echoing to {outputPath}"); + outputWriter = File.CreateText(outputPath); + } + + IEnumerable GetInputs() + { + foreach (string line in inputFileLines ?? []) + { + var trimmed = line.Trim(); + // File inputs weren't echoed to the terminal so they need to be echoed here + Console.WriteLine($"{trimmed}"); + yield return trimmed; + } + + string? input; + do + { + input = Console.ReadLine(); + yield return input; + } + while (input is not null); + // The last null is returned because an EOF means we should quit the repl. + } + + IScriptHandler? handler = new ReplScriptHandler(); + + Console.Write(handler.Prompt); + foreach (string? nextInput in GetInputs()) + { + // Handle quitting directly. + if (nextInput is null || nextInput == "quit" || nextInput == "exit") + { + break; + } + string input = nextInput.Trim(); + outputWriter?.WriteLine(input); + outputWriter?.Flush(); + + // Delegate all other command parsing to the handler. + handler = handler.HandleInput(input); + + // Quit if the handler ends processing, otherwise prompt for the next command. + if (handler is null) + { + break; + } + Console.Write(handler.Prompt); + } + + Console.WriteLine("exiting"); } } diff --git a/MultiversalDiplomacy/Script/IScriptHandler.cs b/MultiversalDiplomacy/Script/IScriptHandler.cs new file mode 100644 index 0000000..20ef562 --- /dev/null +++ b/MultiversalDiplomacy/Script/IScriptHandler.cs @@ -0,0 +1,21 @@ +namespace MultiversalDiplomacy.Script; + +/// +/// A handler that interprets and executes 5dp script commands. Script handlers may create additional script handlers +/// and delegate handling to them, allowing a sort of recursive parsing of script commands. +/// +public interface IScriptHandler +{ + /// + /// When used interactively, the prompt that should be displayed. + /// + public string Prompt { get; } + + /// + /// Process a line of input. + /// + /// + /// The handler that should handle the next line of input, or null if script handling should end. + /// + public IScriptHandler? HandleInput(string input); +} diff --git a/MultiversalDiplomacy/Script/ReplScriptHandler.cs b/MultiversalDiplomacy/Script/ReplScriptHandler.cs new file mode 100644 index 0000000..c8ed795 --- /dev/null +++ b/MultiversalDiplomacy/Script/ReplScriptHandler.cs @@ -0,0 +1,16 @@ +namespace MultiversalDiplomacy.Script; + +/// +/// A script handler for the interactive repl. +/// +public class ReplScriptHandler : IScriptHandler +{ + public string Prompt => "5dp> "; + + public IScriptHandler? HandleInput(string input) + { + Console.WriteLine($"[{input}]"); + + return this; + } +}