void CheckAllFiles(GUICallback callback = null) { // The shared context for all script analysis. var analysisContext = new Yarn.Analysis.Context(); // We shouldn't try to perform program analysis if // any of the files fails to compile, because that // analysis would be performed on incomplete data. bool shouldPerformAnalysis = true; // How many files have we finished checking? int complete = 0; // Let's get started! // First, ensure that we're looking at all of the scripts. UpdateYarnScriptList(); // Next, compile each one. foreach (var result in checkResults) { // Attempt to compile the file. Record any compiler messages. CheckerResult.State state; var messages = ValidateFile(result.script, analysisContext, out state); result.state = state; result.messages = messages; // Don't perform whole-program analysis if any file failed to compile if (result.state != CheckerResult.State.Passed) { shouldPerformAnalysis = false; } // We're done with it; if we have a callback to call after // each file is validated, do so. complete++; if (callback != null) { callback(complete, checkResults.Count); } } var results = new List <Yarn.Analysis.Diagnosis>(); if (shouldPerformAnalysis) { var scriptAnalyses = analysisContext.FinishAnalysis(); results.AddRange(scriptAnalyses); } var environmentAnalyses = AnalyseEnvironment(); results.AddRange(environmentAnalyses); diagnoses = results; }
public void TestAnalysis() { ICollection <Yarn.Analysis.Diagnosis> diagnoses; Yarn.Analysis.Context context; // this script has the following variables: // $foo is read from and written to // $bar is written to but never read // $bas is read from but never written to // this means that there should be two diagnosis results var script = "// testing\n<<set $foo to 1>><<set $bar to $foo>><<set $bar to $bas>>"; context = new Yarn.Analysis.Context(typeof(Yarn.Analysis.UnusedVariableChecker)); dialogue.LoadString(script); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); Assert.AreEqual(2, diagnoses.Count); dialogue.UnloadAll(); context = new Yarn.Analysis.Context(typeof(Yarn.Analysis.UnusedVariableChecker)); dialogue.LoadFile(Path.Combine(UnityDemoScriptsPath, "Ship.yarn.txt")); dialogue.LoadFile(Path.Combine(UnityDemoScriptsPath, "Sally.yarn.txt")); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); // This script should contain no unused variables Assert.IsEmpty(diagnoses); }
public void TestAnalysis() { ICollection <Yarn.Analysis.Diagnosis> diagnoses; Yarn.Analysis.Context context; // this script has the following variables: // $foo is read from and written to // $bar is written to but never read // $bas is read from but never written to // this means that there should be two diagnosis results var script = "// testing\n<<set $foo to 1>><<set $bar to $foo>><<set $bar to $bas>>"; context = new Yarn.Analysis.Context(); dialogue.LoadString(script); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); Assert.IsTrue(diagnoses.Count == 2); dialogue.UnloadAll(); context = new Yarn.Analysis.Context(); dialogue.LoadFile("../Unity/Assets/Yarn Spinner/Examples/Demo Assets/Space/Ship.json"); dialogue.LoadFile("../Unity/Assets/Yarn Spinner/Examples/Demo Assets/Space/Sally.json"); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); // This script should contain no unused variables Assert.IsEmpty(diagnoses); }
static int Verify(VerifyOptions options) { CheckFileList(options.files, ALLOWED_EXTENSIONS); Dialogue dialogue; try { dialogue = CreateDialogue(options, null); } #pragma warning disable CA1031 // Do not catch general exception types catch { return(1); } #pragma warning restore CA1031 // Do not catch general exception types if (options.compileAndExit) { var result = dialogue.GetByteCode(); Console.WriteLine(result); return(0); } var context = new Yarn.Analysis.Context(); dialogue.Analyse(context); foreach (var diagnosis in context.FinishAnalysis()) { switch (diagnosis.severity) { case Analysis.Diagnosis.Severity.Error: Error(diagnosis.ToString(showSeverity: false)); break; case Analysis.Diagnosis.Severity.Warning: Warn(diagnosis.ToString(showSeverity: false)); break; case Analysis.Diagnosis.Severity.Note: Note(diagnosis.ToString(showSeverity: false)); break; default: throw new ArgumentOutOfRangeException(); } } return(0); }
public void TestAnalysis() { ICollection <Yarn.Analysis.Diagnosis> diagnoses; Yarn.Analysis.Context context; // this script has the following variables: // $foo is read from and written to // $bar is written to but never read // this means that there should be one diagnosis result context = new Yarn.Analysis.Context(typeof(Yarn.Analysis.UnusedVariableChecker)); var path = Path.Combine(TestDataPath, "AnalysisTest.yarn"); CompilationJob compilationJob = CompilationJob.CreateFromFiles(path); compilationJob.Library = dialogue.Library; var result = Compiler.Compile(compilationJob); Assert.Empty(result.Diagnostics); stringTable = result.StringTable; dialogue.SetProgram(result.Program); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); Assert.Equal(1, diagnoses.Count); Assert.Contains("Variable $bar is assigned, but never read from", diagnoses.First().message); dialogue.UnloadAll(); context = new Yarn.Analysis.Context(typeof(Yarn.Analysis.UnusedVariableChecker)); result = Compiler.Compile(CompilationJob.CreateFromFiles(new[] { Path.Combine(SpaceDemoScriptsPath, "Ship.yarn"), Path.Combine(SpaceDemoScriptsPath, "Sally.yarn"), }, dialogue.Library)); Assert.Empty(result.Diagnostics); dialogue.SetProgram(result.Program); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); // This script should contain no unused variables Assert.Empty(diagnoses); }
public void TestAnalysis() { ICollection <Yarn.Analysis.Diagnosis> diagnoses; Yarn.Analysis.Context context; // this script has the following variables: // $foo is read from and written to // $bar is written to but never read // $bas is read from but never written to // this means that there should be two diagnosis results context = new Yarn.Analysis.Context(typeof(Yarn.Analysis.UnusedVariableChecker)); var path = Path.Combine(TestDataPath, "AnalysisTest.yarn"); Compiler.CompileFile(path, out var program, out stringTable); dialogue.SetProgram(program); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); Assert.Equal(2, diagnoses.Count); dialogue.UnloadAll(); context = new Yarn.Analysis.Context(typeof(Yarn.Analysis.UnusedVariableChecker)); string shipPath = Path.Combine(SpaceDemoScriptsPath, "Ship.yarn"); Compiler.CompileFile(shipPath, out var shipProgram, out var shipStringTable); string sallyPath = Path.Combine(SpaceDemoScriptsPath, "Sally.yarn"); Compiler.CompileFile(sallyPath, out var sallyProgram, out var sallyStringTable); stringTable = shipStringTable.Union(sallyStringTable).ToDictionary(k => k.Key, v => v.Value); var combinedProgram = Program.Combine(shipProgram, sallyProgram); dialogue.SetProgram(combinedProgram); dialogue.Analyse(context); diagnoses = new List <Yarn.Analysis.Diagnosis>(context.FinishAnalysis()); // This script should contain no unused variables Assert.Empty(diagnoses); }
// Finds all .JSON files, and validates them. void Validate() { UpdateJSONList(); var analysisContext = new Yarn.Analysis.Context(); bool shouldPerformAnalysis = true; foreach (var result in checkResults) { CheckerResult.State state; var messages = ValidateFile(result.script, analysisContext, out state); result.state = state; result.messages = messages; // Don't perform whole-program analysis if any file failed to compile if (result.state != CheckerResult.State.Passed) { shouldPerformAnalysis = false; } } var results = new List <Yarn.Analysis.Diagnosis>(); if (shouldPerformAnalysis) { results.AddRange(analysisContext.FinishAnalysis()); } results.AddRange(AnalyseEnvironment()); diagnoses = results; }
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; string outputFile = null; 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; } } // Handle 'output file' parameter if (arg.IndexOf("-f=") != -1) { var argArray = arg.Split('='); if (argArray.Length != 2) { ShowHelpAndExit(); } else { outputFile = 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 (outputFile != null) { var result = dialogue.GetByteCode(); System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(); using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputFile, false, utf8)) { file.Write(result); } return; } 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(); } } }
// Finds all .JSON files, and validates them. void Validate() { UpdateJSONList(); var analysisContext = new Yarn.Analysis.Context(); bool shouldPerformAnalysis = true; foreach (var result in checkResults) { CheckerResult.State state; var messages = ValidateFile(result.script, analysisContext, out state); result.state = state; result.messages = messages; // Don't perform whole-program analysis if any file failed to compile if (result.state != CheckerResult.State.Passed) { shouldPerformAnalysis = false; } } var results = new List<Yarn.Analysis.Diagnosis>(); if (shouldPerformAnalysis) results.AddRange(analysisContext.FinishAnalysis()); results.AddRange(AnalyseEnvironment()); diagnoses = results; }
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 void TestAnalysis() { ICollection<Yarn.Analysis.Diagnosis> diagnoses; Yarn.Analysis.Context context; // this script has the following variables: // $foo is read from and written to // $bar is written to but never read // $bas is read from but never written to // this means that there should be two diagnosis results var script = "// testing\n<<set $foo to 1>><<set $bar to $foo>><<set $bar to $bas>>"; context = new Yarn.Analysis.Context (); dialogue.LoadString (script); dialogue.Analyse (context); diagnoses = new List<Yarn.Analysis.Diagnosis>(context.FinishAnalysis ()); Assert.IsTrue (diagnoses.Count == 2); dialogue.UnloadAll (); context = new Yarn.Analysis.Context (); dialogue.LoadFile ("../Unity/Assets/Yarn Spinner/Examples/Demo Assets/Space/Ship.json"); dialogue.LoadFile ("../Unity/Assets/Yarn Spinner/Examples/Demo Assets/Space/Sally.json"); dialogue.Analyse (context); diagnoses = new List<Yarn.Analysis.Diagnosis>(context.FinishAnalysis ()); // This script should contain no unused variables Assert.IsEmpty (diagnoses); }