0

我正在开发自己的编译器,但在 java 语法的恐慌模式下的错误恢复设计存在问题。

我想过多种解决方案,但真正的问题是:

我怎么能用 bison c++ 做到这一点?

我这样做了:

包 2

导入java.lang.*;

错误必须消耗到第一个分号,并且使用规则正确运行

package_rule: 包错误 ';'

但是如果我写了这段代码:

包 2

导入 java.lang.*

类 y { void 方法() { int m }

}

我需要像标准编译器这样的解析器来报告错误:

包装行应有标识符。失踪 ';' 在导入指令行报告一个包。发短信';' 在 int m 线。

我的意思是我需要在包错误之后使用令牌直到第一个分号或在声明它们之前在最后一行找到类或接口声明时停止!并报告行后发现的任何其他错误:

int m // 缺少 ';'

请帮助我,在我看来有多种解决方案,但是如何使用 bison c++ for java 语法呢?

4

2 回答 2

1

好吧,您的基本问题是您希望它如何尝试从语法错误中恢复。当您有一个输入序列时

package x import

您是否希望它假设那里应该有一个分号,或者您是否希望它假设在分号之前有其他东西被卡住并且它应该扔掉东西直到它到达一个分号?

后者就是你所拥有的——规则package: PACKAGE error ';'正是这样做的——每当它看到关键字PACKAGE但它之后的内容与规则的其余部分不匹配时package,它应该丢弃输入,直到它看到 a';'并尝试从那里继续。

如果你想要前者,你可以使用这样的规则package: PACKAGE name error——如果它看到PACKAGE的东西看起来像一个有效的包名但没有分号,则将其视为那里有一个分号并尝试继续。

让它能够同时做上述两件事是非常困难的。最接近的是让语法看起来像:

package: PACKAGE name ';'  /* normal package decl */
       | PACKAGE name      /* missing semicolon -- treat this as a semantic error */
       | PACKAGE error ';' /* no name -- skip up to the next semicolon to recover */

但是,这种事情可能会给您带来难以解决的语法冲突。

于 2013-02-04T19:19:57.833 回答
1

您不会介意以 C++ OOP 方式而不是野牛方式来解决该问题,对吗?

考虑您定义了这些类型的 AST 节点

struct BaseExpression {
    virtual std::string toIdentifier() = 0;
    // other member. remember to declare a virtual destructor
};

struct IntLiteral : BaseExpression {
    std::string toIdentifier() {
        error::toAnIdentifier();
        return "";
    }
};

struct Identifier: BaseExpression {
    std::string ident;

    explicit Identifier(std::string id) : ident(id) {}

    std::string toIdentifer() {
        return ident;
    }
};

定义这样的规则

%union {
    BaseExpression* expr_type;
}

%type <expr_type> simple_expr

package_expr: simple_expr
{
    $1->toIdentifer(); // thus integers or float numbers would generate errors
    // do sth with that identifer
}
;

package_expr: package_rule '.' simple_expr
{
    $3->toIdentifer(); // same trick
    // do sth with that identifer
}
;

simple_expr在哪里

simple_expr: int_literal { return new IntLiteral; }
           | ...
           | identifier { return new Identifier(yytext); }
;
于 2013-02-04T15:57:46.280 回答