FLEX/YACC 在操作之间携带变量

FLEX/YACC carry variables between operations

我正在使用 YACC/FLEX 构建一个计算器作为作业的一部分,但有点卡住了。我想知道如何将先前求解的变量从一个方程式转移到下一个方程式。

即,输入将总结如下:

11 x = 44 ;
y = (2 + 4*x);
y abc = 18;
4 z123 = (9*x -4*abc);
x _Inconnue = 12;

其中左侧是已解决的项(无,或数字,或先前已解决的变量)后跟未解决的变量等于格式正确的算术表达式,只能包含已解决的项和运算符。

结果应该是

4
18
1
8
3

到目前为止,我的 lex 文件如下所示:

%{
#include <iostream>
#include "y.tab.h"
extern "C" int yylex();
%}

%option yylineno

%%


[1-9]+                                                  { return NOMBRE;   }
[a-zA-Z_]([a-zA-Z0-9])*                                 { return INCONNUE; }
";"                                                     { return ';'; }
"="                                                     { return '='; }
"+"                                                     { return '+'; }
"*"                                                     { return '*'; }
"/"                                                     { return '/'; }
"-"                                                     { return '-'; }
"("                                                     { return '('; }
")"                                                     { return ')'; }
\n                                                      { ; }
[\t ]+                                                  { ; }
.                                                       { std::cout << "Caractere inconnu !" << std::endl;      }

%%

到目前为止,我的 yacc 文件如下所示:

%token NOMBRE INCONNUE ';' '=' '+' '*' '/' '-' '(' ')'

%left '+' '-'
%left '*' '/' '%'
%left '(' ')'

%start sys_eq

%{
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
using namespace std;

/* Type des $$, ,  etc. : */
#define YYSTYPE uint64_t

extern FILE *yyin;
extern char yytext[];
extern "C" int yylex();
int yyparse();
extern int yyerror(string);
extern int yylineno;

%}

%%

        sys_eq          : terme rest sys_eq    //a system of equations is a resolved term followed by the rest of the equations; there can be many systems within one system/file. 
                        |
                        ;

        rest            : eqn nl rest       //rest of the equations is a list of equations or nothing
                        |
                        ;

        eqn             : terme INCONNUE '=' expr_paren {if(==0) {cout << atoi(yytext);} else cout << atoi(yytext)/;}
                        |
                        ;       //an equation is a resolved term followed by a variable, followed by '=' and an arithmetic expression

        expr_paren      : terme                         { $$ = ; }   //standard def for expr arith
                        | expr_paren '+' expr_paren     { $$ =  + ; }
                        | expr_paren '-' expr_paren     { $$ =  - ; }
                        | expr_paren '*' expr_paren     { $$ =  * ; }
                        | expr_paren '/' expr_paren     { $$ =  / ; }
                        | '(' expr_paren ')'            { $$ = ; }
                        ;

        nl              : ';'                           {cout << endl;}
                        ;

        terme           : NOMBRE INCONNUE '=' NOMBRE {if( == 0){$$ = atoi(yytext);} else $$ = atoi(yytext)/;} nl
                        | NOMBRE {$$ = atoi(yytext);} nl     //a resolved term is a number or a number divided by another number (to solve the axiome equation that looks like 2 x = 4;)
                        |
                        ;


%%

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

        yyparse(); // Analyse syntaxique

        return 0;
} // main()

int yyerror(string msg)
{
        cout << msg << " sur la ligne #"  << yylineno << endl; return 0;
} // yyerror()

备注:

  1. 我知道我还没有添加任何东西来输出结果,但只要我能正确计算它们,我就可以稍后将它们打印到屏幕上。那个不需要任何帮助。
  2. 我知道在我的 yacc 操作中使用 yytext 是不被接受的。请忽略它,因为此问题需要在 yacc 操作中使用 yytext。我知道这很糟糕。
  3. 是的,我是 YACC/FLEX 的新手。我已经阅读了很多关于 YACC 的文章以获取我在这里的代码,但我正在努力。经过数小时无知的反复试验后,很难解释我的尝试。这就是为什么我试图评论每条指令应该做什么,希望它能让我走的路更清楚一些。

问题: 目前代码显然是垃圾,但我看不到问题所在。

  1. 我遇到了 2 个 shift/reduce 冲突,我不太明白为什么。我知道错误的含义,但不明白我为什么有罪。为什么?
  2. 输入文件的第 2 行返回内存溢出错误。我在这里创建了什么样的无限循环?
  3. 我正在努力解决的真正问题是如何将答案从一行传递到下一行的表达式中。如果我能理解这一点,我想我就能解决整个问题。

如果您需要保留带有名称的值,您基本上需要一个将名称与值相关联的映射。 bison 和 flex 都不会帮助你,因为它与解析无关。但是 C++ 有很大帮助,因为它的标准库包括有序和无序映射实现。 (我会使用 unordered_map,因为在您的问题描述中不需要按字母顺序保留变量名称。但两者都可以正常工作。)

当然,您必须将变量名从词法分析器传递到解析器,并且您将无法使用当前规则执行此操作,除非您接受您确实不能使用 yytext 在解析器操作中。 (INCONNUE 不是这两个规则中读取的最后一个标记。)

这与您在 terme 的第一个产品的操作中遇到的问题相同,它试图将第一个 NOMBRE 的语义值用作 </code>。该尝试将失败,因为您的扫描仪在返回 <code>NOMBRE 之前未填写 yylval。而与第一个 NOMBRE 相对应的 yytext 在动作执行时是古老的历史。 (使用 yytext 是否适用于第二个 NOMBRE 取决于语法的其余部分。)

有一种方法可以解决这个问题,如果你的教授坚持要你使用它,他们真的应该已经解释清楚了。 (“单位生产”是我们用英语说的。)但坦率地说,我认为你应该用正常的方式,在你的扫描仪上做,并向你的教授解释它是如何完成的。由你决定。


你遇到冲突是因为你有

eqn     : terme INCONNUE '=' expr_paren
        |

terme   : NOMBRE INCONNUE '=' NOMBRE { action; } nl
        | NOMBRE {$$ = atoi(yytext);} nl
        |

输入从 NOMBRE INCONNUE 开始,无法知道 terme 的第一个产生式是否适用,在这种情况下 INCONNUEterme 的一部分,必须移动,或者第二个产生式,在这种情况下 INCONNUEeqn 的一部分,现在需要减少 terme

在输入的开头会产生另一个冲突,因为 terme 可以为空。