#grammar #treesitter
Вопрос:
Я пытаюсь создать сиделку на дереве для грамматики функций Minecraft.
Структура языка выглядит следующим образом:
command @e[key=value] other args
У меня возникла проблема со значением во втором аргументе (целевой селектор) в приведенном выше примере. Это значение может быть многими вещами, такими как строки, числа, логические значения и две аналогичные структуры объектов (NBT и объект табло).
Вот примеры каждого:
НБТ
{key:value}
Объект Табло
{key=number} // where number is: N, ..N, N.., or N..N
Мой файл грамматики содержит следующий код:
// unrelated code removed
module.exports = grammar({
name: "mcfunction",
rules: {
root: $ => repeat(
choice(
$.command
)
),
command: $ => prec.right(seq(
field("command_name", $.identifier),
repeat(
choice(
$.selector
)
),
"n"
)),
identifier: $ => /[A-Za-z][w-] /,
number: $ => prec(1, /-?d (.d )?/),
boolean: $ => choice(
"true",
"false"
),
string: $ => seq(
""",
repeat(
choice(
$._escape_sequence,
/[^"]/
)
),
"""
),
_escape_sequence: $ => seq("\", """),
selector: $ => seq(
token(
seq(
"@",
choice(
"p", "a", "e", "s", "r"
)
)
),
optional(
seq(
token.immediate("["),
optional(
repeat(
seq(
$.selector_option,
optional(",")
)
)
),
"]"
)
),
),
selector_option: $ => seq(
$.selector_key,
"=",
$.selector_value
),
selector_key: $ => /[a-z_-] /,
selector_value: $ => choice(
$.item,
$.path,
$.selector_key,
$.selector_number,
$.number,
$.boolean,
$.selector_object
),
selector_number: $ => prec.right(1, choice(
seq(
"..",
$.number
),
seq(
$.number,
"..",
$.number
),
seq(
$.number,
".."
),
$.number
)),
selector_object: $ => choice(
seq(
"{",
repeat(
seq(
$.selector_score,
optional(",")
)
),
"}"
),
seq(
"{",
repeat(
seq(
$.selector_nbt,
optional(",")
)
),
"}"
)
),
selector_nbt: $ => seq(
$.nbt_object_key,
":",
$.nbt_object_value
),
selector_score: $ => seq(
field("selector_score_key", $.selector_key),
"=",
field("selector_score_value", $.selector_number)
),
_namespace: $ => /[a-z_-] :/,
item: $ => seq(
$._namespace,
$.selector_key
),
path: $ => seq(
choice($.item, /[a-z_] /),
repeat1(
token("/", /[a-z_]/)
)
),
nbt: $ => choice(
$.nbt_array,
$.nbt_object
),
nbt_object: $ => seq(
"{",
repeat(
seq(
$.nbt_object_key,
":",
$.nbt_object_value,
optional(",")
)
),
"}"
),
nbt_array: $ => seq(
"[",
repeat(
seq(
$.nbt_object_value,
optional(",")
)
),
"]"
),
nbt_object_key: $ => choice(
$.string,
$.number,
$.identifier
),
nbt_object_value: $ => choice(
$.string,
$.nbt_number,
$.boolean,
$.nbt
),
nbt_number: $ => seq(
$.number,
field("nbt_number_suffix", optional(choice("l","s","d","f","b")))
)
}
});
Однако, если я скомпилирую и проанализирую test @e[scores={example=1..}]
, я получу:
(root [0, 0] - [6, 0]
(command [0, 0] - [1, 0]
command_name: (identifier [0, 0] - [0, 4])
(selector [0, 5] - [0, 29]
(selector_option [0, 8] - [0, 28]
(selector_key [0, 8] - [0, 14])
(selector_value [0, 15] - [0, 28]
(selector_object [0, 15] - [0, 28]
(ERROR [0, 16] - [0, 27]
(nbt_object_key [0, 16] - [0, 23]
(identifier [0, 16] - [0, 23]))))))))
tests/test.mcfunction 0 ms (ERROR [0, 16] - [0, 27])
Ожидалось: вместо ERROR
этого должно быть selector_score
, и должно быть score_key
и score_value
.
Этого не произойдет, если я удалю selector_nbt
последовательность из selector_object
. Однако при выполнении синтаксического анализа (с обеими последовательностями или только selector_nbt
) по команде с использованием данных nbt ошибок нет.
Что я делаю не так?
Ответ №1:
Я решил эту проблему, используя один choice
из двух конфликтующих ключей, что-то вроде этого:
choice(
alias($.key_1, $.key_2),
$.key_2
)
ahlinc на GitHub ответил:
Вы можете исправить свою ошибку в приведенной выше грамматике, присвоив терминалу приоритет лексера
selector_key
надidentifier
терминалом, например:selector_key: $ => token(prec(1, /[a-z_-] /)),
Но вы должны отметить, что вы используете регулярные выражения, которые конфликтуют:
identifier: $ => /[A-Za-z][w-] /, selector_key: $ => token(prec(1, /[a-z_-] /)),
Если невозможно переписать приведенные выше регулярные выражения, чтобы в них не было конфликтов, вам может потребоваться обходной путь, описанный здесь: #1287 (ответ в потоке)