Skip to content

[maybe bug] Nested matching always chooses first case #62

@forflo

Description

@forflo

given the following class hierarchy

struct Expression {};

struct ExpBinary : Expression {
    Expression *left, *right;

    ExpBinary(Expression *o1, Expression *o2)
        : left(o1)
        , right(o2) {}
};

struct ExpArith : ExpBinary {
    enum function { 
        SUB, ADD, MUL, DIV 
    };

    function arithOp;

    ExpArith(function f, Expression *l, Expression *r)
        : ExpBinary(l, r)
        , arithOp(f) {}
};

struct ExpLogical : ExpBinary {
    enum function_log { 
        GT, LT, EQ 
    };

    function_log logicalOp;

    ExpLogical(function_log f, Expression *l, Expression *r)
        : ExpBinary(l, r)
        , logicalOp(f) {}
};

struct ExpInteger : Expression {
    int value;
    ExpInteger(int v) : value(v) {}
};

The following evaluator somehow does not work:

int evaluate(const Expression& exp){
    var<Expression&> left, right;
    var<ExpLogical::function_log> lOp;
    var<ExpArith::function> aOp;
    int val;

    Match(exp){
        Case(C<ExpBinary>(&left, &right)) {
            Match(exp) {
                // cout << typeid(exp).name() << endl;
                Case(C<ExpArith>(aOp)) {
                    //cout << "exparith!\n";
                    Match(aOp) {
                        Case(ExpArith::ADD) {
                            return evaluate(left) + evaluate(right);
                        }
                        Case(ExpArith::SUB) {
                            return evaluate(left) - evaluate(right);
                        }
                        Case(ExpArith::MUL) {
                            return evaluate(left) * evaluate(right);
                        }
                        Case(ExpArith::DIV) {
                            return evaluate(left) / evaluate(right);
                        }
                        Otherwise() {
                            return 0;
                        }
                    } EndMatch
                }
                Case(C<ExpLogical>(lOp)) {
                    cout << "explogical!\n"; //never reached, but it should
                    Match(lOp) {
                        Case(ExpLogical::GT) {
                            return evaluate(left) > evaluate(right);
                        }
                        Case(ExpLogical::LT) {
                            return evaluate(left) < evaluate(right);
                        }
                        Case(ExpLogical::EQ) {
                            return evaluate(left) == evaluate(right);
                        }
                        Otherwise() {
                            return 0;
                        }
                    } EndMatch
                }
                Otherwise() {
                    cout << "Error in exp arith/logical matching!\n";
                    return 0;
                }
            } EndMatch
        }
        Case(C<ExpInteger>(val)) {
            return val;
        }
        Otherwise() {
            cout << "Error in evaluate!\n";
            return 0;
        }
    } EndMatch
}

If I build the expression

Expression *log =
        new ExpLogical(ExpLogical::GT,
                       new ExpInteger(3),
                       new ExpInteger(4));

and try to evaluate it using the statement,

cout << evaluate(*log) << endl;

the program outputs -1 which is wrong. Somehow the statement Case(C<ExpLogical>(lOp)) is not reached, instead the second Match(exp) chooses Case(C<ExpArith>(aOp)). I don't understand why, since the patterns against which the subjecting values are being matched are clearly different. Do you have any idea? Is it disallowed to nest Match statements?

I also provided the whole (compilable) example as a gist.

Best regards

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions