Как распечатать дерево разбора с помощью Bison? - PullRequest
1 голос
/ 03 апреля 2012

Я реализовал парсер, но он ничего не печатает. Если данный ввод синтаксически неверен, он не печатает «Ошибка», хотя я включил его в подпрограмму yyerror(). Также, если ввод правильный, он не печатает дерево разбора. Какова может быть возможная причина этого? Я поместил свой main() в .lex файл вместо .y. Это возможная причина для этого? Это основной метод:

int main( argc, argv )
int argc;
char **argv;
{
    ++argv, --argc; 
    if ( argc > 0 )
    yyin = fopen( argv[0], "r" );
    else
    yyin = stdin;     

    yyparse();
}

Файл грамматики:

%{
#include "parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
%}

%union {
  char* a_variable;
  tree* a_tree;
}

%start file
%token <a_variable> TOKID TOKSEMICOLON TOLCOLON TOKCOMMA TOKUNRECOG TOKDOT TOKMINUS TOKCOLON
%type <a_tree> field file obj ID
%right TOKMINUS

%%

file      :     /*empty*/ { return NULL; }
      |     field file { printtree($1, 1);  }
  ;

field     : ID TOKCOLON field {$$ = make_op($1, ':', $3); }
  | ID TOKCOMMA field {$$ = make_op($1, ',', $3); }
  | obj { $$ = $1; }
  ;

obj       :     ID TOKSEMICOLON { $$ = make_op($1, ';', NULL); }
      ;

ID        :     TOKID { $$ = $1; }

%%

#include <stdio.h>
yyerror(char *str)
{
  fprintf(stderr,"error FAIL: %s\n",str);
}

int yywrap()
{
  return 1;
}

Вот так выглядит мой .lex файл:

%{
/* need this for the call to atof() below */
#include <math.h>
#include "parser.h"
#include "idf.tab.h"
%}

DIGIT    [0-9]
ID       [a-zA-Z]*
%option noyywrap

%% 


{ID} |  
-?{DIGIT}+"."{DIGIT}* |    
-?{DIGIT}+      { yylval.a_variable = findname(yytext); return TOKID; }

";"           return TOKSEMICOLON;
":"           return TOKCOLON;
","           return TOKCOMMA;
"."           return TOKDOT;
"-"           return TOKMINUS; 
.           return TOKUNRECOG;
%%

int main( int argc, char** argv )
{
++argv, --argc; 
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;

yyparse();
}

Ответы [ 2 ]

1 голос
/ 04 апреля 2012

Я не уверен, что вы можете объединить несколько правил в одно, как это:

{ID} |  -?{DIGIT}+"."{DIGIT}* |    -?{DIGIT}+      return TOKID;

lex чувствителен к пробелам; Я думаю, что это должно быть так:

{ID} |
-?{DIGIT}+"."{DIGIT}* |
-?{DIGIT}+      return TOKID;

Символ | интерпретируется как особый вид действия, который означает «то же действие, что и действие следующей строки». Внутри шаблона | обозначает ветвь регулярного выражения. Но у вас есть все эти места там.

Ваш комментарий совпадает с подделкой:

"{"[\^{}}\n]*"}"     /* eat up one-line comments */   

Я думаю, что вы хотели использовать здесь отрицательный класс символов, но вы добавили экранирование для символа ^, что просто приводит к включению ^ в класс символов.

Какой смысл этого:

"!"+"-"[\n] return TOKCOMMENT;

Последовательность !, за которой следует - и символ новой строки - это какой-то комментарий, который вы не игнорируете, а возвращаете как маркер, а?

Это правило разбора не может работать должным образом из-за некорректного поведения в вашем лексере:

ID        :     TOKID { $$ = $1; }

Выражение $1 хочет получить доступ к yystack[<whatever>].a_variable, потому что вы определили TOKID как семантический тип a_variable. Но ваше правило lex, которое выдает TOKID, ничего не помещает в a_variable. Он просто делает return TOKID;, оставляя этот указатель для мусора. Ваше правило лексера должно быть присвоено yylval.a_variable.

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

1 голос
/ 03 апреля 2012

Функция yylex() является лексическим сканером, а не грамматическим парсером;парсер yyparse().Поэтому обновите вашу программу так, чтобы она вызывала yyparse() вместо yylex(), оставляя yyparse() для вызова yylex(), когда ей нужен новый токен:

while (yyparse() != 0)
    ;

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

Что касается сопутствующей заметки, я не могу придумать вескую причину для использования объявления K & Rmain().Всегда используйте int main(int argc, char **argv).И если вы используете нотацию K & R, то вы должны вернуть значение из main(), обычно ноль в случае успеха и ненулевое значение в случае сбоя.Хотя C99 позволяет вам опустить окончательный возврат с main() (что эквивалентно return 0; в единственном исключительном случае main()), я рекомендую включить его.


Более поздние примечания

Хорошая идея - сделать так, чтобы людям было легко проверить, с чем вы обращаетесь за помощью.Предоставьте достаточно источника, чтобы сделать его компилируемым.Удалите достаточно источника, чтобы минимизировать усилия по компиляции.

Было не очень сложно нейтрализовать различные функции действия в грамматике.Файл parser.h должен содержать что-то вроде typedef struct tree tree;.И грамматика должна была быть idf.y, чтобы bison -d idf.y генерировал idf.tab.h и idf.tab.c.

Одна из первых вещей, которые я делаю с лексическим анализатором, - убедитесь, что он печатает то, что делает,Итак, я изменил правила, чтобы сделать что-то вроде:

{ID} |  
-?{DIGIT}+"."{DIGIT}* |    
-?{DIGIT}+      { printf("ID or number: %s\n", yytext); /*yylval.a_variable = findname(yytext);*/ return TOKID; }

";"           { printf("Semi-colon\n"); return TOKSEMICOLON;}
":"           { printf("Colon\n"); return TOKCOLON;}

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

[ \t]         { printf("White space\n"); }

Это должно появиться перед правилом «сожирающая точка», конечно.

Имея это в виду, я смог запустить программу и получить лексический вывод:

$ ./idf
abc ;
ID or number: abc
White space
Semi-colon

$

Я набрал abc ;, и он идентифицировал все в порядке.Поскольку в действиях грамматики не осталось кода, не было выходных данных самой грамматики.Вероятно, стоило бы скомпилировать с -DYYDEBUG и установить yydebug = 1; в функции main() - вам может понадобиться добавить extern int yydebug; в исходный файл лексического анализатора, так как main() есть.

$ flex scanner.l
$ bison -d idf.y
$ gcc -DYYDEBUG -o idf idf.tab.c lex.yy.c
$ ./idf
Starting parse
Entering state 0
Reading a token: abc ;
ID or number: abc
Next token is token TOKID ()
Shifting token TOKID ()
Entering state 1
Reducing stack by rule 7 (line 32):
   $1 = token TOKID ()
-> $$ = nterm ID ()
Stack now 0
Entering state 5
Reading a token: White space
Semi-colon
Next token is token TOKSEMICOLON ()
Shifting token TOKSEMICOLON ()
Entering state 8
Reducing stack by rule 6 (line 29):
   $1 = nterm ID ()
   $2 = token TOKSEMICOLON ()
-> $$ = nterm obj ()
Stack now 0
Entering state 4
Reducing stack by rule 5 (line 26):
   $1 = nterm obj ()
-> $$ = nterm field ()
Stack now 0
Entering state 3
Reading a token: 
Now at end of input.
Reducing stack by rule 1 (line 20):
$

Теперь ваши проблемы в функциях, которые вы не показывали.Это твое решение.

...