Вопрос или проблема
Я хочу сгенерировать вложенное дерево разбора для приведенного ниже образца файла YAML с помощью инструмента ANTLR. Я пробовал следующий грамматический файл, но по какой-то причине он неправильно отображает вложенность узлов в соответствии с файлом YAML.
Файл YAML:
kind: Test
metadata:
name: target
labels:
runnable: target
annotations:
message_value: Hi
id: 1
node_id: 2
hex_id: 3
Грамматика ANTLR, которую я пробовал:
grammar Sample;
yaml: (entry NEWLINE)* EOF;
entry: (keyValue | mapping);
keyValue: SPACE* KEY COLON SPACE* value SPACE* NEWLINE;
mapping: SPACE* KEY COLON SPACE* NEWLINE (nestedEntry)+;
nestedEntry: SPACE* keyValue | mapping;
value: STRING | NUMBER | (NEWLINE SPACE* mapping);
KEY: [a-zA-Z_]+[0-9]*[a-zA-Z_]*;
STRING: [a-zA-Z._]+;
NUMBER: [0-9]+;
NEWLINE: [\r\n]+;
SPACE: [ ] -> skip;
COLON: ':' -> skip;
Ожидаемый вывод дерева разбора выглядит следующим образом:
- Корневой узел ‘yaml’ должен иметь два узла ‘kind’ и ‘metadata’.
- Узел ‘kind’ должен иметь только один листовой узел, то есть ‘Test’.
- Узел ‘metadata’ должен иметь три узла: ‘name’, ‘labels’ и ‘annotations’.
- Узел ‘name’ должен иметь один листовой узел ‘target’.
- Узел ‘labels’ должен иметь узел ‘runnable’, который имеет листовой узел ‘target’.
- Узел ‘annotations’ должен иметь четыре узла: ‘message_value’, ‘id’, ‘node_id’ и ‘hex_id’, которые в свою очередь имеют листовые узлы ‘Hi’, 1, 2, 3 соответственно.
Как я могу добиться правильного дерева разбора?
Есть идеи, в чем может быть проблема в приведенной выше грамматике и как это исправить?
Ваши правила KEY
и STRING
слишком перекрываются, из-за чего STRING
почти никогда не сопоставляется. В ANTLR, когда две (или более) правила совпадают, то правило, определенное первым, "выигрывает". Таким образом, Test
, target
и hi
не будут сопоставляться как STRING
, а как KEY
.
Кроме того, вы пропускаете SPACE
и COLON
в вашем лексере, что делает их недоступными в правилах парсера. COLON
не должен пропускаться в первую очередь.
Попробуйте что-то вроде этого:
yaml : entry (NEWLINE+ entry)* NEWLINE* EOF;
entry : (keyValue | mapping);
keyValue : KEY_OR_VALUE COLON value;
mapping : KEY_OR_VALUE COLON NEWLINE nestedEntry+;
nestedEntry : keyValue | mapping;
value : KEY_OR_VALUE+ | NUMBER | NEWLINE mapping;
KEY_OR_VALUE : [a-zA-Z_]+ [a-zA-Z_0-9.]*;
NUMBER : [0-9]+;
COLON : ':';
NEWLINE : [\r\n]+;
SPACE : [ \t] -> skip;
Это позволит разобрать ваш пример входных данных следующим образом:
Я уверен, вы это знаете, но написание грамматики ANTLR для YAML довольно сложная задача из-за отступов. Вы можете ознакомиться с этим ссылкой.
Ответ или решение
Создание разбора вложенного дерева парсинга для YAML с использованием инструмента ANTLR может быть сложной задачей, особенно из-за особенностей форматирования и отступов YAML. Ваша изначальная грамматика нуждается в некоторых исправлениях, чтобы правильно отразить нужное дерево разбора. Давайте разберем предложенные изменения и поправки, которые помогут вам добиться желаемого результата.
Проблемы вашей первоначальной грамматики
-
Переопределение правил: Правила
KEY
иSTRING
перекрываются, что приводит к тому, что значения, такие какTest
илиtarget
, распознаются какKEY
, а не какSTRING
. В ANTLR, если несколько правил могут соответствовать одному и тому же входу, то правило, определенное первым, «выигрывает». -
Пропуск сочетаний: Вы пропускаете ключевые символы, такие как
SPACE
иCOLON
, что делает их недоступными для правил парсера. Убедитесь, чтоCOLON
не пропускается, так как это критично для правильного разборки YAML.
Обновленная грамматика
Ниже приведен исправленный вариант грамматики, который должен корректно обрабатывать ваш YAML-файл:
grammar Sample;
yaml : entry (NEWLINE+ entry)* NEWLINE* EOF;
entry : (keyValue | mapping);
keyValue : KEY_OR_VALUE COLON value;
mapping : KEY_OR_VALUE COLON NEWLINE nestedEntry+;
nestedEntry : keyValue | mapping;
value : KEY_OR_VALUE | NUMBER | NEWLINE mapping;
KEY_OR_VALUE : [a-zA-Z_]+ [a-zA-Z_0-9.]*;
NUMBER : [0-9]+;
COLON : ':';
NEWLINE : [\r\n]+;
SPACE : [ \t] -> skip;
Описание изменений
-
Правило
KEY_OR_VALUE
: Теперь это универсальное правило, которое охватывает как ключи, так и значения. Оно позволяет избежать проблем с перекрытием. -
Логика обработки разметки: Обратите внимание на порядок правил. Убедитесь, что обрабатываются отступы, так как YAML использует их для определения вложенности.
-
Новый порядок правил: Распределение правил влияет на обработку вводимых данных. Теперь дерево разбора должно правильно отображать иерархические структуры.
Ожидаемое дерево разбора
После применения исправлений, вы должны получить следующее дерево разбора для вашего примера YAML:
yaml
kind
Test
metadata
name
target
labels
runnable
target
annotations
message_value
Hi
id
1
node_id
2
hex_id
3
Заключение
Использование ANTLR для разбора формата YAML может быть непростой задачей из-за специфики форматирования и возможных ошибок в грамматике. Применив вышеуказанные изменения, вы сможете достичь правильного разбора вложенного дерева. Рекомендуется также обратить внимание на существующие реализации парсеров YAML, такие как FastYaml, чтобы изучить более продвинутые и тестируемые подходы к этой задаче.
Если у вас есть дополнительные вопросы или необходима дальнейшая помощь, не стесняйтесь обращаться!