Функция монстр маркер парсер


Эта функция будет считывать выражение (состоят из элементов) из векторных токенов, передаваемых в него. Он будет удалить маркеры, которые составляют выражения из вектора и вернуть их.

Маркер добавляется в выражение, Если

  • это первый маркер
  • это был оператор, прежде чем он

Скобки обрабатываются в parseBracketTokens (который вызывает эту функцию)

RawExpression *parseExpressionTokens(
        vector<Token*> &tokens, bool brackets = false) {

    // This is a vector of elements that make up an expression
    vector<RawExpressionElement*> elements;

    while(tokens.size() > 0) {

        // Operands must be at the start of an expression or after an operator
        if(tokenIsOperand(tokens[0])) {

            if(elements.size() == 0 ||
                    eElementIsRelationalOperator(elements.back())) {
                elements.push_back(makeEElementFromToken(tokens[0]));
                removeToken(tokens, 0);
                continue;
            }

            // This token is part of the next expression, break from the loop   
            break;      
        }

        if(tokenIsRelationalOperator(tokens[0])) {

            // Make sure we have an operand left of it

            if(tokens[0]->type == TOKEN_MINUS) {
                if(elements.size() == 0 ||
                    !eElementIsOperand(elements.back())) {

                    // handle the minus number or symbol :O

                }
            }

            if(elements.size() != 0 &&
                    eElementIsOperand(elements.back())) {
                elements.push_back(makeEElementFromToken(tokens[0]));
                removeToken(tokens, 0);
                continue;
            }           

            throw(string("Operator has bad lhand:") + tokenToString(tokens[0]));
        }

        // A closing bracket marks the end of an expression

        if(tokens[0]->type == TOKEN_CLOSEBRACKET) {
            if(!brackets)
                throw(string("Unmatched closing bracket"));

            break;
        }

        // Handle brackets and function calls

        if(tokens[0]->type == TOKEN_OPENBRACKET) {  

            // Check if it follows an identifier (if so, its a function call)

            if(elements.size() != 0 && 
                    elements.back()->e_type == REE_IDENTIFIER) {
                RawExpression *e = parseBracketTokens(tokens);
                string id = ((IdentifierREElement*)elements.back())->id;
                removeExpressionElement(elements, elements.size()-1);
                elements.push_back(new FunctionCallREElement(id, e));
                continue;
            }

            // This is not a function call
            if(elements.size() == 0 || 
                    !eElementIsOperand(elements.back())) {
                RawExpression *e = parseBracketTokens(tokens);
                elements.push_back(e);
                continue;
            }   

            // This is part of another expression
            break;      
        }

        // Parse Identifiers

        if(tokens[0]->type == TOKEN_IDENTIFIER) {
            elements.push_back(makeEElementFromToken(tokens[0]));
            removeToken(tokens, 0);
            continue;
        }

        // Unknown token, throw an error

        throw(string("Unexpected token in expression:") + 
                tokenToString(tokens[0]));
    }

    // Make sure expression doesn't end with an operator.
    if(elements.size() > 0 && !eElementIsOperand(elements.back()))
        throw(string("Operator missing rhand value"));

    return new RawExpression(elements);
}

Я хотел бы разделить этот код на что-то вроде

if(processOperand(tokens)) continue;
if(processRelationalOperator(tokens)) continue;
if(precessBracket(tokens)) continue;
if(processIdentifier(tokens)) continue;

Но то, что он должен продолжать или перерыв в некоторых случаях оставляет меня сомнение лучшей была к этому подступиться.

Я думаю, что я мог бы определить новый тип что-то вроде { NOT_RELEVANT, PART_OF_EXPRESSION, END_OF_EXPRESSION }

и после этого

ExpressionElement *e; // Use this an out ptr in the function call below
result = processOperand(tokens, e);
if(result == END_OF_EXPRESSION) break;
else if(result == PART_OF_EXPRESSION) addItToExpression(e);
// else its NOT_RELAVANT so try parsing the next type

Но это кажется плохой идеей, чтобы определить новый тип для этого.

Что вы думаете?



805
8
c++
задан 30 марта 2011 в 11:03 Источник Поделиться
Комментарии
2 ответа

Моя первая мысль заключается в том, что анализаторы следует рассматривать как таблично государственной машины ровно по одной причине вы видите в коде :-)

На самом деле, я думаю, что ломать этого в набор отдельных функций, что является отличным подходом, а также добавление нового перечисления, чтобы поддержать это делает это очень ясным, что состояние выражений после каждого шага.

Другой вариант я бы рассматривать определения пути через код и, возможно, делать некоторые оптимизации, за счет ясности. Существует несколько мест, где после того, как вы впервые вошли в условный блок, вы взяли на себя обязательство не делать каких-либо других код в цикле. Перестановке они могут устранить некоторые отдельные продолжения/операторы Break и облегчает рефакторинг.

7
ответ дан 1 апреля 2011 в 12:04 Источник Поделиться

О рефакторинге этот конкретный режим (не сам алгоритм):


  • Заменить элементы.размер() == 0 на элементы.пустой(); это быстрее.

  • Заменить весь как цикл for_each

  • Нормализации маркеров идентификации ( теперь некоторые маркеры идентифицируются отдельные процедуры, но некоторые из них определены через Если(тип == blabl) )

  • Двигаться логике после каждого маркера определения , если для отдельных подпрограмм:

    class TokenToElements{
    typedef vector<RawExpressionElement *> el_vec_t;
    el_vec_t &elements;
    public:
    TokenToElements( el_vec_t &e ):elements( e ){};

    enum TokenType{
    TK_OPERAND,
    TK_REL_OP,
    TK_CLOSEBRACKET,
    TK_OPENBRACKET,
    TK_IDENTIFIER,
    TK_UNKNOWN
    };

    TokenType get_token_type( Token *token ){
    if ( tokenIsOperand( token ) ) return TK_OPERAND;
    if ( tokenIsRelationOperator( token ) ) return TK_REL_OP;
    if ( token->type == TOKEN_CLOSEBRACKET ) return TK_CLOSEBRACKET;
    if ( token->type == TOKEN_OPENBRACKET ) return TK_OPENBRACKET;
    if ( token->type == TOKEN_IDENTIFIER ) return TK_IDENTIFIER;
    return TK_UNKNOWN;
    };

    void do_operand( Token *token ){ ... };
    void do_relation_operator( Token *token ){ ... };
    void do_closebraket( Token *token ){ ... };
    void do_openbraket( Token *token ){ ... };
    void do_identifier( Token *token ){ ... };

    void opearator()( Token *token ){
    switch( get_token_type( token ) ){
    case TK_OPERAND:
    do_operand( token );
    break;
    case TK_REL_OP:
    do_relation_operator( token );
    break;
    case TK_CLOSEBRACKET:
    do_closebraket( token );
    break;
    case TK_OPENBRACKET:
    do_openbraket( token );
    break;
    case TK_IDENTIFIER:
    do_identifier( token );
    break;
    default:
    throw std::runtime_error( "Unexpected token in expression:" + tokenToString( token ) );
    };
    };
    };

    RawExpression *parseExpressionTokens(
    vector<Token*> &tokens, bool brackets = false) {

    // This is a vector of elements that make up an expression
    vector<RawExpressionElement*> elements;

    std::for_each( tokens.begin(), tokens.end(), TokenToElements( elements ) );
    };

    Другое решение-вернуть указатель на функцию обратного вызова от get_token_type и назвать ее вместо переключателя. Код TokenToElement::оператор() будет еще короче:

    typedef void (TokenToElement::*hndl_fun_t)( Token *);

    void operator()( Token * token ){
    hndl_fun_t hndl = get_token_handler( token )
    (this->*hdnl)( token );
    };


5
ответ дан 14 апреля 2011 в 11:04 Источник Поделиться