Никто не собирается смеяться. На самом деле, вы проделали довольно хорошую работу с первой попытки. Конечно, есть место для улучшения! :)
Сначала несколько замечаний: вы можете отрицать только отдельные символы. Поскольку ваше правило 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
.