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; } } } }
public override void ResolveReferences(Story context) { base.ResolveReferences(context); VariableAssignment varDecl = null; if (this.isNewTemporaryDeclaration && story.variableDeclarations.TryGetValue(variableName, out varDecl)) { if (varDecl.isGlobalDeclaration) { Error("global variable '" + variableName + "' already exists with the same name (declared on " + varDecl.debugMetadata + ")"); return; } } 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 (IsReservedKeyword(variableName)) { Error("cannot use '" + variableName + "' as a variable since it's a reserved ink keyword"); return; } if (!this.isNewTemporaryDeclaration) { if (!context.ResolveVariableWithName(this.variableName, fromNode: this).found) { if (story.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); } } } }
public void TryAddNewVariableDeclaration(VariableAssignment varDecl) { var varName = varDecl.variableName; if (variableDeclarations.ContainsKey(varName)) { var prevDeclError = ""; var debugMetadata = variableDeclarations [varName].debugMetadata; if (debugMetadata != null) { prevDeclError = " (" + variableDeclarations [varName].debugMetadata + ")"; } Error("found declaration variable '" + varName + "' that was already declared" + prevDeclError, varDecl, false); return; } variableDeclarations [varDecl.variableName] = varDecl; }
protected Parsed.Object TempDeclarationOrAssignment() { Whitespace (); bool isNewDeclaration = ParseTempKeyword(); Whitespace (); string varName = null; if (isNewDeclaration) { varName = (string)Expect (Identifier, "variable name"); } else { varName = Parse(Identifier); } if (varName == null) { return null; } Whitespace(); // Optional assignment Expression assignedExpression = null; if (ParseString ("=") != null) { assignedExpression = (Expression)Expect (Expression, "value expression to be assigned to temporary variable"); } // If it's neither an assignment nor a new declaration, // it's got nothing to do with this rule (e.g. it's actually just "~ myExpr" or even "~ myFunc()" else if (!isNewDeclaration) { return null; } // Default zero assignment else { assignedExpression = new Number (0); } var result = new VariableAssignment (varName, assignedExpression); result.isNewTemporaryDeclaration = isNewDeclaration; return result; }
// 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; } } } } }
protected Parsed.Object VariableDeclaration() { Whitespace (); var id = Parse (Identifier); if (id != "VAR") return null; Whitespace (); var varName = Expect (Identifier, "variable name") as string; Whitespace (); Expect (String ("="), "the '=' for an assignment of a value, e.g. '= 5' (initial values are mandatory)"); Whitespace (); var expr = Expect (Expression, "initial value for ") as Parsed.Expression; if (!(expr is Number || expr is StringExpression || expr is DivertTarget || expr is VariableReference)) { Error ("initial value for a variable must be a number, constant, or divert target"); } // Ensure string expressions are simple else if (expr is StringExpression) { var strExpr = expr as StringExpression; if (!strExpr.isSingleString) Error ("Constant strings cannot contain any logic."); } var result = new VariableAssignment (varName, expr); result.isGlobalDeclaration = true; return result; }
public void TryAddNewVariableDeclaration(VariableAssignment varDecl) { var varName = varDecl.variableName; if (variableDeclarations.ContainsKey (varName)) { var prevDeclError = ""; var debugMetadata = variableDeclarations [varName].debugMetadata; if (debugMetadata != null) { prevDeclError = " ("+variableDeclarations [varName].debugMetadata+")"; } Error("found declaration variable '"+varName+"' that was already declared"+prevDeclError, varDecl, false); return; } variableDeclarations [varDecl.variableName] = varDecl; }