Как разобрать оператор if в бизоне - PullRequest
0 голосов
/ 10 января 2010

Я использую Bison для создания компилятора для простого языка. Вот часть грамматики:

stmt: IF '(' exp ')' stmt{
        if(exp) $$=$5;
      }
      |PRINT ';' {
        cout<<"hi";
       }
;

exp: true|false
;

Я столкнулся с проблемой при анализе этого оператора if: скажем, у меня есть этот код:

if(false) print;

"hi" будет напечатан в любом случае, потому что stmt-> PRINT анализируется перед stmt-> IF (exp) stmt. Что я могу сделать? (Я новичок в бизоне и компиляции, поэтому не стесняйтесь исправлять ошибки в моем вопросе).

Ответы [ 2 ]

1 голос
/ 10 января 2010

Это естественно. Вы анализируете оператор print, поэтому выполняется cout. То, что вы хотите сделать, это создать какой-то байт-код, где вы храните инструкцию печати. Эту инструкцию печати можно затем объединить с выражением if для формирования инструкции if-Statement.

Вдобавок ко всему, я не могу вспомнить точные утверждения в Bison (вам нужно заглянуть в документацию), но вы, как правило, хотите определить тип для вашего стека значений, обычно объединение структур что-то вроде этого (хотя для него есть команда bison, которая сделает его типом для стека значений).

union {
 int type;
 struct {
  int type; // must always be first, this is a union
  union stmt *stmt; // conditional stmt to execute
  union expr *expr; // expression to evaluate
 } if_stmt;
 struct {
  int type;
 } print_stmt;
} stmt;

Что позволит вам вставить правило в вашу грамматику, что-то вроде этого

stmt: IF '(' expr ')' stmt   { $$.type = IF_STMT; $$.if_stmt.expr = copy ($3); $$.if_stmt.stmt = copy ($5); }

и т. Д. (Там может быть ошибка «один за другим», не помню, если $ s начинается с 0 или 1). Вам нужно будет самостоятельно реализовать функцию copy для управления распределением памяти, bison предоставит только стек для значений. В конце концов у вас будет дерево (которое, как я считаю, обычно называется синтаксическим деревом), которое вы можете запустить для узлов с типом IF_STMT, evalute if_stmt.expr и, если оно вернет true, вычислить if_stmt.stmt, и т. д.

Затем, когда вы закончите синтаксический анализ вашего языка, вы можете «выполнить» свой байт-код, когда вы нажмете оператор if, оцените выражение, если оно истинно (а это не так), выполните оператор, как описано выше и затем, когда вы нажимаете на инструкцию print (что не произойдет, поскольку выражение ложно), вы печатаете свое «привет», и у вас есть результат, который вы ищете (то есть, ничего не печатается).

Это в значительной степени то, как вам нужно это делать. Вы не можете (легко) выполнить условное выполнение при разборе с бизоном.

0 голосов
/ 04 декабря 2013

Если вы хотите, чтобы это работало только для if, вы можете сделать стек, на EXP eval, вставить в стек значение истинности EXP, в блоках IF просто проверить, можете ли вы выполнить вызов функции, назначить и т. Д., На в противном случае отмените последнее значение в стеке и при уменьшении ПЧ, просто извлеките значение из стека.

Ex:

    Stack (bool) x;


    CanExec()
    {
        if x== NULL //no IF yet
            return true;
        for i in x
            if i == false
                return false; //if we have at last 1 if banch with FALSE, we cannot execute it
        return true;
    }

    if_stmt:    IF if_exp block                {Pop()}
                |IF if_exp block else block    {Pop()}

    if_exp:     '(' exp ')'                    {Push((bool)exp)}

    else:       ELSE                           {tmp = Pop(); Push(!tmp)}

Теперь все, что вам нужно сделать, это проверить на всех побочных эффектах, если CanExec ()

...