Как написать разбор файлов flex и bison на этом языке? - PullRequest
0 голосов
/ 10 октября 2011

Давайте определим язык:

VAR := [0-9A-Za-z_]+
Exp := VAR 
   | VAR,'=',VAR 
   | '(', Exp, ')'
   | Exp, '&', Exp 
   | Exp ,'|', Exp       

Например: "(a = b) & (c | (d = e))" допустимо

Я прочитал YASSРуководство & Lex, но я в замешательстве, я просто хочу компилятор, который может анализировать этот языкМожете ли вы сказать мне, как написать файл конфигурации flex & bison для этого языка?

Я уже сделал:

файл al:

%{

#include <string.h>
#include "stdlib.h"
#include "stdio.h"
#include "y.tab.h"

%}

%%

("&"|"and"|"AND")   { return AND; }
("|"|"or"|"OR")   { return OR; }
("="|"eq"|"EQ")   { return EQ; }
([A-Za-z0-9_]+)   { return VAR;}
("(")   { return LB ;}
(")")   { return RB ;}
("\n")   { return LN ;}



%%

int main(void)
{
 yyparse();
 return 0;
}

int yywrap(void)
{
 return 0;
}

int yyerror(void)
{
  printf("Error\n");
  exit(1);
}

файл ay

%{
#include <stdio.h>
%}

%token AND OR EQ VAR LB RB LN

%left AND OR
%left EQ

%%

line : 
       | exp LN{ printf("LN: %s",$1);}
;

exp:    VAR             { printf("var:%s",$1);}
    |  VAR EQ VAR      { printf("var=:%s %s %s",$1,$2,$3);}
    |  exp AND exp      { printf("and :%s %s %s",$1,$2,$3);}
    |  exp OR exp      { printf("or :%s %s %s",$1,$2,$3);}
    |  LB exp RB      { printf("abstract :%s %s %s",$1,$2,$3);}    

    ;

Теперь я отредактировал файл под руководством Криса Додда, он выглядит намного лучше (по крайней мере, lex работал нормально), но я получаю вывод, подобный этому:

disk_path>myprogram
a=b
var=:(null) (null) (null)LN: (null)ab=b
Error

Итак, почему функциявывод printf нуль?а после ввода второго выдается ошибка и выход из программы?

Ответы [ 2 ]

1 голос
/ 10 октября 2011

Ваше правило lex ("[0-9A-Za-z_]+") будет соответствовать (только) буквальной строке [0-9A-Za-z_]+ - избавьтесь от символов ", чтобы иметь шаблон, соответствующий любому идентификатору или номеру.

ВашКод yacc не соответствует вашему лекс-коду для пунктуации - код lex возвращает AND для &, в то время как код yacc ожидает & - поэтому либо измените код lex, чтобы он возвратил '&', либо измените yaccкод для использования токена AND и аналогично для |, ( и ).Вы также можете игнорировать пробелы в лекс-коде (вместо того, чтобы рассматривать их как ошибки).У вас также нет правила lex для сопоставления и возврата '\n', даже если вы используете это в своей грамматике yacc.

В противном случае ваш код yacc корректен, но неоднозначен, что дает вам сдвиг / уменьшение конфликтов.Это потому, что ваша грамматика неоднозначна - входные данные типа a&b|c могут быть проанализированы как (a&b)|c или a&(b|c).Вам нужно решить, как устранить эту неоднозначность, и отразить это в вашей грамматике - либо с помощью большего количества нетерминалов, либо с помощью встроенной поддержки приоритетов yacc для устранения такого рода двусмысленности.Если вы вставите объявления:

%left '|'
%left '&'

в верхней части вашего файла yacc, это разрешит неоднозначность, сделав & и | левоассоциативными, а & более высоким приоритетом, чем |, что было бы нормальной интерпретацией.

Редактировать

Теперь у вас проблема в том, что вы никогда не определяете YYSTYPE (ни напрямую, ни с помощью% union) в вашем.y файл, и вы никогда не устанавливаете yylval в вашем .l файле.Первая проблема означает, что $1 и т. Д. Являются просто int с, а не указателями (поэтому нет смысла пытаться печатать их с помощью %s - вы должны получить предупреждение от вашего компилятора C по этому поводу).Вторая проблема означает, что они никогда не имеют значения в любом случае, так что это просто всегда значение по умолчанию 0 неинициализированной глобальной переменной

Самое простое решение - добавить

%union {
    const char *name;
}
%token <name> VAR LB RB LN
%left <name> AND OR
%left <name> EQ
%type <name> expr

в началофайла YACC.Затем измените все правила lex на что-то вроде

([A-Za-z0-9_]+)   { yylval.name = strdup(yytext); return VAR;}

Наконец, вам также нужно изменить действия зубров для expr, чтобы установить $$, например:

|  LB exp RB      { asprintf(&$$, "%s %s %s",$1,$2,$3);  printf("abstract: %s\n", $$); }

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

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

line: /* empty */
    | line exp LN { printf....
1 голос
/ 10 октября 2011

Сначала напишите файл lex для токенизации ввода (и распечатайте то, что он видит)

Вы хотите представить терминалы:

  • [0-9A-Za-z_]+ --> VAR
  • ( --> LPAREN и ) --> RPAREN
  • & --> AND
  • | --> OR
  • = --> EQUAL

и просто распечатайте слово длякаждый.Для вашего примера

( a = b ) & ( c | (d=e) ) --> LPAREN VAR EQUAL VAR RPAREN AND LPAREN VAR OR LPAREN VAR EQUAL VAR RPAREN RPAREN

Это выполнимо в чистом лекс.Когда вы сделаете это, обновите свой ответ, и мы сможем поговорить о следующем шаге

...