public void PostParse(Parsed.Story parsedStory) { foreach (var plugin in _plugins) { plugin.PostParse(parsedStory); } }
public void PostExport(Parsed.Story parsedStory, Runtime.Story runtimeStory) { foreach (var plugin in _plugins) { plugin.PostExport(parsedStory, runtimeStory); } }
public override void ResolveReferences (Story context) { base.ResolveReferences (context); VariableAssignment varDecl = null; if (this.isNewTemporaryDeclaration && story.variableDeclarations.TryGetValue(variableName, out varDecl) ) { if (varDecl.isGlobalDeclaration) { Error ("global variable '" + variableName + "' already exists with the same name (declared on " + varDecl.debugMetadata + ")"); return; } } if (this.isGlobalDeclaration) { var variableReference = expression as VariableReference; if (variableReference && !variableReference.isConstantReference) { Error ("global variable assignments cannot refer to other variables, only literal values and constants"); } } if (IsReservedKeyword (variableName)) { Error ("cannot use '" + variableName + "' as a variable since it's a reserved ink keyword"); return; } if (!this.isNewTemporaryDeclaration) { if (!context.ResolveVariableWithName (this.variableName, fromNode:this).found) { if (story.constants.ContainsKey (variableName)) { Error ("Can't re-assign to a constant (do you need to use VAR when declaring '" + this.variableName + "'?)", this); } else { Error ("Variable could not be found to assign to: '" + this.variableName + "'", this); } } } }
protected object IncludeStatement() { Whitespace(); if (ParseString("INCLUDE") == null) { return(null); } Whitespace(); var filename = (string)Expect(() => ParseUntilCharactersFromString("\n\r"), "filename for include statement"); filename = filename.TrimEnd(' ', '\t'); var fullFilename = filename; if (_rootDirectory != null) { fullFilename = System.IO.Path.Combine(_rootDirectory, filename); } Parsed.Story includedStory = null; string includedString = null; try { includedString = System.IO.File.ReadAllText(fullFilename); } catch { string message = "Failed to load: " + filename; if (_rootDirectory != null) { message += "' (root directory is " + _rootDirectory + "). File not found perhaps?"; } Error(message); } if (includedString != null) { InkParser parser = new InkParser(includedString, filename, _rootDirectory); includedStory = parser.Parse(); if (includedStory == null) { // This error should never happen: if the includedStory isn't // returned, then it should've been due to some error that // has already been reported, so this is a last resort. if (!parser.hadError) { Error("Failed to parse included file '" + filename); } } } // Return valid IncludedFile object even when story failed to parse and we have a null story: // we don't want to attempt to re-parse the include line as something else return(new IncludedFile(includedStory)); }
public CommandLinePlayer(Story story, bool autoPlay = false, Parsed.Story parsedStory = null, bool keepOpenAfterStoryFinish = false) { this.story = story; this.autoPlay = autoPlay; this.parsedStory = parsedStory; this.keepOpenAfterStoryFinish = keepOpenAfterStoryFinish; _debugSourceRanges = new List <DebugSourceRange> (); }
public override void ResolveReferences (Story context) { var pathToReJoin = _reJoinTarget.path; foreach (var branch in branches) { branch.returnDivert.targetPath = pathToReJoin; } base.ResolveReferences (context); }
protected object IncludeStatement() { Whitespace(); if (ParseString("INCLUDE") == null) { return(null); } Whitespace(); var filename = (string)Expect(() => ParseUntilCharactersFromString("\n\r"), "filename for include statement"); filename = filename.TrimEnd(' ', '\t'); // Working directory should already have been set up relative to the root ink file. var workingDirectory = Directory.GetCurrentDirectory(); var fullFilename = System.IO.Path.Combine(workingDirectory, filename); if (FilenameIsAlreadyOpen(fullFilename)) { Error("Recursive INCLUDE detected: '" + fullFilename + "' is already open."); ParseUntilCharactersFromString("\r\n"); return(new IncludedFile(null)); } else { AddOpenFilename(fullFilename); } Parsed.Story includedStory = null; string includedString = null; try { includedString = File.ReadAllText(fullFilename); } catch { Error("Failed to load: '" + filename + "' (relative to directory: " + workingDirectory + ")"); } if (includedString != null) { InkParser parser = new InkParser(includedString, filename, _externalErrorHandler, _rootParser); includedStory = parser.Parse(); } RemoveOpenFilename(fullFilename); // Return valid IncludedFile object even if there were errors when parsing. // We don't want to attempt to re-parse the include line as something else, // and we want to include the bits that *are* valid, so we don't generate // more errors than necessary. return(new IncludedFile(includedStory)); }
public Parsed.Story PostParse(Parsed.Story parsedStory) { object[] args = new object[] { parsedStory }; foreach (var plugin in _plugins) { typeof(IPlugin).InvokeMember("PostParse", BindingFlags.InvokeMethod, null, plugin, args); } return((Parsed.Story)args[0]); }
public Runtime.Story PostExport(Parsed.Story parsedStory, Runtime.Story runtimeStory) { object[] args = new object[] { parsedStory, runtimeStory }; foreach (var plugin in _plugins) { typeof(IPlugin).InvokeMember("PostExport", BindingFlags.InvokeMethod, null, plugin, args); } return((Runtime.Story)args[1]); }
public override void ResolveReferences (Story context) { base.ResolveReferences (context); // Work is already done if it's a constant reference if (isConstantReference) { return; } // Is it a read count? var parsedPath = new Path (path); Parsed.Object targetForCount = parsedPath.ResolveFromContext (this); if (targetForCount) { targetForCount.containerForCounting.visitsShouldBeCounted = true; _runtimeVarRef.pathForCount = targetForCount.runtimePath; _runtimeVarRef.name = null; // Check for very specific writer error: getting read count and // printing it as content rather than as a piece of logic // e.g. Writing {myFunc} instead of {myFunc()} var targetFlow = targetForCount as FlowBase; if (targetFlow && targetFlow.isFunction) { // Is parent context content rather than logic? if ( parent is Weave || parent is ContentList || parent is FlowBase) { Warning ("'" + targetFlow.name + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()"); } } return; } // Definitely a read count, but wasn't found? else if (path.Count > 1) { Error ("Could not find target for read count: " + parsedPath); return; } if (!context.ResolveVariableWithName (this.name, fromNode: this).found) { Error("Unresolved variable: "+this.ToString(), this); } }
public override void ResolveReferences (Story context) { base.ResolveReferences (context); var parentStory = this.story; // Enforce rule that stitches must not have the same // name as any knots that exist in the story foreach (var stitchNamePair in subFlowsByName) { var stitchName = stitchNamePair.Key; var knotWithStitchName = parentStory.ContentWithNameAtLevel (stitchName, FlowLevel.Knot, false); if (knotWithStitchName) { var stitch = stitchNamePair.Value; var errorMsg = string.Format ("Stitch '{0}' has the same name as a knot (on {1})", stitch.name, knotWithStitchName.debugMetadata); Error(errorMsg, stitch); } } }
public void PostExport(Ink.Parsed.Story parsedStory, Ink.Runtime.Story runtimeStory) { var choiceJsonArray = new JArray(); var allChoices = parsedStory.FindAll <Choice>(); foreach (Ink.Parsed.Choice choice in allChoices) { var sb = new StringBuilder(); if (choice.startContent != null) { sb.Append(choice.startContent.ToString()); } if (choice.choiceOnlyContent) { sb.Append(choice.choiceOnlyContent.ToString()); } // Note that this choice text is an approximation since // it can be dynamically generated at runtime. We are therefore // making the assumption that the startContent and choiceOnlyContent // lists contain only string value content. var choiceTextApproximation = sb.ToString(); var filename = choice.debugMetadata.fileName; var jsonObj = new JObject(); jsonObj ["filename"] = filename; jsonObj ["choiceText"] = choiceTextApproximation.ToString(); choiceJsonArray.Add(jsonObj); } var jsonString = choiceJsonArray.ToString(); File.WriteAllText("choiceList.json", jsonString, System.Text.Encoding.UTF8); }
void CheckExternalArgumentValidity(Story context) { string externalName = target.firstComponent; ExternalDeclaration external = null; var found = context.externals.TryGetValue(externalName, out external); System.Diagnostics.Debug.Assert (found, "external not found"); int externalArgCount = external.argumentNames.Count; int ownArgCount = 0; if (arguments != null) { ownArgCount = arguments.Count; } if (ownArgCount != externalArgCount) { Error ("incorrect number of arguments sent to external function '" + externalName + "'. Expected " + externalArgCount + " but got " + ownArgCount); } }
public override void ResolveReferences(Story context) { base.ResolveReferences (context); foreach(var gatherPoint in gatherPointsToResolve) { gatherPoint.divert.targetPath = gatherPoint.targetRuntimeObj.path; } CheckForWeavePointNamingCollisions (); }
public override void ResolveReferences (Story context) { if (!context.ResolveVariableWithName (varName, fromNode:this).found) { Error ("variable for "+incrementDecrementWord+" could not be found: '"+varName+"' after searching: "+this.descriptionOfScope); } }
public override void ResolveReferences(Story context) { if (isToGather || isEnd || isDone) { return; } if (targetContent) { runtimeDivert.targetPath = targetContent.runtimePath; } // Resolve children (the arguments) base.ResolveReferences (context); // May be null if it's a built in function (e.g. TURNS_SINCE) // or if it's a variable target. var targetFlow = targetContent as FlowBase; if (targetFlow) { if (!targetFlow.isFunction && this.isFunctionCall) { base.Error (targetFlow.name + " hasn't been marked as a function, but it's being called as one. Do you need to delcare the knot as '== function " + targetFlow.name + " =='?"); } else if (targetFlow.isFunction && !this.isFunctionCall && !(this.parent is DivertTarget)) { base.Error (targetFlow.name + " can't be diverted to. It can only be called as a function since it's been marked as such: '" + targetFlow.name + "(...)'"); } } // Check validity of target content bool targetWasFound = targetContent != null; bool isBuiltIn = false; bool isExternal = false; if (target.numberOfComponents == 1 ) { // BuiltIn means TURNS_SINCE, CHOICE_COUNT, RANDOM or SEED_RANDOM isBuiltIn = FunctionCall.IsBuiltIn (target.firstComponent); // Client-bound function? isExternal = context.IsExternal (target.firstComponent); if (isBuiltIn || isExternal) { if (!isFunctionCall) { base.Error (target.firstComponent + " must be called as a function: ~ " + target.firstComponent + "()"); } if (isExternal) { runtimeDivert.isExternal = true; if( arguments != null ) runtimeDivert.externalArgs = arguments.Count; runtimeDivert.pushesToStack = false; runtimeDivert.targetPath = new Runtime.Path (this.target.firstComponent); CheckExternalArgumentValidity (context); } return; } } // Variable target? if (runtimeDivert.variableDivertName != null) { return; } if( !targetWasFound && !isBuiltIn && !isExternal ) Error ("target not found: '" + target + "'"); }
//public override Runtime.Object GenerateRuntimeObject () //{ // var initialValues = new Runtime.InkList (); // foreach (var itemDef in itemDefinitions) { // if (itemDef.inInitialList) { // var item = new Runtime.InkListItem (this.name, itemDef.name); // initialValues [item] = itemDef.seriesValue; // } // } // // Set origin name, so // initialValues.SetInitialOriginName (name); // return new Runtime.ListValue (initialValues); //} public override void ResolveReferences(Story context) { base.ResolveReferences(context); context.CheckForNamingCollisions(this, name, Story.SymbolType.List); }
public override void ResolveReferences(Story context) { base.ResolveReferences (context); foreach (var toResolve in _sequenceDivertsToResove) { toResolve.divert.targetPath = toResolve.targetContent.path; } }
CommandLineTool(string[] args) { if (ProcessArguments(args) == false) { ExitWithUsageInstructions(); } if (opts.testMode) { opts.inputFile = "test.ink"; } if (opts.inputFile == null) { ExitWithUsageInstructions(); } if (opts.outputFile == null) { opts.outputFile = Path.ChangeExtension(opts.inputFile, ".ink.json"); } string inputString = null; string rootDirectory = System.IO.Directory.GetCurrentDirectory(); if (opts.workingDirectory != null) { rootDirectory = Path.GetFullPath(opts.workingDirectory); } if (opts.stressTest) { StressTestContentGenerator stressTestContent = null; TimeOperation("Generating test content", () => { stressTestContent = new StressTestContentGenerator(100); }); Console.WriteLine("Generated ~{0}k of test ink", stressTestContent.sizeInKiloChars); inputString = stressTestContent.content; } else { try { string fullFilename = opts.inputFile; if (!Path.IsPathRooted(fullFilename)) { fullFilename = Path.Combine(rootDirectory, fullFilename); } inputString = File.ReadAllText(fullFilename); } catch { Console.WriteLine("Could not open file '" + opts.inputFile + "'"); Environment.Exit(ExitCodeError); } } InkParser parser = null; Parsed.Story parsedStory = null; Runtime.Story story = null; errors = new List <string> (); warnings = new List <string> (); authorMessages = new List <string> (); var pluginManager = new PluginManager(pluginNames); var inputIsJson = opts.inputFile.EndsWith(".json"); // Loading a normal ink file (as opposed to an already compiled json file) if (!inputIsJson) { TimeOperation("Creating parser", () => { parser = new InkParser(inputString, opts.inputFile, rootDirectory, OnError); }); TimeOperation("Parsing", () => { parsedStory = parser.Parse(); }); TimeOperation("PostParsePlugins", () => { pluginManager.PostParse(parsedStory); }); if (parsedStory != null) { if (opts.countAllVisits) { parsedStory.countAllVisits = true; } TimeOperation("Exporting runtime", () => { story = parsedStory.ExportRuntime(OnError); }); TimeOperation("PostParsePlugins", () => { pluginManager.PostExport(parsedStory, story); }); } } // Opening up a compiled json file for playing else { story = Runtime.Story.CreateWithJson(inputString); // No purpose for loading an already compiled file other than to play it opts.playMode = true; } PrintMessages(authorMessages, ConsoleColor.Green); PrintMessages(warnings, ConsoleColor.Blue); PrintMessages(errors, ConsoleColor.Red); if (story == null) { Environment.Exit(ExitCodeError); } // JSON round trip testing // if (opts.testMode) { // var jsonStr = story.ToJsonString (indented:true); // Console.WriteLine (jsonStr); // // Console.WriteLine ("---------------------------------------------------"); // // var reloadedStory = Runtime.Story.CreateWithJson (jsonStr); // var newJsonStr = reloadedStory.ToJsonString (indented: true); // Console.WriteLine (newJsonStr); // // story = reloadedStory; // } // Play mode // Test mode may use "-tp" in commmand line args to specify that // the test script is also played if (opts.playMode) { // Always allow ink external fallbacks story.allowExternalFunctionFallbacks = true; var player = new CommandLinePlayer(story, false, parsedStory); player.Begin(); } // Compile mode else { var jsonStr = story.ToJsonString(opts.indentedJson); try { File.WriteAllText(opts.outputFile, jsonStr, System.Text.Encoding.UTF8); } catch { Console.WriteLine("Could write to output file '" + opts.outputFile + "'"); Environment.Exit(ExitCodeError); } } }
public void PostParse(Ink.Parsed.Story parsedStory) { // Nothing }
protected object IncludeStatement() { Whitespace(); if (ParseString("INCLUDE") == null) { return(null); } Whitespace(); var filename = (string)Expect(() => ParseUntilCharactersFromString("\n\r"), "filename for include statement"); filename = filename.TrimEnd(' ', '\t'); // Working directory should already have been set up relative to the root ink file. var workingDirectory = Directory.GetCurrentDirectory(); var fullFilename = System.IO.Path.Combine(workingDirectory, filename); if (FilenameIsAlreadyOpen(fullFilename)) { Error("Recursive INCLUDE detected: '" + fullFilename + "' is already open."); ParseUntilCharactersFromString("\r\n"); return(new IncludedFile(null)); } else { AddOpenFilename(fullFilename); } Parsed.Story includedStory = null; string includedString = null; try { includedString = File.ReadAllText(fullFilename); } catch { Error("Failed to load: '" + filename + "' (relative to directory: " + workingDirectory + ")"); } if (includedString != null) { InkParser parser = new InkParser(includedString, filename, _externalErrorHandler, _rootParser); includedStory = parser.Parse(); if (includedStory == null) { // This error should never happen: if the includedStory isn't // returned, then it should've been due to some error that // has already been reported, so this is a last resort. if (!parser.hadError) { Error("Failed to parse included file '" + filename); } } } RemoveOpenFilename(fullFilename); // Return valid IncludedFile object even when story failed to parse and we have a null story: // we don't want to attempt to re-parse the include line as something else return(new IncludedFile(includedStory)); }
public override void ResolveReferences(Story context) { _conditionalDivert.targetPath = _contentContainer.path; base.ResolveReferences (context); }
public override void ResolveReferences(Story context) { if (isToGather || isEnd || isDone) { return; } if (targetContent) { runtimeDivert.targetPath = targetContent.runtimePath; } // Resolve children (the arguments) base.ResolveReferences(context); // May be null if it's a built in function (e.g. TURNS_SINCE) // or if it's a variable target. var targetFlow = targetContent as FlowBase; if (targetFlow) { if (!targetFlow.isFunction && this.isFunctionCall) { base.Error(targetFlow.name + " hasn't been marked as a function, but it's being called as one. Do you need to delcare the knot as '== function " + targetFlow.name + " =='?"); } else if (targetFlow.isFunction && !this.isFunctionCall && !(this.parent is DivertTarget)) { base.Error(targetFlow.name + " can't be diverted to. It can only be called as a function since it's been marked as such: '" + targetFlow.name + "(...)'"); } } // Check validity of target content bool targetWasFound = targetContent != null; bool isBuiltIn = false; bool isExternal = false; if (target.numberOfComponents == 1) { // BuiltIn means TURNS_SINCE or CHOICE_COUNT isBuiltIn = FunctionCall.IsValidName(target.firstComponent); // Client-bound function? isExternal = context.IsExternal(target.firstComponent); if (isBuiltIn || isExternal) { if (!isFunctionCall) { base.Error(target.firstComponent + " must be called as a function: ~ " + target.firstComponent + "()"); } if (isExternal) { runtimeDivert.isExternal = true; if (arguments != null) { runtimeDivert.externalArgs = arguments.Count; } runtimeDivert.pushesToStack = false; runtimeDivert.targetPath = new Runtime.Path(this.target.firstComponent); CheckExternalArgumentValidity(context); } return; } } // Variable target? if (runtimeDivert.variableDivertName != null) { return; } if (!targetWasFound && !isBuiltIn && !isExternal) { Error("target not found: '" + target + "'"); } }
public override void ResolveReferences(Story context) { _conditionalDivert.targetPath = _contentContainer.path; base.ResolveReferences(context); }
public override void ResolveReferences(Story context) { // Weave style choice - target own content container if (_innerContentContainer) { _runtimeChoice.pathOnChoice = _innerContentContainer.path; if (onceOnly) _innerContentContainer.visitsShouldBeCounted = true; } if( _divertToStartContentOuter ) _divertToStartContentOuter.targetPath = _startContentRuntimeContainer.path; if( _divertToStartContentInner ) _divertToStartContentInner.targetPath = _startContentRuntimeContainer.path; base.ResolveReferences (context); }
CommandLineTool(string[] args) { // Set console's output encoding to UTF-8 Console.OutputEncoding = System.Text.Encoding.UTF8; if (ProcessArguments(args) == false) { ExitWithUsageInstructions(); } if (opts.inputFile == null) { ExitWithUsageInstructions(); } string inputString = null; string workingDirectory = Directory.GetCurrentDirectory(); if (opts.outputFile == null) { opts.outputFile = Path.ChangeExtension(opts.inputFile, ".ink.json"); } if (!Path.IsPathRooted(opts.outputFile)) { opts.outputFile = Path.Combine(workingDirectory, opts.outputFile); } try { string fullFilename = opts.inputFile; if (!Path.IsPathRooted(fullFilename)) { fullFilename = Path.Combine(workingDirectory, fullFilename); } // Make the working directory the directory for the root ink file, // so that relative paths for INCLUDE files are correct. workingDirectory = Path.GetDirectoryName(fullFilename); Directory.SetCurrentDirectory(workingDirectory); // Now make the input file relative to the working directory, // but just getting the file's actual name. opts.inputFile = Path.GetFileName(fullFilename); inputString = File.ReadAllText(opts.inputFile); } catch { Console.WriteLine("Could not open file '" + opts.inputFile + "'"); Environment.Exit(ExitCodeError); } var inputIsJson = opts.inputFile.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase); if (inputIsJson && opts.stats) { Console.WriteLine("Cannot show stats for .json, only for .ink"); Environment.Exit(ExitCodeError); } Parsed.Story parsedStory = null; Runtime.Story story = null; Compiler compiler = null; // Loading a normal ink file (as opposed to an already compiled json file) if (!inputIsJson) { compiler = new Compiler(inputString, new Compiler.Options { sourceFilename = opts.inputFile, pluginNames = pluginNames, countAllVisits = opts.countAllVisits, errorHandler = OnError, fileHandler = this }); // Only want stats, don't need to code-gen if (opts.stats) { parsedStory = compiler.Parse(); // Print any errors PrintAllMessages(); // Generate stats, then print as JSON var stats = Ink.Stats.Generate(compiler.parsedStory); var writer = new Runtime.SimpleJson.Writer(); writer.WriteObjectStart(); writer.WriteProperty("words", stats.words); writer.WriteProperty("knots", stats.knots); writer.WriteProperty("stitches", stats.stitches); writer.WriteProperty("functions", stats.functions); writer.WriteProperty("choices", stats.choices); writer.WriteProperty("gathers", stats.gathers); writer.WriteProperty("diverts", stats.diverts); writer.WriteObjectEnd(); Console.WriteLine(writer.ToString()); return; } // Full compile else { story = compiler.Compile(); } } // Opening up a compiled json file for playing else { story = new Runtime.Story(inputString); // No purpose for loading an already compiled file other than to play it opts.playMode = true; } PrintAllMessages(); if (story == null || _errors.Count > 0) { Environment.Exit(ExitCodeError); } // Play mode if (opts.playMode) { _playing = true; // Always allow ink external fallbacks story.allowExternalFunctionFallbacks = true; var player = new CommandLinePlayer(story, false, compiler, opts.keepOpenAfterStoryFinish); //Capture a CTRL+C key combo so we can restore the console's foreground color back to normal when exiting Console.CancelKeyPress += OnExit; try { player.Begin(); } catch (Runtime.StoryException e) { if (e.Message.Contains("Missing function binding")) { OnError(e.Message, ErrorType.Error); PrintAllMessages(); } else { throw e; } } catch (System.Exception e) { string storyPath = "<END>"; var path = story.state.currentPathString; if (path != null) { storyPath = path.ToString(); } throw new System.Exception(e.Message + " (Internal story path: " + storyPath + ")", e); } } // Compile mode else { var jsonStr = story.ToJson(); try { File.WriteAllText(opts.outputFile, jsonStr, System.Text.Encoding.UTF8); } catch { Console.WriteLine("Could not write to output file '" + opts.outputFile + "'"); Environment.Exit(ExitCodeError); } } }
public CommandLinePlayer(Story story, bool autoPlay = false, Parsed.Story parsedStory = null) { this.story = story; this.autoPlay = autoPlay; this.parsedStory = parsedStory; }
CommandLineTool(string[] args) { // Set console's output encoding to UTF-8 Console.OutputEncoding = System.Text.Encoding.UTF8; if (ProcessArguments(args) == false) { ExitWithUsageInstructions(); } if (opts.testMode) { opts.inputFile = "test.ink"; } if (opts.inputFile == null) { ExitWithUsageInstructions(); } string inputString = null; string workingDirectory = Directory.GetCurrentDirectory(); if (opts.outputFile == null) { opts.outputFile = Path.ChangeExtension(opts.inputFile, ".ink.json"); } if (!Path.IsPathRooted(opts.outputFile)) { opts.outputFile = Path.Combine(workingDirectory, opts.outputFile); } if (opts.stressTest) { StressTestContentGenerator stressTestContent = null; TimeOperation("Generating test content", () => { stressTestContent = new StressTestContentGenerator(100); }); Console.WriteLine("Generated ~{0}k of test ink", stressTestContent.sizeInKiloChars); inputString = stressTestContent.content; } else { try { string fullFilename = opts.inputFile; if (!Path.IsPathRooted(fullFilename)) { fullFilename = Path.Combine(workingDirectory, fullFilename); } // Make the working directory the directory for the root ink file, // so that relative paths for INCLUDE files are correct. workingDirectory = Path.GetDirectoryName(fullFilename); Directory.SetCurrentDirectory(workingDirectory); // Now make the input file relative to the working directory, // but just getting the file's actual name. opts.inputFile = Path.GetFileName(fullFilename); inputString = File.ReadAllText(opts.inputFile); } catch { Console.WriteLine("Could not open file '" + opts.inputFile + "'"); Environment.Exit(ExitCodeError); } } InkParser parser = null; Parsed.Story parsedStory = null; Runtime.Story story = null; errors = new List <string> (); warnings = new List <string> (); authorMessages = new List <string> (); var pluginManager = new PluginManager(pluginNames); var inputIsJson = opts.inputFile.EndsWith(".json"); // Loading a normal ink file (as opposed to an already compiled json file) if (!inputIsJson) { TimeOperation("Creating parser", () => { parser = new InkParser(inputString, opts.inputFile, OnError); }); TimeOperation("Parsing", () => { parsedStory = parser.Parse(); }); TimeOperation("PostParsePlugins", () => { pluginManager.PostParse(parsedStory); }); if (parsedStory != null && errors.Count == 0) { if (opts.countAllVisits) { parsedStory.countAllVisits = true; } TimeOperation("Exporting runtime", () => { story = parsedStory.ExportRuntime(OnError); }); TimeOperation("PostParsePlugins", () => { pluginManager.PostExport(parsedStory, story); }); } } // Opening up a compiled json file for playing else { story = new Runtime.Story(inputString); // No purpose for loading an already compiled file other than to play it opts.playMode = true; } PrintAllMessages(); if (story == null || errors.Count > 0) { Environment.Exit(ExitCodeError); } // JSON round trip testing //if (opts.testMode) { // var jsonStr = story.ToJsonString (); // Console.WriteLine (jsonStr); // Console.WriteLine ("---------------------------------------------------"); // var reloadedStory = new Runtime.Story (jsonStr); // var newJsonStr = reloadedStory.ToJsonString (); // Console.WriteLine (newJsonStr); // story = reloadedStory; //} // Play mode // Test mode may use "-tp" in commmand line args to specify that // the test script is also played if (opts.playMode) { _playing = true; // Always allow ink external fallbacks story.allowExternalFunctionFallbacks = true; var player = new CommandLinePlayer(story, false, parsedStory, opts.keepOpenAfterStoryFinish); //Capture a CTRL+C key combo so we can restore the console's foreground color back to normal when exiting Console.CancelKeyPress += OnExit; try { player.Begin(); } catch (Runtime.StoryException e) { if (e.Message.Contains("Missing function binding")) { OnError(e.Message, ErrorType.Error); PrintAllMessages(); } else { throw e; } } catch (System.Exception e) { string storyPath = "<END>"; var path = story.state.currentPath; if (path != null) { storyPath = path.ToString(); } throw new System.Exception(e.Message + " (Internal story path: " + storyPath + ")", e); } } // Compile mode else { var jsonStr = story.ToJsonString(); try { File.WriteAllText(opts.outputFile, jsonStr, System.Text.Encoding.UTF8); } catch { Console.WriteLine("Could not write to output file '" + opts.outputFile + "'"); Environment.Exit(ExitCodeError); } } }
public override void ResolveReferences (Story context) { if (_finalLooseEndTarget) { var flowEndPath = _finalLooseEndTarget.path; foreach (var finalLooseEndDivert in _finalLooseEnds) { finalLooseEndDivert.targetPath = flowEndPath; } } if (_startingSubFlowDivert) { _startingSubFlowDivert.targetPath = _startingSubFlowRuntime.path; } base.ResolveReferences(context); // Check validity of parameter names if (arguments != null) { foreach (var arg in arguments) { // Don't allow reserved words for argument names if (VariableAssignment.IsReservedKeyword (arg.name)) { Error ("Argument '" + arg.name + "' is a reserved word, please choose another name"); continue; } // Does argument conflict with a knot/stitch/label? var pathOfTheoreticalTarget = new Path (arg.name); Parsed.Object target = pathOfTheoreticalTarget.ResolveFromContext (this); if (target) { Error ("Argument '" + arg.name + "' conflicts with a " + target.GetType().Name + " on " + target.debugMetadata + ", "); continue; } // Does argument conflict with another variable name? if (context.ResolveVariableWithName (arg.name, fromNode: this.parent).found) { Error("Argument '"+ arg.name + "' conflicts with existing variable definition at higher scope."); continue; } } } }
public override void ResolveReferences (Story context) { base.ResolveReferences (context); if( _turnCountDivertTarget ) { var divert = _turnCountDivertTarget.divert; var attemptingTurnCountOfVariableTarget = divert.runtimeDivert.variableDivertName != null; if( attemptingTurnCountOfVariableTarget ) { Error("When getting the TURNS_SINCE() of a variable target, remove the '->' - i.e. it should just be TURNS_SINCE("+divert.runtimeDivert.variableDivertName+")"); return; } var targetObject = divert.targetContent; if( targetObject == null ) { if( !attemptingTurnCountOfVariableTarget ) { Error("Failed to find target for TURNS_SINCE: '"+divert.target+"'"); } } else { targetObject.containerForCounting.turnIndexShouldBeCounted = true; } } else if( _turnCountVariableReference ) { var runtimeVarRef = _turnCountVariableReference.runtimeVarRef; if( runtimeVarRef.pathForCount != null ) { Error("Should be TURNS_SINCE(-> "+_turnCountVariableReference.name+"). Without the '->' it expects a variable target"); } } }
//public override void GenerateIntoContainer (Runtime.Container container) //{ // Expression constantValue = null; // // If it's a constant reference, just generate the literal expression value // // It's okay to access the constants at code generation time, since the // // first thing the ExportRuntime function does it search for all the constants // // in the story hierarchy, so they're all available. // if ( story.constants.TryGetValue (name, out constantValue) ) { // constantValue.GenerateConstantIntoContainer (container); // isConstantReference = true; // return; // } // _runtimeVarRef = new Runtime.VariableReference (name); // // List item reference? // // Path might be to a list (listName.listItemName or just listItemName) // if (path.Count == 1 || path.Count == 2) { // string listItemName = null; // string listName = null; // if (path.Count == 1) listItemName = path [0]; // else { // listName = path [0]; // listItemName = path [1]; // } // var listItem = story.ResolveListItem (listName, listItemName, this); // if (listItem) { // isListItemReference = true; // } // } // container.AddContent (_runtimeVarRef); //} public override void ResolveReferences(Story context) { base.ResolveReferences(context); // Work is already done if it's a constant or list item reference if (isConstantReference || isListItemReference) { return; } // Is it a read count? var parsedPath = new Path(path); Parsed.Object targetForCount = parsedPath.ResolveFromContext(this); if (targetForCount) { //targetForCount.containerForCounting.visitsShouldBeCounted = true; // If this is an argument to a function that wants a variable to be // passed by reference, then the Parsed.Divert will have generated a // Runtime.VariablePointerValue instead of allowing this object // to generate its RuntimeVariableReference. This only happens under // error condition since we shouldn't be passing a read count by // reference, but we don't want it to crash! //if (_runtimeVarRef == null) return; //_runtimeVarRef.pathForCount = targetForCount.runtimePath; //_runtimeVarRef.name = null; // Check for very specific writer error: getting read count and // printing it as content rather than as a piece of logic // e.g. Writing {myFunc} instead of {myFunc()} var targetFlow = targetForCount as FlowBase; if (targetFlow && targetFlow.isFunction) { // Is parent context content rather than logic? if (parent is Weave || parent is ContentList || parent is FlowBase) { Warning("'" + targetFlow.name + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()"); } } return; } // Couldn't find this multi-part path at all, whether as a divert // target or as a list item reference. if (path.Count > 1) { var errorMsg = "Could not find target for read count: " + parsedPath; if (path.Count <= 2) { errorMsg += ", or couldn't find list item with the name " + string.Join(",", path.ToArray()); } Error(errorMsg); return; } if (!context.ResolveVariableWithName(this.name, fromNode: this).found) { Error("Unresolved variable: " + this.ToString(), this); } }