ANTLR соответствует части правила синтаксического анализатора, даже если он не может полностью соответствовать правилу

Я начинаю с ANTLR и пытаюсь создать парсер для очень простого языка. В настоящее время моя грамматика определяется как:

/*
 * Parser Rules
 */

public compileUnit
    : DEFINE IDENTIFIER END_OF_STATEMENT { Console.WriteLine($IDENTIFIER.text); };

/*
 * Lexer Rules
 */


DEFINE : 'define';

// Basic tokens
INT : '0'..'9'+;

END_OF_STATEMENT : ';';

// Whitespace
WS :  (' '|'\t'|'\r'|'\n')+ {Skip();} ;

// Sub-statement tokens

IDENTIFIER : ('a'..'z' | 'A'..'Z')+ (INT | ('a'..'z' | 'A'..'Z') | '_')*;

Язык - CSharp3.

Когда я пытаюсь ввести следующее, он работает правильно, печатая имя идентификатора:

define My_Identifier1;

Однако ввод garbage также вызывает распечатку garbage, как если бы это был идентификатор.

Почему compileUnit не генерирует исключение? Может ли это быть проблемой с порядком, в котором определены правила грамматики?


person ose    schedule 16.11.2013    source источник
comment
Вы имеете в виду слово "мусор"? Потому что ваше правило идентификатора позволяет это. Кстати. ваше регулярное выражение для идентификатора нечетное - оно должно быть как минимум ('a'..'z' | 'A'..'Z') (INT | 'a'..'z' | 'A'..'Z' | '_')*.   -  person greenoldman    schedule 17.11.2013
comment
Ой, ты прав, плюса быть не должно.   -  person ose    schedule 17.11.2013
comment
Входной мусор должен быть отклонен. Только определите мусор; должно быть разрешено   -  person ose    schedule 17.11.2013
comment
С этого момента начинается угадывание, потому что я не знаю об ANTLR (я использую свой собственный набор C # NLT - sourceforge.net/projects/naivelangtools), но я распечатал бы то, что было схвачено DEFINE терминалом. Вы знаете, как простая отладка. Просто убедитесь, что вы напечатали $DEFINE.text # $IDENTIFIER.text (подойдет любой разделитель, кроме пробела), чтобы убедиться, что вы знаете, какая часть какая.   -  person greenoldman    schedule 17.11.2013
comment
Когда я распечатываю значение токена определения на правильном входе, он печатает определение. Когда это делается на вводе мусора, он печатает пропущенное определение.   -  person ose    schedule 17.11.2013


Ответы (2)


Просто для записи, если вы хотите переключиться, это то, как это было бы в NLT, есть некоторые незначительные отличия, но ничего трудного для понимания.

/*
 * Parser Rules
 */
compileUnit -> DEFINE id:IDENTIFIER END_OF_STATEMENT 
               {{ Console.WriteLine(id); return null; }};

/*
 * Lexer Rules
 */

"define" -> DEFINE;
/[0-9]+/ -> INT,Convert.ToInt32($text);
";" -> END_OF_STATEMENT;

// Whitespace
/[ \\t\\r\\n]+/ { };

// Sub-statement tokens
/[a-zA-z][a-zA-Z0-9_]*/ -> IDENTIFIER;

Это всего лишь черновик.

person greenoldman    schedule 17.11.2013
comment
Я решил придерживаться ANTLR, поскольку он уже есть в моем проекте, но для справки см. Мой ответ ниже с решением проблемы. - person ose; 18.11.2013

Решением этой проблемы было изменение правила парсера на (обратите внимание на добавление токена EOF):

public compileUnit
: DEFINE IDENTIFIER END_OF_STATEMENT EOF { Console.WriteLine($IDENTIFIER.text); };

И переопределите метод ReportError класса парсера.

public override void ReportError(Antlr.Runtime.RecognitionException e)
{
    base.ReportError(e);
    throw e;
}

Теперь вызывающий код имеет доступ к исключению, и разработка может продолжаться.

Стоит отметить, что, наткнувшись на решение, я быстро поискал и нашел этот вопрос о переполнении стека, который кажется похожим. Это кажется не очень очевидной частью ANTLR (т.е. как ANTLR обрабатывает грамматики BNF) и потенциальным камнем преткновения для новичков на платформе.

person ose    schedule 18.11.2013