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; } }
public override void ResolveReferences(IFiction context) { base.ResolveReferences(context); if (name != null && name.Length > 0) { context.CheckForNamingCollisions(this, name, SymbolType.SubFlowAndWeave); } }
public override void ResolveReferences(IFiction context) { base.ResolveReferences(context); foreach (var toResolve in _sequenceDivertsToResove) { toResolve.divert.targetPath = toResolve.targetContent.path; } }
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."); } } }
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); } }
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); } }
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); }