如何让Bison/YACC在解析整个字符串之前不识别命令?

如何让Bison/YACC在解析整个字符串之前不识别命令?,c,parsing,yacc,bison,C,Parsing,Yacc,Bison,我有一些野牛语法: input: /* empty */ | input command ; command: builtin | external ; builtin: CD { printf("Changing to home directory...\n"); } | CD WORD { printf("Changing to directory %s\n", $2); } ; 我想知道如何让Bison在读

我有一些野牛语法:

input: /* empty */
       | input command
;

command:
        builtin
        | external
;

builtin:
        CD { printf("Changing to home directory...\n"); }
        | CD WORD { printf("Changing to directory %s\n", $2); }
;
我想知道如何让Bison在读取所有输入之前不接受(YYACCEPT?)某个
命令。因此,我可以使用下面所有的规则,使用递归或其他任何方法来构建东西,这要么导致一个有效的命令,要么导致一些不起作用的东西

我用上面的代码做的一个简单测试就是输入“cd mydir mydir”。Bison解析了
CD
WORD
,然后说:“嘿!这是一个命令,把它放到最上面!”。然后它找到的下一个标记就是没有规则的
WORD
,然后它报告一个错误

我想让它通读整行,意识到
CD-WORD
不是规则,然后报告错误。我想我错过了一些明显的东西,非常感谢您的帮助-谢谢


另外-我尝试过使用
输入命令换行
或类似的命令,但它仍然将
CD WORD作为命令推到顶部,然后分别解析额外的
单词。

通常情况下,事情并不是按照您描述的方式完成的

对于Bison/Yakk/Lex,人们通常会仔细设计它们的语法,以完全满足它们的需要。因为Bison/Yakk/Lex很自然地使用它们的正则表达式,所以这应该对您有所帮助

那么,这个怎么样

由于您一次解析整个,我认为我们可以利用这一事实来修改语法

input : /* empty */
      | line


command-break : command-break semi-colon
              | semi-colon

line : commands new-line

commands : commands command-break command
         | commands command-break command command-break
         | command
         | command command-break

...
新行
中,“分号
lex
源代码中定义为类似于
\n
\t`。这将为您寻找的命令提供UNIX风格的语法。所有的事情都是可能的,它有点臃肿,允许使用多个分号,并且没有考虑到空格,但是你应该明白这一点


Lex和Yakk是一个强大的工具,我觉得它们非常有趣——至少在你没有截止日期的时候是这样。

有时我会通过简化语法来处理这些情况

在您的例子中,向换行符和命令分隔符(;)的lexer中添加标记可能是有意义的,这样您就可以将它们显式地放在Bison语法中,这样解析器在接受命令作为commmand之前,将期望命令有完整的输入行

sep:   NEWLINE | SEMICOLON
   ;

command:  CD  sep
   |  CD WORD sep
   ;
或者,对于任意参数列表(如真实shell):

args:
    /* empty */
  | args WORD
  ;

command:
      CD args sep
   ;

如果整个过程正常,您不能将规则匹配操作更改为附加到要执行的操作列表中吗?然后,在处理完整个输入之后,您可以根据是否看到任何解析错误来决定是否要执行该操作列表中的操作。

与其直接调用操作,不如先构建一个抽象语法树。然后根据结果和您的偏好,您要么执行部分,要么什么也不执行。如果在树构建过程中出现解析错误,您可能需要使用%destructor指令告诉bison如何进行清理


这实际上是一种正确的方法,因为您可以完全控制内容和逻辑,并且让bison负责解析。

似乎您的
内置
应该以换行符终止(而不是
输入
)。与在
CD-WORD新行中一样
您的
builtin
缺少一个
{
,应该是
| CD-WORD{printf(“更改为directroy%s\n”,$2);}
哎呀,对不起!我用来测试的代码中没有这一点……这似乎是可行的。但是,我必须特别提到每个命令的分隔符表达式,这是一个令人不快的问题。我可能会在某个时候改为任意参数……但还没有!我仍然好奇是否有其他方法可以做到这一点……更正:这适用于2单词(cd hello hello),但此时它会弹出标记。然后出于某种原因它会再次启动。所以“cd hello1 hello2 hello3”会弹出cd、hello1和hello2,但随后它会尝试为hello3匹配单独的规则。如果使用“args”,我很困惑上面第二部分中的规则应该匹配任意数字。我仍然不完全理解所有这些,我在Bison中仍然得到一些古怪的结果,但这确实有帮助。感谢你的回答-在我目前所在的课程中,我们正在做一个项目,我们解析语言,构建AST,并生成代码。遗憾的是,我没有那个经验回到我使用Bison和YACC的课堂上。再次感谢,下次我可能会用不同的方式来思考这个问题。