Помогите с разбором лог файла (ANTLR3) - PullRequest
5 голосов
/ 12 мая 2010

Мне нужно небольшое руководство по написанию грамматики для разбора лог-файла игры 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'; 

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

Спасибо!

Ответы [ 2 ]

5 голосов
/ 12 мая 2010

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

Сначала несколько замечаний: вы можете отрицать только отдельные символы. Поскольку ваше правило 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.

2 голосов
/ 12 мая 2010

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

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 : '/' | '\\';

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

...