ResolveFromContext() публичный Метод

public ResolveFromContext ( Parsed context ) : Parsed.Object
context Parsed
Результат Parsed.Object
Пример #1
0
        public override void ResolveReferences(Story context)
        {
            base.ResolveReferences(context);

            // Work is already done if it's a constant reference
            if (isConstantReference)
            {
                return;
            }

            // Is it a read count?
            var parsedPath = new Path(path);

            Parsed.Object targetForCount = parsedPath.ResolveFromContext(this);
            if (targetForCount)
            {
                targetForCount.containerForCounting.visitsShouldBeCounted = true;

                _runtimeVarRef.pathForCount = targetForCount.runtimePath;
                _runtimeVarRef.name         = null;

                // Check for very specific writer error: getting read count and
                // printing it as content rather than as a piece of logic
                // e.g. Writing {myFunc} instead of {myFunc()}
                var targetFlow = targetForCount as FlowBase;
                if (targetFlow && targetFlow.isFunction)
                {
                    // Is parent context content rather than logic?
                    if (parent is Weave || parent is ContentList || parent is FlowBase)
                    {
                        Warning("'" + targetFlow.name + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()");
                    }
                }

                return;
            }

            // Definitely a read count, but wasn't found?
            else if (path.Count > 1)
            {
                Error("Could not find target for read count: " + parsedPath);
                return;
            }

            if (!context.ResolveVariableWithName(this.name, fromNode: this).found)
            {
                Error("Unresolved variable: " + this.ToString(), this);
            }
        }
Пример #2
0
        public override void ResolveReferences(Story context)
        {
            if (_finalLooseEndTarget)
            {
                var flowEndPath = _finalLooseEndTarget.path;
                foreach (var finalLooseEndDivert in _finalLooseEnds)
                {
                    finalLooseEndDivert.targetPath = flowEndPath;
                }
            }

            if (_startingSubFlowDivert)
            {
                _startingSubFlowDivert.targetPath = _startingSubFlowRuntime.path;
            }

            base.ResolveReferences(context);

            // Check validity of parameter names
            if (arguments != null)
            {
                foreach (var arg in arguments)
                {
                    // Don't allow reserved words for argument names
                    if (VariableAssignment.IsReservedKeyword(arg.name))
                    {
                        Error("Argument '" + arg.name + "' is a reserved word, please choose another name");
                        continue;
                    }

                    // Does argument conflict with a knot/stitch/label?
                    var           pathOfTheoreticalTarget = new Path(arg.name);
                    Parsed.Object target = pathOfTheoreticalTarget.ResolveFromContext(this);
                    if (target)
                    {
                        Error("Argument '" + arg.name + "' conflicts with a " + target.GetType().Name + " on " + target.debugMetadata + ", ");
                        continue;
                    }

                    // Does argument conflict with another variable name?
                    if (context.ResolveVariableWithName(arg.name, fromNode: this.parent).found)
                    {
                        Error("Argument '" + arg.name + "' conflicts with existing variable definition at higher scope.");
                        continue;
                    }
                }
            }
        }
Пример #3
0
        public override void ResolveReferences (Story context)
        {
            base.ResolveReferences (context);

            // Work is already done if it's a constant reference
            if (isConstantReference) {
                return;
            }
                
            // Is it a read count?
            var parsedPath = new Path (path);
            Parsed.Object targetForCount = parsedPath.ResolveFromContext (this);
            if (targetForCount) {

                targetForCount.containerForCounting.visitsShouldBeCounted = true;

                _runtimeVarRef.pathForCount = targetForCount.runtimePath;
                _runtimeVarRef.name = null;

                // Check for very specific writer error: getting read count and
                // printing it as content rather than as a piece of logic
                // e.g. Writing {myFunc} instead of {myFunc()}
                var targetFlow = targetForCount as FlowBase;
                if (targetFlow && targetFlow.isFunction) {

                    // Is parent context content rather than logic?
                    if ( parent is Weave || parent is ContentList || parent is FlowBase) {
                        Warning ("'" + targetFlow.name + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()");
                    }
                }

                return;
            } 

            // Definitely a read count, but wasn't found?
            else if (path.Count > 1) {
                Error ("Could not find target for read count: " + parsedPath);
                return;
            }

            if (!context.ResolveVariableWithName (this.name, fromNode: this).found) {
                Error("Unresolved variable: "+this.ToString(), this);
            }
        }
Пример #4
0
        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);
            }
        }
Пример #5
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;
        }
Пример #6
0
        public override void ResolveReferences(Story context)
        {
            base.ResolveReferences(context);

            // Work is already done if it's a constant or list item reference
            if (isConstantReference || isListItemReference)
            {
                return;
            }

            // Is it a read count?
            var parsedPath = new Path(pathIdentifiers);

            Parsed.Object targetForCount = parsedPath.ResolveFromContext(this);
            if (targetForCount)
            {
                targetForCount.containerForCounting.visitsShouldBeCounted = true;

                // If this is an argument to a function that wants a variable to be
                // passed by reference, then the Parsed.Divert will have generated a
                // Runtime.VariablePointerValue instead of allowing this object
                // to generate its RuntimeVariableReference. This only happens under
                // error condition since we shouldn't be passing a read count by
                // reference, but we don't want it to crash!
                if (_runtimeVarRef == null)
                {
                    return;
                }

                _runtimeVarRef.pathForCount = targetForCount.runtimePath;
                _runtimeVarRef.name         = null;

                // Check for very specific writer error: getting read count and
                // printing it as content rather than as a piece of logic
                // e.g. Writing {myFunc} instead of {myFunc()}
                var targetFlow = targetForCount as FlowBase;
                if (targetFlow && targetFlow.isFunction)
                {
                    // Is parent context content rather than logic?
                    if (parent is Weave || parent is ContentList || parent is FlowBase)
                    {
                        Warning("'" + targetFlow.identifier + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()");
                    }
                }

                return;
            }

            // Couldn't find this multi-part path at all, whether as a divert
            // target or as a list item reference.
            if (path.Count > 1)
            {
                var errorMsg = "Could not find target for read count: " + parsedPath;
                if (path.Count <= 2)
                {
                    errorMsg += ", or couldn't find list item with the name " + string.Join(",", path.ToArray());
                }
                Error(errorMsg);
                return;
            }

            if (!context.ResolveVariableWithName(this.name, fromNode: this).found)
            {
                Error("Unresolved variable: " + this.ToString(), this);
            }
        }
Пример #7
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;
                        }
                    }
                }
            }
        }
Пример #8
0
        public override void ResolveReferences (Story context)
        {
            if (_finalLooseEndTarget) {
                var flowEndPath = _finalLooseEndTarget.path;
                foreach (var finalLooseEndDivert in _finalLooseEnds) {
                    finalLooseEndDivert.targetPath = flowEndPath;
                }
            }

            if (_startingSubFlowDivert) {
                _startingSubFlowDivert.targetPath = _startingSubFlowRuntime.path;
            }

            base.ResolveReferences(context);

            // Check validity of parameter names
            if (arguments != null) {
                foreach (var arg in arguments) {

                    // Don't allow reserved words for argument names
                    if (VariableAssignment.IsReservedKeyword (arg.name)) {
                        Error ("Argument '" + arg.name + "' is a reserved word, please choose another name");
                        continue;
                    }

                    // Does argument conflict with a knot/stitch/label?
                    var pathOfTheoreticalTarget = new Path (arg.name);
                    Parsed.Object target = pathOfTheoreticalTarget.ResolveFromContext (this);
                    if (target) {
                        Error ("Argument '" + arg.name + "' conflicts with a " + target.GetType().Name + " on " + target.debugMetadata + ", ");
                        continue;
                    }

                    // Does argument conflict with another variable name?
                    if (context.ResolveVariableWithName (arg.name, fromNode: this.parent).found) {
                        Error("Argument '"+ arg.name + "' conflicts with existing variable definition at higher scope.");
                        continue;
                    }
                }
            }
        }