public override Runtime.Object GenerateRuntimeObject() { // Check whether flow has a loose end: // - Most flows should end in a choice or a divert (otherwise issue a warning) // - Functions need a return, otherwise an implicit one is added ValidateTermination(); if (isFunction) { CheckForDisallowedFunctionFlowControl(); } var container = new Runtime.Container(); container.name = name; if (this.story.countAllVisits) { container.visitsShouldBeCounted = true; container.turnIndexShouldBeCounted = true; } GenerateArgumentVariableAssignments(container); // Run through content defined for this knot/stitch: // - First of all, any initial content before a sub-stitch // or any weave content is added to the main content container // - The first inner knot/stitch is automatically entered, while // the others are only accessible by an explicit divert // - The exception to this rule is if the knot/stitch takes // parameters, in which case it can't be auto-entered. // - Any Choices and Gathers (i.e. IWeavePoint) found are // processsed by GenerateFlowContent. int contentIdx = 0; while (content != null && contentIdx < content.Count) { Parsed.Object obj = content [contentIdx]; // Inner knots and stitches if (obj is FlowBase) { var childFlow = (FlowBase)obj; var childFlowRuntime = childFlow.runtimeObject; // First inner knot/stitch - automatically step into it if (contentIdx == 0 && !childFlow.hasParameters) { _startingSubFlowDivert = new Runtime.Divert(); container.AddContent(_startingSubFlowDivert); _startingSubFlowRuntime = childFlowRuntime; } // Check for duplicate knots/stitches with same name var namedChild = (Runtime.INamedContent)childFlowRuntime; Runtime.INamedContent existingChild = null; if (container.namedContent.TryGetValue(namedChild.name, out existingChild)) { var errorMsg = string.Format("{0} already contains flow named '{1}' (at {2})", this.GetType().Name, namedChild.name, (existingChild as Runtime.Object).debugMetadata); Error(errorMsg, childFlow); } container.AddToNamedContentOnly(namedChild); } // Other content (including entire Weaves that were grouped in the constructor) // At the time of writing, all FlowBases have a maximum of one piece of "other content" // and it's always the root Weave else { container.AddContent(obj.runtimeObject); } contentIdx++; } // Tie up final loose ends to the very end if (_rootWeave && _rootWeave.looseEnds != null && _rootWeave.looseEnds.Count > 0) { foreach (var looseEnd in _rootWeave.looseEnds) { if (looseEnd is Divert) { if (_finalLooseEnds == null) { _finalLooseEnds = new List <Ink.Runtime.Divert> (); _finalLooseEndTarget = Runtime.ControlCommand.NoOp(); container.AddContent(_finalLooseEndTarget); } _finalLooseEnds.Add((Runtime.Divert)looseEnd.runtimeObject); } } } return(container); }
public override Runtime.Object GenerateRuntimeObject() { Return foundReturn = null; if (isFunction) { CheckForDisallowedFunctionFlowControl(); } // Non-functon: Make sure knots and stitches don't attempt to use Return statement else if (flowLevel == FlowLevel.Knot || flowLevel == FlowLevel.Stitch) { foundReturn = Find <Return> (); if (foundReturn != null) { Error("Return statements can only be used in knots that are declared as functions: == function " + this.identifier + " ==", foundReturn); } } var container = new Runtime.Container(); container.name = identifier?.name; if (this.story.countAllVisits) { container.visitsShouldBeCounted = true; } GenerateArgumentVariableAssignments(container); // Run through content defined for this knot/stitch: // - First of all, any initial content before a sub-stitch // or any weave content is added to the main content container // - The first inner knot/stitch is automatically entered, while // the others are only accessible by an explicit divert // - The exception to this rule is if the knot/stitch takes // parameters, in which case it can't be auto-entered. // - Any Choices and Gathers (i.e. IWeavePoint) found are // processsed by GenerateFlowContent. int contentIdx = 0; while (content != null && contentIdx < content.Count) { Parsed.Object obj = content [contentIdx]; // Inner knots and stitches if (obj is FlowBase) { var childFlow = (FlowBase)obj; var childFlowRuntime = childFlow.runtimeObject; // First inner stitch - automatically step into it // 20/09/2016 - let's not auto step into knots if (contentIdx == 0 && !childFlow.hasParameters && this.flowLevel == FlowLevel.Knot) { _startingSubFlowDivert = new Runtime.Divert(); container.AddContent(_startingSubFlowDivert); _startingSubFlowRuntime = childFlowRuntime; } // Check for duplicate knots/stitches with same name var namedChild = (Runtime.INamedContent)childFlowRuntime; Runtime.INamedContent existingChild = null; if (container.namedContent.TryGetValue(namedChild.name, out existingChild)) { var errorMsg = string.Format("{0} already contains flow named '{1}' (at {2})", this.GetType().Name, namedChild.name, (existingChild as Runtime.Object).debugMetadata); Error(errorMsg, childFlow); } container.AddToNamedContentOnly(namedChild); } // Other content (including entire Weaves that were grouped in the constructor) // At the time of writing, all FlowBases have a maximum of one piece of "other content" // and it's always the root Weave else { container.AddContent(obj.runtimeObject); } contentIdx++; } // CHECK FOR FINAL LOOSE ENDS! // Notes: // - Functions don't need to terminate - they just implicitly return // - If return statement was found, don't continue finding warnings for missing control flow, // since it's likely that a return statement has been used instead of a ->-> or something, // or the writer failed to mark the knot as a function. // - _rootWeave may be null if it's a knot that only has stitches if (flowLevel != FlowLevel.Story && !this.isFunction && _rootWeave != null && foundReturn == null) { _rootWeave.ValidateTermination(WarningInTermination); } return(container); }