C++ 使用yacc和readline解析行

C++ 使用yacc和readline解析行,c++,parsing,bison,readline,yacc,C++,Parsing,Bison,Readline,Yacc,我开始为管理图形的单一语言编写一个light解释器。我正在使用flex和bison,在定义语法时遇到了一些问题 现在,我只想分析三个单独的命令: 加载“文件名” 保存“文件名” 退出 这是yacc中的语法: %{ # include <iostream> using namespace std; int yylex(void); void yyerror(char const *); %} %token LOAD SAVE RIF COD EXIT STRCON

我开始为管理图形的单一语言编写一个light解释器。我正在使用flex和bison,在定义语法时遇到了一些问题

现在,我只想分析三个单独的命令:

  • 加载“文件名”
  • 保存“文件名”
  • 退出
  • 这是yacc中的语法:

    %{
    
    # include <iostream>  
    
      using namespace std;
    
     int yylex(void);
     void yyerror(char const *);
    
    %}
    
    %token LOAD SAVE RIF COD EXIT STRCONST VARNAME 
    
    %%
    
    input: line
    ;
    
    line: cmd_unit '\n'
    {
      cout << "PARSED LINE with EOL" << endl; 
    }
    | cmd_unit
    {
      cout << "PARSED LINE without EOL" << endl; 
    }
    ;  
    
    cmd_unit: LOAD STRCONST
    {
      cout << "PARSED LOAD" << endl;
    }
    | SAVE STRCONST 
    {
      cout << "PARSED SAVE" << endl;
    }
    | EXIT { }
    ;
    
    %%
    
    也就是说,语法肯定是错误的,它无法识别规则

    cmd_unit: LOAD STRCONST
    
    好吧,虽然我确信我不会主宰语法世界,但我已经花了一些重要的时间来理解这个小而简单的规范,我仍然无法理解为什么它不能解析一个非常单一的规则。我几乎可以肯定这是一个愚蠢的错误,但我知道哪个是

    因此,我非常感谢您的帮助。

    这里有一个问题:

    {NEWLINE} { ++curr_lineno; return NEWLINE; }
    
    我甚至不知道它是如何编译的,因为
    换行符
    没有定义为令牌。我在任何地方都看不到它的任何定义(模式宏不起作用,因为它们在生成扫描仪之前就被解析了。)

    由于您的语法期望
    '\n'
    作为换行符的标记值,因此您需要返回:

    {NEWLINE} { ++curr_lineno; return '\n'; }
    

    在没有调试辅助的情况下解决这样的问题可能很棘手。幸运的是,flex和bison都提供了调试选项,这使得查看正在发生的事情变得非常简单(并且避免了在bison操作中包含您自己的跟踪消息的必要性)

    对于flex,在生成扫描仪时使用
    -d
    标志。这将打印有关扫描仪进度的大量信息。(无论如何,在这种情况下,这似乎是最有可能的起点。)

    对于bison,在生成解析器时使用
    -t
    标志,并将全局变量
    yydebug
    设置为非零值。由于bison跟踪取决于
    yydebug
    全局变量(其默认值为0)的设置,因此您只需将
    -t
    标志添加到bison调用中,就不必重新生成文件来关闭跟踪


    注意:在
    ID
    VARNAME
    规则中,将
    yytext
    插入语义值:

    yylval.symbol = yytext;
    
    那不行
    yytext
    仅在下次调用
    yylex
    之前有效,因此在执行使用语义值的bison操作时,
    yytext
    指向的字符串将发生更改。(即使bison操作仅引用右侧的最后一个标记,这也可能是正确的,因为bison通常在决定执行缩减之前读取一个前瞻标记。)您必须复制该标记(例如使用
    strdup
    ),并记住在不再需要该值时释放它


    关于风格的说明。只是个人意见,随意忽略:

    就个人而言,我发现过度使用模式宏会分散注意力。您可以将该规则写成:

    \n        { ++curr_lineno; return '\n'; }
    
    类似地,您可以使用Posix标准字符类来代替定义,例如,
    数字、
    大写字母等:

    INTEGER   [[:digit:]]+
    VAR_NAME  [[:alpha:]][[:alnum:]_.-]*
    

    (在字符类中不需要反斜杠转义。)

    非常感谢@rici。然而,我已经做了所有你建议的mods,它还不起作用。我无法理解为什么它无法识别
    cmd\u装置的第二条规则。A在没有readline的情况下进行了测试,将字符串常量'load\'name'传递给它“而且还是这样fails@lrleon:使用您生成的错误消息可能不会有什么坏处:)否则,您或多或少是瞎了眼,不是吗?为什么不启用flex和bison跟踪,以便查看实际情况?对于flex,生成扫描仪时只需使用
    -d
    标志。(无论如何,这似乎是最有可能开始的地方。)对于bison,在生成解析器时使用
    -t
    标志,并将全局变量
    yydebug
    设置为非零值。已修复!你的建议使我能够追踪错误的来源。标头
    net parser.H
    包含一个
    enum yytokentype
    ,该数字与
    %token
    规范冲突。再次感谢!是的,我是一只瞎子,因为我的编译经验不仅仅局限于学校的练习
    \n        { ++curr_lineno; return '\n'; }
    
    INTEGER   [[:digit:]]+
    VAR_NAME  [[:alpha:]][[:alnum:]_.-]*