#java #parsing #antlr #antlr4 #lexer
#java #синтаксический анализ #antlr #antlr4 #лексер
Вопрос:
Я полный новичок в ANTLR.
У меня есть следующая грамматика ANTLR:
grammar DrugEntityRecognition;
// Parser Rules
derSentence : ACTION (INT | FRACTION | RANGE) FORM TEXT;
// Lexer Rules
ACTION : 'TAKE' | 'INFUSE' | 'INJECT' | 'INHALE' | 'APPLY' | 'SPRAY' ;
INT : [0-9] ;
FRACTION : [1] '/' [1-9] ;
RANGE : INT '-' INT ;
FORM : ('TABLET' | 'TABLETS' | 'CAPSULE' | 'CAPSULES' | 'SYRINGE') ;
TEXT : ('A'..'Z' | WHITESPACE | ',') ;
WHITESPACE : ('t' | ' ' | 'r' | 'n' | 'u000C') -> skip ;
И когда я пытаюсь разобрать предложение следующим образом:
String upperLine = line.toUpperCase();
org.antlr.v4.runtime.CharStream stream = new ANTLRInputStream(upperLine);
DrugEntityRecognitionLexer lexer = new DrugEntityRecognitionLexer(stream);
lexer.removeErrorListeners();
lexer.addErrorListener(ThrowingErrorListener.INSTANCE);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
DrugEntityRecognitionParser parser = new DrugEntityRecognitionParser(tokenStream);
try {
DrugEntityRecognitionParser.DerSentenceContext ctx = parser.derSentence();
StringBuilder sb = new StringBuilder();
sb.append("ACTION: ").append(ctx.ACTION());
sb.append(", ");
sb.append("FORM: ").append(ctx.FORM());
sb.append(", ");
sb.append("INT: ").append(ctx.INT());
sb.append(", ");
sb.append("FRACTION: ").append(ctx.FRACTION());
sb.append(", ");
sb.append("RANGE: ").append(ctx.RANGE());
System.out.println(upperLine);
System.out.println(sb.toString());
} catch (ParseCancellationException e) {
//e.printStackTrace();
}
Пример ввода в лексер:
take 10 Tablet (25MG) by oral route every week
В этом случае узел ДЕЙСТВИЯ не заполняется, а take
распознается только как ТЕКСТОВЫЙ узел, а не как узел ДЕЙСТВИЯ. 10
однако распознается как узел INT.
Как я могу изменить эту грамматику, чтобы она работала правильно, где узел ДЕЙСТВИЯ заполняется правильно (а также ФОРМА, которая также не заполняется)?
Ответ №1:
В вашей грамматике есть несколько проблем:
- Ваше ТЕКСТОВОЕ правило соответствует только прописным буквам. То же самое для ДЕЙСТВИЯ.
- Вы не должны смешивать знаки препинания и текст в одном текстовом правиле (здесь запятая), иначе вы не сможете свободно разрешать пробелы между токенами.
- Вы вообще не сопоставляете скобки, следовательно
(25MG)
, ввод недопустим, и анализатор возвращается в состоянии ошибки. - Вы не проверяли наличие каких-либо синтаксических ошибок, чтобы узнать, что пошло не так во время распознавания.
Кроме того, если вы сомневаетесь, всегда печатайте свою последовательность токенов из источника токена, чтобы узнать, действительно ли входные данные были помечены, как вы ожидаете. Начните с исправления грамматики, прежде чем переходить к синтаксическому анализатору.
О чувствительности к регистру: обычно (если ваш язык не чувствителен к регистру) у вас есть такие правила:
fragment A: [aA];
fragment B: [bB];
fragment C: [cC];
fragment D: [dD];
...
чтобы сопоставить букву в любом случае, а затем определить ключевые слова так:
ACTION : T A K E | I N F U S E | I N J E C T | I N H A L E | A P P L Y | S P R A Y;
Комментарии:
1. 1. Я преобразовываю предложения в верхний регистр перед запуском лексера. 2. Спасибо, я изменю это и посмотрю, как это происходит. 3. Как я должен сопоставлять круглые скобки. 4. То же самое, как я должен проверять наличие синтаксических ошибок. Кроме того, как мне распечатать мою последовательность токенов из источника токенов, чтобы узнать, была ли она помечена так, как я ожидаю? Опять же, ПОЛНЫЙ новичок здесь — мне нужна конкретная помощь.
2. Для части скобок создайте другое правило, соответствующее ему, например
dosage: '(' INT ('M' | 'μ') 'G' ')';
, или аналогичное, возможно, с дополнительными частями. Список токенов легко получить. Посмотрите ваш поток токенов и егоgetTokens()
метод. Большинство классов во время выполнения ANTLR имеют метод toString(), который можно использовать для печати некоторой информации. Для ошибок вы можете использовать Parser.getNumberOfSyntaxErrors() , но реальная обработка ошибок требует немного больше работы (с использованием собственного прослушивателя ошибок). Это должно быть обработано в собственном вопросе.