예제 #1
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            // Initial condition
            if (this.initialCondition)
            {
                container.AddContent(initialCondition.runtimeObject);
            }

            // Individual branches
            foreach (var branch in branches)
            {
                var branchContainer = (Container)branch.runtimeObject;
                container.AddContent(branchContainer);
            }

            // If it's a switch-like conditional, each branch
            // will have a "duplicate" operation for the original
            // switched value. If there's no final else clause
            // and we fall all the way through, we need to clean up.
            // (An else clause doesn't dup but it *does* pop)
            if (this.initialCondition != null && branches [0].ownExpression != null && !branches [branches.Count - 1].isElse)
            {
                container.AddContent(Runtime.ControlCommand.PopEvaluatedValue());
            }

            // Target for branches to rejoin to
            _reJoinTarget = Runtime.ControlCommand.NoOp();
            container.AddContent(_reJoinTarget);

            return(container);
        }
예제 #2
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            // Initial condition
            if (this.initialCondition)
            {
                container.AddContent(initialCondition.runtimeObject);
            }

            // Individual branches
            foreach (var branch in branches)
            {
                var branchContainer = (Container)branch.runtimeObject;
                container.AddContent(branchContainer);
            }

            // If no branches matched, tidy up after ourselves
            if (this.initialCondition)
            {
                container.AddContent(Runtime.ControlCommand.PopEvaluatedValue());
            }

            // Target for branches to rejoin to
            _reJoinTarget = Runtime.ControlCommand.NoOp();
            container.AddContent(_reJoinTarget);

            return(container);
        }
예제 #3
0
파일: Expression.cs 프로젝트: paulloz/ink
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            // x = x + y
            // ^^^ ^ ^ ^
            //  4  1 3 2
            // Reverse polish notation: (x 1 +) (assign to x)

            // 1.
            container.AddContent(new Runtime.VariableReference(varName));

            // 2.
            // - Expression used in the form ~ x += y
            // - Simple version: ~ x++
            if (expression)
            {
                expression.GenerateIntoContainer(container);
            }
            else
            {
                container.AddContent(new Runtime.IntValue(1));
            }

            // 3.
            container.AddContent(Runtime.NativeFunctionCall.CallWithName(isInc ? "+" : "-"));

            // 4.
            container.AddContent(new Runtime.VariableAssignment(varName, false));
        }
예제 #4
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            FlowBase newDeclScope = null;

            if (isGlobalDeclaration)
            {
                newDeclScope = story;
            }
            else if (isNewTemporaryDeclaration)
            {
                newDeclScope = ClosestFlowBase();
            }

            if (newDeclScope)
            {
                newDeclScope.TryAddNewVariableDeclaration(this);
            }

            // Global declarations don't generate actual procedural
            // runtime objects, but instead add a global variable to the story itself.
            // The story then initialises them all in one go at the start of the game.
            if (isGlobalDeclaration)
            {
                return(null);
            }

            var container = new Runtime.Container();

            // The expression's runtimeObject is actually another nested container
            container.AddContent(expression.runtimeObject);

            container.AddContent(new Runtime.VariableAssignment(variableName, isNewTemporaryDeclaration));

            return(container);
        }
예제 #5
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            // Evaluate expression
            if (returnedExpression)
            {
                container.AddContent(returnedExpression.runtimeObject);
            }

            // Return Runtime.Void when there's no expression to evaluate
            // (This evaluation will just add the Void object to the evaluation stack)
            else
            {
                container.AddContent(Runtime.ControlCommand.EvalStart());
                container.AddContent(new Runtime.Void());
                container.AddContent(Runtime.ControlCommand.EvalEnd());
            }

            // Then pop the call stack
            // (the evaluated expression will leave the return value on the evaluation stack)
            container.AddContent(Runtime.ControlCommand.PopFunction());

            return(container);
        }
예제 #6
0
        public override Runtime.Object GenerateRuntimeObject ()
        {
            FlowBase newDeclScope = null;
            if (isGlobalDeclaration) {
                newDeclScope = story;
            } else if(isNewTemporaryDeclaration) {
                newDeclScope = ClosestFlowBase ();
            }

            if( newDeclScope )
                newDeclScope.TryAddNewVariableDeclaration (this);

            // Global declarations don't generate actual procedural
            // runtime objects, but instead add a global variable to the story itself.
            // The story then initialises them all in one go at the start of the game.
            if( isGlobalDeclaration )
                return null;

            var container = new Runtime.Container ();

            // The expression's runtimeObject is actually another nested container
            container.AddContent (expression.runtimeObject);

            container.AddContent (new Runtime.VariableAssignment (variableName, isNewTemporaryDeclaration));

            return container;
        }
 public override void GenerateIntoContainer(Runtime.Container container)
 {
     if (value is int)
     {
         container.AddContent(new Runtime.IntValue((int)value));
     }
     else if (value is float)
     {
         container.AddContent(new Runtime.FloatValue((float)value));
     }
 }
예제 #8
0
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            container.AddContent(Runtime.ControlCommand.BeginString());

            foreach (var c in content)
            {
                container.AddContent(c.runtimeObject);
            }

            container.AddContent(Runtime.ControlCommand.EndString());
        }
예제 #9
0
파일: FunctionCall.cs 프로젝트: nyyjen/ink
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            if (isChoiceCount)
            {
                if (arguments.Count > 0)
                {
                    Error("The CHOICE_COUNT() function shouldn't take any arguments");
                }

                container.AddContent(Runtime.ControlCommand.ChoiceCount());
            }

            else if (isTurnsSince)
            {
                var literalDivertTarget  = arguments [0] as DivertTarget;
                var variableDivertTarget = arguments[0] as VariableReference;

                if (arguments.Count != 1 || (literalDivertTarget == null && variableDivertTarget == null))
                {
                    Error("The TURNS_SINCE() function should take one argument: a divert target to the target knot, stitch, gather or choice you want to check. e.g. TURNS_SINCE(-> myKnot)");
                    return;
                }

                if (literalDivertTarget)
                {
                    _turnCountDivertTarget = literalDivertTarget;
                    AddContent(_turnCountDivertTarget);

                    _turnCountDivertTarget.GenerateIntoContainer(container);
                }

                else
                {
                    _turnCountVariableReference = variableDivertTarget;
                    AddContent(_turnCountVariableReference);

                    _turnCountVariableReference.GenerateIntoContainer(container);

                    if (!story.countAllVisits)
                    {
                        Error("Attempting to get TURNS_SINCE for a variable target without -c compiler option. You need the compiler switch turned on so that it can track turn counts for everything, not just those you directly reference.");
                    }
                }


                container.AddContent(Runtime.ControlCommand.TurnsSince());
            }

            // Normal function call
            else
            {
                container.AddContent(_proxyDivert.runtimeObject);
            }
        }
예제 #10
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            container.name = name;

            if (this.story.countAllVisits)
            {
                container.visitsShouldBeCounted    = true;
                container.turnIndexShouldBeCounted = true;
            }

            container.countingAtStartOnly = true;

            // A gather can have null content, e.g. it's just purely a line with "-"
            if (content != null)
            {
                foreach (var c in content)
                {
                    container.AddContent(c.runtimeObject);
                }
            }

            return(container);
        }
예제 #11
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            if (content != null)
            {
                foreach (var obj in content)
                {
                    var contentObjRuntime = obj.runtimeObject;

                    // Some objects (e.g. author warnings) don't generate runtime objects
                    if (contentObjRuntime)
                    {
                        container.AddContent(contentObjRuntime);
                    }
                }
            }

            if (dontFlatten)
            {
                story.DontFlattenContainer(container);
            }

            return(container);
        }
예제 #12
0
파일: Expression.cs 프로젝트: paulloz/ink
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            leftExpression.GenerateIntoContainer(container);
            rightExpression.GenerateIntoContainer(container);

            opName = NativeNameForOp(opName);

            container.AddContent(Runtime.NativeFunctionCall.CallWithName(opName));
        }
예제 #13
0
파일: TunnelOnwards.cs 프로젝트: inkle/ink
        public override Runtime.Object GenerateRuntimeObject ()
        {
            var container = new Runtime.Container ();

            // Set override path for tunnel onwards (or nothing)
            container.AddContent (Runtime.ControlCommand.EvalStart ());
            if (overrideReturnPath != null) {
                _overrideDivertTarget = new Runtime.DivertTargetValue ();
                container.AddContent (_overrideDivertTarget);
            } else {
                container.AddContent (new Runtime.Void ());
            }
            container.AddContent (Runtime.ControlCommand.EvalEnd ());

            container.AddContent (Runtime.ControlCommand.PopTunnel ());

            return container;
        }
예제 #14
0
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            divert.GenerateRuntimeObject();

            _runtimeDivert            = (Runtime.Divert)divert.runtimeDivert;
            _runtimeDivertTargetValue = new Runtime.DivertTargetValue();

            container.AddContent(_runtimeDivertTargetValue);
        }
예제 #15
0
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            var runtimeRawList = new Runtime.InkList();

            if (itemIdentifierList != null)
            {
                foreach (var itemIdentifier in itemIdentifierList)
                {
                    var nameParts = itemIdentifier?.name.Split('.');

                    string listName     = null;
                    string listItemName = null;
                    if (nameParts.Length > 1)
                    {
                        listName     = nameParts [0];
                        listItemName = nameParts [1];
                    }
                    else
                    {
                        listItemName = nameParts [0];
                    }

                    var listItem = story.ResolveListItem(listName, listItemName, this);
                    if (listItem == null)
                    {
                        if (listName == null)
                        {
                            Error("Could not find list definition that contains item '" + itemIdentifier + "'");
                        }
                        else
                        {
                            Error("Could not find list item " + itemIdentifier);
                        }
                    }
                    else
                    {
                        if (listName == null)
                        {
                            listName = ((ListDefinition)listItem.parent).identifier?.name;
                        }
                        var item = new Runtime.InkListItem(listName, listItem.name);

                        if (runtimeRawList.ContainsKey(item))
                        {
                            Warning("Duplicate of item '" + itemIdentifier + "' in list.");
                        }
                        else
                        {
                            runtimeRawList [item] = listItem.seriesValue;
                        }
                    }
                }
            }

            container.AddContent(new Runtime.ListValue(runtimeRawList));
        }
예제 #16
0
파일: Expression.cs 프로젝트: inkle/ink
		public override Runtime.Object GenerateRuntimeObject ()
		{
            var container = new Runtime.Container ();

            // Tell Runtime to start evaluating the following content as an expression
            container.AddContent (Runtime.ControlCommand.EvalStart());

            GenerateIntoContainer (container);

            // Tell Runtime to output the result of the expression evaluation to the output stream
            if (outputWhenComplete) {
                container.AddContent (Runtime.ControlCommand.EvalOutput());
            }

            // Tell Runtime to stop evaluating the content as an expression
            container.AddContent (Runtime.ControlCommand.EvalEnd());

            return container;
		}
예제 #17
0
파일: ContentList.cs 프로젝트: tomkail/ink
 public override Runtime.Object GenerateRuntimeObject()
 {
     var container = new Runtime.Container ();
     if (content != null) {
         foreach (var obj in content) {
             container.AddContent (obj.runtimeObject);
         }
     }
     return container;
 }
예제 #18
0
파일: Expression.cs 프로젝트: paulloz/ink
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            // Tell Runtime to start evaluating the following content as an expression
            container.AddContent(Runtime.ControlCommand.EvalStart());

            GenerateIntoContainer(container);

            // Tell Runtime to output the result of the expression evaluation to the output stream
            if (outputWhenComplete)
            {
                container.AddContent(Runtime.ControlCommand.EvalOutput());
            }

            // Tell Runtime to stop evaluating the content as an expression
            container.AddContent(Runtime.ControlCommand.EvalEnd());

            return(container);
        }
예제 #19
0
        public override Runtime.Object GenerateRuntimeObject ()
        {
            var container = new Runtime.Container ();

            // Initial condition
            if (this.initialCondition) {
                container.AddContent (initialCondition.runtimeObject);
            }

            // Individual branches
            foreach (var branch in branches) {
                var branchContainer = (Container) branch.runtimeObject;
                container.AddContent (branchContainer);
            }

            // Target for branches to rejoin to
            _reJoinTarget = Runtime.ControlCommand.NoOp ();
            container.AddContent (_reJoinTarget);

            return container;
        }
예제 #20
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            // Set override path for tunnel onwards (or nothing)
            container.AddContent(Runtime.ControlCommand.EvalStart());
            if (overrideReturnPath != null)
            {
                _overrideDivertTarget = new Runtime.DivertTargetValue();
                container.AddContent(_overrideDivertTarget);
            }
            else
            {
                container.AddContent(new Runtime.Void());
            }
            container.AddContent(Runtime.ControlCommand.EvalEnd());

            container.AddContent(Runtime.ControlCommand.PopTunnel());

            return(container);
        }
예제 #21
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            if (content != null)
            {
                foreach (var obj in content)
                {
                    container.AddContent(obj.runtimeObject);
                }
            }
            return(container);
        }
예제 #22
0
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            // x = x + 1
            // ^^^ ^ ^ ^
            //  4  1 3 2
            // Reverse polish notation: (x 1 +) (assign to x)

            // 1.
            container.AddContent(new Runtime.VariableReference(varName));

            // 2.
            container.AddContent(new Runtime.IntValue(isInc ? 1 : -1));

            // 3.
            container.AddContent(Runtime.NativeFunctionCall.CallWithName("+"));

            // 4.
            container.AddContent(new Runtime.VariableAssignment(varName, false));

            // Finally, leave the variable on the stack so it can be used as a sub-expression
            container.AddContent(new Runtime.VariableReference(varName));
        }
예제 #23
0
파일: Expression.cs 프로젝트: paulloz/ink
        // When generating the value of a constant expression,
        // we can't just keep generating the same constant expression into
        // different places where the constant value is referenced, since then
        // the same runtime objects would be used in multiple places, which
        // is impossible since each runtime object should have one parent.
        // Instead, we generate a prototype of the runtime object(s), then
        // copy them each time they're used.
        public void GenerateConstantIntoContainer(Runtime.Container container)
        {
            if (_prototypeRuntimeConstantExpression == null)
            {
                _prototypeRuntimeConstantExpression = new Runtime.Container();
                GenerateIntoContainer(_prototypeRuntimeConstantExpression);
            }

            foreach (var runtimeObj in _prototypeRuntimeConstantExpression.content)
            {
                container.AddContent(runtimeObj.Copy());
            }
        }
예제 #24
0
파일: Return.cs 프로젝트: inkle/ink
        public override Runtime.Object GenerateRuntimeObject ()
        {
            var container = new Runtime.Container ();

            // Evaluate expression
            if (returnedExpression) {
                container.AddContent (returnedExpression.runtimeObject);
            } 

            // Return Runtime.Void when there's no expression to evaluate
            // (This evaluation will just add the Void object to the evaluation stack)
            else {
                container.AddContent (Runtime.ControlCommand.EvalStart ());
                container.AddContent (new Runtime.Void());
                container.AddContent (Runtime.ControlCommand.EvalEnd ());
            }

            // Then pop the call stack
            // (the evaluated expression will leave the return value on the evaluation stack)
            container.AddContent (Runtime.ControlCommand.PopFunction());

            return container;
        }
예제 #25
0
파일: DivertTarget.cs 프로젝트: nyyjen/ink
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            divert.GenerateRuntimeObject();

            _runtimeDivert = (Runtime.Divert)divert.runtimeDivert;
            _runtimeLiteralDivertTarget = new Runtime.LiteralDivertTarget();

            if (divert.arguments != null && divert.arguments.Count > 0)
            {
                Error("Can't use a divert target as a variable if it has parameters");
                return;
            }

            container.AddContent(_runtimeLiteralDivertTarget);
        }
예제 #26
0
파일: FlowBase.cs 프로젝트: yuhangwang/ink
        void GenerateArgumentVariableAssignments(Runtime.Container container)
        {
            if (this.arguments == null || this.arguments.Count == 0)
            {
                return;
            }

            // Assign parameters in reverse since they'll be popped off the evaluation stack
            // No need to generate EvalStart and EvalEnd since there's nothing being pushed
            // back onto the evaluation stack.
            for (int i = arguments.Count - 1; i >= 0; --i)
            {
                var paramName = arguments [i].name;

                var assign = new Runtime.VariableAssignment(paramName, isNewDeclaration: true);
                container.AddContent(assign);
            }
        }
예제 #27
0
파일: Expression.cs 프로젝트: paulloz/ink
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            //    A && B && C && D
            // => (((A B &&) C &&) D &&) etc
            bool isFirst = true;

            foreach (var conditionExpr in subExpressions)
            {
                conditionExpr.GenerateIntoContainer(container);

                if (!isFirst)
                {
                    container.AddContent(Runtime.NativeFunctionCall.CallWithName("&&"));
                }

                isFirst = false;
            }
        }
예제 #28
0
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            Expression constantValue = null;

            // Name can be null if it's actually a path to a knot/stitch etc for a read count
            var varName = name;

            // If it's a constant reference, just generate the literal expression value
            if (varName != null && story.constants.TryGetValue(varName, out constantValue))
            {
                constantValue.GenerateIntoContainer(container);
                isConstantReference = true;
            }
            else
            {
                _runtimeVarRef = new Runtime.VariableReference(name);
                container.AddContent(_runtimeVarRef);
            }
        }
예제 #29
0
        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);
        }
예제 #30
0
파일: Gather.cs 프로젝트: inkle/ink
        public override Runtime.Object GenerateRuntimeObject ()
        {
            var container = new Runtime.Container ();
            container.name = name;

            if (this.story.countAllVisits) {
                container.visitsShouldBeCounted = true;
                container.turnIndexShouldBeCounted = true;
            }

            container.countingAtStartOnly = true;

            // A gather can have null content, e.g. it's just purely a line with "-"
            if (content != null) {
                foreach (var c in content) {
                    container.AddContent (c.runtimeObject);
                }
            }

            return container;

        }
예제 #31
0
파일: Divert.cs 프로젝트: magno-morais/jrpg
        public override Runtime.Object GenerateRuntimeObject()
        {
            // End = end flow immediately
            // Done = return from thread or instruct the flow that it's safe to exit
            if (isEnd)
            {
                return(Runtime.ControlCommand.End());
            }
            if (isDone)
            {
                return(Runtime.ControlCommand.Done());
            }

            runtimeDivert = new Runtime.Divert();

            // Normally we resolve the target content during the
            // Resolve phase, since we expect all runtime objects to
            // be available in order to find the final runtime path for
            // the destination. However, we need to resolve the target
            // (albeit without the runtime target) early so that
            // we can get information about the arguments - whether
            // they're by reference - since it affects the code we
            // generate here.
            ResolveTargetContent();


            CheckArgumentValidity();

            // Passing arguments to the knot
            bool requiresArgCodeGen = arguments != null && arguments.Count > 0;

            if (requiresArgCodeGen || isFunctionCall || isTunnel || isThread)
            {
                var container = new Runtime.Container();

                // Generate code for argument evaluation
                // This argument generation is coded defensively - it should
                // attempt to generate the code for all the parameters, even if
                // they don't match the expected arguments. This is so that the
                // parameter objects themselves are generated correctly and don't
                // get into a state of attempting to resolve references etc
                // without being generated.
                if (requiresArgCodeGen)
                {
                    // Function calls already in an evaluation context
                    if (!isFunctionCall)
                    {
                        container.AddContent(Runtime.ControlCommand.EvalStart());
                    }

                    List <FlowBase.Argument> targetArguments = null;
                    if (targetContent)
                    {
                        targetArguments = (targetContent as FlowBase).arguments;
                    }

                    for (var i = 0; i < arguments.Count; ++i)
                    {
                        Expression        argToPass   = arguments [i];
                        FlowBase.Argument argExpected = null;
                        if (targetArguments != null && i < targetArguments.Count)
                        {
                            argExpected = targetArguments [i];
                        }

                        // Pass by reference: argument needs to be a variable reference
                        if (argExpected != null && argExpected.isByReference)
                        {
                            var varRef = argToPass as VariableReference;
                            if (varRef == null)
                            {
                                Error("Expected variable name to pass by reference to 'ref " + argExpected.name + "' but saw " + argToPass.ToString());
                                break;
                            }

                            // Check that we're not attempting to pass a read count by reference
                            var           targetPath     = new Path(varRef.path);
                            Parsed.Object targetForCount = targetPath.ResolveFromContext(this);
                            if (targetForCount != null)
                            {
                                Error("can't pass a read count by reference. '" + targetPath.dotSeparatedComponents + "' is a knot/stitch/label, but '" + target.dotSeparatedComponents + "' requires the name of a VAR to be passed.");
                                break;
                            }

                            var varPointer = new Runtime.VariablePointerValue(varRef.name);
                            container.AddContent(varPointer);
                        }

                        // Normal value being passed: evaluate it as normal
                        else
                        {
                            argToPass.GenerateIntoContainer(container);
                        }
                    }

                    // Function calls were already in an evaluation context
                    if (!isFunctionCall)
                    {
                        container.AddContent(Runtime.ControlCommand.EvalEnd());
                    }
                }


                // Starting a thread? A bit like a push to the call stack below... but not.
                // It sort of puts the call stack on a thread stack (argh!) - forks the full flow.
                if (isThread)
                {
                    container.AddContent(Runtime.ControlCommand.StartThread());
                }

                // If this divert is a function call, tunnel, we push to the call stack
                // so we can return again
                else if (isFunctionCall || isTunnel)
                {
                    runtimeDivert.pushesToStack = true;
                    runtimeDivert.stackPushType = isFunctionCall ? Runtime.PushPopType.Function : Runtime.PushPopType.Tunnel;
                }

                // Jump into the "function" (knot/stitch)
                container.AddContent(runtimeDivert);

                return(container);
            }

            // Simple divert
            else
            {
                return(runtimeDivert);
            }
        }
예제 #32
0
파일: Choice.cs 프로젝트: inkle/ink
		public override Runtime.Object GenerateRuntimeObject ()
        {
            _outerContainer = new Runtime.Container ();

            // Content names for different types of choice:
            //  * start content [choice only content] inner content
            //  * start content   -> divert
            //  * start content
            //  * [choice only content]

            // Hmm, this structure has become slightly insane!
            //
            // [
            //     EvalStart
            //     assign $r = $r1   -- return target = return label 1
            //     BeginString
            //     -> s
            //     [(r1)]            -- return label 1 (after start content)
            //     EndString
            //     BeginString
            //     ... choice only content
            //     EndEval
            //     Condition expression
            //     choice: -> "c"
            //     (s) = [
            //         start content
            //         -> r          -- goto return label 1 or 2
            //     ]
            //     (c) = [
            //         EvalStart
            //         assign $r = $r2   -- return target = return label 2
            //         EndEval
            //         -> s
            //         [(r2)]            -- return label 1 (after start content)
            //         inner content
            //     ]
            // ]

            _runtimeChoice = new Runtime.ChoicePoint (onceOnly);
            _runtimeChoice.isInvisibleDefault = this.isInvisibleDefault;

            if (startContent || choiceOnlyContent || condition) {
                _outerContainer.AddContent (Runtime.ControlCommand.EvalStart ());
            }

            // Start content is put into a named container that's referenced both
            // when displaying the choice initially, and when generating the text
            // when the choice is chosen.
            if (startContent) {

                // Generate start content and return
                //  - We can't use a function since it uses a call stack element, which would
                //    put temporary values out of scope. Instead we manually divert around.
                //  - $r is a variable divert target contains the return point
                _returnToR1 = new Runtime.DivertTargetValue ();
                _outerContainer.AddContent (_returnToR1);
                var varAssign = new Runtime.VariableAssignment ("$r", true);
                _outerContainer.AddContent (varAssign);

                // Mark the start of the choice text generation, so that the runtime
                // knows where to rewind to to extract the content from the output stream.
                _outerContainer.AddContent (Runtime.ControlCommand.BeginString ());

                _divertToStartContentOuter = new Runtime.Divert ();
                _outerContainer.AddContent (_divertToStartContentOuter);

                // Start content itself in a named container
                _startContentRuntimeContainer = startContent.GenerateRuntimeObject () as Runtime.Container;
                _startContentRuntimeContainer.name = "s";

                // Effectively, the "return" statement - return to the point specified by $r
                var varDivert = new Runtime.Divert ();
                varDivert.variableDivertName = "$r";
                _startContentRuntimeContainer.AddContent (varDivert);

                // Add the container
                _outerContainer.AddToNamedContentOnly (_startContentRuntimeContainer);

                // This is the label to return to
                _r1Label = new Runtime.Container ();
                _r1Label.name = "$r1";
                _outerContainer.AddContent (_r1Label);

                _outerContainer.AddContent (Runtime.ControlCommand.EndString ());

                _runtimeChoice.hasStartContent = true;
            }

            // Choice only content - mark the start, then generate it directly into the outer container
            if (choiceOnlyContent) {
                _outerContainer.AddContent (Runtime.ControlCommand.BeginString ());

                var choiceOnlyRuntimeContent = choiceOnlyContent.GenerateRuntimeObject () as Runtime.Container;
                _outerContainer.AddContentsOfContainer (choiceOnlyRuntimeContent);

                _outerContainer.AddContent (Runtime.ControlCommand.EndString ());

                _runtimeChoice.hasChoiceOnlyContent = true;
            }

            // Generate any condition for this choice
            if (condition) {
                condition.GenerateIntoContainer (_outerContainer);
                _runtimeChoice.hasCondition = true;
            }

            if (startContent || choiceOnlyContent || condition) {
                _outerContainer.AddContent (Runtime.ControlCommand.EvalEnd ());
            }
                
            // Add choice itself
            _outerContainer.AddContent (_runtimeChoice);

            // Container that choice points to for when it's chosen
            _innerContentContainer = new Runtime.Container ();

            // Repeat start content by diverting to its container
            if (startContent) {

                // Set the return point when jumping back into the start content
                //  - In this case, it's the $r2 point, within the choice content "c".
                _returnToR2 = new Runtime.DivertTargetValue ();
                _innerContentContainer.AddContent (Runtime.ControlCommand.EvalStart ());
                _innerContentContainer.AddContent (_returnToR2);
                _innerContentContainer.AddContent (Runtime.ControlCommand.EvalEnd ());
                var varAssign = new Runtime.VariableAssignment ("$r", true);
                _innerContentContainer.AddContent (varAssign);

                // Main divert into start content
                _divertToStartContentInner = new Runtime.Divert ();
                _innerContentContainer.AddContent (_divertToStartContentInner);

                // Define label to return to
                _r2Label = new Runtime.Container ();
                _r2Label.name = "$r2";
                _innerContentContainer.AddContent (_r2Label);
            }

            // Choice's own inner content
            if (innerContent) {
				var innerChoiceOnlyContent = innerContent.GenerateRuntimeObject () as Runtime.Container;
                _innerContentContainer.AddContentsOfContainer (innerChoiceOnlyContent);
            }

            // Fully parsed choice will be a full line, so it needs to be terminated
            if (startContent || innerContent) {
                _innerContentContainer.AddContent(new Runtime.StringValue("\n"));
            }

            // Use "c" as the destination name within the choice's outer container
            _innerContentContainer.name = "c";
            _outerContainer.AddToNamedContentOnly (_innerContentContainer);

            if (this.story.countAllVisits) {
                _innerContentContainer.visitsShouldBeCounted = true;
                _innerContentContainer.turnIndexShouldBeCounted = true;
            }

            _innerContentContainer.countingAtStartOnly = true;

            // Does this choice end in an explicit divert?
            if (terminatingDivert) {
                _innerContentContainer.AddContent (terminatingDivert.runtimeObject);
            }

            return _outerContainer;
		}
예제 #33
0
파일: FlowBase.cs 프로젝트: inkle/ink
        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) {
                    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;
        }
예제 #34
0
파일: Divert.cs 프로젝트: inkle/ink
		public override Runtime.Object GenerateRuntimeObject ()
		{
            // End = end flow immediately
            // Done = return from thread or instruct the flow that it's safe to exit
            if (isEnd) {
                return Runtime.ControlCommand.End ();
            }
            if (isDone) {
                return Runtime.ControlCommand.Done ();
            }

            runtimeDivert = new Runtime.Divert ();

            // Normally we resolve the target content during the
            // Resolve phase, since we expect all runtime objects to
            // be available in order to find the final runtime path for
            // the destination. However, we need to resolve the target
            // (albeit without the runtime target) early so that
            // we can get information about the arguments - whether
            // they're by reference - since it affects the code we 
            // generate here.
            ResolveTargetContent ();


            CheckArgumentValidity ();

            // Passing arguments to the knot
            bool requiresArgCodeGen = arguments != null && arguments.Count > 0;
            if ( requiresArgCodeGen || isFunctionCall || isTunnel || isThread ) {

                var container = new Runtime.Container ();

                // Generate code for argument evaluation
                // This argument generation is coded defensively - it should
                // attempt to generate the code for all the parameters, even if
                // they don't match the expected arguments. This is so that the
                // parameter objects themselves are generated correctly and don't
                // get into a state of attempting to resolve references etc
                // without being generated.
                if (requiresArgCodeGen) {

                    // Function calls already in an evaluation context
                    if (!isFunctionCall) {
                        container.AddContent (Runtime.ControlCommand.EvalStart());
                    }

                    List<FlowBase.Argument> targetArguments = null;
                    if( targetContent )
                        targetArguments = (targetContent as FlowBase).arguments;

                    for (var i = 0; i < arguments.Count; ++i) {
                        Expression argToPass = arguments [i];
                        FlowBase.Argument argExpected = null; 
                        if( targetArguments != null && i < targetArguments.Count ) 
                            argExpected = targetArguments [i];

                        // Pass by reference: argument needs to be a variable reference
                        if (argExpected != null && argExpected.isByReference) {

                            var varRef = argToPass as VariableReference;
                            if (varRef == null) {
                                Error ("Expected variable name to pass by reference to 'ref " + argExpected.name + "' but saw " + argToPass.ToString ());
                                break;
                            }

                            var varPointer = new Runtime.VariablePointerValue (varRef.name);
                            container.AddContent (varPointer);
                        } 

                        // Normal value being passed: evaluate it as normal
                        else {
                            argToPass.GenerateIntoContainer (container);
                        }
                    }

                    // Function calls were already in an evaluation context
                    if (!isFunctionCall) {
                        container.AddContent (Runtime.ControlCommand.EvalEnd());
                    }
                }
                    

                // Starting a thread? A bit like a push to the call stack below... but not.
                // It sort of puts the call stack on a thread stack (argh!) - forks the full flow.
                if (isThread) {
                    container.AddContent(Runtime.ControlCommand.StartThread());
                }

                // If this divert is a function call, tunnel, we push to the call stack
                // so we can return again
                else if (isFunctionCall || isTunnel) {
                    runtimeDivert.pushesToStack = true;
                    runtimeDivert.stackPushType = isFunctionCall ? Runtime.PushPopType.Function : Runtime.PushPopType.Tunnel;
                }

                // Jump into the "function" (knot/stitch)
                container.AddContent (runtimeDivert);

                return container;
            } 

            // Simple divert
            else {
                return runtimeDivert;
            }			
		}
예제 #35
0
파일: Sequence.cs 프로젝트: inkle/ink
        // Generate runtime code that looks like:
        //   chosenIndex = MIN(sequence counter, num elements) e.g. for "Stopping"
        //   if chosenIndex == 0, divert to s0
        //   if chosenIndex == 1, divert to s1  [etc]
        //   increment sequence
        //
        //   - s0:
        //      <content for sequence element>
        //      divert back to increment point
        //   - s1:
        //      <content for sequence element>
        //      divert back to increment point
        //
        public override Runtime.Object GenerateRuntimeObject ()
        {
            var container = new Runtime.Container ();
            container.visitsShouldBeCounted = true;
            container.countingAtStartOnly = true;

            _sequenceDivertsToResove = new List<SequenceDivertToResolve> ();

            // Get sequence read count
            container.AddContent (Runtime.ControlCommand.EvalStart ());
            container.AddContent (Runtime.ControlCommand.VisitIndex ());

            // Chosen sequence index:
            //  - Stopping: take the MIN(read count, num elements - 1)
            if (sequenceType == SequenceType.Stopping) {
                container.AddContent (new Runtime.IntValue (sequenceElements.Count - 1));
                container.AddContent (Runtime.NativeFunctionCall.CallWithName ("MIN"));
            } 

            // - Cycle: take (read count % num elements)
            else if (sequenceType == SequenceType.Cycle) {
                container.AddContent (new Runtime.IntValue (sequenceElements.Count));
                container.AddContent (Runtime.NativeFunctionCall.CallWithName ("%"));
            }

            // Once: allow sequence count to be unbounded
            else if (sequenceType == SequenceType.Once) {
                // Do nothing - the sequence count will simply prevent
                // any content being referenced when it goes out of bounds
            } 

            // Shuffle
            else if (sequenceType == SequenceType.Shuffle) {
                // This one's a bit more complex! Choose the index at runtime.
                container.AddContent (new Runtime.IntValue (sequenceElements.Count));
                container.AddContent (Runtime.ControlCommand.SequenceShuffleIndex ());
            }

            // Not implemented
            else {
                throw new System.NotImplementedException ();
            }

            container.AddContent (Runtime.ControlCommand.EvalEnd ());

            // Create point to return to when sequence is complete
            var postSequenceNoOp = Runtime.ControlCommand.NoOp ();

            var elIndex = 0;
            foreach (var el in sequenceElements) {

                // This sequence element:
                //  if( chosenIndex == this index ) divert to this sequence element
                // duplicate chosen sequence index, since it'll be consumed by "=="
                container.AddContent (Runtime.ControlCommand.EvalStart ());
                container.AddContent (Runtime.ControlCommand.Duplicate ()); 
                container.AddContent (new Runtime.IntValue (elIndex));
                container.AddContent (Runtime.NativeFunctionCall.CallWithName ("=="));
                container.AddContent (Runtime.ControlCommand.EvalEnd ());

                // Divert branch for this sequence element
                var sequenceDivert = new Runtime.Divert ();
                sequenceDivert.isConditional = true;
                container.AddContent (sequenceDivert);

                // Generate content for this sequence element
                var contentContainerForSequenceBranch = (Runtime.Container) el.runtimeObject;
                contentContainerForSequenceBranch.name = "s" + elIndex;
                contentContainerForSequenceBranch.InsertContent (Runtime.ControlCommand.PopEvaluatedValue (), 0);

                // When sequence element is complete, divert back to end of sequence
                var seqBranchCompleteDivert = new Runtime.Divert ();
                contentContainerForSequenceBranch.AddContent (seqBranchCompleteDivert);
                container.AddToNamedContentOnly (contentContainerForSequenceBranch);

                // Save the diverts for reference resolution later (in ResolveReferences)
                AddDivertToResolve (sequenceDivert, contentContainerForSequenceBranch);
                AddDivertToResolve (seqBranchCompleteDivert, postSequenceNoOp);

                elIndex++;
            }

            container.AddContent (postSequenceNoOp);

            return container;
        }
예제 #36
0
파일: Expression.cs 프로젝트: paulloz/ink
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            innerExpression.GenerateIntoContainer(container);

            container.AddContent(Runtime.NativeFunctionCall.CallWithName(nativeNameForOp));
        }
예제 #37
0
파일: Choice.cs 프로젝트: tomkail/ink
        public override Runtime.Object GenerateRuntimeObject()
        {
            _outerContainer = new Runtime.Container ();

            // Content names for different types of choice:
            //  * start content [choice only content] inner content
            //  * start content   -> divert
            //  * start content
            //  * [choice only content]

            // Hmm, this structure has become slightly insane!
            //
            // [
            //     EvalStart
            //     BeginString
            //     PUSH (function)
            //     -> s
            //     EndString
            //     BeginString
            //     ... choice only content
            //     EndEval
            //     Condition expression
            //     choice: -> "c"
            //     (s) = [
            //         start content
            //     ]
            //     (c) = [
            //         PUSH (function)
            //         -> s
            //         inner content
            //     ]
            // ]

            _runtimeChoice = new Runtime.ChoicePoint (onceOnly);
            _runtimeChoice.isInvisibleDefault = this.isInvisibleDefault;

            if (startContent || choiceOnlyContent || condition) {
                _outerContainer.AddContent (Runtime.ControlCommand.EvalStart ());
            }

            // Start content is put into a named container that's referenced both
            // when displaying the choice initially, and when generating the text
            // when the choice is chosen.
            if (startContent) {

                // Mark the start of the choice text generation, so that the runtime
                // knows where to rewind to to extract the content from the output stream.
                _outerContainer.AddContent (Runtime.ControlCommand.BeginString ());

                // "Function call" to generate start content
                _divertToStartContentOuter = new Runtime.Divert (Runtime.PushPopType.Function);
                _outerContainer.AddContent (_divertToStartContentOuter);

                // Start content itself in a named container
                _startContentRuntimeContainer = startContent.GenerateRuntimeObject () as Runtime.Container;
                _startContentRuntimeContainer.name = "s";
                _outerContainer.AddToNamedContentOnly (_startContentRuntimeContainer);

                _outerContainer.AddContent (Runtime.ControlCommand.EndString ());

                _runtimeChoice.hasStartContent = true;
            }

            // Choice only content - mark the start, then generate it directly into the outer container
            if (choiceOnlyContent) {
                _outerContainer.AddContent (Runtime.ControlCommand.BeginString ());

                var choiceOnlyRuntimeContent = choiceOnlyContent.GenerateRuntimeObject () as Runtime.Container;
                _outerContainer.AddContentsOfContainer (choiceOnlyRuntimeContent);

                _outerContainer.AddContent (Runtime.ControlCommand.EndString ());

                _runtimeChoice.hasChoiceOnlyContent = true;
            }

            // Generate any condition for this choice
            if (condition) {
                condition.GenerateIntoContainer (_outerContainer);
                _runtimeChoice.hasCondition = true;
            }

            if (startContent || choiceOnlyContent || condition) {
                _outerContainer.AddContent (Runtime.ControlCommand.EvalEnd ());
            }

            // Add choice itself
            _outerContainer.AddContent (_runtimeChoice);

            // Container that choice points to for when it's chosen
            _innerContentContainer = new Runtime.Container ();

            // Repeat start content by diverting to its container
            if (startContent) {
                _divertToStartContentInner = new Runtime.Divert (Runtime.PushPopType.Function);
                _innerContentContainer.AddContent (_divertToStartContentInner);
            }

            // Choice's own inner content
            if (innerContent) {
                var innerChoiceOnlyContent = innerContent.GenerateRuntimeObject () as Runtime.Container;
                _innerContentContainer.AddContentsOfContainer (innerChoiceOnlyContent);
            }

            // Fully parsed choice will be a full line, so it needs to be terminated
            if (startContent || innerContent) {
                _innerContentContainer.AddContent(new Runtime.StringValue("\n"));
            }

            // Use "c" as the destination name within the choice's outer container
            _innerContentContainer.name = "c";
            _outerContainer.AddToNamedContentOnly (_innerContentContainer);

            if (this.story.countAllVisits) {
                _innerContentContainer.visitsShouldBeCounted = true;
                _innerContentContainer.turnIndexShouldBeCounted = true;
            }

            _innerContentContainer.countingAtStartOnly = true;

            // Does this choice end in an explicit divert?
            if (terminatingDivert) {
                _innerContentContainer.AddContent (terminatingDivert.runtimeObject);
            }

            return _outerContainer;
        }
예제 #38
0
        // 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:", System.StringComparison.InvariantCulture)) {
                            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 = shouldMatchEquality && !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 (shouldMatchEquality)
                    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";

            if( duplicatesStackValue )
                _contentContainer.InsertContent (Runtime.ControlCommand.PopEvaluatedValue (), 0);

            container.AddToNamedContentOnly (_contentContainer);

            returnDivert = new Runtime.Divert ();
            _contentContainer.AddContent (returnDivert);

            return container;
        }
예제 #39
0
파일: Story.cs 프로젝트: inkle/ink
        public Runtime.Story ExportRuntime(ErrorHandler errorHandler = null)
		{
            _errorHandler = errorHandler;

            // Find all constants before main export begins, so that VariableReferences know
            // whether to generate a runtime variable reference or the literal value
            constants = new Dictionary<string, Expression> ();
            foreach (var constDecl in FindAll<ConstantDeclaration> ()) {

                // Check for duplicate definitions
                Parsed.Expression existingDefinition = null;
                if (constants.TryGetValue (constDecl.constantName, out existingDefinition)) {
                    if (!existingDefinition.Equals (constDecl.expression)) {
                        var errorMsg = string.Format ("CONST '{0}' has been redefined with a different value. Multiple definitions of the same CONST are valid so long as they contain the same value. Initial definition was on {1}.", constDecl.constantName, existingDefinition.debugMetadata);
                        Error (errorMsg, constDecl, isWarning:false);
                    }
                }

                constants [constDecl.constantName] = constDecl.expression;
            }

            externals = new Dictionary<string, ExternalDeclaration> ();

			// Get default implementation of runtimeObject, which calls ContainerBase's generation method
            var rootContainer = runtimeObject as Runtime.Container;

            // Export initialisation of global variables
            // TODO: We *could* add this as a declarative block to the story itself...
            var variableInitialisation = new Runtime.Container ();
            variableInitialisation.AddContent (Runtime.ControlCommand.EvalStart ());

            // Global variables are those that are local to the story and marked as global
            foreach (var nameDeclPair in variableDeclarations) {
                var varName = nameDeclPair.Key;
                var varDecl = nameDeclPair.Value;
                if (varDecl.isGlobalDeclaration) {
                    varDecl.expression.GenerateIntoContainer (variableInitialisation);
                    var runtimeVarAss = new Runtime.VariableAssignment (varName, isNewDeclaration:true);
                    runtimeVarAss.isGlobal = true;
                    variableInitialisation.AddContent (runtimeVarAss);
                }
            }

            variableInitialisation.AddContent (Runtime.ControlCommand.EvalEnd ());
            variableInitialisation.AddContent (Runtime.ControlCommand.End ());

            if (variableDeclarations.Count > 0) {
                variableInitialisation.name = "global decl";
                rootContainer.AddToNamedContentOnly (variableInitialisation);
            }

            // Signal that it's safe to exit without error, even if there are no choices generated
            // (this only happens at the end of top level content that isn't in any particular knot)
            rootContainer.AddContent (Runtime.ControlCommand.Done ());

			// Replace runtimeObject with Story object instead of the Runtime.Container generated by Parsed.ContainerBase
            var runtimeStory = new Runtime.Story (rootContainer);
			runtimeObject = runtimeStory;

            if (hadError)
                return null;

            // Optimisation step - inline containers that can be
            FlattenContainersIn (rootContainer);

			// Now that the story has been fulled parsed into a hierarchy,
			// and the derived runtime hierarchy has been built, we can
			// resolve referenced symbols such as variables and paths.
			// e.g. for paths " -> knotName --> stitchName" into an INKPath (knotName.stitchName)
			// We don't make any assumptions that the INKPath follows the same
			// conventions as the script format, so we resolve to actual objects before
			// translating into an INKPath. (This also allows us to choose whether
			// we want the paths to be absolute)
			ResolveReferences (this);

            if (hadError)
                return null;

            runtimeStory.ResetState ();

			return runtimeStory;
		}
예제 #40
0
        // 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()
        {
            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 = shouldMatchEquality && !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 (shouldMatchEquality)
                    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";

            if( duplicatesStackValue )
                _contentContainer.InsertContent (Runtime.ControlCommand.PopEvaluatedValue (), 0);

            container.AddToNamedContentOnly (_contentContainer);

            returnDivert = new Runtime.Divert ();
            _contentContainer.AddContent (returnDivert);

            return container;
        }
예제 #41
0
        // Generate runtime code that looks like:
        //   chosenIndex = MIN(sequence counter, num elements) e.g. for "Stopping"
        //   if chosenIndex == 0, divert to s0
        //   if chosenIndex == 1, divert to s1  [etc]
        //   increment sequence
        //
        //   - s0:
        //      <content for sequence element>
        //      divert back to increment point
        //   - s1:
        //      <content for sequence element>
        //      divert back to increment point
        //
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            container.visitsShouldBeCounted = true;
            container.countingAtStartOnly   = true;

            _sequenceDivertsToResove = new List <SequenceDivertToResolve> ();

            // Get sequence read count
            container.AddContent(Runtime.ControlCommand.EvalStart());
            container.AddContent(Runtime.ControlCommand.VisitIndex());

            // Chosen sequence index:
            //  - Stopping: take the MIN(read count, num elements - 1)
            if (sequenceType == SequenceType.Stopping)
            {
                container.AddContent(new Runtime.LiteralInt(sequenceElements.Count - 1));
                container.AddContent(Runtime.NativeFunctionCall.CallWithName("MIN"));
            }

            // - Cycle: take (read count % num elements)
            else if (sequenceType == SequenceType.Cycle)
            {
                container.AddContent(new Runtime.LiteralInt(sequenceElements.Count));
                container.AddContent(Runtime.NativeFunctionCall.CallWithName("%"));
            }

            // Once: allow sequence count to be unbounded
            else if (sequenceType == SequenceType.Once)
            {
                // Do nothing - the sequence count will simply prevent
                // any content being referenced when it goes out of bounds
            }

            // Shuffle
            else if (sequenceType == SequenceType.Shuffle)
            {
                // This one's a bit more complex! Choose the index at runtime.
                container.AddContent(new Runtime.LiteralInt(sequenceElements.Count));
                container.AddContent(Runtime.ControlCommand.SequenceShuffleIndex());
            }

            // Not implemented
            else
            {
                throw new System.NotImplementedException();
            }

            container.AddContent(Runtime.ControlCommand.EvalEnd());

            // Create point to return to when sequence is complete
            var postSequenceNoOp = Runtime.ControlCommand.NoOp();

            var elIndex = 0;

            foreach (var el in sequenceElements)
            {
                // This sequence element:
                //  if( chosenIndex == this index ) divert to this sequence element
                // duplicate chosen sequence index, since it'll be consumed by "=="
                container.AddContent(Runtime.ControlCommand.EvalStart());
                container.AddContent(Runtime.ControlCommand.Duplicate());
                container.AddContent(new Runtime.LiteralInt(elIndex));
                container.AddContent(Runtime.NativeFunctionCall.CallWithName("=="));
                container.AddContent(Runtime.ControlCommand.EvalEnd());

                // Divert branch for this sequence element
                var sequenceDivert = new Runtime.Divert();
                container.AddContent(new Runtime.Branch(sequenceDivert));

                // Generate content for this sequence element
                var contentContainerForSequenceBranch = (Runtime.Container)el.runtimeObject;
                contentContainerForSequenceBranch.name = "s" + elIndex;

                // When sequence element is complete, divert back to end of sequence
                var seqBranchCompleteDivert = new Runtime.Divert();
                contentContainerForSequenceBranch.AddContent(seqBranchCompleteDivert);
                container.AddToNamedContentOnly(contentContainerForSequenceBranch);

                // Save the diverts for reference resolution later (in ResolveReferences)
                AddDivertToResolve(sequenceDivert, contentContainerForSequenceBranch);
                AddDivertToResolve(seqBranchCompleteDivert, postSequenceNoOp);

                elIndex++;
            }

            container.AddContent(postSequenceNoOp);

            return(container);
        }
예제 #42
0
        public override void GenerateIntoContainer(Runtime.Container container)
        {
            if (isChoiceCount)
            {
                if (arguments.Count > 0)
                {
                    Error("The CHOICE_COUNT() function shouldn't take any arguments");
                }

                container.AddContent(Runtime.ControlCommand.ChoiceCount());
            }
            else if (isTurnsSince)
            {
                var divertTarget         = arguments [0] as DivertTarget;
                var variableDivertTarget = arguments [0] as VariableReference;

                if (arguments.Count != 1 || (divertTarget == null && variableDivertTarget == null))
                {
                    Error("The TURNS_SINCE() function should take one argument: a divert target to the target knot, stitch, gather or choice you want to check. e.g. TURNS_SINCE(-> myKnot)");
                    return;
                }

                if (divertTarget)
                {
                    _turnCountDivertTarget = divertTarget;
                    AddContent(_turnCountDivertTarget);

                    _turnCountDivertTarget.GenerateIntoContainer(container);
                }
                else
                {
                    _turnCountVariableReference = variableDivertTarget;
                    AddContent(_turnCountVariableReference);

                    _turnCountVariableReference.GenerateIntoContainer(container);

                    if (!story.countAllVisits)
                    {
                        Error("Attempting to get TURNS_SINCE for a variable target without -c compiler option. You need the compiler switch turned on so that it can track turn counts for everything, not just those you directly reference.");
                    }
                }


                container.AddContent(Runtime.ControlCommand.TurnsSince());
            }

            else if (isRandom)
            {
                if (arguments.Count != 2)
                {
                    Error("RANDOM should take 2 parameters: a minimum and a maximum integer");
                }

                // We can type check single values, but not complex expressions
                for (int arg = 0; arg < arguments.Count; arg++)
                {
                    if (arguments [arg] is Number)
                    {
                        var num = arguments [arg] as Number;
                        if (!(num.value is int))
                        {
                            string paramName = arg == 0 ? "minimum" : "maximum";
                            Error("RANDOM's " + paramName + " parameter should be an integer");
                        }
                    }

                    arguments[arg].GenerateIntoContainer(container);
                }

                container.AddContent(Runtime.ControlCommand.Random());
            }

            else if (isSeedRandom)
            {
                if (arguments.Count != 1)
                {
                    Error("SEED_RANDOM should take 1 parameter - an integer seed");
                }

                var num = arguments [0] as Number;
                if (num && !(num.value is int))
                {
                    Error("SEED_RANDOM's parameter should be an integer seed");
                }

                arguments [0].GenerateIntoContainer(container);

                container.AddContent(Runtime.ControlCommand.SeedRandom());
            }

            // Normal function call
            else
            {
                container.AddContent(_proxyDivert.runtimeObject);
            }

            // Function calls that are used alone on a tilda-based line:
            //  ~ func()
            // Should tidy up any returned value from the evaluation stack,
            // since it's unused.
            if (shouldPopReturnedValue)
            {
                container.AddContent(Runtime.ControlCommand.PopEvaluatedValue());
            }
        }
예제 #43
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            var container = new Runtime.Container();

            // Set override path for tunnel onwards (or nothing)
            container.AddContent(Runtime.ControlCommand.EvalStart());

            if (divertAfter)
            {
                // Generate runtime object's generated code and steal the arguments runtime code
                var returnRuntimeObj       = divertAfter.GenerateRuntimeObject();
                var returnRuntimeContainer = returnRuntimeObj as Runtime.Container;
                if (returnRuntimeContainer)
                {
                    // Steal all code for generating arguments from the divert
                    var args = divertAfter.arguments;
                    if (args != null && args.Count > 0)
                    {
                        // Steal everything betwen eval start and eval end
                        int evalStart = -1;
                        int evalEnd   = -1;
                        for (int i = 0; i < returnRuntimeContainer.content.Count; i++)
                        {
                            var cmd = returnRuntimeContainer.content [i] as Runtime.ControlCommand;
                            if (cmd)
                            {
                                if (evalStart == -1 && cmd.commandType == Runtime.ControlCommand.CommandType.EvalStart)
                                {
                                    evalStart = i;
                                }
                                else if (cmd.commandType == Runtime.ControlCommand.CommandType.EvalEnd)
                                {
                                    evalEnd = i;
                                }
                            }
                        }

                        for (int i = evalStart + 1; i < evalEnd; i++)
                        {
                            var obj = returnRuntimeContainer.content [i];
                            obj.parent = null; // prevent error of being moved between owners
                            container.AddContent(returnRuntimeContainer.content [i]);
                        }
                    }
                }

                // Supply the divert target for the tunnel onwards target, either variable or more commonly, the explicit name
                var returnDivertObj = returnRuntimeObj as Runtime.Divert;
                if (returnDivertObj != null && returnDivertObj.hasVariableTarget)
                {
                    var runtimeVarRef = new Runtime.VariableReference(returnDivertObj.variableDivertName);
                    container.AddContent(runtimeVarRef);
                }
                else
                {
                    _overrideDivertTarget = new Runtime.DivertTargetValue();
                    container.AddContent(_overrideDivertTarget);
                }
            }

            // No divert after tunnel onwards
            else
            {
                container.AddContent(new Runtime.Void());
            }

            container.AddContent(Runtime.ControlCommand.EvalEnd());

            container.AddContent(Runtime.ControlCommand.PopTunnel());

            return(container);
        }
예제 #44
0
        public override Runtime.Object GenerateRuntimeObject()
        {
            _outerContainer = new Runtime.Container();

            // Content names for different types of choice:
            //  * start content [choice only content] inner content
            //  * start content   -> divert
            //  * start content
            //  * [choice only content]

            // Hmm, this structure has become slightly insane!
            //
            // [
            //     EvalStart
            //     assign $r = $r1   -- return target = return label 1
            //     BeginString
            //     -> s
            //     [(r1)]            -- return label 1 (after start content)
            //     EndString
            //     BeginString
            //     ... choice only content
            //     EndEval
            //     Condition expression
            //     choice: -> "c-0"
            //     (s) = [
            //         start content
            //         -> r          -- goto return label 1 or 2
            //     ]
            //  ]
            //
            //  in parent's container: (the inner content for the choice)
            //
            //  (c-0) = [
            //      EvalStart
            //      assign $r = $r2   -- return target = return label 2
            //      EndEval
            //      -> s
            //      [(r2)]            -- return label 1 (after start content)
            //      inner content
            //  ]
            //

            _runtimeChoice = new Runtime.ChoicePoint(onceOnly);
            _runtimeChoice.isInvisibleDefault = this.isInvisibleDefault;

            if (startContent || choiceOnlyContent || condition)
            {
                _outerContainer.AddContent(Runtime.ControlCommand.EvalStart());
            }

            // Start content is put into a named container that's referenced both
            // when displaying the choice initially, and when generating the text
            // when the choice is chosen.
            if (startContent)
            {
                // Generate start content and return
                //  - We can't use a function since it uses a call stack element, which would
                //    put temporary values out of scope. Instead we manually divert around.
                //  - $r is a variable divert target contains the return point
                _returnToR1 = new Runtime.DivertTargetValue();
                _outerContainer.AddContent(_returnToR1);
                var varAssign = new Runtime.VariableAssignment("$r", true);
                _outerContainer.AddContent(varAssign);

                // Mark the start of the choice text generation, so that the runtime
                // knows where to rewind to to extract the content from the output stream.
                _outerContainer.AddContent(Runtime.ControlCommand.BeginString());

                _divertToStartContentOuter = new Runtime.Divert();
                _outerContainer.AddContent(_divertToStartContentOuter);

                // Start content itself in a named container
                _startContentRuntimeContainer      = startContent.GenerateRuntimeObject() as Runtime.Container;
                _startContentRuntimeContainer.name = "s";

                // Effectively, the "return" statement - return to the point specified by $r
                var varDivert = new Runtime.Divert();
                varDivert.variableDivertName = "$r";
                _startContentRuntimeContainer.AddContent(varDivert);

                // Add the container
                _outerContainer.AddToNamedContentOnly(_startContentRuntimeContainer);

                // This is the label to return to
                _r1Label      = new Runtime.Container();
                _r1Label.name = "$r1";
                _outerContainer.AddContent(_r1Label);

                _outerContainer.AddContent(Runtime.ControlCommand.EndString());

                _runtimeChoice.hasStartContent = true;
            }

            // Choice only content - mark the start, then generate it directly into the outer container
            if (choiceOnlyContent)
            {
                _outerContainer.AddContent(Runtime.ControlCommand.BeginString());

                var choiceOnlyRuntimeContent = choiceOnlyContent.GenerateRuntimeObject() as Runtime.Container;
                _outerContainer.AddContentsOfContainer(choiceOnlyRuntimeContent);

                _outerContainer.AddContent(Runtime.ControlCommand.EndString());

                _runtimeChoice.hasChoiceOnlyContent = true;
            }

            // Generate any condition for this choice
            if (condition)
            {
                condition.GenerateIntoContainer(_outerContainer);
                _runtimeChoice.hasCondition = true;
            }

            if (startContent || choiceOnlyContent || condition)
            {
                _outerContainer.AddContent(Runtime.ControlCommand.EvalEnd());
            }

            // Add choice itself
            _outerContainer.AddContent(_runtimeChoice);

            // Container that choice points to for when it's chosen
            _innerContentContainer = new Runtime.Container();

            // Repeat start content by diverting to its container
            if (startContent)
            {
                // Set the return point when jumping back into the start content
                //  - In this case, it's the $r2 point, within the choice content "c".
                _returnToR2 = new Runtime.DivertTargetValue();
                _innerContentContainer.AddContent(Runtime.ControlCommand.EvalStart());
                _innerContentContainer.AddContent(_returnToR2);
                _innerContentContainer.AddContent(Runtime.ControlCommand.EvalEnd());
                var varAssign = new Runtime.VariableAssignment("$r", true);
                _innerContentContainer.AddContent(varAssign);

                // Main divert into start content
                _divertToStartContentInner = new Runtime.Divert();
                _innerContentContainer.AddContent(_divertToStartContentInner);

                // Define label to return to
                _r2Label      = new Runtime.Container();
                _r2Label.name = "$r2";
                _innerContentContainer.AddContent(_r2Label);
            }

            // Choice's own inner content
            if (innerContent)
            {
                var innerChoiceOnlyContent = innerContent.GenerateRuntimeObject() as Runtime.Container;
                _innerContentContainer.AddContentsOfContainer(innerChoiceOnlyContent);
            }

            if (this.story.countAllVisits)
            {
                _innerContentContainer.visitsShouldBeCounted = true;
            }

            _innerContentContainer.countingAtStartOnly = true;

            return(_outerContainer);
        }