public override void ResolveReferences(Story 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 + "'"); } }
// 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; } } } } }