public Choice(ContentList startContent, ContentList choiceOnlyContent, ContentList innerContent, Divert divert) { this.startContent = startContent; this.choiceOnlyContent = choiceOnlyContent; this.innerContent = innerContent; this.indentationDepth = 1; if (startContent) { AddContent(this.startContent); } if (choiceOnlyContent) { AddContent(this.choiceOnlyContent); } if (innerContent) { AddContent(this.innerContent); } this.onceOnly = true; // default if (divert) { terminatingDivert = divert; AddContent(terminatingDivert); } }
void ValidateTerminatingDivert(Divert terminatingDivert) { if (terminatingDivert.isFunctionCall) { WarningInTermination(terminatingDivert); return; } if (terminatingDivert.isTunnel) { WarningInTermination(terminatingDivert, "When final tunnel to '" + terminatingDivert.target + " ->' returns it won't have anywhere to go."); } }
public Choice(ContentList startContent, ContentList choiceOnlyContent, ContentList innerContent, Divert divert) { this.startContent = startContent; this.choiceOnlyContent = choiceOnlyContent; this.innerContent = innerContent; this.indentationDepth = 1; if (startContent) AddContent (this.startContent); if (choiceOnlyContent) AddContent (this.choiceOnlyContent); if( innerContent ) AddContent (this.innerContent); this.onceOnly = true; // default if (divert) { terminatingDivert = divert; AddContent (terminatingDivert); } }
protected List<Parsed.Object> MultiDivert() { Whitespace (); List<Parsed.Object> diverts = null; // Try single thread first var threadDivert = Parse(StartThread); if (threadDivert) { diverts = new List<Object> (); diverts.Add (threadDivert); return diverts; } // Normal diverts and tunnels var arrowsAndDiverts = Interleave<object> ( ParseDivertArrowOrTunnelOnwards, DivertIdentifierWithArguments); if (arrowsAndDiverts == null) return null; diverts = new List<Parsed.Object> (); // Possible patterns: // -> -- explicit gather // ->-> -- tunnel onwards // -> div -- normal divert // ->-> div -- tunnel onwards, followed by override divert // -> div -> -- normal tunnel // -> div ->-> -- tunnel then tunnel continue // -> div -> div -- tunnel then divert // -> div -> div -> -- tunnel then tunnel // -> div -> div ->-> (etc) bool hasInitialTunnelOnwards = false; bool hasFinalTunnelOnwards = false; // Look at the arrows and diverts for (int i = 0; i < arrowsAndDiverts.Count; ++i) { bool isArrow = (i % 2) == 0; // Arrow string if (isArrow) { string arrow = arrowsAndDiverts [i] as string; if (arrow == "->->") { if (i == 0) { hasInitialTunnelOnwards = true; } else if (i == arrowsAndDiverts.Count - 1) { hasFinalTunnelOnwards = true; } else { Error ("Tunnel onwards '->->' must only come at the begining or the start of a divert"); } } } // Divert else { var divert = arrowsAndDiverts [i] as Divert; // More to come? (further arrows) Must be tunnelling. if (i < arrowsAndDiverts.Count - 1) { divert.isTunnel = true; } diverts.Add (divert); } } // ->-> (with optional override divert) if (hasInitialTunnelOnwards) { if (arrowsAndDiverts.Count > 2) { Error ("Tunnel onwards '->->' must either be on its own or followed by a single target"); } var tunnelOnwards = new TunnelOnwards (); // Optional override target to divert to after tunnel onwards? // Replace divert with the tunnel onwards to that target. if (arrowsAndDiverts.Count > 1) { var overrideDivert = diverts [0] as Parsed.Divert; tunnelOnwards.overrideReturnPath = overrideDivert.target; diverts.RemoveAt (0); } diverts.Add (tunnelOnwards); } // Single -> else if (diverts.Count == 0 && arrowsAndDiverts.Count == 1) { var gatherDivert = new Divert ((Parsed.Object)null); gatherDivert.isToGather = true; diverts.Add (gatherDivert); } // Divert that terminates in ->-> else if (hasFinalTunnelOnwards) { diverts.Add (new TunnelOnwards ()); } return diverts; }
void ValidateTerminatingDivert(Divert terminatingDivert) { if (terminatingDivert.isFunctionCall) { WarningInTermination (terminatingDivert); return; } if (terminatingDivert.isTunnel) { WarningInTermination (terminatingDivert, "When final tunnel to '"+terminatingDivert.target+" ->' returns it won't have anywhere to go."); } }
protected List<Parsed.Object> MultiDivert() { Whitespace (); List<Parsed.Object> diverts = null; // Try single thread first var threadDivert = Parse(StartThread); if (threadDivert) { diverts = new List<Object> (); diverts.Add (threadDivert); return diverts; } // Normal diverts and tunnels var arrowsAndDiverts = Interleave<object> ( ParseDivertArrowOrTunnelOnwards, DivertIdentifierWithArguments); if (arrowsAndDiverts == null) return null; diverts = new List<Parsed.Object> (); // Divert arrow only: // -> // ->-> // (with no target) if (arrowsAndDiverts.Count == 1) { // Single: // -> // Assume if there are no target components, it must be a divert to a gather point if ( (string) arrowsAndDiverts [0] == "->") { var gatherDivert = new Divert ((Parsed.Object)null); gatherDivert.isToGather = true; diverts.Add (gatherDivert); } // Double: (tunnel onwards) // ->-> else { diverts.Add (new TunnelOnwards()); } } // Possible patterns: // -> div -- normal divert // -> div -> -- normal tunnel // -> div ->-> -- tunnel then tunnel continue // -> div -> div -- tunnel then divert // -> div -> div -> -- tunnel then tunnel // -> div -> div ->-> (etc) else { bool hasFinalTunnelOnwards = false; // Look at the arrows and diverts for (int i = 0; i < arrowsAndDiverts.Count; ++i) { bool isArrow = (i % 2) == 0; // Arrow string if (isArrow) { string arrow = arrowsAndDiverts [i] as string; if (arrow == "->->") { if (i == arrowsAndDiverts.Count - 1) { hasFinalTunnelOnwards = true; } else { Error ("Unexpected content after a '->->' tunnel onwards"); } } } // Divert else { var divert = arrowsAndDiverts [i] as Divert; // More to come? (further arrows) Must be tunnelling. if (i < arrowsAndDiverts.Count - 1) { divert.isTunnel = true; } diverts.Add (divert); } } if (hasFinalTunnelOnwards) diverts.Add(new TunnelOnwards()); } return diverts; }
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; // Count visits on all knots and stitches 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 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++; } // Tie up final loose ends to the very end if (_rootWeave && _rootWeave.looseEnds != null && _rootWeave.looseEnds.Count > 0) { foreach (var looseEnd in _rootWeave.looseEnds) { Divert looseEndDivert = looseEnd as Divert; if (looseEndDivert == null) { continue; } if (_finalLooseEnds == null) { _finalLooseEnds = new List <Ink.Runtime.Divert> (); _finalLooseEndTarget = Runtime.ControlCommand.NoOp(); container.AddContent(_finalLooseEndTarget); } _finalLooseEnds.Add((Runtime.Divert)looseEndDivert.runtimeObject); } } return(container); }