Использование бизона для разбора списка элементов - PullRequest
0 голосов
/ 31 октября 2009

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

Я использовал абстрактное синтаксическое дерево , определенное с классами, для выполнения всей работы (для упрощения проверки типов и генерации промежуточного кода) .. поэтому у меня есть класс-предок ASTNode и все нисходящие классы, такие как ASTFloat, ASTExpression, ASTIdentifier и т. Д.

В .y файле я могу создать AST обычным способом:

nexp:
T_LPAR nexp T_RPAR { $$ = $2; }
| nexp OP_PLUS nexp { $$ = new ASTBExpression('+', (ASTExpression*)$1, (ASTExpression*)$3); }
| nexp OP_MINUS nexp { $$ = new ASTBExpression('-', (ASTExpression*)$1, (ASTExpression*)$3); }
| nexp OP_TIMES nexp { $$ = new ASTBExpression('*', (ASTExpression*)$1, (ASTExpression*)$3); }

и он работает довольно хорошо, но затем я попытался сгенерировать операторы области видимости (например, тело оператора if ) следующим образом: я использовал класс ASTStatements, у которого есть список из ASTNode*, который должен заполняться парсером при каждом обнаружении оператора.

Таким образом, подход будет примерно таким:

statements:
statement { if ($$ == null) $$ = new ASTStatements(); ((ASTStatements*)$$)->addStatement($1); } statements { $$->generateASM(); }
;

Проблема в том, что элемент должен быть инициализирован только один раз для каждого блока операторов, но я не знаю, как это сделать. Использование if ($$ == null) - это хак, который я пробовал, но он не работает, потому что yylval может содержать все, что до этого момента.

Какой нормальный / лучший способ справиться с подобными ситуациями с помощью Bison?

Ответы [ 3 ]

1 голос
/ 05 ноября 2009

Существуют различные причины, чтобы предпочесть леворекурсивные правила для yacc, во-первых, вы можете уменьшить их как можно раньше при вводе.

В любом случае, когда вы это сделаете, вы можете использовать такой шаблон:

statements:                { $$ = new ... }
    | statements statement { /* now $1 and $2 do just what you want */ }
    ;
1 голос
/ 06 ноября 2009

Я решил эту проблему, создав не список операторов, а вырожденное дерево. Таким образом, задействованный объект класса:

ASTStatements
{
    ASTStatements *m_next;
    ASTStatement *m_statement;

    ....

    public:
        ASTStatements(ASTStatement *statement) // used for last one
        ASTStatements(ASTStatement *stat, ASTStatements *next) // used with a next one
}

используя правила в .y следующим образом:

statements: /* empty */ { $$ = null; }
| statements statement { if ($1 == null) $$ = new ASTStatements($2); else $$ = new ASTStatements($2, (ASTStatements*)$1); }

И действительно, это леворекурсивный способ, позволяющий сокращать операторы как можно быстрее, не загромождая стек. Я придерживался того же подхода для любого другого вида «списка символов», связанных с моим языком.

1 голос
/ 01 ноября 2009

Попробуйте расширенную грамматику, например:

statements: statement { $$ = new ASTStatements();
                       ((ASTStatements*)$$)->addStatement($1); }      
 | statements statement { ((ASTStatements*)$$)->addStatement($2); }

Не уверен, поможет ли это.

...