/// <summary>
        /// The constructor for TBlock.
        /// </summary>
        /// <param name="interpreter">The interpreter that the constructor is being called from.</param>
        /// <param name="exception">An exception that will be written to if there was an error.</param>
        /// <param name="elseAllowed">
        /// Whether the 'else' keyword is allowed to be used in the top level of the block. Usually only used in when
        /// creating a block in IF statements.
        /// </param>
        public TBlock(Interpreter interpreter, out TException exception, bool elseAllowed)
        {
            statements       = new List <string>();
            elseLocation     = -1;
            this.elseAllowed = elseAllowed;
            exception        = null;
            currentLine      = -1;

            int blockLevel = 0;

            while (true) // Keep adding statements until the appropriate terminator (i.e. 'end') is found
            {
                string statement = interpreter.GetInput().Trim();

                // Split the statement into symbols so we can analyse it to find out where the block begin/ends are
                bool isComment;
                Interpreter.Group symbolGroup = interpreter.SplitIntoSymbols(statement, out exception, out isComment);
                if (exception != null)
                {
                    statements.Clear();
                    break;
                }
                if (isComment)
                {
                    continue;
                }

                // A bit of Linq to easily count the number of a particular keyword in a group
                string keywordName  = "";
                var    keywordQuery =
                    from object item in symbolGroup
                    where (item is string) && ((string)item == keywordName)
                    select item;

                // If there's a begin, increment the block level. If there are too many then return an error
                keywordName = "begin";
                int beginCount = keywordQuery.Count();
                if (beginCount > 1)
                {
                    exception = new TException(interpreter, "Having more than one begin on a line is forbidden");
                    break;
                }
                else if (beginCount == 1)
                {
                    ++blockLevel;
                }

                // If there's an end, decrement the block level
                keywordName = "end";
                int endCount = keywordQuery.Count();
                if (endCount > 1)
                {
                    exception = new TException(interpreter, "Having more than one end on a line is forbidden");
                    break;
                }
                else if (endCount == 1)
                {
                    if (blockLevel == 0)
                    {
                        break;
                    }
                    else
                    {
                        --blockLevel;
                    }
                }

                // Increment the block level if there is an IF statement or WHILE loop where a block of code is to
                // follow, i.e. there is no statement after the comma after the condition
                if (symbolGroup.IndexOf("if") >= 0)
                {
                    string commaStr = symbolGroup[symbolGroup.Count - 1] as string;
                    if ((commaStr != null) && (commaStr == ","))
                    {
                        ++blockLevel;
                    }
                }

                if (symbolGroup.IndexOf("while") >= 0)
                {
                    string commaStr = symbolGroup[symbolGroup.Count - 1] as string;
                    if ((commaStr != null) && (commaStr == ","))
                    {
                        ++blockLevel;
                    }
                }

                // Increment if there is a multi-line function declaration (let <ident><group> =<nothing>)
                int equalsIndex;
                if ((equalsIndex = symbolGroup.IndexOf("=")) == symbolGroup.Count - 1)
                {
                    if ((equalsIndex > 1) && (symbolGroup[equalsIndex - 1] is Interpreter.Group))
                    {
                        if (symbolGroup.LastIndexOf("let", equalsIndex - 2) >= 0)
                        {
                            ++blockLevel;
                        }
                    }
                }

                bool breakNow  = false; // For a double break
                int  elseIndex = -1;
                while ((elseIndex = symbolGroup.IndexOf("else", elseIndex + 1)) >= 0)
                {
                    if (elseIndex == 0) // If the else is at the beginning of the line
                    {
                        if (blockLevel == 0)
                        {
                            // If a top level 'else' has not already been used and the use of 'else' is allowed
                            if ((elseLocation < 0) && elseAllowed)
                            {
                                elseLocation = statements.Count;
                                if (elseIndex < symbolGroup.Count - 1) // If there is a statement after the 'else'
                                {
                                    /* Convert
                                     *      <block>
                                     *      else <statement>
                                     *
                                     * into
                                     *      <block>
                                     *      else
                                     *      <block>
                                     *
                                     * and stop populating the block with statments
                                     */
                                    statements.Add("else");
                                    statements.Add(statement.Substring(4, statement.Length - 4));
                                    breakNow = true;
                                }
                            }
                            else
                            { // 'else' already used or it is not allowed, error
                                exception =
                                    new TException(interpreter, "Unexpected keyword 'else'",
                                                   elseAllowed ? "else already used" : "else not allowed in this construct");
                                statements.Clear();
                                breakNow = true;
                                break;
                            }
                        }
                        else if (elseIndex < symbolGroup.Count - 1)
                        {
                            --blockLevel;
                        }
                        // if a statement follows the 'else' on the same line then decrement the block level
                    }
                    else if (symbolGroup.LastIndexOf("if", elseIndex) >= 0)
                    {
                        /* If there is an IF statement of the form
                         *
                         *      if <condition>, <statement> else
                         *      <block>
                         *      end
                         *
                         *      I.e. if the last occurrence of 'if' is before the current 'else'
                         *      and the current 'else' is the last symbol of the line
                         *
                         * increment the block level.
                         */
                        if (elseIndex == symbolGroup.Count - 1)
                        {
                            ++blockLevel;
                        }
                    }
                }
                if (breakNow)
                {
                    break;
                }

                statements.Add(statement);
            }
        }
        /// <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;
            }
        }
        /// <summary>
        /// Parses a group, searching for any modulus brackets and replacing the modulus brackets and their contents
        /// with the absolute values.
        /// </summary>
        /// <param name="group">The group to parse.</param>
        /// <returns>A TException on failure, otherwise null.</returns>
        TException ParseModulusBracketsOfGroup(Group group)
        {
            int index;
            while ((index = group.IndexOf(TType.MODULUS_CHARACTER.ToString())) >= 0)
            {
                TException exception = new TException(this, "Modulus brackets not closed", "another | required");
                if (index + 2 >= group.Count) return exception;
                if (group[index + 2] is string)
                {
                    if ((string)group[index + 2] != TType.MODULUS_CHARACTER.ToString()) return exception;
                }
                else return exception;

                TType value = TType.Parse(this, group[index + 1]);
                if (value is TException) return value as TException;

                TType result = Operations.Math.Modulus(this, value);
                if (result == null) return new TException(this, "Modulus operation failed", "reason unknown");
                if (result is TException) return result as TException;
                group[index] = result;
                group.RemoveRange(index + 1, 2);
            }

            return null;
        }
        /// <summary>
        /// Splits a string into symbols and puts them in an Interpreter.Group.
        /// (This method is probably really inefficient with the way it handles strings...)
        /// </summary>
        /// <param name="statement">The string to split into symbols.</param>
        /// <param name="exception">A value that will be set if an error occurs.</param>
        /// <returns>The Interpreter.Group that contains the resulting symbols. Returns null on failure.</returns>
        public Group SplitIntoSymbols(string statement, out TException exception, out bool isCommentLine)
        {
            exception = null;
            statement = statement.Trim();

            // Ignore comments and empty strings
            int commentIndex;
            if ((commentIndex = statement.IndexOf("//")) >= 0) statement = statement.Remove(commentIndex);
            if (statement == "")
            {
                isCommentLine = true;
                return null;
            }
            isCommentLine = false;

            // Extract the strings from the statement, storing them in a list
            List<string> strings = new List<string>();
            int index = -1;
            while ((index = statement.IndexOf(TType.STRING_CHARACTER, index + 1)) >= 0)
            {
                // Find the string terminator
                int index2 = statement.IndexOf(TType.STRING_CHARACTER, index + 1);
                if (index2 < 0)
                {
                    exception = new TException(this, "String not closed", "another \" required");
                    return null;
                }

                // Add the substring between the speech marks to the list, and leave the beginning speech mark of the
                // string in tact so that the strings can be substituded back into the final group
                string str = statement.Substring(index + 1, (index2 - index) - 1);
                statement = statement.Remove(index + 1, index2 - index);
                strings.Add(str);
            }

            // Split up the symbols
            foreach (string s in SYMBOLS_TO_SPLIT_BY)
            {
                string newS = " " + s + " ";
                statement = statement.Replace(s, newS); // 2 character symbols are broken, fix ahead...
            }

            // Populate a list with the symbols
            List<string> symbols = new List<string>();
            foreach (string s in statement.Split(' '))
            {
                string str = s.Trim();
                if (str != "") symbols.Add(str);
            }

            Group superGroup = new Group(null);
            Group currentGroup = superGroup;
            int groupDepth = 0, stringId = 0;
            bool modulusOpen = false;

            for (int i = 0; i < symbols.Count; ++i)
            {
                // Fix for broken 2 character symbols
                if (i + 1 < symbols.Count)
                {
                    if (symbols[i + 1] == "=")
                    {
                        if (symbols[i] == "~")
                        {
                            symbols[i] = "~=";
                            symbols.RemoveAt(i + 1);
                        }
                        else if (symbols[i] == ">")
                        {
                            symbols[i] = ">=";
                            symbols.RemoveAt(i + 1);
                        }
                        else if (symbols[i] == "<")
                        {
                            symbols[i] = "<=";
                            symbols.RemoveAt(i + 1);
                        }
                        else if (symbols[i] == "/")
                        {
                            symbols[i] = "/=";
                            symbols.RemoveAt(i + 1);
                        }
                    }
                }

                string s = symbols[i];

                // If any opening brackets are found, create a new Group to contain the symbols within the brackets
                if ((s == "(") || ((s == "|") && !modulusOpen))
                {
                    Group newGroup;
                    if (s == "|") // If a modulus bracket is found, create a new group between the modulus brackets
                    {
                        // Do this to the modulus brackets: (|(stuff)|) so 'my_func|x|' will work
                        newGroup = new Group(currentGroup);
                        currentGroup = newGroup;
                        ++groupDepth;

                        modulusOpen = true;
                        currentGroup.Add(s);
                    }
                    newGroup = new Group(currentGroup);
                    currentGroup = newGroup;
                    ++groupDepth;
                }
                else if ((s == ")") || ((s == "|") && modulusOpen))
                {   // If any closing brackets are found, finish adding to the current group and start adding to the
                    // parent group
                    if (currentGroup.ParentGroup != null) currentGroup = currentGroup.ParentGroup;
                    --groupDepth;
                    if (s == "|")
                    {
                        modulusOpen = false;
                        currentGroup.Add(s);
                        if (currentGroup.ParentGroup != null) currentGroup = currentGroup.ParentGroup;
                        --groupDepth;
                    }
                }
                else if (s == "\"")
                {
                    currentGroup.Add(new TString(strings[stringId]));
                    ++stringId;
                }
                else currentGroup.Add(s);
            }

            // Return TExceptions if the brackets were not matched up properly
            if (groupDepth > 0)
            {
                exception = new TException(this, "Too few closing brackets", groupDepth.ToString() + " required");
                return null;
            }
            if (groupDepth < 0)
            {
                exception = new TException(this, "Too many closing brackets", "remove " + (-groupDepth).ToString());
                return null;
            }

            return superGroup;
        }
        /// <summary>
        /// The constructor for TBlock.
        /// </summary>
        /// <param name="interpreter">The interpreter that the constructor is being called from.</param>
        /// <param name="exception">An exception that will be written to if there was an error.</param>
        /// <param name="elseAllowed">
        /// Whether the 'else' keyword is allowed to be used in the top level of the block. Usually only used in when
        /// creating a block in IF statements.
        /// </param>
        public TBlock(Interpreter interpreter, out TException exception, bool elseAllowed)
        {
            statements = new List<string>();
            elseLocation = -1;
            this.elseAllowed = elseAllowed;
            exception = null;
            currentLine = -1;

            int blockLevel = 0;
            while (true) // Keep adding statements until the appropriate terminator (i.e. 'end') is found
            {
                string statement = interpreter.GetInput().Trim();

                // Split the statement into symbols so we can analyse it to find out where the block begin/ends are
                bool isComment;
                Interpreter.Group symbolGroup = interpreter.SplitIntoSymbols(statement, out exception, out isComment);
                if (exception != null)
                {
                    statements.Clear();
                    break;
                }
                if (isComment) continue;

                // A bit of Linq to easily count the number of a particular keyword in a group
                string keywordName = "";
                var keywordQuery =
                    from object item in symbolGroup
                    where (item is string) && ((string)item == keywordName)
                    select item;

                // If there's a begin, increment the block level. If there are too many then return an error
                keywordName = "begin";
                int beginCount = keywordQuery.Count();
                if (beginCount > 1)
                {
                    exception = new TException(interpreter, "Having more than one begin on a line is forbidden");
                    break;
                }
                else if (beginCount == 1) ++blockLevel;

                // If there's an end, decrement the block level
                keywordName = "end";
                int endCount = keywordQuery.Count();
                if (endCount > 1)
                {
                    exception = new TException(interpreter, "Having more than one end on a line is forbidden");
                    break;
                }
                else if (endCount == 1)
                {
                    if (blockLevel == 0) break;
                    else --blockLevel;
                }

                // Increment the block level if there is an IF statement or WHILE loop where a block of code is to
                // follow, i.e. there is no statement after the comma after the condition
                if (symbolGroup.IndexOf("if") >= 0)
                {
                    string commaStr = symbolGroup[symbolGroup.Count - 1] as string;
                    if ((commaStr != null) && (commaStr == ",")) ++blockLevel;
                }

                if (symbolGroup.IndexOf("while") >= 0)
                {
                    string commaStr = symbolGroup[symbolGroup.Count - 1] as string;
                    if ((commaStr != null) && (commaStr == ",")) ++blockLevel;
                }

                // Increment if there is a multi-line function declaration (let <ident><group> =<nothing>)
                int equalsIndex;
                if ((equalsIndex = symbolGroup.IndexOf("=")) == symbolGroup.Count - 1)
                {
                    if ((equalsIndex > 1) && (symbolGroup[equalsIndex - 1] is Interpreter.Group))
                    {
                        if (symbolGroup.LastIndexOf("let", equalsIndex - 2) >= 0) ++blockLevel;
                    }
                }

                bool breakNow = false; // For a double break
                int elseIndex = -1;
                while ((elseIndex = symbolGroup.IndexOf("else", elseIndex + 1)) >= 0)
                {
                    if (elseIndex == 0) // If the else is at the beginning of the line
                    {
                        if (blockLevel == 0)
                        {
                            // If a top level 'else' has not already been used and the use of 'else' is allowed
                            if ((elseLocation < 0) && elseAllowed)
                            {
                                elseLocation = statements.Count;
                                if (elseIndex < symbolGroup.Count - 1) // If there is a statement after the 'else'
                                {
                                    /* Convert
                                     *      <block>
                                     *      else <statement>
                                     *
                                     * into
                                     *      <block>
                                     *      else
                                     *      <block>
                                     *
                                     * and stop populating the block with statments
                                     */
                                    statements.Add("else");
                                    statements.Add(statement.Substring(4, statement.Length - 4));
                                    breakNow = true;
                                }
                            }
                            else
                            { // 'else' already used or it is not allowed, error
                                exception =
                                    new TException(interpreter, "Unexpected keyword 'else'",
                                        elseAllowed ? "else already used" : "else not allowed in this construct");
                                statements.Clear();
                                breakNow = true;
                                break;
                            }
                        }
                        else if (elseIndex < symbolGroup.Count - 1) --blockLevel;
                            // if a statement follows the 'else' on the same line then decrement the block level
                    }
                    else if (symbolGroup.LastIndexOf("if", elseIndex) >= 0)
                    {
                        /* If there is an IF statement of the form
                         *
                         *      if <condition>, <statement> else
                         *      <block>
                         *      end
                         *
                         *      I.e. if the last occurrence of 'if' is before the current 'else'
                         *      and the current 'else' is the last symbol of the line
                         *
                         * increment the block level.
                         */
                        if (elseIndex == symbolGroup.Count - 1) ++blockLevel;
                    }
                }
                if (breakNow) break;

                statements.Add(statement);
            }
        }