【问题标题】:Grammar to Lex/Yacc语法到 Lex/Yacc
【发布时间】:2014-05-10 20:51:41
【问题描述】:

我的任务是一个项目,该项目涉及我获取语法(BNF 形式)并创建一个词法扫描器(使用 lex)和一个解析器(使用野牛)。我从未使用过这些程序中的任何一个,我认为一个很好的参考是看看这些项目是如何从语法中创建的。我正在寻找一种语法,它是关联的 .l 和 .ypp 文件,最好是 C++。我已经能够找到示例文件或示例语法,但不能同时找到它们。我花了一些时间搜索,但找不到任何东西。我想我会在这里发帖,希望有人对我有所帮助,但与此同时我会继续搜索。

我目前正在阅读 Tom Niemann 的 http://epaperpress.com/lexandyacc/download/LexAndYaccTutorial.pdf 这似乎写得很好并且可以理解。

谢谢

编辑:我还在寻找,我开始认为我要寻找的东西不存在。 Google 通常不会让我失望!

编辑 2:也许如果我提供一些语法,你们可以向我展示适当的 .l 和 .ypp 文件的外观。这只是语法的 sn-p,我只需要对它的工作原理有一点“品味”,我想我可以从那里得到它。

语法:

Program ::= Compound
Statements ::= Compound | Assignment | ...
Assignment ::= Var ASSIGN Expression
Expression ::= Var | Operator Expression Expression | Number
Compound := START Statements END
Number ::= NUMBER

说明:

Assignment is the equal sign ":="

Var is an identifier that begins with a lower case letter and is followed by lower case letters or digits

START is the "start" keyword

END is the "end keyword

Operator is "+", "-", "*", "/"

Number is decimal digits which could potentially be negative (minus sign in front)

【问题讨论】:

  • 找到这样一个简单的语法会很困难。
  • @JonathanLeffler 你不是在开玩笑。也许我们可以在这里想出一些简单的东西,以便将来像我一样的人有东西可看!

标签: c++ grammar bison lex


【解决方案1】:

其中大部分都相当简单。然而,有一部分确实是有问题的。您已定义一个数字以(可能)包含前导 -,这是一个问题。

问题很简单。给定一个像321-123 这样的输入,词法分析器(通常不会跟踪当前状态)基本上不可能猜测这应该是两个标记(321-123 还是三个321-123)。在这种情况下,- 几乎可以肯定是要与123 分开,但如果输入是321 + -123,您显然希望-123 作为单个标记。

为了解决这个问题,您可能想要更改语法,因此前导 - 不是数字的一部分。相反,您总是希望将- 视为运算符,而数字本身仅由数字组成。然后由解析器来对 - 是一元还是二元的表达式进行排序。

考虑到这一点,词法分析器文件看起来像这样:

%{
#include "y.tab.h"
%}

%option noyywrap case-insensitive  
%%

:=        { return ASSIGN;   }
start     { return START;    }
end       { return END;      }
[+/*]     { return OPERATOR; }
-         { return MINUS;    }
[0-9]+    { return NUMBER;   }
[a-z][a-z0-9]* { return VAR; }
[ \r\n]   { ; }

%%

void yyerror(char const *s) { fputs(s, stderr); }

匹配的 yacc 文件如下所示:

%token ASSIGN START END OPERATOR MINUS NUMBER VAR
%left '-' '+' '*' '/'
%%

program : compound

statement : compound
            | assignment
            ;

assignment : VAR ASSIGN expression
            ;

statements :
            | statements statement
            ;

expression : VAR
            | expression OPERATOR expression 
            | expression MINUS expression
            | value
            ;

value: NUMBER
     | MINUS NUMBER
     ;          

compound : START statements END

%%

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

注意:我只对这些进行了非常最低限度的测试——足以验证我认为是语法的输入,例如:start a:=1 b:=2 endstart a:=1+3*3 b:=a+4 c:=b*3 end 被接受(没有打印出错误消息)我认为输入是不合语法的,例如:9:=13a=13 do 都打印出 syntax error 消息。由于这并没有尝试对表达式做更多的事情,而是识别那些符合语法或不符合语法的表达式,所以这是我们能做的最好的事情。

【讨论】:

  • 根据他的描述,OP 需要前缀运算符,而不是中缀。这使得不明确的- 问题变得更糟,因为一元和二进制- 都出现在任何操作数之前,这意味着直到你到达整个表达式的末尾才知道它是哪个。它也很模棱两可——比如- - a b,哪个减号是一元的,哪个是二元的?
  • @ChrisDodd:哎呀——我没有仔细阅读他的语法,并错过了它应该使用前缀运算符的事实。但是,是的,这让问题变得更糟。
猜你喜欢
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2013-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多