/// <summary> /// Appends the contents of another TParameterList to this TParameterList. /// </summary> /// <param name="parameterList">The TParameterList to append.</param> public void Add(TParameterList parameterList) { for (int i = 0; i < parameterList.Count; ++i) parameters.Add(parameterList[i]); }
/// <summary> /// Parses an assignment operation. /// </summary> /// <param name="group">The group to parse.</param> /// <returns>The assigned variable or function on success, otherwise a TException.</returns> TType ParseAssignmentGroup(Group group) { /* BNF for assignment: * <assignment> ::= 'let' ( <variable-assignment> | <function-declaration> ) * <var-assignment> ::= <identifier> '=' <expression> * <func-declaration> ::= <identifier> '(' { <parameters> } ')' '=' <statements> * <parameters> ::= <identifier> { ',' <identifier> }* * <statements> ::= <block> | <statement> * <block> ::= <new-line> (<statement> <new-line>)* 'end' * You can probably guess what <identifier>, <statement> and <new-line> are * The 'let' is assumed to have already been checked for */ int equalsIndex = group.IndexOf("="); if (equalsIndex < 0) return new TException(this, "Variable or function could not be assigned a value"); // Could be assigning a dereferenced variable TException exception = ParseReferencesOfGroup(group, equalsIndex); if (exception != null) return exception; if (group.Count == 1) return new TException(this, "Could not assign variable", "no variable name given"); string variableName = group[1] as string; TVariable existingVariable = null; if (variableName == null) { exception = new TException(this, "Could not assign variable", "invalid variable name given"); // Check if an existing variable or function is being assigned Group groupToParse = group[1] as Group; TType value = group[1] as TType; if (groupToParse != null) value = ParseGroup(groupToParse); if (value == null) return exception; TVariable variable = value as TVariable; if (variable != null) existingVariable = variable; else { TFunction function = value as TFunction; if (function != null) variableName = function.Name; else return exception; } } if (group.Count == 2) return new TException(this, "Variable could not be assigned a value"); string assignmentOperator = group[2] as string; if (assignmentOperator == null) // Now we assume that we're dealing with a function declaration { Group paramGroup = group[2] as Group; if (paramGroup == null) // The user probably just wanted to assign a variable but made a typo return new TException(this, "Variable could not be assigned a value", "value to assign to variable must be given"); // Get the identifiers of all the parameters within the brackets, keeping strict watch on comma usage TParameterList paramList = new TParameterList(); bool commaExpected = false; for (int i = 0; i < paramGroup.Count; ++i) { if (commaExpected && (i == paramGroup.Count - 1)) return new TException(this, "Parameters could not be parsed", "last parameter missing"); string paramName = paramGroup[i] as string; if (commaExpected && (paramName != ",")) paramName = null; if (paramName == null) return new TException(this, "Parameters could not be parsed", "invalid parameter name given"); if (!commaExpected) paramList.Add(paramName); commaExpected = !commaExpected; } exception = new TException(this, "Function could not be given a body", "function body must be given"); if (group.Count == 3) return exception; assignmentOperator = group[3] as string; if (assignmentOperator == null) return exception; else if (assignmentOperator != "=") return exception; TFunction function; if (group.Count == 4) // statement is just 'let <i><params> =', so get a block { TBlock block = new TBlock(this, out exception, false); if (exception != null) return exception; function = new TFunction(variableName ?? existingVariable.Identifier, block, paramList.ParameterNames, null); } else // Create a single line function { Group funcBody = new Group(null); funcBody.AddRange(group.GetRange(4, group.Count - 4)); function = new TFunction(variableName ?? existingVariable.Identifier, funcBody.ToString(), paramList.ParameterNames, null); } exception = TFunction.AddFunction(this, function); if (exception != null) return exception; return function; } { // Assume that we're dealing with a variable assigment exception = new TException(this, "Variable could not be assigned a value", "value to assign to variable must be given"); if (assignmentOperator != "=") return exception; if (group.Count == 3) return exception; // Parse the expression on the right hand side of the assigment operator, and if the result is a // TVariable, then get it's value (we don't want to assign the TVariable itself to the new TVariable) Group valueGroup = new Group(null); valueGroup.AddRange(group.GetRange(3, group.Count - 3)); TType value = ParseGroup(valueGroup); if (value is TException) return value; TVariable variable = value as TVariable; if (variable != null) value = variable.Value; // Make sure we don't get any circular references; they cause stack overflows when outputting them variable = existingVariable ?? Stack.FindVariable(variableName); if (value == variable) return new TException(this, "Illegal assignment attempted", "variables cannot reference themselves"); TVariable circularRefCheckVar = value as TVariable; while (circularRefCheckVar != null) { TVariable variableValue = circularRefCheckVar.Value as TVariable; if (variableValue != null) { if (variableValue == variable) return new TException(this, "Illegal assignment attempted", "circular reference detected"); else circularRefCheckVar = variableValue; } else circularRefCheckVar = null; } if (variable == null) // If the variable doesn't exist already, add it to the stack variable dictionary { variable = new TVariable(variableName, value); Stack.AddVariable(variable); } else variable.Value = value; return variable; } }