File parser.cpp

File List > src > parser.cpp

Go to the documentation of this file

#include "parser.h"
#include "ast/SeleniumASTVisitor.h"

parser::parser(string&& content)
    : scanner { Scanner(std::move(content)) }, tree(AST()) {}

bool parser::program() { //program -> test program | epsilon
    if(token == 0) return true; // program -> epsilon
    if(!test()) return false;
    if(!program()) return false;

    return true; // program -> test program
}

bool parser::test() {
    if(token == TEST_NAME) {
        tree.tests.emplace_back(std::move(yysval), vector<ActionNode*>());
        token = scanner.yylex();
        if(token == LEFT_BRACE) {
            token = scanner.yylex();
            if(!body()){
                return recoverFromError({TEST_NAME});
            }

            if(token != RIGHT_BRACE) {
                reportError("RIGHT BRACE");
                return recoverFromError({TEST_NAME});
            }
        }else{
            reportError("LEFT BRACE");
            return recoverFromError({TEST_NAME});
        }

        token = scanner.yylex();
        return true;
    }

    reportError("TEST NAME");
    return recoverFromError({TEST_NAME});
}

bool parser::action() {
    switch (token) {
        case CLICK:
            return click();
        case HOVER_OVER:
            return hover();
        case VISIT:
            return visit();
        case TYPE:
            return type();
        case CHECK_IF:
            return check();
        default:
            return false;
    }
}


bool parser::visit() {
    if(token == VISIT) {
        token = scanner.yylex();

        if(token != URL) {
            reportError("URL");
            return recoverFromError({CLICK, HOVER_OVER, VISIT, TYPE, CHECK_IF, RIGHT_BRACE});
        }

        tree.tests.back().actions.push_back(new VisitNode(std::move(yysval)));

        token = scanner.yylex();
        return true;
    }

    return false;
}

bool parser::body() {
    if(token == CLICK || token == HOVER_OVER || token == TYPE || token == CHECK_IF || token == VISIT) {
        return action() && body();
    }

    return true;
}

bool parser::elem_type() {
    if(token == BUTTON || token == LINK || token == TEXT || token == IMAGE || token == INPUT){
        token = scanner.yylex();
        return true;
    }

    return false;
}

bool parser::click() {
    vector<Tokens> recoveryTokens = {CLICK, HOVER_OVER, VISIT, TYPE, CHECK_IF, RIGHT_BRACE};
    if(token == CLICK) {
        token = scanner.yylex();
        string element_type = yysval;
        if(!elem_type()){
            reportError("ELEMENT TYPE");
            return recoverFromError(recoveryTokens);
        }
        if(token == WITH_DESC) {
            token = scanner.yylex();

            if(token != NLD) {
                reportError("NLD");
                return recoverFromError(recoveryTokens);
            }
        }else{
            reportError("WITH DESCRIPTION");
            return recoverFromError(recoveryTokens);
        }

        tree.tests.back().actions.push_back(new ClickNode(std::move(element_type), std::move(yysval)));

        token = scanner.yylex();
        return true;
    }

    return false;
}

bool parser::check() {
    vector<Tokens> recoveryTokens = {CLICK, HOVER_OVER, VISIT, TYPE, CHECK_IF, RIGHT_BRACE};
    if(token == CHECK_IF) {
        token = scanner.yylex();
        string element_type = yysval;
        if(!elem_type()){
            reportError("ELEMENT TYPE");
            return recoverFromError(recoveryTokens);
        }
        if(token == WITH_DESC) {
            token = scanner.yylex();
            if(token != NLD){
                reportError("NLD");
                return recoverFromError(recoveryTokens);
            }

            string xpath = yysval;
            token = scanner.yylex();
            tree.tests.back().actions.push_back(new CheckNode(std::move(element_type), std::move(xpath), token == DISPLAYED));
            if(state()){
                return true;
            }else{
                reportError("STATE");
                return recoverFromError(recoveryTokens);
            }
        }else{
            reportError("WITH DESCRIPTION");
            return recoverFromError(recoveryTokens);
        }
    }

    return false;
}

bool parser::hover() {
    vector<Tokens> recoveryTokens = {CLICK, HOVER_OVER, VISIT, TYPE, CHECK_IF, RIGHT_BRACE};
    if(token == HOVER_OVER) {
        token = scanner.yylex();
        if(!elem_type()){
            reportError("ELEMENT TYPE");
            return recoverFromError(recoveryTokens);
        }
        if(token == WITH_DESC) {
            token = scanner.yylex();

            if(token != NLD){
                reportError("NLD");
                return recoverFromError(recoveryTokens);
            }
        } else {
            reportError("WITH DESCRIPTION");
            return recoverFromError(recoveryTokens);
        }

        token = scanner.yylex();
        return true;
    }

    return false;
}

bool parser::type() {
    vector<Tokens> recoveryTokens = {CLICK, HOVER_OVER, VISIT, TYPE, CHECK_IF, RIGHT_BRACE};
    if(token != TYPE) return false;
    token = scanner.yylex();
    if(token != NLD) {
        reportError("CONTENT");
        return recoverFromError(recoveryTokens);
    } // TODO: IT SHOULD BE CONTENT
    string content = yysval;
    token = scanner.yylex();
    if(token != ON){
        reportError("ON");
        return recoverFromError(recoveryTokens);
    }
    token = scanner.yylex();
    string element_type = yysval;
    if(!elem_type()){
        reportError("ELEMENT TYPE");
        return recoverFromError(recoveryTokens);
    }
    if(token != WITH_DESC){
        reportError("WITH DESCRIPTION");
        return recoverFromError(recoveryTokens);
    }
    token = scanner.yylex();
    if(token != NLD){
        reportError("NLD");
        return recoverFromError(recoveryTokens);
    }


    tree.tests.back().actions.push_back(new TypeNode(std::move(content), std::move(element_type), std::move(yysval)));

    token = scanner.yylex();
    return true;
}

bool parser::state() {
    if(token == DISPLAYED || token == HIDDEN) {
        token = scanner.yylex();
        return true;
    }

    return false;
}

bool parser::recoverFromError(const vector<enum Tokens>& syncSet) {
    while (token != 0) {
        for (auto syncToken : syncSet) {
            if (token == syncToken) {
                cerr << "[Recovery] Found synchronizing token: " << Scanner::getTokenName(token) << " at line " <<
                        to_string(scanner.line_number()) << endl;
                return true;
            }
        }
        token = scanner.yylex();
    }

    cerr << "Failed to recover. Reached EOF.\n";
    return false;
}

void parser::reportError(const string& expectedToken) {
    errors.push_back("Expected " + expectedToken + ", but found " + Scanner::getTokenName(token) + " instead at line " + std::to_string(scanner.line_number()));
}

expected<AST, vector<string>> parser::parse() {
    token = scanner.yylex();
    if (program() && errors.empty()) {
        return tree;
    }

    return unexpected(errors);
}