Грамматика бизонов ломает повторяющиеся токены / выражения? - PullRequest
0 голосов
/ 04 февраля 2019

Используя довольно простую грамматику Bison / Flex, я пытаюсь вытянуть токены / выражения в объекты C ++, чтобы сгенерировать три кода операции (т.е. внутреннее представление).Я делаю это, потому что этот конкретный парсер представляет меньшее подмножество большего парсера.Моя проблема связана с повторными выражениями / токенами.

Например:

10 + 55 будет анализироваться как 10 + 10.

10 + VARIABLLENAME будет анализироваться нормально, так как INT и VARIABLE - это разные токены.

55-HELLOWORLD / 100 снова будет анализироваться нормально, возможно потому, что по обе стороны выражения никогда не бывает двух одинаковых токенов.

55-HELLOWORLD - 100 Seg Faults out.Повторение токенов операции (т. Е. -, +, / и т. Д. Приводит к сбою синтаксического анализатора).

TLDR: при повторении типов значений (т. Е. INT, FLOAT, VARIABLE) один и тот же токен возвращается дважды.При повторении операций происходит сбой сегмента синтаксического анализатора.

Я предполагаю, что я делаю что-то, когда загружаю значения $ 1 / $ 3 в объекты класса, а затем добавляю их в стек синтаксического анализатора.Я попытался проверить адреса памяти каждой переменной + указатель, который я генерирую, и все они выглядят так, как я ожидал (т.е. я не перезаписываю один и тот же объект).Я пытался убедиться, что значения загружены как токены значения, INT |и переменный |оба корректно загружают свои соответствующие переменные в классы.

Кажется, проблема связана с выражениями операторов выражения OPERATION, когда используются два значения одного типа, выражения идентичны.Чтобы использовать более ранний пример:

10 + 55 -> выражение PLUS выражение -> $ 1 = 10, $ 3 = 10

Когда переменные загружаются как INT, обе являются ожидаемыми?

Вот мой соответствующий parser.y, а также объект, в который я пытаюсь загрузить значения.

%{
  #include <cstdio>
  #include <iostream>
  #include "TOC/Operation.h"
  #include "TOC/Value.h"
  #include "TOC/Variable.h"
  #include "TOC.h"

  using namespace std;

  extern int yylex();
  extern int yyparse();
  extern FILE *yyin;

  void yyerror(const char *s);
%}

%code requires {
    // This is required to force bison to include TOC before the preprocessing of union types and YYTYPE.
    #include "TOC.h"
}

%union {
  int ival;
  float fval;
  char *vval;
  TOC * toc_T;
}

%token <ival> INT
%token <fval> FLOAT
%token <vval> VARIABLE

%token ENDL PLUS MINUS MUL DIV LPAREN RPAREN

%type <toc_T> expression1
%type <toc_T> expression

%right PLUS MINUS
%right MUL DIV

%start start
%%
start:
        expressions;
expressions:
    expressions expression1 ENDL
    | expression1 ENDL;
expression1:
    expression { 
        TOC* x = $1;
        cout<<x->toTOCStr()<<endl; 
    }; 
expression: 
    expression PLUS expression { 
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::ADD);
        TOC *t = &op;
        $$ = t;
    }
    |expression MINUS expression { 
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::SUBTRACT);
        TOC *t = &op;
        $$ = t;    
    }
    |expression MUL expression {
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::MULTIPLY);
        TOC *t = &op;
        $$ = t;
    }
    |expression DIV expression { 
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::DIVIDE);
        TOC *t = &op;
        $$ = t;
    }
    |LPAREN expression RPAREN { 
        TOC *t = $2; 
        $$ =  t;
    }
    | INT { 
        Value<int> v = $1;
        TOC *t = &v; 
        $$ =  t;
    }
    | FLOAT { 
        Value<float> v = $1;
        TOC *t = &v;
        $$ = t; 
    }
    | VARIABLE {
        char* name = $1;
        Variable v(name);
        TOC *t = &v;
        $$ = t;
    }
%%

void yyerror(const char *s) {
  cout << "Parser Error:  Message: " << s << endl;
  exit(-1);
}

И значения, которые я пытаюсь загрузить (для большей ясности объединены в один файл).

Operation.h

enum OPS {
    SUBTRACT,
    ADD,
    MULTIPLY,
    DIVIDE,
    EXPONENT
};

class Operation : public TOC{

    OPS op;
    public:
        TOC* arg1;
        TOC* arg2;
        Operation(TOC* arg1_in, TOC* arg2_in, OPS operation){
            tt = TOC_TYPES::OPERATION_E;
            arg1 = arg1_in;
            arg2 = arg2_in;
            op = operation;
        };


        std::string toOPType(OPS e){
            switch (e){
                case SUBTRACT:
                    return "-";
                case ADD:
                    return "+";
                case MULTIPLY:
                    return "*";
                case DIVIDE:
                    return "/";
                case EXPONENT:
                    return "^";
                default:
                    return "[Operation Error!]";
            }
        }

        std::string toTOCStr(){
            return arg1->toTOCStr() + toOPType(op) + arg2->toTOCStr();
        }
};

Value.h

template <class T> class Value : public TOC {
    public:
        T argument;
        Value(T arg){
            tt = TOC_TYPES::VALUE_E;
            argument = arg;
        }

        std::string toTOCStr(){
            std::string x = std::to_string(argument);
            return x;
        }
};

Переменная. H

class Variable : public TOC {
    public:
        char *name;
        Variable(char* name_in){
            tt = TOC_TYPES::VARIABLE_E;
            name = name_in;
        }
        std::string toTOCStr(){
            std::string x = name;
            return x;
        }
};

TOC.h, если этоНужен

enum TOC_TYPES { 
    VARIABLE_E, 
    VALUE_E,
    OPERATION_E
};

class TOC{
    public:
        TOC_TYPES tt;   
        virtual std::string toTOCStr() = 0;
};

Файл My Main просто загружается в файл и устанавливает yyin в его содержимое перед вызовом yyparse.Я не включил это, но могу, если нужно (это не очень интересно).

В идеале, я хотел бы загрузить все дерево анализа RD в оглавление *, которое затем можно перебрать, чтобы сгенерировать три кода операции на каждом уровне.Однако эта ошибка, повторяющая повторяющиеся токены и операции, ставит меня в тупик.

1 Ответ

0 голосов
/ 05 февраля 2019

Вот пример проблемы:

    Operation op(a1, a2, OPS::ADD);
    TOC *t = &op;
    $$ = t;

(t не нужно; с тем же успехом вы могли бы написать $$ = &op;. Но это только примечание.)

op - это автоматическая переменная, время жизни которой заканчивается при выходе из блока.И это происходит сразу после сохранения его адреса в $$.Это делает семантическое значение производства «висячим указателем».

Использование адреса переменной, срок действия которой закончился, является неопределенным поведением, но вы, вероятно, можете догадаться, что происходит: при следующем вводе блокастек находится в том же месте, и новый op имеет тот же адрес, что и старый.(Нет гарантии, что это произойдет: неопределенное поведение не определено по определению. Но этот конкретный результат согласуется с вашими наблюдениями.)

Короче говоря, станьте удобнее с оператором new:

$$ = new Operation(a1, a2, OPS::ADD);

И не забудьте delete в нужный момент.

...