灵活的新线扫描野牛

我想为解释器使用相同的flex / bison扫描器/解析器并加载要解释的文件。 在这两种情况下,我无法让换行符正确工作。

  • 解释器:有一个提示,我可以输入命令,按ENTER键终止。
  • 文件:这是一个示例输入文件:
  • - - -切 - - - - -

    begin(
        print("well done"), 1)
    

    - - 切 - - - -

    所以,第一行有一个换行符,并且在'('应该被吃掉。

    在我的scanner.l中,我有

    %%
    [ t]                       {   errorLineCol += strlen(yytext); }
    
    n                          {   errorLineNumber++;
                                    errorLineCol = 0; }
    
    ("-"?[0-9])[0-9]*           {   errorLineCol += strlen(yytext);
                                    yylval = stringToInteger(yytext);
                                    return TINTEGER; }
    

    .....

    然后这适用于文件方案,但不适用于解释器。 我必须按ENTER键后再加上Ctrl + D。 如果我改变

    n                          {   errorLineNumber++;
                                    errorLineCol = 0;
                                    return 0; }
    

    然后解释者工作,但不是文件阅读; 然后在遇到第一个换行符后停止。 解决这个问题的好方法是什么?

    编辑:

    以下是解析器的最高级别:

    input: uexpr                        {   parseValue = $1; }
        | /* empty */                   {   parseValue = myNull; }
        | error                         {   parseValue = myNull; }
        ;
    
    uexpr: list                          
        | atom                         
        ;
    

    可能的解决方案:似乎是使用

    n                          {   errorLineNumber++;
                                    errorLineCol = 0;
                                    if (yyin == stdin) return 0; }
    

    主要的问题是你的解析器函数ypparse不会返回,直到它将整个语言缩减为开始符号。

    如果您的语法的顶层是类似的:

    language : commands ;
    
    commands : command commands | /* empty */ ;
    

    当然机器会期望一个完整的脚本(由你敲击Ctrl-D来终止)。 如果你的翻译是这样的逻辑:

    loop:
      print("prompt>")
      yyparse()
      if (empty statement)
        break
    

    它不会工作,因为yyparse在返回之前正在使用整个脚本。

    return 0; 解决了这种交互模式的问题,因为令牌值0表示解析器的EOF ,使得它认为脚本已经结束。

    我不同意制作n令牌的解决方案。 它只会使语法复杂化(迄今为止一个微不足道的空白现在很重要),并最终无法工作,因为yyparse函数仍然需要处理完整的语法。 也就是说,如果将换行符作为标记,但语法的起始符号表示整个脚本,则yyparse仍不会返回到交互式提示符循环。

    快速而肮脏的破解是让词法分析器知道交互模式是否有效。 然后它可以有条件地return 0; 对于新行的每个实例,如果它处于交互模式。 如果输入不是完整的语句,那么将会有语法错误,因为整个脚本在换行符处结束。 在正常的文件读取模式下,您的词法分析器可以在不返回的情况下吃掉所有的空格,就像之前允许用一个yyparse处理整个文件一样。

    如果您希望交互式输入和文件读取不需要在词法分析器中实现两种行为模式,您可以执行的操作是更改语法,以便仅解析语言的一个语句: yyparse函数针对语言的每个顶级语句返回。 (而且词法分析器像以前一样吃换行符,不会返回0)。 即语法的开始符号只是一个语句(可能为空)。 然后你的文件解析器必须被实现为一个循环(由你编写),它调用yyparse从文件中获取所有语句,直到yyparse遇到一个空输入为止。 这种方法的缺点是,如果用户键入不完整的语法(例如悬空的圆括号),解析器将继续扫描输入,直到满意为止。 这是不友好的,就像使用scanf进行交互式用户输入的程序一样(这是同样的问题: scanf是一个解析器,只有在它满足时才会返回)。

    另一种可能性是有一个交互模式,它执行自己的用户输入,而不是调用yyparse来获取输入并解析它。 在这种模式下,您可以将用户的输入读入行缓冲区。 然后你有解析器处理行缓冲区。 处理行缓冲区而不是FILE *流是完全可能的。 您只需编写自定义输入处理(您自己定义的YY_INPUT宏)。 如果你通过行编辑和历史回忆来实现一个体面的交互模式,比如使用libedit或者GNU readline ,那么这种方法最终会需要反正。


    如果按下ENTER键终止一个命令,那么词法分析器应该为 n返回一个标记。 返回0告诉解析器输入源已完成(文件的结尾或终端的^ D)。 在语法中添加行尾标记并让词法分析器在看到 n时返回。

    ETA:但不要忘记处理最后一行没有以ENTER结尾的情况。 除非最后一个字符是 n,否则让您的词法分析器在文件末尾返回一个结束标记。

    链接地址: http://www.djcxy.com/p/41737.html

    上一篇: Flex newline scanning for bison

    下一篇: Common tokens for flex and bison