【问题标题】:how typedef-name - identifier issue is resolved in C?如何在 C 中解决 typedef-name - 标识符问题?
【发布时间】:2013-06-16 15:31:34
【问题描述】:

我最近一直在为基于 C 的语言编写解析器。我正在使用 CUP(Yacc for Java)。

我想实现“The lexer hack”(http://eli.thegreenplace.net/2011/05/02/the-context-sensitivity-of-c%E2%80%99s-grammar-revisited/https://en.wikipedia.org/wiki/The_lexer_hack),以区分 typedef 名称和变量/函数名称等。启用与前面声明的类型同名的声明变量(来自第一个链接的示例):

typedef int AA;

void foo() {
    AA aa;       /* OK - define variable aa of type AA */
    float AA;    /* OK - define variable AA of type float */
}

我们必须引入一些新的产品,其中变量/函数名称可以是IDENTIFIERTYPENAME。这就是困难发生的时刻——语法冲突。

我试图不为 gcc 3.4 (http://yaxx.googlecode.com/svn-history/r2/trunk/gcc-3.4.0/gcc/c-parse.y) 使用这个凌乱的 Yacc 语法,但这次我不知道如何自己解决冲突。我看了一下Yacc语法:

declarator:
    after_type_declarator
    | notype_declarator
    ;

after_type_declarator:
    ...
    | TYPENAME
    ;

notype_declarator:
    ...
    | IDENTIFIER
    ;

fndef:
    declspecs_ts setspecs declarator
    // some action code
    // the rest of production
...

setspecs: /* empty */
    // some action code

declspecs_ts 表示声明说明符,其中 “是否见过类型说明符;在类型说明符之后,typedef 名称是要重新声明的标识符(_ts 或 _nots)。”

从 declspecs_ts 我们可以到达

typespec_nonreserved_nonattr:
    TYPENAME
    ...
    ;

乍一看,我不敢相信 shift/reduce 冲突没有出现! setspecs 是空的,所以我们有declspecs_ts 后跟declarator,因此我们可以预期解析器应该混淆TYPENAME 是来自declspecs_ts 还是来自declarator

谁能简要(甚至准确地)解释一下。提前致谢!

编辑: 有用链接:http://www.gnu.org/software/bison/manual/bison.html#Semantic-Tokens

【问题讨论】:

    标签: parsing compilation grammar context-sensitive-grammar


    【解决方案1】:

    具体代码我不能说。

    但基本技巧是 C 词法分析器检查每个 IDENTIFIER,并确定是否可能是 typedef 的名称。如果是这样,那么它将词位类型更改为 TYPEDEF 并将其交给解析器。

    词法分析器如何知道哪些标识符是 typedef?解析器实际上必须通过在运行时捕获 typedef 信息来告诉它。在与声明相关的语法中的某处,必须有一个动作来提供此信息。我本来希望它被附加到 typedef 声明的语法规则中。

    你没有展示“setspec”做了什么;也许就是这个地方。 LR解析器生成器使用的一个常见技巧是引入一个右手空的语法规则E(您的示例“setspec”?),在其他一些语法规则(您的示例“fndef”)中间调用只是为了启用在处理该规则的过程中访问语义操作。

    如果你不能从其他标识符中区分 typedef,这整个技巧就是绕过解析歧义。如果您的解析器可以容忍歧义,那么您根本不需要这个 hack;只需解析,并使用两个(子)解析构建 AST。获取 AST 后,树遍历可以找到类型信息并消除不一致的子解析。我们使用 GLR 为 C 和 C++ 执行此操作,它很好地将解析与名称解析分开。

    【讨论】:

    • 你是对的,但在这种情况下会发生不同的事情。您可以在代码 sn-p 上方的链接中阅读 setspec 定义。我在这段代码中看得更深一些。 declspecs_ts 只包含一个 TYPENAME 和其他一些说明符(限定符,如 INLINE 等)。还有其他变体,例如declspecs_nots,我们必须将不同的declspecsafter_type_declaratornotype_declaraor 组合起来以支持所有可能的组合。这甚至更复杂(因为属性),但它的组织/拆分足够智能以防止冲突。
    • 欢迎使用生产语法,它不仅具有“标准”语言结构,而且具有特定编译器/方言(例如 GCC、MS、GreenHills)扔进的所有其他垃圾。当您添加人们所做的所有扩展时,语法往往不再简单。如果幸运的话,语法仍然“足够聪明地组织/拆分”以易于管理。 GLR 解析器实际上使处理大型复杂语法变得容易得多,因为不必纠缠诸如 TYPEDEF 之类的东西检查语法本身。
    • 这是我的看法:declspecs_ts after_type_declaration 用于将 typename 重新声明为变量(可能是其他一些 typename2)的情况,例如:typedef int myType1; typedef float myType2; { myType1 myType2; /* variable myType2 (name) of type myType1 */ } declspecs_nots notype_declaration 用于简单的int foo;
    • 好的,你能推荐任何适用于 Java 的 GLR 解析器吗?
    • 考虑strategoxt.org/Stratego/JSGLR 我对此没有具体经验,但Stratego 的人是一个相当不错的行为。如果您在附近找到 C 语法,我不会感到惊讶。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    相关资源
    最近更新 更多