Exemplo n.º 1
0
        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(varIdentifier?.name));

            // 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.
            _runtimeAssignment = new Runtime.VariableAssignment(varIdentifier?.name, false);
            container.AddContent(_runtimeAssignment);
        }
Exemplo n.º 2
0
        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);
            }
        }
Exemplo n.º 3
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
            if (expression != null)
            {
                container.AddContent(expression.runtimeObject);
            }
            else if (listDefinition != null)
            {
                container.AddContent(listDefinition.runtimeObject);
            }

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

            return(container);
        }
Exemplo n.º 4
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);
        }
Exemplo n.º 5
0
        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;
            }

            // List definitions are treated like constants too - they should be usable
            // from other variable declarations.
            _listDefs = new Dictionary <string, ListDefinition> ();
            foreach (var listDef in FindAll <ListDefinition> ())
            {
                _listDefs [listDef.identifier?.name] = listDef;
            }

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

            // Resolution of weave point names has to come first, before any runtime code generation
            // since names have to be ready before diverts start getting created.
            // (It used to be done in the constructor for a weave, but didn't allow us to generate
            // errors when name resolution failed.)
            ResolveWeavePointNaming();

            // 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
            var runtimeLists = new List <Runtime.ListDefinition> ();

            foreach (var nameDeclPair in variableDeclarations)
            {
                var varName = nameDeclPair.Key;
                var varDecl = nameDeclPair.Value;
                if (varDecl.isGlobalDeclaration)
                {
                    if (varDecl.listDefinition != null)
                    {
                        _listDefs[varName] = varDecl.listDefinition;
                        variableInitialisation.AddContent(varDecl.listDefinition.runtimeObject);
                        runtimeLists.Add(varDecl.listDefinition.runtimeListDefinition);
                    }
                    else
                    {
                        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, runtimeLists);

            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);
        }
Exemplo n.º 6
0
Arquivo: Story.cs Projeto: 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;
		}
Exemplo n.º 7
0
        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> ())
            {
                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);
            }

            // 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);
        }
Exemplo n.º 8
0
Arquivo: Choice.cs Projeto: 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;
		}
Exemplo n.º 9
0
        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);
            }
        }