public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            // Check that choices nested within conditionals and sequences are terminated
            if (looseEnds != null && looseEnds.Count > 0)
            {
                var isNestedWeave = false;
                for (var ancestor = this.parent; ancestor != null; ancestor = ancestor.parent)
                {
                    if (ancestor is Sequence || ancestor is Conditional)
                    {
                        isNestedWeave = true;
                        break;
                    }
                }
                if (isNestedWeave)
                {
                    ValidateTermination(BadNestedTerminationHandler);
                }
            }

            foreach (var gatherPoint in gatherPointsToResolve)
            {
                gatherPoint.divert.targetPath = gatherPoint.targetRuntimeObj.path;
            }

            CheckForWeavePointNamingCollisions();
        }
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            if (divertAfter && divertAfter.targetContent)
            {
                _overrideDivertTarget.targetPath = divertAfter.targetContent.runtimePath;
            }
        }
Exemple #3
0
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            if (name != null && name.Length > 0)
            {
                context.CheckForNamingCollisions(this, name, SymbolType.SubFlowAndWeave);
            }
        }
Exemple #4
0
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            foreach (var toResolve in _sequenceDivertsToResove)
            {
                toResolve.divert.targetPath = toResolve.targetContent.path;
            }
        }
Exemple #5
0
 public virtual void ResolveReferences(IFiction context)
 {
     if (content != null)
     {
         foreach (var obj in content)
         {
             obj.ResolveReferences(context);
         }
     }
 }
        public override void ResolveReferences(IFiction context)
        {
            var pathToReJoin = _reJoinTarget.path;

            foreach (var branch in branches)
            {
                branch.returnDivert.targetPath = pathToReJoin;
            }

            base.ResolveReferences(context);
        }
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            // If we aren't using the proxy divert after all (e.g. if
            // it's a native function call), but we still have arguments,
            // we need to make sure they get resolved since the proxy divert
            // is no longer in the content array.
            if (!content.Contains(_proxyDivert) && arguments != null)
            {
                foreach (var arg in arguments)
                {
                    arg.ResolveReferences(context);
                }
            }

            if (_divertTargetToCount)
            {
                var divert = _divertTargetToCount.divert;
                var attemptingTurnCountOfVariableTarget = divert.runtimeDivert.variableDivertName != null;

                if (attemptingTurnCountOfVariableTarget)
                {
                    Error("When getting the TURNS_SINCE() of a variable target, remove the '->' - i.e. it should just be TURNS_SINCE(" + divert.runtimeDivert.variableDivertName + ")");
                    return;
                }

                var targetObject = divert.targetContent;
                if (targetObject == null)
                {
                    if (!attemptingTurnCountOfVariableTarget)
                    {
                        Error("Failed to find target for TURNS_SINCE: '" + divert.target + "'");
                    }
                }
                else
                {
                    targetObject.containerForCounting.turnIndexShouldBeCounted = true;
                }
            }

            else if (_variableReferenceToCount)
            {
                var runtimeVarRef = _variableReferenceToCount.runtimeVarRef;
                if (runtimeVarRef.pathForCount != null)
                {
                    Error("Should be " + name + "(-> " + _variableReferenceToCount.name + "). Usage without the '->' only makes sense for variable targets.");
                }
            }
        }
Exemple #8
0
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            var varResolveResult = context.ResolveVariableWithName(varName, fromNode: this);

            if (!varResolveResult.found)
            {
                Error("variable for " + incrementDecrementWord + " could not be found: '" + varName + "' after searching: " + this.descriptionOfScope);
            }

            _runtimeAssignment.isGlobal = varResolveResult.isGlobal;

            if (!(parent is Weave) && !(parent is FlowBase) && !(parent is ContentList))
            {
                Error("Can't use " + incrementDecrementWord + " as sub-expression");
            }
        }
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            // List definitions are checked for conflicts separately
            if (this.isDeclaration && listDefinition == null)
            {
                context.CheckForNamingCollisions(this, variableName, this.isGlobalDeclaration ? SymbolType.Var : SymbolType.Temp);
            }

            // Initial VAR x = [intialValue] declaration, not re-assignment
            if (this.isGlobalDeclaration)
            {
                var variableReference = expression as VariableReference;
                if (variableReference && !variableReference.isConstantReference && !variableReference.isListItemReference)
                {
                    Error("global variable assignments cannot refer to other variables, only literal values, constants and list items");
                }
            }

            if (!this.isNewTemporaryDeclaration)
            {
                var resolvedVarAssignment = context.ResolveVariableWithName(this.variableName, fromNode: this);
                if (!resolvedVarAssignment.found)
                {
                    if (ParsedFiction.constants.ContainsKey(variableName))
                    {
                        Error("Can't re-assign to a constant (do you need to use VAR when declaring '" + this.variableName + "'?)", this);
                    }
                    else
                    {
                        Error("Variable could not be found to assign to: '" + this.variableName + "'", this);
                    }
                }

                // A runtime assignment may not have been generated if it's the initial global declaration,
                // since these are hoisted out and handled specially in Story.ExportRuntime.
                if (_runtimeAssignment != null)
                {
                    _runtimeAssignment.isGlobal = resolvedVarAssignment.isGlobal;
                }
            }
        }
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            var parentStory = this.ParsedFiction;

            // Enforce rule that stitches must not have the same
            // name as any knots that exist in the story
            foreach (var stitchNamePair in subFlowsByName)
            {
                var stitchName = stitchNamePair.Key;

                var knotWithStitchName = parentStory.ContentWithNameAtLevel(stitchName, FlowLevel.Knot, false);
                if (knotWithStitchName)
                {
                    var stitch   = stitchNamePair.Value;
                    var errorMsg = string.Format("Stitch '{0}' has the same name as a knot (on {1})", stitch.name, knotWithStitchName.debugMetadata);
                    Error(errorMsg, stitch);
                }
            }
        }
        void CheckExternalArgumentValidity(IFiction context)
        {
            string externalName          = target.firstComponent;
            ExternalDeclaration external = null;
            var found = context.externals.TryGetValue(externalName, out external);

            System.Diagnostics.Debug.Assert(found, "external not found");

            int externalArgCount = external.argumentNames.Count;
            int ownArgCount      = 0;

            if (arguments != null)
            {
                ownArgCount = arguments.Count;
            }

            if (ownArgCount != externalArgCount)
            {
                Error("incorrect number of arguments sent to external function '" + externalName + "'. Expected " + externalArgCount + " but got " + ownArgCount);
            }
        }
Exemple #12
0
        public override void ResolveReferences(IFiction context)
        {
            // Weave style choice - target own content container
            if (_innerContentContainer)
            {
                _runtimeChoice.pathOnChoice = _innerContentContainer.path;

                if (onceOnly)
                {
                    _innerContentContainer.visitsShouldBeCounted = true;
                }
            }

            if (_returnToR1)
            {
                _returnToR1.targetPath = _r1Label.path;
            }

            if (_returnToR2)
            {
                _returnToR2.targetPath = _r2Label.path;
            }

            if (_divertToStartContentOuter)
            {
                _divertToStartContentOuter.targetPath = _startContentRuntimeContainer.path;
            }

            if (_divertToStartContentInner)
            {
                _divertToStartContentInner.targetPath = _startContentRuntimeContainer.path;
            }

            base.ResolveReferences(context);

            if (name != null && name.Length > 0)
            {
                context.CheckForNamingCollisions(this, name, SymbolType.SubFlowAndWeave);
            }
        }
        public override void ResolveReferences(IFiction context)
        {
            if (_startingSubFlowDivert)
            {
                _startingSubFlowDivert.targetPath = _startingSubFlowRuntime.path;
            }

            base.ResolveReferences(context);

            // Check validity of parameter names
            if (arguments != null)
            {
                foreach (var arg in arguments)
                {
                    context.CheckForNamingCollisions(this, arg.name, SymbolType.Arg, "argument");
                }

                // Separately, check for duplicate arugment names, since they aren't Parsed.Objects,
                // so have to be checked independently.
                for (int i = 0; i < arguments.Count; i++)
                {
                    for (int j = i + 1; j < arguments.Count; j++)
                    {
                        if (arguments [i].name == arguments [j].name)
                        {
                            Error("Multiple arguments with the same name: '" + arguments [i].name + "'");
                        }
                    }
                }
            }

            // Check naming collisions for knots and stitches
            if (flowLevel != FlowLevel.Story)
            {
                // Weave points aren't FlowBases, so this will only be knot or stitch
                var symbolType = flowLevel == FlowLevel.Knot ? SymbolType.Knot : SymbolType.SubFlowAndWeave;
                context.CheckForNamingCollisions(this, name, symbolType);
            }
        }
Exemple #14
0
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            // Check for the following case:
            //
            //    (not A) ? B
            //
            // Since this easy to accidentally do:
            //
            //    not A ? B
            //
            // when you intend:
            //
            //    not (A ? B)
            if (NativeNameForOp(opName) == "?")
            {
                var leftUnary = leftExpression as UnaryExpression;
                if (leftUnary != null && (leftUnary.op == "not" || leftUnary.op == "!"))
                {
                    Error("Using 'not' or '!' here negates '" + leftUnary.innerExpression + "' rather than the result of the '?' or 'has' operator. You need to add parentheses around the (A ? B) expression.");
                }
            }
        }
        public override void ResolveReferences(IFiction context)
        {
            if (isEmpty || isEnd || isDone)
            {
                return;
            }

            if (targetContent)
            {
                runtimeDivert.targetPath = targetContent.runtimePath;
            }

            // Resolve children (the arguments)
            base.ResolveReferences(context);

            // May be null if it's a built in function (e.g. TURNS_SINCE)
            // or if it's a variable target.
            var targetFlow = targetContent as FlowBase;

            if (targetFlow)
            {
                if (!targetFlow.isFunction && this.isFunctionCall)
                {
                    base.Error(targetFlow.name + " hasn't been marked as a function, but it's being called as one. Do you need to delcare the knot as '== function " + targetFlow.name + " =='?");
                }
                else if (targetFlow.isFunction && !this.isFunctionCall && !(this.parent is DivertTarget))
                {
                    base.Error(targetFlow.name + " can't be diverted to. It can only be called as a function since it's been marked as such: '" + targetFlow.name + "(...)'");
                }
            }

            // Check validity of target content
            bool targetWasFound = targetContent != null;
            bool isBuiltIn      = false;
            bool isExternal     = false;

            if (target.numberOfComponents == 1)
            {
                // BuiltIn means TURNS_SINCE, CHOICE_COUNT, RANDOM or SEED_RANDOM
                isBuiltIn = FunctionCall.IsBuiltIn(target.firstComponent);

                // Client-bound function?
                isExternal = context.IsExternal(target.firstComponent);

                if (isBuiltIn || isExternal)
                {
                    if (!isFunctionCall)
                    {
                        base.Error(target.firstComponent + " must be called as a function: ~ " + target.firstComponent + "()");
                    }
                    if (isExternal)
                    {
                        runtimeDivert.isExternal = true;
                        if (arguments != null)
                        {
                            runtimeDivert.externalArgs = arguments.Count;
                        }
                        runtimeDivert.pushesToStack = false;
                        runtimeDivert.targetPath    = new Runtime.Path(this.target.firstComponent);
                        CheckExternalArgumentValidity(context);
                    }
                    return;
                }
            }

            // Variable target?
            if (runtimeDivert.variableDivertName != null)
            {
                return;
            }

            if (!targetWasFound && !isBuiltIn && !isExternal)
            {
                Error("target not found: '" + target + "'");
            }
        }
        public override void ResolveReferences(IFiction 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(path);

            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.name + "' 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);
            }
        }
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            if (divert.isDone || divert.isEnd)
            {
                Error("Can't Can't use -> DONE or -> END as variable divert targets", this);
                return;
            }

            Parsed.Object usageContext = this;
            while (usageContext && usageContext is Expression)
            {
                bool badUsage   = false;
                bool foundUsage = false;

                var usageParent = usageContext.parent;
                if (usageParent is BinaryExpression)
                {
                    // Only allowed to compare for equality

                    var binaryExprParent = usageParent as BinaryExpression;
                    if (binaryExprParent.opName != "==" && binaryExprParent.opName != "!=")
                    {
                        badUsage = true;
                    }
                    else
                    {
                        if (!(binaryExprParent.leftExpression is DivertTarget || binaryExprParent.leftExpression is VariableReference))
                        {
                            badUsage = true;
                        }
                        if (!(binaryExprParent.rightExpression is DivertTarget || binaryExprParent.rightExpression is VariableReference))
                        {
                            badUsage = true;
                        }
                    }
                    foundUsage = true;
                }
                else if (usageParent is FunctionCall)
                {
                    var funcCall = usageParent as FunctionCall;
                    if (!funcCall.isTurnsSince && !funcCall.isReadCount)
                    {
                        badUsage = true;
                    }
                    foundUsage = true;
                }
                else if (usageParent is Expression)
                {
                    badUsage   = true;
                    foundUsage = true;
                }
                else if (usageParent is MultipleConditionExpression)
                {
                    badUsage   = true;
                    foundUsage = true;
                }
                else if (usageParent is Choice && ((Choice)usageParent).condition == usageContext)
                {
                    badUsage   = true;
                    foundUsage = true;
                }
                else if (usageParent is Conditional || usageParent is ConditionalSingleBranch)
                {
                    badUsage   = true;
                    foundUsage = true;
                }

                if (badUsage)
                {
                    Error("Can't use a divert target like that. Did you intend to call '" + divert.target + "' as a function: likeThis(), or check the read count: likeThis, with no arrows?", this);
                }

                if (foundUsage)
                {
                    break;
                }

                usageContext = usageParent;
            }

            // Example ink for this case:
            //
            //     VAR x = -> blah
            //
            // ...which means that "blah" is expected to be a literal stitch  target rather
            // than a variable name. We can't really intelligently recover from this (e.g. if blah happens to
            // contain a divert target itself) since really we should be generating a variable reference
            // rather than a concrete DivertTarget, so we list it as an error.
            if (_runtimeDivert.hasVariableTarget)
            {
                Error("Since '" + divert.target.dotSeparatedComponents + "' is a variable, it shouldn't be preceded by '->' here.");
            }

            // Main resolve
            _runtimeDivertTargetValue.targetPath = _runtimeDivert.targetPath;

            // Tell hard coded (yet variable) divert targets that they also need to be counted
            // TODO: Only detect DivertTargets that are values rather than being used directly for
            // read or turn counts. Should be able to detect this by looking for other uses of containerForCounting
            var targetContent = this.divert.targetContent;

            if (targetContent != null)
            {
                var target = targetContent.containerForCounting;
                if (target != null)
                {
                    // Purpose is known: used directly in TURNS_SINCE(-> divTarg)
                    var parentFunc = this.parent as FunctionCall;
                    if (parentFunc && parentFunc.isTurnsSince)
                    {
                        target.turnIndexShouldBeCounted = true;
                    }

                    // Unknown purpose, count everything
                    else
                    {
                        target.visitsShouldBeCounted    = true;
                        target.turnIndexShouldBeCounted = true;
                    }
                }

                // Unfortunately not possible:
                // https://github.com/inkle/ink/issues/538
                //
                // VAR func = -> double
                //
                // === function double(ref x)
                //    ~ x = x * 2
                //
                // Because when generating the parameters for a function
                // to be called, it needs to know ahead of time when
                // compiling whether to pass a variable reference or value.
                //
                var targetFlow = (targetContent as FlowBase);
                if (targetFlow != null && targetFlow.arguments != null)
                {
                    foreach (var arg in targetFlow.arguments)
                    {
                        if (arg.isByReference)
                        {
                            Error("Can't store a divert target to a knot or function that has by-reference arguments ('" + targetFlow.name + "' has 'ref " + arg.name + "').");
                        }
                    }
                }
            }
        }
        public override void ResolveReferences(IFiction context)
        {
            _conditionalDivert.targetPath = _contentContainer.path;

            base.ResolveReferences(context);
        }
        public override void ResolveReferences(IFiction context)
        {
            base.ResolveReferences(context);

            context.CheckForNamingCollisions(this, name, SymbolType.List);
        }