const PREC = {
IMMEDIATE_CHILD: 1,
COMMENT: 1,
STRING: 2,
WILDCARD_NODE: 1,
};
const IDENTIFIER = /[a-zA-Z0-9.\-_\$]+/;
module.exports = grammar({
name: "query",
extras: $ => [
$.comment,
/\s+/,
],
rules: {
program: $ => repeat($._definition),
_definition: $ => choice(
$.named_node,
$.anonymous_node,
$.grouping,
$.predicate,
$.list,
$.field_definition,
),
_group_expression: $ => choice(
$._definition,
immediate_child($._group_expression),
),
_named_node_expression: $ => choice(
$._definition,
$.negated_field,
immediate_child($._named_node_expression),
),
_string: $ => seq(
'"',
repeat(choice(
token.immediate(prec(PREC.STRING, /[^"\\]+/)),
$.escape_sequence,
)),
'"',
),
escape_sequence: $ => token.immediate(seq(
'\\',
choice(
/[^xu0-7]/,
/[0-7]{1,3}/,
/x[0-9a-fA-F]{2}/,
/u[0-9a-fA-F]{4}/,
/u{[0-9a-fA-F]+}/
)
)),
quantifier: $ => choice("*", "+", "?"),
identifier: $ => IDENTIFIER,
_immediate_identifier: $ => alias(token.immediate(IDENTIFIER), $.identifier),
_node_identifier: $ => choice($.identifier, prec(PREC.WILDCARD_NODE, "_")),
capture: $ => seq("@", field("name", $._immediate_identifier)),
string: $ => $._string,
parameters: $ => repeat1(choice($.capture, $.string, $._node_identifier)),
comment: $ => token(prec(PREC.COMMENT, seq(";", /.*/))),
list: $ => seq("[", repeat($._definition), "]", quantifier($), captures($)),
grouping: $ => seq(
"(",
repeat($._group_expression),
")",
quantifier($),
captures($),
),
anonymous_node: $ => seq(
field("name", choice(alias($._string, $.identifier), "_")),
quantifier($),
captures($),
),
named_node: $ => seq(
"(",
field("name", $._node_identifier),
optional(
seq(
optional("."),
choice(
repeat1($._named_node_expression),
seq(
repeat($._named_node_expression),
seq($._named_node_expression, "."),
)
),
),
),
")",
quantifier($),
captures($)
),
_field_name: $ => seq($.identifier, ":"),
field_definition: $ => seq(
field("name", $._field_name),
$._definition,
),
negated_field: $ => seq("!", $.identifier),
predicate: $ =>
seq(
"(",
field("name", seq("#", $._immediate_identifier, field("type", $.predicate_type))),
field("parameters", $.parameters),
")"
),
predicate_type: $ => token.immediate(choice("?", "!")),
}
});
function captures($) {
return repeat($.capture);
}
function quantifier($) {
return optional(field("quantifier", $.quantifier));
}
function immediate_child(expression) {
return prec.left(
PREC.IMMEDIATE_CHILD,
seq(expression, ".", expression),
);
}