Я разрабатываю свой собственный компилятор, и у меня есть проблема с дизайном восстановления после ошибок
в режиме паники для грамматики Java.
Я думал о нескольких решениях, но реальный вопрос:
Как я мог сделать это с Bison C ++?
Я это сделал:
пакет 2
импорт java.lang. *;
ошибка должна указывать на первую точку с запятой, и это правильно работает с правилом
package_rule: ошибка PACKAGE ‘;’
но если бы я написал этот код:
пакет 2
импорт java.lang. *
class y {void method () {int m}
}
что мне нужно от парсера как стандартный компилятор, чтобы сообщать об ошибках:
Ожидается идентификатор в строке пакета.
отсутствует ‘;’ укажите один для пакета в строке инструкции по импорту.
mssing ‘;’ в строке м.
Я имею в виду, что мне нужно после ошибки пакета использовать токен до первой точки с запятой или остановиться, когда найти объявление класса или интерфейса в последней строке перед тем, как объявить их! и сообщать о любых других ошибках, найденных после строки:
int m // отсутствует ‘;’
Пожалуйста, помогите мне, на мой взгляд, несколько решений, но как это сделать с Bison C ++ для Java грамматики?
Ну, ваша основная проблема в том, как вы хотите, чтобы он пытался восстановиться после синтаксических ошибок. Когда у вас есть входная последовательность, как
package x import
Вы хотите, чтобы он предполагал, что там должна быть точка с запятой, или вы хотите, чтобы он предполагал, что что-то еще застряло до точки с запятой, и он должен выбрасывать вещи, пока не достигнет точки с запятой?
Последнее то, что у вас есть — правило package: PACKAGE error ';'
делает именно это — всякий раз, когда он видит ключевое слово PACKAGE
но то, что последует за ним, не соответствует остальным package
Правило, он должен выбросить ввод, пока не увидит ';'
и попытаться продолжить оттуда.
Если вы хотите первое, вы бы использовали правило как 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 */
Однако такого рода вещи, вероятно, дадут вам грамматические конфликты, которые трудно разрешить.
Вы не будете возражать, чтобы решить эту проблему способом C ++ ООП, а не бизоном, не так ли?
Предположим, у вас есть определенные типы узлов 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); }
;