Ошибка компиляции Lex & YACC: ожидается ")" перед "." знак - PullRequest
0 голосов
/ 12 января 2019

Я должен написать парсер для мини-языка, и у меня есть некоторые проблемы. Вот файл YACC:

%{
#include <stdio.h>
int yylex();
void yyerror(char *s);

%}

%union {int num; char id; double d; char *s;}
%start program
%token <num> DIGIT
%token <s> IDENTIFIER
%token <num> NO
%type <num> term condition
%type <s> expression assignstmt stmt
%%
program : "##LAZY###" "vars" decllist cmpdstmt   {;}
        ;
decllist : declaration                           {;}
        | declaration decllist                  {;}
        ;
declaration : "in" IDENTIFIER                    {int $2;}
            | "in" '[' NO ']' IDENTIFIER         {int $5[$3];}
            ;                   
cmpdstmt : "exec" stmtlist "stop"                {;}
stmtlist : stmt                                  {;}
        | stmt stmtlist                         {;}
        ;
stmt : assignstmt                                {;}
    | ifstmt                                    {;}
    | whilestmt                                 {;} 
    ;
assignstmt : IDENTIFIER '=' expression           {$1 = $3;}
        ;
expression : expression '+' term                 {$$ = $1 + $3;}
        | term '+' term                       {$$ = $1 + $3;}
        ;
term : DIGIT                                     {$$ = $1;}
    | IDENTIFIER                                {$$ = $1;}
    ;
ifstmt : "if" '(' condition ')' '{' stmt '}'     {if($3){$6;}}
    ;
whilestmt : "wh" '(' condition ')' '{' stmt '}'  {while($3){$6;}}
        ;
condition : expression "<" expression            {$$ = ($1 < $3);}
        | expression "<=" expression           {$$ = ($1 <= $3);}
        | expression "==" expression           {$$ = ($1 == $3);}
        | expression "!=" expression           {$$ = ($1 != $3);}
        | expression ">=" expression           {$$ = ($1 >= $3);}
        | expression ">" expression            {$$ = ($1 > $3);}
        ;
%%

int main() {
    printf("WORKING\n");
    return yyparse();
}

void yyerror(char*s) { printf("%s\n", s); }

Но когда я пытаюсь скомпилировать его с помощью: cc lex.yy.c y.tab.c, я получаю следующие ошибки и не знаю, как их исправить или почему я их получаю:

lazy.y: In function ‘yyparse’:
lazy.y:21:19: error: expected ‘)’ before ‘.’ token
declaration : "in" IDENTIFIER                    {int $2;}
                ^
lazy.y:22:19: error: expected ‘)’ before ‘.’ token
            | "in" '[' NO ']' IDENTIFIER         {int $5[$3];}

Я также опубликую файл Lex, если это необходимо.

Ответы [ 2 ]

0 голосов
/ 12 января 2019

Вы уже знаете, что семейство генераторов синтаксического анализатора YACC работает, генерируя код C, который вы затем компилируете. Что может быть неясно, так это то, что когда речь идет о семантических действиях, они в основном служат шаблонизаторами. Они совершенно готовы создать мусорный код, если это соответствует шаблону действия, который вы представляете. Скорее всего, вы не узнаете, что они сделали это, пока не попробуете скомпилировать полученный код.

Кроме того, ваш компилятор и генератор синтаксического анализатора сотрудничают, чтобы показать вам строки кода YACC, ответственные за предельные синтаксические ошибки C, которые приводят к вашему случаю. Это очень полезно для определения , где вам нужно применить исправление, но оно не очень хорошо объясняет природу проблемы. Однако это лучшее из того, что он может сделать, потому что компилятор знает только, почему код C неправильный, а не почему код YACC, из которого он был получен, неправильный.

Так почему же код YACC неверен? Несколько причин, но в первую очередь потому, что семантическое действие, предназначенное для установки семантической ценности продукции, должно быть сделано путем присвоения специального символа $$. Оператор C, который начинается с имени типа, например, созданного вашими конкретными действиями, является декларацией. Даже если он окажется действительным (что определенно не будет иметь место), он не установит семантическое значение. Вместо этого вы хотите что-то более похожее на

{ $$ = $2; }

и

{ $$ = $5[$3]; }

НО у вас проблема с типами данных. С $2 в первом действии и $5 во втором действии, соответствующем токенам одного типа, невозможно, чтобы оба вышеуказанных действия были совместимы с (необъявленным) типом вашей продукции declaration. Как дикое предположение, возможно, вы пытались очистить это, приведя один или оба к типу int, ala $$ = (int) $2;. Хотя это может исправить ваши ошибки компиляции, это оставляет вас с результатом, который вы не можете использовать, потому что вам нужно знать исходный тип, а также потому, что преобразование из указателя на int может быть по своей сути потерянным.

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

Обновление

Мне приходит в голову, что, возможно, вы вообще не пытались установить семантическое значение, а создали парсер, который генерирует C-код. Если это так, то вы допустили ошибку кадра. Семантические действия вносят вклад в код самого сгенерированного синтаксического анализатора, то есть код, используемый при синтаксическом анализе языка. Если вы намереваетесь перевести пользовательский язык в эквивалентный код C, то переведенный код должен быть вывод синтаксическим анализатором, а не part синтаксического анализатора. Например, вы можете добиться этого, печатая требуемые операторы в файл, но более распространенным подходом является создание синтаксического анализатора абстрактного синтаксического дерева, которое вы обрабатываете после завершения синтаксического анализа.

0 голосов
/ 12 января 2019

от

declaration : "in" IDENTIFIER                    {int $2;}
       | "in" '[' NO ']' IDENTIFIER         {int $5[$3];}
       ;                   

ошибка происходит от {int $2;} и {int $5[$3];}

что вы ожидали от них?

Это законно:

declaration : "in" IDENTIFIER                    {char * s = $2;}
            | "in" '[' NO ']' IDENTIFIER         {int i =  $5[$3];}
            ;

за исключением, конечно, что эти переменные являются локальными, поэтому просто не имеют реального интереса

...