using System.Collections.Generic; namespace Ink.Parsed { public class ConditionalSingleBranch : Parsed.Object { // bool condition, e.g.: // { 5 == 4: // - the true branch // - the false branch // } public bool isTrueBranch { get; set; } // When each branch has its own expression like a switch statement, // this is non-null. e.g. // { x: // - 4: the value of x is four (ownExpression is the value 4) // - 3: the value of x is three // } public Expression ownExpression { get { return _ownExpression; } set { _ownExpression = value; if (_ownExpression) { AddContent (_ownExpression); } } } // In the above example, match equality of x with 4 for the first branch. // This is as opposed to simply evaluating boolean equality for each branch, // example when shouldMatchEqualtity is FALSE: // { // 3 > 2: This will happen // 2 > 3: This won't happen // } public bool matchingEquality { get; set; } public bool isElse { get; set; } public bool isInline { get; set; } public Runtime.Divert returnDivert { get; protected set; } public ConditionalSingleBranch (List<Parsed.Object> content) { // Branches are allowed to be empty if (content != null) { _innerWeave = new Weave (content); AddContent (_innerWeave); } } // Runtime content can be summarised as follows: // - Evaluate an expression if necessary to branch on // - Branch to a named container if true // - Divert back to main flow // (owner Conditional is in control of this target point) public override Runtime.Object GenerateRuntimeObject () { // Check for common mistake, of putting "else:" instead of "- else:" if (_innerWeave) { foreach (var c in _innerWeave.content) { var text = c as Parsed.Text; if (text) { // Don't need to trim at the start since the parser handles that already if (text.text.StartsWith ("else:")) { Warning ("Saw the text 'else:' which is being treated as content. Did you mean '- else:'?", text); } } } } var container = new Runtime.Container (); // Are we testing against a condition that's used for more than just this // branch? If so, the first thing we need to do is replicate the value that's // on the evaluation stack so that we don't fully consume it, in case other // branches need to use it. bool duplicatesStackValue = matchingEquality && !isElse; if ( duplicatesStackValue ) container.AddContent (Runtime.ControlCommand.Duplicate ()); _conditionalDivert = new Runtime.Divert (); // else clause is unconditional catch-all, otherwise the divert is conditional _conditionalDivert.isConditional = !isElse; // Need extra evaluation? if( !isTrueBranch && !isElse ) { bool needsEval = ownExpression != null; if( needsEval ) container.AddContent (Runtime.ControlCommand.EvalStart ()); if (ownExpression) ownExpression.GenerateIntoContainer (container); // Uses existing duplicated value if (matchingEquality) container.AddContent (Runtime.NativeFunctionCall.CallWithName ("==")); if( needsEval ) container.AddContent (Runtime.ControlCommand.EvalEnd ()); } // Will pop from stack if conditional container.AddContent (_conditionalDivert); _contentContainer = GenerateRuntimeForContent (); _contentContainer.name = "b"; // Multi-line conditionals get a newline at the start of each branch // (as opposed to the start of the multi-line conditional since the condition // may evaluate to false.) if (!isInline) { _contentContainer.InsertContent (new Runtime.StringValue ("\n"), 0); } if( duplicatesStackValue || (isElse && matchingEquality) ) _contentContainer.InsertContent (Runtime.ControlCommand.PopEvaluatedValue (), 0); container.AddToNamedContentOnly (_contentContainer); returnDivert = new Runtime.Divert (); _contentContainer.AddContent (returnDivert); return container; } Runtime.Container GenerateRuntimeForContent() { // Empty branch - create empty container if (_innerWeave == null) { return new Runtime.Container (); } return _innerWeave.rootContainer; } public override void ResolveReferences (Story context) { _conditionalDivert.targetPath = _contentContainer.path; base.ResolveReferences (context); } Runtime.Container _contentContainer; Runtime.Divert _conditionalDivert; Expression _ownExpression; Weave _innerWeave; } }