How to avoid the overuse of dynamic casting when it comes to polymorphic types / am I using them correctly for my AST?

108 views Asked by At

I have written a compiler that works in its current form. However I feel like I am overusing dynamic casting when differentiating different sub classes for the nodes while visiting nodes on the AST. Here is an example:

I have a recursive method called visit that dynamically casts a node parameter for each node sub class. It will check if the cast was successful and then take appropriate action as such:

VariableValue visit(Node *node) {
    Num* num = dynamic_cast<Num*>(node);
    if (num != NULL) {
        return visit_Num(num);
    }
    delete num;
    BinOp* binop = dynamic_cast<BinOp*>(node);
    if (binop != NULL) {
        return visit_BinOp(binop);
    }
    delete binop;
    UnOp* unop = dynamic_cast<UnOp*>(node);
    if (unop != NULL) {
        return visit_UnaryOp(unop);
    }
    delete unop;
    ...

The issue is I have a lot of different node sub classes, so these blocks of if statements go on for a while. I feel this approach is not very efficient on CPU and memory as a cast will be created for each node sub class and will have to be checked against each one. I have attempted to improve this by using delete when a cast fails.

Is there a better way to achieve my desired results within a polymorphic environment?

For some context here are the node classes including the parent:

class Node {
    public:
        virtual std::string toString() = 0;
        virtual int getNodeAttribute() { return 0; }
        Token token;
        Node(){}
};

class BinOp : public Node{
    public:
        Node *left, *right; 
        Token op;
        BinOp(Node *cleft, Token cop, Node *cright) {
            left = cleft;
            Node::token = cop;
            op = cop;
            right = cright;
        }
        std::string toString() {
            return "BinOp Node";
        }
};

class Num : public Node{
    public:
        TokenValue value;
        Num(Token ctoken) {
            Node::token = ctoken;
            value = ctoken.value;
        }
        std::string toString() {
            return "Num Node";
        }
};

class UnOp : public Node {
    public:
        Node *expr;
        UnOp(Token ctoken, Node *cexpr) {
            Node::token = ctoken;
            expr = cexpr;
        }
        std::string toString() {
            return "UnOp Node";
        }
};

...

And here is how a node is generated within the parser (Num node for example)

Node* Parser::factor() {
    ...
    else if (token.type == TOKENTYPE::INTEGER) {
        eat(TOKENTYPE::INTEGER);
        Num* numNode = new Num(token);
        return numNode;
    }
    ...

0

There are 0 answers