/// <summary>
        /// Does an equality comparison of one TType with another.
        /// </summary>
        /// <param name="interpreter">The interpreter that the method is being called from.</param>
        /// <param name="a">The left hand operand of the comparison.</param>
        /// <param name="b">The right hand operand of the comparison.</param>
        /// <param name="strict">
        /// Whether the equality should be approximate or not. Give true for exact equality comparisons, and false for
        /// approximate equality comparisons of TNumbers.
        /// </param>
        /// <returns>
        /// An TBoolean containing the result of the comparison. Returns a TException or null when there is an error.
        /// </returns>
        public static TType Equal(Interpreter interpreter, TType a, TType b, bool strict)
        {
            // If arguments are TVariable, get their value. The values of the TVariable need to be compared,
            // not the TVariable objects themselves
            TVariable variable = a as TVariable;
            if (variable != null) a = variable.Value;
            variable = b as TVariable;
            if (variable != null) b = variable.Value;

            // Make sure that each operand is of the same type. TNumbers are an exception; any TNumber derivative can
            // be compared with any other TNumber derivative
            if ((a.TypeName != b.TypeName) && !((a is TNumber) && (b is TNumber)))
                return new TException(interpreter,
                    "Type '" + a.TypeName + "' cannot be compared with type '" + b.TypeName + "'");

            // Using 'as' syntax instead of '()' for casting, because it looks cleaner. The result of the 'as' will not
            // return null because we've done the necessary check beforehand (i.e. if 'a' is a TNumber, then 'b' must
            // also be a TNumber)
            if (a is TNumber)
            {
                bool result;
                if (strict) result =
                    (System.Math.Abs((a as TNumber).TRealValue - (b as TNumber).TRealValue) < 0.000001);
                else
                {
                    double aVal = (a as TNumber).TRealValue, bVal = (b as TNumber).TRealValue;
                    if ((System.Math.Round(aVal) == bVal) || (System.Math.Round(bVal) == aVal)) result = true;
                    else result = (System.Math.Abs(aVal - bVal) < 0.5);
                }
                return new TBoolean(result);
            }
            else if (a is TBoolean)
            {
                return new TBoolean((a as TBoolean).Value == (b as TBoolean).Value);
            }
            else if (a is TString) {
                return new TBoolean((a as TString).Value == (b as TString).Value);
            }
            else if (a is TVariable) // i.e. if argument 'a' is a reference
            {
                return new TBoolean(a == b);
            }
            else if (a is TFunction)
            {
                TFunction funcA = a as TFunction, funcB = b as TFunction;
                return new TBoolean(
                    (funcA.HardCodedFunction == funcB.HardCodedFunction) &&
                    (funcA.CustomFunction == funcB.CustomFunction) &&
                    (funcA.Block == funcB.Block));
            }
            else if (a is TNil)
            {
                return new TBoolean(b is TNil);
            }

            return null;
        }
        /// <summary>
        /// The method that represents the 'exit' function in the language.
        /// Exits from the current function, returning any value passed to the function.
        /// </summary>
        /// <param name="interpreter">The interpreter that the method is being called from.</param>
        /// <param name="args">The arguments being passed to the function as a TArgumentList.</param>
        /// <returns>The argument passed to the function. If no arguments were passed, it returns TNil.</returns>
        public static TType Exit(Interpreter interpreter, TArgumentList args)
        {
            // Get the argument passed (if any) to return
            TType returnValue = TNil.Instance;
            if (args.Count > 0) returnValue = args[0];

            // Pops from the function stack or kill the interpreter if the stack level is already at it's lowest
            // When this method returns, the calling method should check the stack level and exit accordingly
            if (interpreter.Stack.Level <= 1) interpreter.Kill();
            else interpreter.Stack.Pop();

            return returnValue;
        }
 /// <summary>
 /// Does a 'not equal to' inequality comparison of one TType with another. Always does a strict comparison,
 /// unlike the Operations.Equal method where strictness can be specified.
 /// </summary>
 /// <param name="interpreter">The interpreter that the method is being called from.</param>
 /// <param name="a">The left hand operand of the comparison.</param>
 /// <param name="b">The right hand operand of the comparison.</param>
 /// <returns>
 /// An TBoolean containing the result of the comparison. Returns a TException or null when there is an error.
 /// </returns>
 public static TType NotEqual(Interpreter interpreter, TType a, TType b)
 {
     // Do an equality comparison of the arguments, and return the result if it's a TException or null.
     // If the result is a TBoolean then invert its value
     TType value = Equal(interpreter, a, b, true);
     TBoolean result = value as TBoolean;
     if (result == null) return value;
     result.Value = !result.Value;
     return result;
 }
            /// <summary>
            /// Takes two TNumbers and subtracts one from the other.
            /// </summary>
            /// <param name="interpreter">The interpreter that the method is being called from.</param>
            /// <param name="a">The left hand operand of the operation.</param>
            /// <param name="b">The right hand operand of the operation.</param>
            /// <returns>
            /// The TType resulting from the operation. An MExcpetion or null is returned when there is an error.
            /// </returns>
            public static TType Subtract(Interpreter interpreter, TType a, TType b)
            {
                // Try to get TNumber values from the TType arguments
                TNumber numberA, numberB;
                TException exception = AssignNumberValue(interpreter, out numberA, a);
                if (exception != null) return exception;
                exception = AssignNumberValue(interpreter, out numberB, b);
                if (exception != null) return exception;

                // No errors, but one or both of the arguments could be a TString; check them
                if ((numberA == null) || (numberB == null))
                    return new TException(interpreter, "Strings cannot be used in subtraction operations");

                switch (numberA.TypeName)
                {
                    case TType.T_INTEGER_TYPENAME:
                        {
                            // If the other operand is a fraction, treat this integer as a fraction (i.e. value/1)
                            TFraction rhsFraction = numberB as TFraction;
                            if (rhsFraction != null)
                            {
                                // Order of fractions matters in this case
                                TFraction lhsFraction = new TFraction(numberA.TIntegerValue, 1);
                                lhsFraction.Subtract(rhsFraction.Numerator, rhsFraction.Denominator);
                                return lhsFraction;
                            }
                            return new TInteger(numberA.TIntegerValue - numberB.TIntegerValue);
                        }

                    case TType.T_REAL_TYPENAME:
                        return new TReal(numberA.TRealValue - numberB.TRealValue);

                    case TType.T_FRACTION_TYPENAME:
                        {
                            // Create a copy of the left hand fraction
                            TFraction fraction = numberA as TFraction;
                            fraction = new TFraction(fraction.Numerator, fraction.Denominator);

                            // Convert the right hand operand to a fraction
                            long numerator, denominator;
                            TFraction otherFraction = numberB as TFraction;
                            if (otherFraction != null) // If it's a fraction, simply copy the values
                            {
                                numerator = otherFraction.Numerator;
                                denominator = otherFraction.Denominator;
                            }
                            else
                            {
                                // Check if it's a TInteger first. It might not need to use DoubleToFraction
                                if (numberB is TInteger)
                                {
                                    numerator = numberB.TIntegerValue;
                                    denominator = 1;
                                }
                                else Operations.Misc.DoubleToFraction(numberB.TRealValue, out numerator,
                                    out denominator);
                            }

                            fraction.Subtract(numerator, denominator);
                            return fraction;
                        }
                }

                return null;
            }
            /// <summary>
            /// Takes two TNumbers and returns the first one to the power of the other.
            /// </summary>
            /// <param name="interpreter">The interpreter that the method is being called from.</param>
            /// <param name="a">The left hand operand of the operation.</param>
            /// <param name="b">The right hand operand of the operation.</param>
            /// <returns>
            /// The TType resulting from the operation. An MExcpetion or null is returned when there is an error.
            /// </returns>
            public static TType Pow(Interpreter interpreter, TType a, TType b)
            {
                // Try to get TNumber values from the TType arguments
                TNumber numberA, numberB;
                TException exception = AssignNumberValue(interpreter, out numberA, a);
                if (exception != null) return exception;
                exception = AssignNumberValue(interpreter, out numberB, b);
                if (exception != null) return exception;

                // No errors, but one or both of the arguments could be a TString; check them
                if ((numberA == null) || (numberB == null))
                    return new TException(interpreter, "Strings cannot be used in exponentiation operations");

                switch (numberA.TypeName)
                {
                    case TType.T_INTEGER_TYPENAME:
                        return new TInteger(
                            (long)System.Math.Round(System.Math.Pow(numberA.TRealValue, numberB.TRealValue)));

                    case TType.T_REAL_TYPENAME:
                        return new TReal(System.Math.Pow(numberA.TRealValue, numberB.TRealValue));

                    case TType.T_FRACTION_TYPENAME:
                        TFraction fraction = numberA as TFraction;
                        long numerator =
                            (long)System.Math.Round(System.Math.Pow(fraction.Numerator, numberB.TRealValue));
                        long denominator =
                            (long)System.Math.Round(System.Math.Pow(fraction.Denominator, numberB.TRealValue));
                        return new TFraction(numerator, denominator);
                }

                return null;
            }
            /// <summary>
            /// Takes a TNumber and returns its absolute value.
            /// </summary>
            /// <param name="interpreter">The interpreter that the method is being called from.</param>
            /// <param name="value">The TNumber to get the absolute value of.</param>
            /// <returns>
            /// The TType resulting from the operation. An MExcpetion or null is returned when there is an error.
            /// </returns>
            public static TType Modulus(Interpreter interpreter, TType value)
            {
                TNumber number;
                TException exception = AssignNumberValue(interpreter, out number, value);
                if (exception != null) return exception;

                // No errors yet, but make sure it's not a string
                if (number == null) return new TException(interpreter, "Strings cannot be used in modulus operations");

                switch (number.TypeName)
                {
                    case TType.T_INTEGER_TYPENAME:
                        return new TInteger(System.Math.Abs(number.TIntegerValue));
                    case TType.T_REAL_TYPENAME:
                        return new TReal(System.Math.Abs(number.TRealValue));
                    case TType.T_FRACTION_TYPENAME:
                        TFraction fraction = number as TFraction;
                        return new TFraction(System.Math.Abs(fraction.Numerator), fraction.Denominator);
                        // No need to abs denominator as TFraction denominators are automatically kept positive
                }

                return null;
            }
            /// <summary>
            /// Compares one TNumber with another based on a given inequality operator.
            /// </summary>
            /// <param name="interpreter">The interpreter that the method is being called from.</param>
            /// <param name="a">The left hand operand of the comparison.</param>
            /// <param name="b">The right hand operand of the comparison.</param>
            /// <param name="inequality">The inequality operator to use in the comparison.</param>
            /// <returns>
            /// An TBoolean containing the result of the comparison. Returns a TException when there is an error.
            /// </returns>
            public static TType Inequality(Interpreter interpreter, TType a, TType b, string inequality)
            {
                // Try to get TNumber values from the TType arguments
                TNumber numberA, numberB;
                TException exception = AssignNumberValue(interpreter, out numberA, a);
                if (exception != null) return exception;
                exception = AssignNumberValue(interpreter, out numberB, b);
                if (exception != null) return exception;

                // No errors, but one or both of the arguments could be a TString; check them
                if ((numberA == null) || (numberB == null))
                    return new TException(interpreter, "Strings cannot be used in inequality comparisons");

                bool result;
                switch (inequality)
                {
                    case ">":
                        result = numberA.TRealValue > numberB.TRealValue;
                        break;
                    case ">=":
                        result = numberA.TRealValue >= numberB.TRealValue;
                        break;
                    case "<":
                        result = numberA.TRealValue < numberB.TRealValue;
                        break;
                    case "<=":
                        result = numberA.TRealValue <= numberB.TRealValue;
                        break;
                    default:
                        return new TException(interpreter, "Invalid inequality operator given");
                }

                return new TBoolean(result);
            }
            /// <summary>
            /// Takes two TNumbers or two TStrings (or up to two TVariables containing TNumbers or TStrings) and
            /// adds them together.
            /// </summary>
            /// <param name="interpreter">The interpreter that the method is being called from.</param>
            /// <param name="a">The left hand operand of the operation.</param>
            /// <param name="b">The right hand operand of the operation.</param>
            /// <returns>
            /// The TType resulting from the operation. An MExcpetion or null is returned when there is an error.
            /// </returns>
            public static TType Add(Interpreter interpreter, TType a, TType b)
            {
                // Convert arguments 'a' and 'b' into either a TNumber or a TString
                TNumber numberA, numberB;
                TString strA = null, strB = null;

                TException exception = AssignNumberValue(interpreter, out numberA, a);
                if (exception != null) return exception;

                if (numberA == null)
                {
                    // No errors yet, and numberA is null, so argument 'a' could be a TString or a TVariable
                    // containing a TString
                    strA = a as TString;
                    if (strA == null)
                    {
                        TVariable variable = a as TVariable;
                        if (variable != null) strA = variable.Value as TString;
                    }
                }
                if ((numberA == null) && (strA == null)) // Nothing useful, return a TException
                    return new TException(interpreter, "Value is not a number or string");

                // Same procedure for argument 'b'
                exception = AssignNumberValue(interpreter, out numberB, b);
                if (exception != null) return exception;
                if (numberB == null)
                {
                    strB = b as TString;
                    if (strB == null)
                    {
                        TVariable variable = b as TVariable;
                        if (variable != null) strB = variable.Value as TString;
                    }
                }
                if ((numberB == null) && (strB == null))
                    return new TException(interpreter, "Value is not a number or string");

                // Attempt addition if both operands are the same type, otherwise return a TException
                if ((numberB == null) && (strA == null))
                    return new TException(interpreter, "Attempted addition of a string to a number");
                else if ((numberA == null) && (strB == null))
                    return new TException(interpreter, "Attempted addition of a number to a string");
                else if ((numberA == null) && (numberB == null))
                {
                    return new TString(strA.Value + strB.Value);
                }
                else
                {
                    //The left hand operand decides the type of the returned value
                    switch (numberA.TypeName)
                    {
                        case TType.T_INTEGER_TYPENAME:
                            {
                                // If the other operand is a fraction, treat this integer as a fraction (i.e. value/1)
                                TFraction fraction = numberB as TFraction;
                                if (fraction != null)
                                {
                                    // Copy the right hand fraction and add the left hand integer to it
                                    fraction = new TFraction(fraction.Numerator, fraction.Denominator);
                                    fraction.Add(numberA.TIntegerValue, 1);
                                    return fraction;
                                }
                                return new TInteger(numberA.TIntegerValue + numberB.TIntegerValue);
                            }

                        case TType.T_REAL_TYPENAME:
                            return new TReal(numberA.TRealValue + numberB.TRealValue);

                        case TType.T_FRACTION_TYPENAME:
                            {
                                // Create a copy of the left hand fraction
                                TFraction fraction = numberA as TFraction;
                                fraction = new TFraction(fraction.Numerator, fraction.Denominator);

                                // Convert the right hand operand to a fraction
                                long numerator, denominator;
                                TFraction otherFraction = numberB as TFraction;
                                if (otherFraction != null) // If it's a fraction, simply copy the values
                                {
                                    numerator = otherFraction.Numerator;
                                    denominator = otherFraction.Denominator;
                                }
                                else
                                {
                                    // Check if it's a TInteger first. It might not need to use DoubleToFraction
                                    if (numberB is TInteger)
                                    {
                                        numerator = numberB.TIntegerValue;
                                        denominator = 1;
                                    }
                                    else Operations.Misc.DoubleToFraction(numberB.TRealValue, out numerator,
                                        out denominator);
                                }

                                fraction.Add(numerator, denominator);
                                return fraction;
                            }
                    }
                }

                return null;
            }
        /// <summary>
        /// Attempts to find a TNumber value for the TType given. If there is no error, but the TNumber reference
        /// given is still null after calling, then the TType is a TString (i.e. some arithmetic will work (+)).
        /// </summary>
        /// <param name="interpreter">The interpreter that the method is being called from.</param>
        /// <param name="number">The TNumber reference to assign the result to.</param>
        /// <param name="value">The TType to get a TNumber out of.</param>
        /// <returns>An exception if there was an error, otherwise null.</returns>
        private static TException AssignNumberValue(Interpreter interpreter, out TNumber number, TType value)
        {
            // Attempt to cast the TType 'value' argument to a TNumber. Failing that, check if it's a TString.
            // If the value is a TNumber or a TString, return null (i.e. no exception). If it's a TVariable then
            // work on the value of the TVariable, otherwise return an exception.

            number = value as TNumber;
            if ((number != null) || (value is TString)) return null;

            TVariable variable = value as TVariable;
            if (variable != null)
            {
                value = variable.Value;
                number = value as TNumber;
                if ((number != null) || (value is TString)) return null;
                return new TException(interpreter, "Value of '" + variable.Identifier + "' is not a number",
                    "it is of type '" + value.TypeName + "'");
            }

            return new TException(interpreter, "'" + value.ToCSString() + "' is not a number",
                "it is of type '" + value.TypeName + "'");
        }
 static void Main(string[] args)
 {
     Types.TFunction.Init();
     Interpreter interpreter = new Interpreter();
     interpreter.Run(args.Length > 0 ? args[0] : "");
 }
 /// <summary>
 /// The method that represents the 'load' function in the language.
 /// Loads a script file with the specified file name (given by a TString) and runs it.
 /// </summary>
 /// <param name="interpreter">The interpreter that the method is being called from.</param>
 /// <param name="args">The arguments being passed to the function as a TArgumentList.</param>
 /// <returns>TNil</returns>
 public static TType Load(Interpreter interpreter, TArgumentList args)
 {
     TString fileName = args[0] as TString;
     if (fileName == null) return new TException(interpreter, "Name of file to load must be given as a string");
     interpreter.LoadFile(fileName.Value);
     return TNil.Instance;
 }
 /// <summary>
 /// The method that represents the 'read_string' function in the language.
 /// Reads in a string returns it as a TString.
 /// </summary>
 /// <param name="interpreter">The interpreter that the method is being called from.</param>
 /// <param name="args">The arguments being passed to the function as a TArgumentList.</param>
 /// <returns>An TString representing the string that was entered.</returns>
 public static TType ReadString(Interpreter interpreter, TArgumentList args)
 {
     return new TString(System.Console.ReadLine());
 }
 /// <summary>
 /// The method that represents the 'read' function in the language.
 /// Reads in a string and converts it into the most suitable TType.
 /// If the string cannot be converted, it just returns the string as a TString.
 /// </summary>
 /// <param name="interpreter">The interpreter that the method is being called from.</param>
 /// <param name="args">The arguments being passed to the function as a TArgumentList.</param>
 /// <returns>An TType of the type that the entered string represents best.</returns>
 public static TType Read(Interpreter interpreter, TArgumentList args)
 {
     // Read in a string and convert it to a suitable TType with TType.Parse.
     string str = System.Console.ReadLine();
     TType value = TType.Parse(interpreter, str);
     if (value is TException) value = new TString(str); // If the string can't be parsed, return it as a TString
     return value;
 }
        /// <summary>
        /// The method that represents the 'random' function in the language.
        /// Generates a random TInteger between 0 and the given value - 1.
        /// </summary>
        /// <param name="interpreter">The interpreter that the method is being called from.</param>
        /// <param name="args">The arguments being passed to the function as a TArgumentList.</param>
        /// <returns>An TInteger with a value between 0 and the given value - 1</returns>
        public static TType Random(Interpreter interpreter, TArgumentList args)
        {
            // Check if the argument passed is a number, and return a new TInteger from the random value generated
            TNumber maximum = args[0] as TNumber;
            if (maximum == null) return new TException(interpreter,
                "Arguments of type '" + args[0].TypeName + "' cannot be used by random function");

            return new TInteger(randomNumberGenerator.Next((int)maximum.TIntegerValue));
        }
        /// <summary>
        /// The method that represents the 'print' function in the language.
        /// Takes any number of values and writes their string values.
        /// </summary>
        /// <param name="interpreter">The interpreter that the method is being called from.</param>
        /// <param name="args">The arguments being passed to the function as a TArgumentList.</param>
        /// <returns>TNil</returns>
        public static TType Print(Interpreter interpreter, TArgumentList args)
        {
            // Loop through arguments, append their string values to a StringBuilder, and output the StringBuilder
            StringBuilder str = new StringBuilder();
            for (int i = 0; i < args.Count; ++i)
            {
                TString arg = args[i] as TString;
                if (arg == null) str.Append(args[i].ToCSString());
                else str.Append(arg.Value);
            }
            System.Console.WriteLine(str);

            return TNil.Instance;
        }