c++castingcompiler-constructiontypecheckingbnfc

Typechecker for a C/C++ like language


Currently I am working on a compiler for a C/C++ like language to be more specific a typechecker. I have generated a parser with a grammar and the bnfc tool. For my typechecker I need to infer the type of expressions. All expressions are derived from a abstract base class Exp like the following:

class Exp {
...
}

class ExpPlus : public {
 ...
}

class ExpAnd : public {
 ...
} 
...

ExpPlus refers to Exp + Exp and ExpAnd to Exp && Exp respectively. My infer function takes a Expression and returns it's type and looks like this:

ype *TypeChecker::infer(Exp *exp)
{
    // check for kind of expression
    /* Base cases */
    if(dynamic_cast<ETrue*>(exp))
    {
        Type *tBool = new Type_bool;
        return tBool;
    }
    if(dynamic_cast<EFalse*>(exp))
    {
        Type *tBool = new Type_bool;
        return tBool;
    }
    if(dynamic_cast<EInt*>(exp))
    {
        Type *tInt = new Type_int;
        return tInt;
    }
    if(dynamic_cast<EDouble*>(exp))
    {
        Type *tDouble = new Type_double;
        return tDouble;
    }
    if(dynamic_cast<EId*>(exp))   // search for type of given Id in contexts
    {
        EId *eId = dynamic_cast<EId*>(exp);

        Type *typeOfSearchedId = env_->lookupVarTypeFromContext(eId->id_);
        return typeOfSearchedId;
    }
    /* Base cases end*/

    /* check expressions */
    if(dynamic_cast<EApp*>(exp))
    {
        EApp *eApp = dynamic_cast<EApp*>(exp);
        Type *retType = env_->lookupFunTypeFromSymbolTable(eApp->id_).returnType;

        return retType;
    }
    if(dynamic_cast<EUPlus*>(exp))
    {
        // cast exp to type of exp
        EUPlus *eu_plus = dynamic_cast<EUPlus*>(exp);

        // infer till base case is derived
        Type *typeExp = infer(eu_plus->exp_);

        // if type is int or double
        if(dynamic_cast<Type_int*>(typeExp) || dynamic_cast<Type_double*>(typeExp))
            return typeExp;

        throw TypeException("Trying to unary add wrong type: " + env_->getStringRepresentationOfType(typeExp));
    }
...

As you can see I have a long list of If-statements that checker of which concrete expression type the expression is, it works fine and stuff but I wonder whether there is any more elegant way of doing this.


Solution

  • You got polymorphism the wrong way around.

    Instead of this:

    class Base {};
    
    class Derived1 : public Base {};
    class Derived2 : public Base {};
    class Derived3 : public Base {};
    //...
    
    void foo(Base* b) {
        if(dynamic_cast<Derived1*>(b)) {
            do_something( dynamic_cast<Derived1*>(b));
        } else if (dynamic_cast<Derived2*>(b)) {
            do_something( dynamic_cast<Derived2*>(b));
        } else if ....
    }
    

    You should acutally make use of polymorhism by using virtual methods:

    class Base {
        virtual void do_something() = 0;
        virtual ~Base() {}
    };
    
    class Derived1 : public Base {
        void do_something() override { /*...*/ }
    };
    class Derived2 : public Base {
        void do_something() override { /*...*/ }
    };
    //...
    
    void foo(Base* b) {
        b->do_something();
    }
    

    Note how now not every method that uses Base* and expects different behavior depending on the dynamic type must actually know the dynamic type. foo just calls do_something and virtual dispatch selects the right method to be called. If you write some bar(Base*) you do not need a second block of if-else but rather call virtual methods again.

    I simplified the example a lot, though it seems like in your code all branches return a type and the only difference is different implementations for different derived classes.