Грамматика Yacc не уменьшает последний литерал ввода правильно
Это код для генерации кода адреса 3 для арифметического выражения.
Проблема, с которой я сталкиваюсь, заключается в том, что моя грамматика способна правильно читать входные данные до последнего литерала. Но не удалось уменьшить последний литерал непосредственно перед 'n 'Пример: x = 1 + 2 * 3 - 4
1,2,3 читаются правильно. Даже с минусом. Когда он читает 4, он может сократить грамматику на 2 шага. Затем выдает ошибку.
Я поставил печать заявления о помощи. Это весь код. Проблема при чтении последнего литерала:
x -> IDEN | NUM | ....
f -> x
t -> f (This doesn't happen)
Icg.y
%{
#include<stdio.h>
#include<stdlib.h>
int temp_reg_num = 0;
void print_three_address_code(char* text1,char oper,char* text2)
{
printf("t%d = %s %c %sn",temp_reg_num,text1,oper,text2);
}
%}
%token IDEN, NUM, FLOAT
%union {
char* text;
}
%left '+' '-'
%left '*' '/'
%%
s : IDEN '=' e 'n' {
printf("startn");
if($3.text == NULL)
printf("%s = t%dn",$1.text,temp_reg_num-1);
else
printf("%s = %sn",$1.text,$3.text);
return 0;
}
;
e : e '+' t {
printf("e -> e + tn");
char temp[5];
print_three_address_code($1.text,'+',$3.text);
sprintf(temp,"t%d",temp_reg_num++);
$$.text = copy_string(temp);
}
| e '-' t {
printf("e -> e - tn");
char temp[5];
print_three_address_code($1.text,'-',$3.text);
sprintf(temp,"t%d",temp_reg_num++);
$$.text = copy_string(temp);
}
| t {
printf("e -> tn");
$$.text = copy_string($1.text);
}
;
t : t '*' f {
printf("t -> t * fn");
char temp[5];
print_three_address_code($1.text,'*',$3.text);
sprintf(temp,"t%d",temp_reg_num++);
$$.text = copy_string(temp);
}
| t '/' f {
printf("t -> t / fn");
char temp[5];
print_three_address_code($1.text,'/',$3.text);
sprintf(temp,"t%d",temp_reg_num++);
$$.text = copy_string(temp);
}
| f {
printf("t -> fn");
$$.text = copy_string($1.text);
}
;
f : f '^' x {
printf("f -> f ^ xn");
char temp[5];
print_three_address_code($1.text,'^',$3.text);
sprintf(temp,"t%d",temp_reg_num++);
$$.text = copy_string(temp);
}
| x {
printf("f -> xn");
$$.text = copy_string($1.text);
printf("Why syntax error??");
}
;
x : '(' s ')' {}
| NUM {
printf("x -> NUMn");
$$.text = copy_string($1.text);
}
| IDEN {
printf("x -> IDENn");
$$.text = copy_string($$.text,$1.text);
}
| FLOAT {
printf("x -> FLOATn");
$$.text = copy_string($1.text);
}
| '-' x {
printf("x -> - xn");
$$.text = (char*)malloc(sizeof(char)*(strlen($2.text)+2));
$$.text[0] = '-';
strcat($$.text,$2.text);
}
;
%%
main()
{
printf("Enter your expression : ");
yyparse();
}
yyerror()
{
printf("Syntax Errorn");
}
yywrap()
{
return 1;
}
Icg.l
%{
#include<stdio.h>
#include<stdlib.h>
#include"y.tab.h"
char* copy_string(char* fromstring)
{
char* tostring = (char*)malloc(sizeof(char)*(strlen(fromstring)+1));
strcpy(tostring,fromstring);
return tostring;
}
%}
iden [a-zA-Z_][a-zA-Z_]*
%%
[0-9]+ {
yylval.text = copy_string(yytext);
return NUM;
}
[0-9]+[.][0-9]+ {
yylval.text = copy_string(yytext);
return FLOAT;
}
{iden} {
yylval.text = copy_string(yytext);
return IDEN;
}
[+] {
return '+';
}
[-] {
return '-';
}
[*] {
return '*';
}
[/] {
return '/';
}
['^'] {
return '^';
}
[=] {
return '=';
}
'n' {
return 'n';
}
. {}
%%
1 ответ:
Вы получаете синтаксическую ошибку из-за этого правила:
'\n' { return '\n'; }
Flex не считает
'
каким-либо особенным, так что это правило будет (только) соответствовать последовательности из трех символов'войдите'. Это не отображается во входных данных, поэтому правило, которое требует\n
, не будет выполнено, и когда вы предоставляете конец файла (или конец файла, если Вы читаете из файла), то вы получите синтаксическую ошибку.Итак, что вы должны написать is:
\n { return '\n'; }
Также,
['^']
Не только соответствует
^
. Он также соответствует'
, потому что, как указано выше,'
никоим образом не является специальным и поэтому класс символов состоит из трех символов,один из которых повторяется. Чтобы точно соответствовать символу^
, используйте двойные кавычки (которые являются специальными):"^"
(однако,
"\n"
не будет работать, если только то, что вы хотите сопоставить, не является точно\
, за которым следуетn
. Просто\n
это то, что вы хотите.)Ты на самом деле можно упростить все эти правила одного символа в одно правило:
[-+*/^()=\n] { return yytext[0]; }
Я включил
(
и)
, хотя они не были в вашей icg.L файл, потому что вы используете их в своей грамматике.Однако правило, в котором вы их используете, неверно:
x : '(' s ')' {}
Поскольку
s
- это назначение. Это позволило быx = 1 + (y = 2 * 3) - 4
Но не допускайте более нормального
x = 1 + (2 * 3) - 4
Чего вы хотите, так это:
x : '(' e ')' { $$ = $2; }
В вашей грамматике есть и другие ошибки., в том числе:
- отсутствует заголовок
#include <string.h>
(дляstrlen
)- пропущенные объявления для
yylex
,yyerror
иcopy_string
- неправильные (или, точнее, устаревшие) прототипы для
main
иyyerror
. "Современный C" - как и в последние 30 лет или около того-предпочитает явные типы возврата, а начиная с C99 (1999) требует их.- экземпляр
copy_string
с двумя аргументами вместо одного, что является неопределенным поведением. (Это было бы помечено как ошибка, если бы вы предоставили декларация дляcopy_string
.)Ваш icg.файл y был обработан без жалоб Berkeley yacc (
byacc
), ноbison
не будет обрабатывать его даже в режиме совместимости с yacc. Проблема заключается в том, что вы используете$1.text
и не можете объявить теги ваших терминалов и нетерминалов. Если вы объявляете типunion
, вы должны предоставить объявления тегов как для ваших терминалов:%token <text> NUM IDEN FLOAT
И ваши не-терминалы (или, по крайней мере, те, которые имеют семантические значения):
Тогда вы можете удалить все селекторы%type <text> s e t f x
.text
из ваших действий, так какbison
(и дажеbyacc
) будет знать, какой селектор использовать. Объявление тегов маркеров делает ваш синтаксический анализатор типобезопасным или, по крайней мере, менее типобезопасным, а также обычно более читаемым. Наконец, вам нужно поработать над управлением памятью. Вы никогда неfree()
ни одной из строк, которые вы выделили вstring_copy
, поэтому вы постепенно создаете довольно много утечек памяти. Кроме того, вы копируете во многих случаи, когда это не требуется; например, в единичном правиле:x : NUM { $$.text = copy_string($1.text); }
Копия совершенно не нужна, так как
$1
вот-вот выскочит из стека синтаксического анализатора, и строка, на которую он ссылается, больше никогда не будет использоваться. Так что вы просто напрасно сливаете копию. Гораздо чище было быНо это на самом деле не нужно, потому что это действие по умолчанию.x : NUM { $$ = $1; }
Изменение единичных производств не остановит другие утечки памяти; в производствах, которые на самом деле сделайте что-нибудь с семантическими значениями, кроме их передачи, вам все равно придется вручную
free
копировать строки, если они вам больше не нужны (что, похоже, имеет место во всех ваших семантических действиях). Или вам нужно будет выяснить, как освободить их позже, если они вам понадобятся позже.Вы можете использовать встроенную функцию трассировки yacc или bison, а не набивать свою программу
printf
s, которые вам позже придется удалить. Просто предоставьте опцию-t
, Когда вы выполнитьyacc
или добавить#define YYDEBUG 1
К' %{...% } 'блок в вашей icg.y файл. Затем измените свою основную функцию:
Это позволит вам точно знать, что происходит во время анализа входных данных, включая информацию о том, какие терминалы принимаются от сканера.int main() { yydebug = 1; return yyparse(); }
Вот полный пример, показывающий один из подходов к управлению памятью. Он основан на вашем коде, но с определенным количеством рефакторинга. Обратите внимание, что синтаксический анализатор никогда не копирует строку, ноemit_tac
выделяет новую строку, а также освобождает строки, которые передаются в качестве аргументов. (Некоторые наблюдатели могут счесть это грубостью.) Вызовyylex_destroy()
вmain
требуется для современныхflex
-генерируемых сканеров, чтобы освободить ресурсы, выделенные лексером. Bison-specific%destructor
предотвращает утечку памяти в случае синтаксической ошибки: если вы не используете bison, вам придется найти другое решение этой проблемы.Icg.y
%{ #include <stdio.h> #include <stdlib.h> #include <string.h> int yylex(); int yylex_destroy(); void yyerror(const char* msg); int temp_reg_num = 0; char* emit_tac(char* text1, char oper, char* text2) { char* rv = malloc(8); snprintf(rv, 7, "t%d", temp_reg_num++); printf("%s = %s %c %s\n", rv, text1 ? text1 : "", oper, text2); free(text1); free(text2); return rv; } %} %union { char* text; } %token <text> IDEN NUMBER %type <text> e t f x %destructor { free($$); } <text> %% s : IDEN '=' e '\n' { printf("%s = %s\n", $1, $3); free($1); free($3); } e : e '+' t { $$ = emit_tac($1,'+',$3); } | e '-' t { $$ = emit_tac($1,'-',$3); } | t t : t '*' f { $$ = emit_tac($1,'*',$3); } | t '/' f { $$ = emit_tac($1,'/',$3); } | f f : f '^' x { $$ = emit_tac($1,'^',$3); } | x x : IDEN | NUMBER | '-' x { $$ = emit_tac(NULL, '-', $2); } | '(' e ')' { $$ = $2; } %% int main() { int rc = yyparse(); yylex_destroy(); return rc; } void yyerror(const char* msg) { printf("%s\n", msg); }
Icg.l (требуется flex)
%{ /* Change to y.tab.h if you use yacc */ #include "icg.tab.h" char* copy_yytext() { char* tostring = malloc(yyleng + 1); memcpy(tostring, yytext, yyleng); tostring[yyleng] = 0; return tostring; } %} %option noinput nounput noyywrap nodefault %% \n { return '\n'; } [[:space:]] /* Ignore */ [[:digit:]]+(\.[[:digit]]*)? { yylval.text = copy_yytext(); return NUMBER; } [[:alpha:]_][[:alnum:]_]* { yylval.text = copy_yytext(); return IDEN; } . { return yytext[0]; }
Компиляция
$ bison -d icg.y $ flex icg.l $ gcc -Wall -o icg lex.yy.c icg.tab.c
Тест с valgrind для демонстрации отсутствия утечек памяти
$ valgrind --leak-check=full ./icg <<< ' x = 1 + 2 - 3 / 4 ^ 5 * ( 6 - 9 )' ==26225== Memcheck, a memory error detector ==26225== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==26225== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==26225== Command: ./icg ==26225== t0 = 1 + 2 t1 = 4 ^ 5 t2 = 3 / t1 t3 = 6 - 9 t4 = t2 * t3 t5 = t0 - t4 x = t5 ==26225== ==26225== HEAP SUMMARY: ==26225== in use at exit: 0 bytes in 0 blocks ==26225== total heap usage: 17 allocs, 17 frees, 16,530 bytes allocated ==26225== ==26225== All heap blocks were freed -- no leaks are possible ==26225== ==26225== For counts of detected and suppressed errors, rerun with: -v ==26225== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)