Я дал ANTLR4 следующую грамматику синтаксического анализатора и лексера в отдельных файлах (имеется в виду простую грамматику для грамматики BNF)
parser grammar BNFParser;
options {tokenVocab = BNFLexer;}
compileUnit
: grammar_rule+
;
grammar_rule : NON_TERMINAL COLON (OR? grammar_rule_alternative)* SEMICOLON
;
grammar_rule_alternative : (NON_TERMINAL|TERMINAL)+
;
и
lexer grammar BNFLexer;
TERMINAL : [A-Z][A-Za-z0-9_]*;
NON_TERMINAL : [a-z][A-Za-z0-9_]*;
OR : '|';
COLON : ':';
SEMICOLON : ';';
WS
: [ \t\r\n]+ -> skip
;
Основная программа
private static void Main(string[] args) {
StreamReader reader = new StreamReader(args[0]);
AntlrInputStream stream = new AntlrInputStream(reader);
BNFLexer lexer = new BNFLexer(stream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
BNFParser parser = new BNFParser(tokens);
IParseTree root = parser.compileUnit();
Console.WriteLine(root.ToStringTree());
}
Также предоставлен следующий тестовый файл для проверки грамматики
compileunit : x a
;
x : S b
;
S : compileunit f
;
Обратите внимание, из грамматики лексера, что нетерминалы начинаются с строчных букв, а терминалы начинаются с заглавных букв. В данной грамматике есть ошибка. Третье правило использует заглавную букву (S) для определения нетерминала S. Ожидаемое поведение - сообщать об этом как об ошибке. С другой стороны, синтаксический анализ завершается успешно, потребляя первые 2 правила и игнорируя третье для S без сообщения об ошибке. Я также видел сгенерированные файлы, и я заметил следующее
try {
EnterOuterAlt(_localctx, 1);
{
State = 7;
_errHandler.Sync(this);
_la = _input.La(1);
do {
{
{
State = 6; grammar_rule();
}
}
State = 9;
_errHandler.Sync(this);
_la = _input.La(1);
} while ( _la==NON_TERMINAL );
}
}
catch (RecognitionException re) {
_localctx.exception = re;
_errHandler.ReportError(this, re);
_errHandler.Recover(this, re);
}
Приведенный выше код показывает, что синтаксический анализатор ожидает нетерминальный символ в начале grammar_rule, чего я и ожидаю. Однако что происходит, когда это не так? Еще одна странная проблема заключается в том, что объект CommonTokenStream, который содержит токены, распознаваемые лексером, содержит только токены до конца второго правила, но не токены третьего правила (S). Это правильное поведение?