Inheritance: Parsed.Object, INamedContent
Beispiel #1
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);
        }
Beispiel #2
0
        public Parsed.Object ContentWithNameAtLevel(string name, FlowLevel?level = null, bool deepSearch = false)
        {
            // Referencing self?
            if (level == this.flowLevel || level == null)
            {
                if (name == this.name)
                {
                    return(this);
                }
            }

            if (level == FlowLevel.WeavePoint || level == null)
            {
                Parsed.Object weavePointResult = null;

                if (_rootWeave)
                {
                    weavePointResult = (Parsed.Object)_rootWeave.WeavePointNamed(name);
                    if (weavePointResult)
                    {
                        return(weavePointResult);
                    }
                }

                // Stop now if we only wanted a result if it's a weave point?
                if (level == FlowLevel.WeavePoint)
                {
                    return(deepSearch ? DeepSearchForAnyLevelContent(name) : null);
                }
            }

            // If this flow would be incapable of containing the requested level, early out
            // (e.g. asking for a Knot from a Stitch)
            if (level != null && level < this.flowLevel)
            {
                return(null);
            }

            FlowBase subFlow = null;

            if (_subFlowsByName.TryGetValue(name, out subFlow))
            {
                if (level == null || level == subFlow.flowLevel)
                {
                    return(subFlow);
                }
            }

            return(deepSearch ? DeepSearchForAnyLevelContent(name) : null);
        }
Beispiel #3
0
        // Returns false if there's an error
        void CheckArgumentValidity()
        {
            if (isEmpty)
            {
                return;
            }

            // Argument passing: Check for errors in number of arguments
            var numArgs = 0;

            if (arguments != null && arguments.Count > 0)
            {
                numArgs = arguments.Count;
            }

            // Missing content?
            // Can't check arguments properly. It'll be due to some
            // other error though, so although there's a problem and
            // we report false, we don't need to report a specific error.
            // It may also be because it's a valid call to an external
            // function, that we check at the resolve stage.
            if (targetContent == null)
            {
                return;
            }

            FlowBase targetFlow = targetContent as FlowBase;

            // No error, crikey!
            if (numArgs == 0 && (targetFlow == null || !targetFlow.hasParameters))
            {
                return;
            }

            if (targetFlow == null && numArgs > 0)
            {
                Error("target needs to be a knot or stitch in order to pass arguments");
                return;
            }

            if (targetFlow.arguments == null && numArgs > 0)
            {
                Error("target (" + targetFlow.name + ") doesn't take parameters");
                return;
            }

            if (this.parent is DivertTarget)
            {
                if (numArgs > 0)
                {
                    Error("can't store arguments in a divert target variable");
                }
                return;
            }

            var paramCount = targetFlow.arguments.Count;

            if (paramCount != numArgs)
            {
                string butClause;
                if (numArgs == 0)
                {
                    butClause = "but there weren't any passed to it";
                }
                else if (numArgs < paramCount)
                {
                    butClause = "but only got " + numArgs;
                }
                else
                {
                    butClause = "but got " + numArgs;
                }
                Error("to '" + targetFlow.name + "' requires " + paramCount + " arguments, " + butClause);
                return;
            }

            // Light type-checking for divert target arguments
            for (int i = 0; i < paramCount; ++i)
            {
                FlowBase.Argument flowArg    = targetFlow.arguments [i];
                Parsed.Expression divArgExpr = arguments [i];

                // Expecting a divert target as an argument, let's do some basic type checking
                if (flowArg.isDivertTarget)
                {
                    // Not passing a divert target or any kind of variable reference?
                    var varRef = divArgExpr as VariableReference;
                    if (!(divArgExpr is DivertTarget) && varRef == null)
                    {
                        Error("Target '" + targetFlow.name + "' expects a divert target for the parameter named -> " + flowArg.name + " but saw " + divArgExpr, divArgExpr);
                    }

                    // Passing 'a' instead of '-> a'?
                    // i.e. read count instead of divert target
                    else if (varRef != null)
                    {
                        // Unfortunately have to manually resolve here since we're still in code gen
                        var           knotCountPath  = new Path(varRef.path);
                        Parsed.Object targetForCount = knotCountPath.ResolveFromContext(varRef);
                        if (targetForCount != null)
                        {
                            Error("Passing read count of '" + knotCountPath.dotSeparatedComponents + "' instead of a divert target. You probably meant '" + knotCountPath + "'");
                        }
                    }
                }
            }

            if (targetFlow == null)
            {
                Error("Can't call as a function or with arguments unless it's a knot or stitch");
                return;
            }

            return;
        }
Beispiel #4
0
        public Parsed.Path PathRelativeTo(Parsed.Object otherObj)
        {
            var ownAncestry   = ancestry;
            var otherAncestry = otherObj.ancestry;

            Parsed.Object highestCommonAncestor = null;
            int           minLength             = System.Math.Min(ownAncestry.Count, otherAncestry.Count);

            for (int i = 0; i < minLength; ++i)
            {
                var a1 = ancestry [i];
                var a2 = otherAncestry [i];
                if (a1 == a2)
                {
                    highestCommonAncestor = a1;
                }
                else
                {
                    break;
                }
            }

            FlowBase commonFlowAncestor = highestCommonAncestor as FlowBase;

            if (commonFlowAncestor == null)
            {
                commonFlowAncestor = highestCommonAncestor.ClosestFlowBase();
            }


            var       pathComponents = new List <string> ();
            bool      hasWeavePoint  = false;
            FlowLevel baseFlow       = FlowLevel.WeavePoint;

            var ancestor = this;

            while (ancestor && (ancestor != commonFlowAncestor) && !(ancestor is Fiction))
            {
                if (ancestor == commonFlowAncestor)
                {
                    break;
                }

                if (!hasWeavePoint)
                {
                    var weavePointAncestor = ancestor as IWeavePoint;
                    if (weavePointAncestor != null && weavePointAncestor.name != null)
                    {
                        pathComponents.Add(weavePointAncestor.name);
                        hasWeavePoint = true;
                        continue;
                    }
                }

                var flowAncestor = ancestor as FlowBase;
                if (flowAncestor)
                {
                    pathComponents.Add(flowAncestor.name);
                    baseFlow = flowAncestor.flowLevel;
                }

                ancestor = ancestor.parent;
            }

            pathComponents.Reverse();

            if (pathComponents.Count > 0)
            {
                return(new Path(baseFlow, pathComponents));
            }

            return(null);
        }
Beispiel #5
0
        // Check given symbol type against everything that's of a higher priority in the ordered SymbolType enum (above).
        // When the given symbol type level is reached, we early-out / return.
        public void CheckForNamingCollisions(Parsed.Object obj, Identifier identifier, SymbolType symbolType, string typeNameOverride = null)
        {
            string typeNameToPrint = typeNameOverride ?? obj.typeName;

            if (IsReservedKeyword(identifier?.name))
            {
                obj.Error("'" + name + "' cannot be used for the name of a " + typeNameToPrint.ToLower() + " because it's a reserved keyword");
                return;
            }

            if (FunctionCall.IsBuiltIn(identifier?.name))
            {
                obj.Error("'" + name + "' cannot be used for the name of a " + typeNameToPrint.ToLower() + " because it's a built in function");
                return;
            }

            // Top level knots
            FlowBase knotOrFunction = ContentWithNameAtLevel(identifier?.name, FlowLevel.Knot) as FlowBase;

            if (knotOrFunction && (knotOrFunction != obj || symbolType == SymbolType.Arg))
            {
                NameConflictError(obj, identifier?.name, knotOrFunction, typeNameToPrint);
                return;
            }

            if (symbolType < SymbolType.List)
            {
                return;
            }

            // Lists
            foreach (var namedListDef in _listDefs)
            {
                var listDefName = namedListDef.Key;
                var listDef     = namedListDef.Value;
                if (identifier?.name == listDefName && obj != listDef && listDef.variableAssignment != obj)
                {
                    NameConflictError(obj, identifier?.name, listDef, typeNameToPrint);
                }

                // We don't check for conflicts between individual elements in
                // different lists because they are namespaced.
                if (!(obj is ListElementDefinition))
                {
                    foreach (var item in listDef.itemDefinitions)
                    {
                        if (identifier?.name == item.name)
                        {
                            NameConflictError(obj, identifier?.name, item, typeNameToPrint);
                        }
                    }
                }
            }

            // Don't check for VAR->VAR conflicts because that's handled separately
            // (necessary since checking looks up in a dictionary)
            if (symbolType <= SymbolType.Var)
            {
                return;
            }

            // Global variable collision
            VariableAssignment varDecl = null;

            if (variableDeclarations.TryGetValue(identifier?.name, out varDecl))
            {
                if (varDecl != obj && varDecl.isGlobalDeclaration && varDecl.listDefinition == null)
                {
                    NameConflictError(obj, identifier?.name, varDecl, typeNameToPrint);
                }
            }

            if (symbolType < SymbolType.SubFlowAndWeave)
            {
                return;
            }

            // Stitches, Choices and Gathers
            var path          = new Path(identifier);
            var targetContent = path.ResolveFromContext(obj);

            if (targetContent && targetContent != obj)
            {
                NameConflictError(obj, identifier?.name, targetContent, typeNameToPrint);
                return;
            }

            if (symbolType < SymbolType.Arg)
            {
                return;
            }

            // Arguments to the current flow
            if (symbolType != SymbolType.Arg)
            {
                FlowBase flow = obj as FlowBase;
                if (flow == null)
                {
                    flow = obj.ClosestFlowBase();
                }
                if (flow && flow.hasParameters)
                {
                    foreach (var arg in flow.arguments)
                    {
                        if (arg.identifier?.name == identifier?.name)
                        {
                            obj.Error(typeNameToPrint + " '" + name + "': Name has already been used for a argument to " + flow.identifier + " on " + flow.debugMetadata);
                            return;
                        }
                    }
                }
            }
        }
Beispiel #6
0
        // Returns false if there's an error
        void CheckArgumentValidity()
        {
            if (isToGather)
            {
                return;
            }

            // Argument passing: Check for errors in number of arguments
            var numArgs = 0;

            if (arguments != null && arguments.Count > 0)
            {
                numArgs = arguments.Count;
            }

            // Missing content?
            // Can't check arguments properly. It'll be due to some
            // other error though, so although there's a problem and
            // we report false, we don't need to report a specific error.
            // It may also be because it's a valid call to an external
            // function, that we check at the resolve stage.
            if (targetContent == null)
            {
                return;
            }

            FlowBase targetFlow = targetContent as FlowBase;

            // No error, crikey!
            if (numArgs == 0 && (targetFlow == null || !targetFlow.hasParameters))
            {
                return;
            }

            if (targetFlow == null && numArgs > 0)
            {
                Error("target needs to be a knot or stitch in order to pass arguments");
                return;
            }

            if (targetFlow.arguments == null && numArgs > 0)
            {
                Error("target (" + targetFlow.name + ") doesn't take parameters");
                return;
            }

            var paramCount = targetFlow.arguments.Count;

            if (paramCount > 0 && this.parent is DivertTarget)
            {
                Error("Can't store a link to a knot that takes parameters in a variable");
                return;
            }

            if (paramCount != numArgs)
            {
                string butClause;
                if (numArgs == 0)
                {
                    butClause = "but there weren't any passed to it";
                }
                else if (numArgs < paramCount)
                {
                    butClause = "but only got " + numArgs;
                }
                else
                {
                    butClause = "but got " + numArgs;
                }
                Error("to '" + targetFlow.name + "' requires " + paramCount + " arguments, " + butClause);
                return;
            }

            // Light type-checking for divert target arguments
            for (int i = 0; i < paramCount; ++i)
            {
                FlowBase.Argument flowArg    = targetFlow.arguments [i];
                Parsed.Expression divArgExpr = arguments [i];

                if (flowArg.isDivertTarget)
                {
                    if (!(divArgExpr is VariableReference || divArgExpr is DivertTarget))
                    {
                        Error("Target '" + targetFlow.name + "' expects a divert target for the parameter named -> " + flowArg.name + " but saw " + divArgExpr, divArgExpr);
                    }
                }
            }

            if (targetFlow == null)
            {
                Error("Can't call as a function or with arguments unless it's a knot or stitch");
                return;
            }

            return;
        }