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.
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.