Пример #1
0
        private void CheckZero(Expression expr)
        {
            bool isZero = false;

            if (expr is IntegerConstant)
            {
                isZero = ((IntegerConstant)expr).Value == 0;
            }
            else
            {
                isZero = FloatUtil.FloatAbsoluteEqualsNoEpislon(((FloatConstant)expr).Value, 0);
            }
            if (isZero)
            {
                throw new ParserException(expr, "Division by 0 error.");
            }
        }
Пример #2
0
        private OperationType GetOperation(
            ResolvedTypeCategory leftType,
            ResolvedTypeCategory rightType,
            string op)
        {
            if (consolidationLookup == null)
            {
                consolidationLookup = new Dictionary <string, Dictionary <ResolvedTypeCategory, Dictionary <ResolvedTypeCategory, OperationType> > >();

                OperationType[] operations = new OperationType[] {
                    new OperationType(ResolvedTypeCategory.NULL, ResolvedTypeCategory.NULL, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, true)); }),
                    new OperationType(ResolvedTypeCategory.NULL, ResolvedTypeCategory.NULL, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, false)); }),
                    new OperationType(ResolvedTypeCategory.BOOLEAN, ResolvedTypeCategory.BOOLEAN, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetBool(opChain.Left) == GetBool(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.BOOLEAN, ResolvedTypeCategory.BOOLEAN, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetBool(opChain.Left) != GetBool(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "&", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) & GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "|", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) | GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "^", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) ^ GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "<<", ResolvedType.INTEGER, (opChain) =>
                    {
                        int right = GetInt(opChain.Right);
                        if (right < 0)
                        {
                            throw new ParserException(opChain.FirstToken, "Cannot bit shift by a negative number.");
                        }
                        return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) << right));
                    }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, ">>", ResolvedType.INTEGER, (opChain) =>
                    {
                        int right = GetInt(opChain.Right);
                        if (right < 0)
                        {
                            throw new ParserException(opChain.FirstToken, "Cannot bit shift by a negative number.");
                        }
                        return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) >> right));
                    }),

                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "+", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) + GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "-", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) - GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "*", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) * GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "/", ResolvedType.INTEGER, (opChain) => { CheckZero(opChain.Right); return(MakeInt(opChain.FirstToken, GetInt(opChain.Left) / GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "%", ResolvedType.INTEGER, (opChain) => { return(MakeInt(opChain.FirstToken, PositiveModInt(opChain.Left, opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "<=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) <= GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, ">=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) >= GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "<", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) < GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, ">", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) > GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) == GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) != GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.INTEGER, "**", ResolvedType.FLOAT, (opChain) =>
                    {
                        int right = GetInt(opChain.Right);
                        int left  = GetInt(opChain.Left);
                        if (right == 0)
                        {
                            return(MakeFloat(opChain.FirstToken, 1.0));
                        }
                        return(MakeFloat(opChain.FirstToken, Math.Pow(left, right)));
                    }),

                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "+", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetInt(opChain.Left) + GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "-", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetInt(opChain.Left) - GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "*", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetInt(opChain.Left) * GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "/", ResolvedType.FLOAT, (opChain) => { CheckZero(opChain.Right); return(MakeFloat(opChain.FirstToken, GetInt(opChain.Left) / GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "%", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, PositiveModFloat(opChain.Left, opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "<=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) <= GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, ">=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) >= GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "<", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) < GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, ">", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) > GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) == GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetInt(opChain.Left) != GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.FLOAT, "**", ResolvedType.FLOAT, (opChain) =>
                    {
                        double right = GetFloat(opChain.Right);
                        int left     = GetInt(opChain.Left);
                        if (FloatUtil.FloatAbsoluteEqualsNoEpislon(right, 0))
                        {
                            return(MakeFloat(opChain.FirstToken, 1.0));
                        }
                        if (!FloatUtil.FloatAbsoluteEqualsNoEpislon(right % 1, 0) && left < 0)
                        {
                            throw new ParserException(opChain.OpToken, "Exponent creates a complex expression.");
                        }
                        return(MakeFloat(opChain.FirstToken, Math.Pow(left, right)));
                    }),

                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "+", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) + GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "-", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) - GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "*", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) * GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "/", ResolvedType.FLOAT, (opChain) => { CheckZero(opChain.Right); return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) / GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "%", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, PositiveModFloat(opChain.Left, opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "<=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) <= GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, ">=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) >= GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "<", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) < GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, ">", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) > GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) == GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) != GetInt(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.INTEGER, "**", ResolvedType.FLOAT, (opChain) =>
                    {
                        int right   = GetInt(opChain.Right);
                        double left = GetFloat(opChain.Left);
                        if (right == 0)
                        {
                            return(MakeFloat(opChain.FirstToken, 1.0));
                        }
                        return(MakeFloat(opChain.FirstToken, Math.Pow(left, right)));
                    }),

                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "+", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) + GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "-", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) - GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "*", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) * GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "/", ResolvedType.FLOAT, (opChain) => { CheckZero(opChain.Right); return(MakeFloat(opChain.FirstToken, GetFloat(opChain.Left) / GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "%", ResolvedType.FLOAT, (opChain) => { return(MakeFloat(opChain.FirstToken, PositiveModFloat(opChain.Left, opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "<=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) <= GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, ">=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) >= GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "<", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) < GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, ">", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) > GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) == GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetFloat(opChain.Left) != GetFloat(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.FLOAT, "**", ResolvedType.FLOAT, (opChain) =>
                    {
                        double right = GetFloat(opChain.Right);
                        double left  = GetFloat(opChain.Left);
                        if (FloatUtil.FloatAbsoluteEqualsNoEpislon(right, 0))
                        {
                            return(MakeFloat(opChain.FirstToken, 1.0));
                        }
                        if (!FloatUtil.FloatAbsoluteEqualsNoEpislon(right % 1, 0) && left < 0)
                        {
                            throw new ParserException(opChain.OpToken, "Exponent creates a complex expression.");
                        }
                        return(MakeFloat(opChain.FirstToken, Math.Pow(left, right)));
                    }),

                    new OperationType(ResolvedTypeCategory.BOOLEAN, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetBool(opChain.Left).ToString() + GetString(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetInt(opChain.Left).ToString() + GetString(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.FLOAT, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetFloatAsString(opChain.Left) + GetString(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.BOOLEAN, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetString(opChain.Left) + GetBool(opChain.Right).ToString())); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.INTEGER, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetString(opChain.Left) + GetInt(opChain.Right).ToString())); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.FLOAT, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetString(opChain.Left) + GetFloatAsString(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, (opChain) => { return(MakeString(opChain.FirstToken, GetString(opChain.Left) + GetString(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.STRING, "==", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetString(opChain.Left) == GetString(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.STRING, "!=", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetString(opChain.Left) != GetString(opChain.Right))); }),

                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.INSTANCE, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.INSTANCE, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.CLASS_DEFINITION, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.CLASS_DEFINITION, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.FUNCTION_POINTER, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.FUNCTION_POINTER, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.LIST, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.LIST, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.DICTIONARY, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.DICTIONARY, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.OBJECT, "+", ResolvedType.STRING, null),
                    new OperationType(ResolvedTypeCategory.OBJECT, ResolvedTypeCategory.STRING, "+", ResolvedType.STRING, null),

                    new OperationType(ResolvedTypeCategory.BOOLEAN, ResolvedTypeCategory.BOOLEAN, "&&", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetBool(opChain.Left) && GetBool(opChain.Right))); }),
                    new OperationType(ResolvedTypeCategory.BOOLEAN, ResolvedTypeCategory.BOOLEAN, "||", ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, GetBool(opChain.Left) || GetBool(opChain.Right))); }),

                    new OperationType(ResolvedTypeCategory.INTEGER, ResolvedTypeCategory.STRING, "*", ResolvedType.STRING, (opChain) => { return(GenerateMultipliedStringIfNotTooLong(opChain, opChain.Left, opChain.Right)); }),
                    new OperationType(ResolvedTypeCategory.STRING, ResolvedTypeCategory.INTEGER, "*", ResolvedType.STRING, (opChain) => { return(GenerateMultipliedStringIfNotTooLong(opChain, opChain.Left, opChain.Right)); }),
                };

                foreach (OperationType ot in operations)
                {
                    if (!consolidationLookup.ContainsKey(ot.Op))
                    {
                        consolidationLookup[ot.Op] = new Dictionary <ResolvedTypeCategory, Dictionary <ResolvedTypeCategory, OperationType> >();
                    }
                    if (!consolidationLookup[ot.Op].ContainsKey(ot.LeftType))
                    {
                        consolidationLookup[ot.Op][ot.LeftType] = new Dictionary <ResolvedTypeCategory, OperationType>();
                    }
                    consolidationLookup[ot.Op][ot.LeftType].Add(ot.RightType, ot); // causes exception if duplicate
                }
            }

            // The op will always have some types registered, so you can dereference the first level of lookups without checking.
            Dictionary <ResolvedTypeCategory, Dictionary <ResolvedTypeCategory, OperationType> > l1 = consolidationLookup[op];

            if (l1.ContainsKey(leftType))
            {
                Dictionary <ResolvedTypeCategory, OperationType> l2 = l1[leftType];
                if (l2.ContainsKey(rightType))
                {
                    return(l2[rightType]);
                }
            }

            if (op == "==")
            {
                // == comparisons between different kinds of objects should resolve to false at compile time.
                // Lazily initialize these operation types as they are encountered.
                if (leftType != rightType &&
                    !(leftType == ResolvedTypeCategory.INTEGER && rightType == ResolvedTypeCategory.FLOAT) &&
                    !(leftType == ResolvedTypeCategory.FLOAT && rightType == ResolvedTypeCategory.INTEGER) &&
                    leftType != ResolvedTypeCategory.ANY &&
                    rightType != ResolvedTypeCategory.ANY &&
                    leftType != ResolvedTypeCategory.OBJECT &&
                    rightType != ResolvedTypeCategory.OBJECT)
                {
                    if (!consolidationLookup[op].ContainsKey(leftType))
                    {
                        consolidationLookup[op][leftType] = new Dictionary <ResolvedTypeCategory, OperationType>();
                    }
                    OperationType ot = new OperationType(leftType, rightType, op, ResolvedType.BOOLEAN, (opChain) => { return(MakeBool(opChain.FirstToken, false)); });
                    consolidationLookup[op][leftType][rightType] = ot;
                    return(ot);
                }
            }

            return(null);
        }