Справка с парсингом файла журнала (ANTLR3)

Мне нужно немного руководства в записи грамматики для парсинга файла журнала игры Aion. Я выбрал использование Antlr3 (потому что это, кажется, инструмент, который может сделать задание, и я полагал, что для меня хорошо учиться использовать его). Однако я столкнулся с проблемами, потому что файл журнала точно не структурирован.

Файл журнала, который я должен проанализировать, похож на тот ниже:

2010.04.27 22:32:22 : You changed the connection status to Online. 
2010.04.27 22:32:22 : You changed the group to the Solo state. 
2010.04.27 22:32:22 : You changed the group to the Solo state. 
2010.04.27 22:32:28 : Legion Message: www.xxxxxxxx.com (forum)



ventrillo: 19x.xxx.xxx.xxx

Port: 3712

Pass: xxxx (blabla) 

 4/27/2010 7:47 PM 
2010.04.27 22:32:28 : You have item(s) left to settle in the sales agency window.

Как Вы видите, большинство строк запускается с метки времени, но существуют исключения. То, что я хотел бы сделать в Antlr3, является записью синтаксический анализатор, который использует только строки, запускающиеся с метки времени, тихо отбрасывая другие.

Это - то, что я записал до сих пор (я - новичок с этими вещами, поэтому не смейтесь :D)

grammar Antlr;

options {
  language = Java;
}

logfile: line* EOF;

line : dataline | textline;

dataline: timestamp WS ':' WS text NL ;
textline: ~DIG text NL;

timestamp: four_dig '.' two_dig '.' two_dig WS two_dig ':' two_dig ':' two_dig ;

four_dig: DIG DIG DIG DIG;
two_dig: DIG DIG;

text: ~NL+;

/* Whitespace */ 
WS: (' ' | '\t')+;

/* New line goes to \r\n or EOF */
NL: '\r'? '\n' ;

/* Digits */
DIG : '0'..'9'; 

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

Спасибо!

5
задан Unknown 12 May 2010 в 10:27
поделиться

2 ответа

Никто не будет смеяться. На самом деле, вы проделали довольно хорошую работу для первой попытки. Конечно, есть куда совершенствоваться! :)

Сначала несколько замечаний: вы можете отрицать только одиночные символы. Поскольку ваше правило NL может состоять из двух символов, вы не можете его отрицать. Кроме того, при отрицании внутри правила(й) парсера, вы не отрицаете одиночные символы, а отрицаете правила лексера. Это может показаться немного запутанным, поэтому позвольте мне пояснить на примере. Возьмем комбинированную (парсер и лексер) грамматику T:

grammar T;

// parser rule
foo
  :  ~A
  ;

// lexer rules
A
  :  'a'
  ;

B
  :  'b'
  ;

C
  :  'c'
  ;

Как вы видите, я отрицаю лексер-правило A в парсер-правиле foo. Правило foo теперь не соответствует любому символу, кроме 'a', но оно соответствует любому правилу лексера, кроме A. Другими словами, он будет соответствовать только символу 'b' или 'c'.

Также, вам не нужно ставить:

options {
  language = Java;
}

в вашей грамматике: целью по умолчанию является Java (конечно, не помешает оставить его там).

Теперь, в вашей грамматике, вы уже можете сделать различие между data- и text-строками в грамматике лексера. Вот возможный способ сделать это:

logfile
  :  line+
  ;

line
  :  dataline 
  |  textline
  ;

dataline
  :  DataLine
  ;

textline
  :  TextLine
  ;

DataLine
  :  TwoDigits TwoDigits '.' TwoDigits '.' TwoDigits Space+ TwoDigits ':' TwoDigits ':' TwoDigits Space+ ':' TextLine
  ;

TextLine
  :  ~('\r' | '\n')* (NewLine | EOF)
  ;

fragment
NewLine
  :  '\r'? '\n'
  |  '\r'
  ;

fragment
TwoDigits
  :  '0'..'9' '0'..'9'
  ;

fragment
Space
  :  ' ' 
  |  '\t'
  ;

Обратите внимание, что часть fragment в правилах лексера означает, что из этих правил не создаются лексемы: они используются только в других правилах лексера. Таким образом, лексер будет создавать только два разных типа лексем: DataLine и TextLine.

5
ответ дан 14 December 2019 в 08:45
поделиться

Стараясь сохранить грамматику как можно ближе, вот как я смог заставить ее работать на основе исходного примера. Поскольку пробельные символы передаются в парсер из лексера, я перенес все ваши лексемы из парсера в фактические правила лексера. Основное изменение заключается в добавлении еще одной опции строки и попытке заставить ее соответствовать вашим тестовым данным, а не реальным другим хорошим данным, я также предположил, что пустая строка должна быть отброшена, как вы можете видеть из правила. Вот что у меня получилось:

logfile: line* EOF;

//line : dataline | textline;
line : dataline | textline | discardline;

dataline: timestamp WS COLON WS text NL ;
textline: ~DIG text NL;

//"new"
discardline: (WS)+ discardtext (text|DIG|PERIOD|COLON|SLASH|WS)* NL
    | (WS)* NL;
discardtext: (two_dig| DIG) WS* SLASH;
// two_dig SLASH four_dig;

timestamp: four_dig PERIOD two_dig PERIOD two_dig WS two_dig COLON two_dig COLON two_dig ;

four_dig: DIG DIG DIG DIG;
two_dig: DIG DIG;

//Following is very different
text: CHAR (CHAR|DIG|PERIOD|COLON|SLASH|WS)*;

/* Whitespace */ 
WS: (' ' | '\t')+ ;

/* New line goes to \r\n or EOF */
NL: '\r'? '\n' ;

/* Digits */
DIG : '0'..'9'; 

//new lexer rules
CHAR : 'a'..'z'|'A'..'Z';
PERIOD : '.';
COLON : ':';
SLASH : '/' | '\\';

Надеюсь, это вам поможет, удачи.

2
ответ дан 14 December 2019 в 08:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: