1) Использование ANTLR 4.6 и данная грамматика и вход, у меня есть следующее сообщение:
line 3:0 no viable alternative at input 'ACOMAND 1.0 1.0\nACOMAND\nACOMAND '
При отладке грамматики, очень полезно перечислить маркеры, замеченные лексером:
$ echo $CLASSPATH
.:/usr/local/lib/antlr-4.6-complete.jar
$ alias grun
alias grun='java org.antlr.v4.gui.TestRig'
$ grun Question question -tokens data.txt
[@0,0:9='ACOMAND ',<KEYWORD>,1:0]
[@1,10:19=' 1.0',<COLUMN>,1:10]
[@2,20:29=' 1.0',<COLUMN>,1:20]
[@3,30:30='\n',<COLUMN>,1:30]
[@4,31:38='ACOMAND\n',<COLUMN>,2:0]
до 4,6, маркеры отображались [@3,30:30='\n',<n>,1:30]
и вы должны были посмотреть в файле -grammar-.tokens
который лексема имеет номер n
. Теперь он прекрасно переводится, и вы сразу видите, что символ новой строки был признан как токен COLUMN
, а не NEWLINE
, как и ожидалось. Это происходит потому, что лексические пытается сопоставить вход с каждым правилом в последовательности:
- делает
'\n'
матч [A-Z]
? Нет, так что это не KEYWORD
, следующее правило
'\n'
соответствует .+?
? Да, так что это COLUMN
, никаких шансов достичь правила NEWLINE
.
Таким образом, вы должны поместить COLUMN
правило послеNEWLINE
правил.
Вы также видите, что вторая строка ввода была лексемы, как [@4,31:38='ACOMAND\n',<COLUMN>,2:0]
, потому что он не может соответствовать
KEYWORD: [A-Z] ... WS*?
, поскольку правило требует белого пространства и есть только NL.Таким образом, замените WS*?
на (WS* | NEWLINE)
.
Наконец упростить избыточные правила:
grammar Question;
question
: KEYWORD COLUMN* NEWLINE
;
KEYWORD : [A-Z] {getCharPositionInLine() == 1}? ([A-Z]|'-')* (WS* | NEWLINE) {getCharPositionInLine() <= 10}? ;
NEWLINE : '\r'? '\n' ;
WS : [ \t] ;
COLUMN: .+? {(getCharPositionInLine() % 10) == 0}? ;
Теперь лексер обеспечивает:
[@0,0:9='ACOMAND ',<KEYWORD>,1:0]
[@1,10:19=' 1.0',<COLUMN>,1:10]
[@2,20:29=' 1.0',<COLUMN>,1:20]
[@3,30:30='\n',<NEWLINE>,1:30]
[@4,31:38='ACOMAND\n',<KEYWORD>,2:0]
.
.
2)
Но все это действительно полезно? Является ли генератор парсера правильным инструментом? Удалить один пробел и посмотреть, что происходит:
line 2:0 extraneous input 'ACOMAND\n' expecting {NEWLINE, COLUMN}
Я думаю, что вы должны оставить лексер сделать простую работу без этих ограничений позиции: создать маркер для не пустых данных и устранить пробелы. Позже в синтаксическом анализаторе или слушателе вы можете проверить позицию: каждый токен имеет такие свойства, как запуск, остановка, строка и т. Д.
Почему не скрипт Ruby? :-)
# Split 80 columns lines into 10 columns wide tokens, associate each token
# with its stop position in line (counting from 1) and an OK/WRONG flag
# if it is not aligned correctly.
tokens = Array.new
IO.readlines("data.txt").each_with_index do | line, i |
if i == 0
then
puts " #{line}"
next
end
line_tokens = Array.new
line = line.chomp # remove NL
print "line #{i + 1} : "
8.times.each do | n | # n = 0 to 7
a = n * 10 # begin of split range counting from 0
b = n * 10 + 9 # end of range
token = line.slice(a..b)
next if token.nil? || token.length == 0 # nil if edge case
print token
good_position = 'OK'
position = b + 1
case n
when 0 # first token must be at column 1
good_position = 'WRONG' if token[0] == ' '
else # other tokens must be right aligned in their 10 columns width field
if token[-1] == ' ' && token != ' ' # not followed by NL
then
good_position = 'WRONG'
unless (pos = token.rindex(' ')).nil?
position = position - 10 + pos - 1
end
end
if token.length != 10 # last in line
then
good_position = 'WRONG'
position = position - 10 + token.length
end
end
line_tokens << [token.strip, position, good_position]
break if b > line.length
end
puts # print a NL because print doesn't do it
tokens << line_tokens
end
puts
puts "Lists of tokens : "
p tokens
Входной data.txt:
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8
ACOMAND 1.0 1.0
ACOMAND
ACOMAND 1.0
ACOMAND 1.0 1.0 1300.2 .9 1.0
ACOMAND 1.0 1.0 1300.2 .9
ACOMAND OKK 1.0 1300.2 .9 1.0 WOW
ACOMAND 1.0 1.0 1300.2
Выход:
$ ruby -w split.rb
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8
line 2 : ACOMAND 1.0 1.0
line 3 : ACOMAND
line 4 : ACOMAND 1.0
line 5 : ACOMAND 1.0 1.0 1300.2 .9 1.0
line 6 : ACOMAND 1.0 1.0 1300.2 .9
line 7 : ACOMAND OKK 1.0 1300.2 .9 1.0 WOW
line 8 : ACOMAND 1.0 1.0 1300.2
Lists of tokens :
[[["ACOMAND", 10, "OK"], ["1.0", 20, "OK"], ["1.0", 29, "WRONG"]],
[["ACOMAND", 10, "OK"]], [["ACOMAND", 10, "OK"], ["1.0", 20, "OK"]],
[["ACOMAND", 10, "OK"], ["1.0", 20, "OK"], ["1.0", 30, "OK"], ["1300.2",
40, "OK"], ["", 50, "OK"], [".9", 58, "WRONG"], ["1.0", 68, "WRONG"]],
[["ACOMAND", 10, "OK"], ["1.0", 20, "OK"], ["1.0", 30, "OK"], ["1300.2",
40, "OK"], ["", 50, "OK"], [".9", 60, "OK"]], [["ACOMAND", 10, "OK"],
["OKK", 20, "OK"], ["1.0", 30, "OK"], ["1300.2", 40, "OK"], ["", 50,
"OK"], [".9", 60, "OK"], ["1.0", 70, "OK"], ["WOW", 80, "OK"]],
[["ACOMAND", 10, "OK"], ["1.0", 20, "OK"], ["1.0", 30, "OK"], ["1300.2",
40, "OK"]]]
1) Имеет ли значение, чтобы проверить, что ACOMAND начинается в колонке 1 и другие значения выровненный по фиксированное положение, иначе почему бы не просто «ID VALUE *?» 2) Пожалуйста, дайте всю необходимую грамматику, чтобы мы могли ее выполнить. У меня отсутствует WS и «неявное определение токена» – BernardK
1) Да, это имеет значение, к сожалению, поэтому я думаю, что я должен использовать предикаты для обеспечения правильного выравнивания. 2) Я добавил отсутствующую грамматику WS, извиняясь за ее отказ. – rooms