// Prepares a loader. 'implementation' is used for logging. public Loader(Dialogue dialogue) { if (dialogue == null) throw new ArgumentNullException ("dialogue"); this.dialogue = dialogue; }
public void Init() { if (System.IO.Path.GetFileName(Environment.CurrentDirectory) != "Tests") { if (TestContext.CurrentContext.TestDirectory == Environment.CurrentDirectory) { // Hop up to the folder that contains the Tests folder var topLevelPath = System.IO.Path.Combine(Environment.CurrentDirectory, "..", "..", ".."); Environment.CurrentDirectory = topLevelPath; } var newWorkingDir = System.IO.Path.Combine (Environment.CurrentDirectory, "Tests"); Environment.CurrentDirectory = newWorkingDir; } dialogue = new Yarn.Dialogue (storage); dialogue.LogDebugMessage = delegate(string message) { Console.WriteLine (message); }; dialogue.LogErrorMessage = delegate(string message) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine ("ERROR: " + message); Console.ResetColor (); if (errorsCauseFailures == true) Assert.Fail(); }; dialogue.library.RegisterFunction ("assert", -1, delegate(Yarn.Value[] parameters) { if (parameters[0].AsBool == false) { if( parameters.Length > 1 && parameters[1].AsBool ) { Assert.Fail ("Assertion failed: " + parameters[1].AsString); } else { Assert.Fail ("Assertion failed"); } } }); dialogue.library.RegisterFunction ("prepare_for_options", 2, delegate(Yarn.Value[] parameters) { nextExpectedOptionCount = (int)parameters[0].AsNumber; nextOptionToSelect = (int)parameters[1].AsNumber; }); dialogue.library.RegisterFunction ("expect_line", -1, delegate(Yarn.Value[] parameters) { nextExpectedLine = parameters[0].AsString; }); dialogue.library.RegisterFunction ("expect_command", -1, delegate(Yarn.Value[] parameters) { nextExpectedCommand = parameters[0].AsString; }); }
internal TreeRunner(Dialogue dialogue) { this.dialogue = dialogue; }
internal VirtualMachine(Dialogue d, Program p) { program = p; dialogue = d; state = new State (); }
public static void Main(string[] args) { if (args.Length == 0) { ShowHelpAndExit (); } bool showTokens = false; bool showParseTree = false; bool waitForLines = false; string onlyConsiderNode = null; bool showDebugging = false; int runTimes = 1; var inputFiles = new List<string> (); string startNode = Dialogue.DEFAULT_START; string[] allowedExtensions = {".node", ".json" }; var defaultVariables = new Dictionary<string,float> (); foreach (var arg in args) { // Handle 'start' parameter if (arg.IndexOf("-s=") != -1) { var startArray = arg.Split (new char[]{ '=' }); if (startArray.Length != 2) { ShowHelpAndExit (); } else { startNode = startArray [1]; continue; } } // Handle variable input if (arg.IndexOf("-v") != -1) { var variable = arg.Substring (2); var variableArray = variable.Split (new char[]{ '=' }); if (variableArray.Length != 2) { ShowHelpAndExit (); } else { var varName = "$" + variableArray [0]; var varValue = float.Parse (variableArray [1]); defaultVariables [varName] = varValue; continue; } } // Handle 'only this node' parameter if (arg.IndexOf("-o=") != -1) { var startArray = arg.Split (new char[]{ '=' }); if (startArray.Length != 2) { ShowHelpAndExit (); } else { onlyConsiderNode = startArray [1]; continue; } } // Handle 'run times' parameter if (arg.IndexOf("-r=") != -1) { var argArray = arg.Split ('='); if (argArray.Length != 2) { ShowHelpAndExit (); } else { runTimes = int.Parse (argArray [1]); continue; } } switch (arg) { case "-t": showTokens = true; showDebugging = true; break; case "-p": showParseTree = true; showDebugging = true; break; case "-w": waitForLines = true; break; case "-d": showDebugging = true; break; case "-h": ShowHelpAndExit (); break; default: // only allow one file if (inputFiles.Count > 0) { Console.Error.WriteLine ("Error: Too many files specified."); Environment.Exit (1); } var extension = System.IO.Path.GetExtension (arg); if (Array.IndexOf(allowedExtensions, extension) != -1) { inputFiles.Add (arg); } break; } } if (inputFiles.Count == 0) { Console.Error.WriteLine ("Error: No files specified."); Environment.Exit (1); } // Create the object that handles callbacks var impl = new ConsoleRunnerImplementation (waitForLines:waitForLines); // load the default variables we got on the command line foreach (var variable in defaultVariables) { impl.SetNumber (variable.Key, variable.Value); } // Load nodes var dialogue = new Dialogue(impl); // Add some methods for testing dialogue.library.RegisterFunction ("add_three_operands", 3, delegate(Value[] parameters) { var f1 = parameters[0].AsNumber; var f2 = parameters[1].AsNumber; var f3 = parameters[2].AsNumber; return f1+f2+f3; }); dialogue.library.RegisterFunction ("last_value", -1, delegate(Value[] parameters) { // return the last value return parameters[parameters.Length-1]; }); dialogue.library.RegisterFunction ("is_even", 1, delegate(Value[] parameters) { return (int)parameters[0].AsNumber % 2 == 0; }); // If debugging is enabled, log debug messages; otherwise, ignore them if (showDebugging) { dialogue.LogDebugMessage = delegate(string message) { Console.WriteLine ("Debug: " + message); }; } else { dialogue.LogDebugMessage = delegate(string message) {}; } dialogue.LogErrorMessage = delegate(string message) { Console.WriteLine ("ERROR: " + message); }; dialogue.LoadFile (inputFiles [0],showTokens, showParseTree, onlyConsiderNode); if (showTokens == false && showParseTree == false) { // Run the conversation for (int run = 0; run < runTimes; run++) { foreach (var step in dialogue.Run (startNode)) { // It can be one of three types: a line to show, options // to present to the user, or an internal command to run if (step is Dialogue.LineResult) { var lineResult = step as Dialogue.LineResult; impl.RunLine (lineResult.line); } else if (step is Dialogue.OptionSetResult) { var optionsResult = step as Dialogue.OptionSetResult; impl.RunOptions (optionsResult.options, optionsResult.setSelectedOptionDelegate); } else if (step is Dialogue.CommandResult) { var commandResult = step as Dialogue.CommandResult; impl.RunCommand (commandResult.command.text); } } impl.DialogueComplete (); } } }
static internal Dialogue CreateDialogue(ExecutionOptions options, ConsoleRunnerImplementation impl) { // Load nodes var dialogue = new Dialogue(impl); if (options.experimental) { Warn("Running YarnSpinner in experimental mode may have unexpected behaviour."); dialogue.experimentalMode = true; } // Add some methods for testing dialogue.library.RegisterFunction("add_three_operands", 3, delegate(Value[] parameters) { return(parameters[0] + parameters[1] + parameters[2]); }); dialogue.library.RegisterFunction("last_value", -1, delegate(Value[] parameters) { // return the last value return(parameters[parameters.Length - 1]); }); dialogue.library.RegisterFunction("is_even", 1, delegate(Value[] parameters) { return((int)parameters[0].AsNumber % 2 == 0); }); // Register the "assert" function, which stops execution if its parameter evaluates to false dialogue.library.RegisterFunction("assert", -1, delegate(Value[] parameters) { if (parameters[0].AsBool == false) { // TODO: Include file, node and line number if (parameters.Length > 1 && parameters[1].AsBool) { dialogue.LogErrorMessage("ASSERTION FAILED: " + parameters[1].AsString); } else { dialogue.LogErrorMessage("ASSERTION FAILED"); } Environment.Exit(1); } }); // Register a function to let test scripts register how many // options they expect to send dialogue.library.RegisterFunction("prepare_for_options", 2, delegate(Value[] parameters) { impl.numberOfExpectedOptions = (int)parameters[0].AsNumber; impl.autoSelectOptionNumber = (int)parameters[1].AsNumber; }); dialogue.library.RegisterFunction("expect_line", 1, delegate(Value[] parameters) { impl.expectedNextLine = parameters[0].AsString; }); dialogue.library.RegisterFunction("expect_command", 1, delegate(Value[] parameters) { impl.expectedNextCommand = parameters[0].AsString; }); // If debugging is enabled, log debug messages; otherwise, ignore them if (options.showDebuggingInfo) { dialogue.LogDebugMessage = delegate(string message) { Note(message); }; } else { dialogue.LogDebugMessage = delegate(string message) { }; } dialogue.LogErrorMessage = delegate(string message) { Warn("Yarn Error: " + message); }; foreach (var file in options.files) { try { dialogue.LoadFile(file, false, false, options.onlyConsiderNode); } catch (Yarn.TokeniserException e) { Warn(e.Message); } catch (Yarn.ParseException e) { Warn(e.Message); } } // Load string table if (options.stringTable != null) { var parsedTable = new Dictionary <string, string>(); using (var reader = new System.IO.StreamReader(options.stringTable)) { using (var csvReader = new CsvReader(reader)) { if (csvReader.ReadHeader() == false) { Error(string.Format("{0} is not a valid string table", options.stringTable)); } foreach (var row in csvReader.GetRecords <LocalisedLine>()) { parsedTable[row.LineCode] = row.LineText; } } } dialogue.AddStringTable(parsedTable); } return(dialogue); }
internal VirtualMachine(Dialogue d, Program p) { program = p; dialogue = d; state = new State(); }
public static void Main(string[] args) { if (args.Length == 0) { ShowHelpAndExit (); } bool showTokens = false; bool showParseTree = false; bool waitForLines = false; string onlyConsiderNode = null; bool showDebugging = false; int runTimes = 1; bool compileToBytecodeOnly = false; bool verifyOnly = false; bool autoSelectFirstOption = false; bool analyseOnly = false; var inputFiles = new List<string> (); string startNode = Dialogue.DEFAULT_START; string[] allowedExtensions = {".node", ".json" }; var defaultVariables = new Dictionary<string,float> (); foreach (var arg in args) { // Handle 'start' parameter if (arg.IndexOf("-s=") != -1) { var startArray = arg.Split (new char[]{ '=' }); if (startArray.Length != 2) { ShowHelpAndExit (); } else { startNode = startArray [1]; continue; } } // Handle variable input if (arg.IndexOf("-v") != -1) { var variable = arg.Substring (2); var variableArray = variable.Split (new char[]{ '=' }); if (variableArray.Length != 2) { ShowHelpAndExit (); } else { var varName = "$" + variableArray [0]; var varValue = float.Parse (variableArray [1]); defaultVariables [varName] = varValue; continue; } } // Handle 'only this node' parameter if (arg.IndexOf("-o=") != -1) { var startArray = arg.Split (new char[]{ '=' }); if (startArray.Length != 2) { ShowHelpAndExit (); } else { onlyConsiderNode = startArray [1]; continue; } } // Handle 'run times' parameter if (arg.IndexOf("-r=") != -1) { var argArray = arg.Split ('='); if (argArray.Length != 2) { ShowHelpAndExit (); } else { runTimes = int.Parse (argArray [1]); continue; } } switch (arg) { case "-V": verifyOnly = true; break; case "-t": showTokens = true; showDebugging = true; break; case "-p": showParseTree = true; showDebugging = true; break; case "-w": waitForLines = true; break; case "-d": showDebugging = true; break; case "-c": compileToBytecodeOnly = true; break; case "-1": autoSelectFirstOption = true; break; case "-h": ShowHelpAndExit (); break; case "-a": analyseOnly = true; break; default: // only allow one file if (inputFiles.Count > 0) { Console.Error.WriteLine ("Error: Too many files specified."); Environment.Exit (1); } var extension = System.IO.Path.GetExtension (arg); if (Array.IndexOf(allowedExtensions, extension) != -1) { inputFiles.Add (arg); } break; } } if (inputFiles.Count == 0) { Console.Error.WriteLine ("Error: No files specified."); Environment.Exit (1); } // Create the object that handles callbacks var impl = new ConsoleRunnerImplementation (waitForLines:waitForLines); // load the default variables we got on the command line foreach (var variable in defaultVariables) { impl.SetNumber (variable.Key, variable.Value); } // Load nodes var dialogue = new Dialogue(impl); // Add some methods for testing dialogue.library.RegisterFunction ("add_three_operands", 3, delegate(Value[] parameters) { return parameters[0]+parameters[1]+parameters[2]; }); dialogue.library.RegisterFunction ("last_value", -1, delegate(Value[] parameters) { // return the last value return parameters[parameters.Length-1]; }); dialogue.library.RegisterFunction ("is_even", 1, delegate(Value[] parameters) { return (int)parameters[0].AsNumber % 2 == 0; }); // Register the "assert" function, which stops execution if its parameter evaluates to false dialogue.library.RegisterFunction ("assert", -1, delegate(Value[] parameters) { if (parameters[0].AsBool == false) { // TODO: Include file, node and line number if( parameters.Length > 1 && parameters[1].AsBool ) { dialogue.LogErrorMessage ("ASSERTION FAILED: " + parameters[1].AsString); } else { dialogue.LogErrorMessage ("ASSERTION FAILED"); } Environment.Exit(1); } }); // Register a function to let test scripts register how many // options they expect to send dialogue.library.RegisterFunction ("prepare_for_options", 2, delegate(Value[] parameters) { impl.numberOfExpectedOptions = (int)parameters [0].AsNumber; impl.autoSelectOptionNumber = (int)parameters[1].AsNumber; }); dialogue.library.RegisterFunction ("expect_line", 1, delegate(Value[] parameters) { impl.expectedNextLine = parameters[0].AsString; }); dialogue.library.RegisterFunction ("expect_command", 1, delegate(Value[] parameters) { impl.expectedNextCommand = parameters[0].AsString; }); if (autoSelectFirstOption == true) { impl.autoSelectFirstOption = true; } // If debugging is enabled, log debug messages; otherwise, ignore them if (showDebugging) { dialogue.LogDebugMessage = delegate(string message) { Console.WriteLine ("Debug: " + message); }; } else { dialogue.LogDebugMessage = delegate(string message) {}; } dialogue.LogErrorMessage = delegate(string message) { Console.WriteLine ("ERROR: " + message); }; if (verifyOnly) { try { dialogue.LoadFile (inputFiles [0],showTokens, showParseTree, onlyConsiderNode); } catch (Exception e) { Console.WriteLine ("Error: " + e.Message); } return; } dialogue.LoadFile (inputFiles [0],showTokens, showParseTree, onlyConsiderNode); if (compileToBytecodeOnly) { var result = dialogue.GetByteCode (); Console.WriteLine (result); return; } if (analyseOnly) { var context = new Yarn.Analysis.Context (); dialogue.Analyse (context); foreach (var diagnosis in context.FinishAnalysis()) { Console.WriteLine (diagnosis.ToString(showSeverity:true)); } return; } // Only run the program when we're not emitting debug output of some kind var runProgram = showTokens == false && showParseTree == false && compileToBytecodeOnly == false && analyseOnly == false; if (runProgram) { // Run the conversation for (int run = 0; run < runTimes; run++) { foreach (var step in dialogue.Run (startNode)) { // It can be one of three types: a line to show, options // to present to the user, or an internal command to run if (step is Dialogue.LineResult) { var lineResult = step as Dialogue.LineResult; impl.RunLine (lineResult.line); } else if (step is Dialogue.OptionSetResult) { var optionsResult = step as Dialogue.OptionSetResult; impl.RunOptions (optionsResult.options, optionsResult.setSelectedOptionDelegate); } else if (step is Dialogue.CommandResult) { var commandResult = step as Dialogue.CommandResult; impl.RunCommand (commandResult.command.text); } } impl.DialogueComplete (); } } }
public YarnDialogTree(Yarn.Dialogue dialogue, string startNode) { this.dialogue = dialogue; this.startNode = startNode; }
static internal int AddLines(AddLabelsOptions options) { YarnSpinnerConsole.CheckFileList(options.files, YarnSpinnerConsole.ALLOWED_EXTENSIONS); var existingKeys = new List <string>(); foreach (var file in options.files) { NodeFormat fileFormat; try { fileFormat = Loader.GetFormatFromFileName(file); } catch (FormatException) { fileFormat = NodeFormat.Unknown; } switch (fileFormat) { case NodeFormat.JSON: break; case NodeFormat.Text: break; default: YarnSpinnerConsole.Warn(string.Format("Skipping file {0}, which is in unsupported format '{1}'", file, fileFormat)); continue; } Dialogue d = YarnSpinnerConsole.CreateDialogueForUtilities(); try { // First, we need to ensure that this file compiles. d.LoadFile(file); } catch { YarnSpinnerConsole.Warn(string.Format("Skipping file {0} due to compilation errors.", file)); continue; } Dictionary <string, LineInfo> linesWithNoTag = new Dictionary <string, LineInfo>(); // Filter the string info table to exclude lines that have a line code foreach (var entry in d.GetStringInfoTable()) { if (entry.Key.StartsWith("line:") == false) { linesWithNoTag[entry.Key] = entry.Value; } } if (linesWithNoTag.Count == 0) { var message = string.Format("{0} had no untagged lines. Either they're all tagged already, or it has no localisable text.", file); YarnSpinnerConsole.Note(message); continue; } // We also need the raw NodeInfo structures contained within this file. // These contain the source code to what we just compiled. Loader.NodeInfo[] nodeInfoList; var nodeFormat = Loader.GetFormatFromFileName(file); using (var reader = new System.IO.StreamReader(file)) { nodeInfoList = d.loader.GetNodesFromText(reader.ReadToEnd(), nodeFormat); } // Convert this list into an easier-to-index dictionary var lineInfo = new Dictionary <string, Loader.NodeInfo>(); foreach (var node in nodeInfoList) { lineInfo[node.title] = node; } // Make a list of line codes that we already know about. // This list will be updated as we tag lines, to prevent collisions. existingKeys.AddRange(lineInfo.Keys); bool anyNodesModified = false; // We now have a list of all strings that do not have a string tag. // Add a new tag to these lines. foreach (var line in linesWithNoTag) { // TODO: There's quite a bit of redundant work done here in each loop. // We're unzipping and re-combining the node for EACH line. Would be better // to do that only once. // Get the node that this line is in. var nodeInfo = lineInfo[line.Value.nodeName]; // Is this line contained within a rawText node? if (nodeInfo.tagsList.FindIndex(i => i == "rawText") != -1) { // We don't need to add a tag to it - genstrings will export // the whole thing for us. continue; } // If we have a tag to consider, and this node doesn't have that tag, // continue if (options.onlyUseTag != null) { if (nodeInfo.tagsList.FindIndex(i => i == options.onlyUseTag) == -1) { continue; } } // Split this node's source by newlines var lines = nodeInfo.body.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); // Get the original line var existingLine = lines[line.Value.lineNumber - 1]; // Generate a new tag for this line var newTag = GenerateString(existingKeys); // Remember that we've used this tag, to prevent it from being re-used existingKeys.Add(newTag); if (options.verbose) { YarnSpinnerConsole.Note(string.Format("Tagged line with ID \"{0}\" in node {1}: {2}", newTag, nodeInfo.title, existingLine)); } // Re-write the line. var newLine = string.Format("{0} #{1}", existingLine, newTag); lines[line.Value.lineNumber - 1] = newLine; // Pack this all back up into a single string nodeInfo.body = string.Join("\n", lines); // Put the updated node in the node collection. lineInfo[line.Value.nodeName] = nodeInfo; anyNodesModified = true; } // If we didn't end up changing anything, don't modify the file if (anyNodesModified == false) { continue; } // All the nodes have been updated; save this back to disk. // Are we doing a dry run? if (options.dryRun) { // Then bail out at this point, before we start // modifying files YarnSpinnerConsole.Note("Would have written to file " + file); continue; } var format = Loader.GetFormatFromFileName(file); switch (format) { case NodeFormat.JSON: break; case NodeFormat.Text: break; default: throw new ArgumentOutOfRangeException(); } // Convert the nodes into the correct format var savedData = FileFormatConverter.ConvertNodes(lineInfo.Values, fileFormat); // Write the file! using (var writer = new System.IO.StreamWriter(file)) { writer.Write(savedData); } } return(0); }
internal VirtualMachine(Dialogue d) { dialogue = d; state = new State(); }