tree-sitter-d2/grammar.js

464 lines
11 KiB
JavaScript
Raw Permalink Normal View History

const PREC = {
COMMENT: -2,
EOL: -1,
2022-12-09 16:08:41 +03:00
TEXT_BLOCK_CONTENT: -1,
2022-12-09 18:49:33 +03:00
UNQUOTED_STRING: -1,
CONTAINER: 2,
CONNECTION: 2,
SHAPE: 3,
2023-06-16 00:43:30 +03:00
CLASSES: 4,
IDENTIFIER: 0,
ARROW: 0,
ATTRIBUTE: 0,
ATTRIBUTE_KEY: 0,
};
// mkWrapCont :: string -> string -> ($ -> Rule) -> $ -> Rule
const mkWrapCont = (start, end) => (onDefinition) => ($) =>
seq(
start,
repeat(choice($._eol, seq(onDefinition($), $._end))),
optional(seq(onDefinition($), optional($._end))),
end
);
const mkBlock = mkWrapCont("{", "}");
const mkList = mkWrapCont("[", "]");
// mkAlias :: ($ -> Rule) -> ($ -> Rule) -> $ -> Rule
const mkAlias = (onAlias) => (onValue) => ($) => alias(onValue($), onAlias($));
const attrKeyAlias = mkAlias(($) => $.attr_key);
const attrAlias = mkAlias(($) => $.attribute);
// mkAttrCont :: ($ -> Rule) -> ($ -> Rule) -> $ -> Rule
const mkAttrCont = (onValue) => (onKey) => ($) =>
seq(onKey($), $._colon, onValue($));
const mkAttr = (onKey) => mkAttrCont(($) => $.attr_value)(attrKeyAlias(onKey));
const mkListAttr = (onKey) =>
mkAttrCont(
either(
($) => $.attr_value_list,
($) => $.attr_value
)
)(attrKeyAlias(onKey));
const either = (onLeft, onRight) => ($) => choice(onLeft($), onRight($));
2022-12-04 00:07:26 +03:00
module.exports = grammar({
name: "d2",
externals: ($) => [
$._text_block_start,
$._text_block_end,
$._text_block_raw_text,
2022-12-13 11:26:52 +03:00
$.block_comment,
],
2022-12-09 18:21:55 +03:00
extras: ($) => [
/[ \f\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/,
$.line_comment,
2022-12-13 11:26:52 +03:00
$.block_comment,
],
2022-12-05 11:36:14 +03:00
conflicts: ($) => [
[$._connection_path, $.container],
[$._container_block],
[$._connection_block],
[$._style_attribute_block],
2023-06-16 00:43:30 +03:00
[$._classes_block],
[$._classes_item_block],
[$.class_list],
[$.attr_value_list],
[$._text_block_attrs],
[$._identifier],
],
2022-12-04 00:07:26 +03:00
rules: {
2022-12-07 23:14:25 +03:00
source_file: ($) => repeat($._root_definition),
2022-12-04 00:07:26 +03:00
2022-12-07 23:14:25 +03:00
_root_definition: ($) =>
choice(
$._eol,
seq(
choice(
alias($._root_attribute, $.attribute),
2023-06-16 00:43:30 +03:00
$.classes,
$.shape,
$.container,
$.connection
),
2022-12-09 12:54:11 +03:00
choice($._end, "\0")
)
2022-12-07 23:14:25 +03:00
),
2022-12-04 00:07:26 +03:00
// connections
2022-12-04 00:07:26 +03:00
connection: ($) =>
2022-12-04 03:25:38 +03:00
seq(
choice(
$._full_connection_path,
alias($._referencing_full_connection_path, $.referencing)
),
2023-06-21 23:52:40 +03:00
optional(seq($._colon, optional($.label))),
optional(seq(alias($._connection_block, $.block)))
2022-12-04 03:25:38 +03:00
),
2022-12-04 00:07:26 +03:00
_referencing_full_connection_path: ($) =>
seq("(", $._full_connection_path, $._referencing_end, optional($.index)),
_referencing_end: ($) => token(prec(PREC.IDENTIFIER + 1, ")")),
index: ($) => seq("[", $.integer, "]"),
_full_connection_path: ($) =>
seq($._connection_path, repeat1(seq($.arrow, $._connection_path))),
_connection_path: ($) =>
2022-12-04 03:36:46 +03:00
seq(
repeat(
prec(PREC.CONNECTION, seq(alias($.shape_key, $.container_key), $.dot))
),
$.shape_key
),
_connection_block: mkBlock(($) => $._connection_attribute),
2023-06-16 00:43:30 +03:00
// classes
classes: ($) =>
prec(
PREC.CLASSES,
seq(
$.keyword_classes,
choice(
optional(seq($.dot, $._classes_item)),
seq(
optional(seq($._colon, optional($.label))),
optional(alias($._classes_block, $.block))
)
)
)
),
_classes_block: mkBlock(($) => $._classes_item),
2023-06-16 00:43:30 +03:00
_classes_item: ($) =>
seq(
$._class_name,
choice(
optional(seq($.dot, $._shape_attribute)),
seq(
optional(seq($._colon, optional($.label))),
optional(alias($._classes_item_block, $.class_block))
)
)
),
_classes_item_block: mkBlock(($) => $._classes_item_attribute),
2023-06-16 00:43:30 +03:00
_classes_item_attribute: attrAlias(($) =>
choice($._base_shape_attribute, $._style_attribute)
),
2023-06-16 00:43:30 +03:00
// containers
container: ($) =>
prec(
PREC.CONTAINER,
seq(
alias($.shape_key, $.container_key),
2022-12-06 12:32:50 +03:00
choice(
seq($.dot, choice($.shape, $.container)),
2022-12-06 12:32:50 +03:00
seq(
optional(seq($._colon, optional($.label))),
2023-06-16 00:43:30 +03:00
optional(alias($._container_block, $.block))
2022-12-06 12:32:50 +03:00
)
)
)
2022-12-04 03:36:46 +03:00
),
_container_block: mkBlock(($) => $._container_block_definition),
2022-12-08 01:15:09 +03:00
_container_block_definition: ($) =>
choice($.shape, $.container, $.connection, $._shape_attribute),
2022-12-07 23:14:25 +03:00
// shapes
2022-12-08 01:37:35 +03:00
shape: ($) =>
prec(
PREC.SHAPE,
seq(
$.shape_key,
optional(
choice(
2023-06-16 00:43:30 +03:00
seq($.dot, $._shape_attribute),
seq(
$._colon,
choice(
$.label,
seq(
$.text_block,
optional(alias($._text_block_attrs, $.block))
)
)
)
)
)
)
),
shape_key: ($) => choice($.keyword_underscore, $.string, $._identifier),
2022-12-05 10:09:14 +03:00
_identifier: ($) =>
seq(
choice(token(prec(PREC.IDENTIFIER, /[\w\d$-]/)), $.escape_sequence),
repeat(
choice(
$.escape_sequence,
token(prec(PREC.IDENTIFIER, /[\w\d'"$(,]+/)),
token(prec(PREC.IDENTIFIER, /( +|-)[\w\d'"$(]+/)),
token(prec(PREC.IDENTIFIER, ")"))
)
),
optional(
choice(
token(prec(PREC.IDENTIFIER, /[\w\d'"$(]+/)),
token(prec(PREC.IDENTIFIER + 1, ")"))
)
),
optional($._dash)
2022-12-09 15:02:21 +03:00
),
2022-12-09 16:08:41 +03:00
text_block: ($) =>
2022-12-10 23:09:11 +03:00
seq(
alias($._text_block_start, "|"),
optional($.language),
/\s/,
alias($._text_block_raw_text, $.raw_text),
alias($._text_block_end, "|")
2022-12-09 18:21:55 +03:00
),
2022-12-09 16:08:41 +03:00
_text_block_attrs: mkBlock(attrAlias(($) => $._text_shape_attribute)),
2022-12-09 16:08:41 +03:00
language: ($) => /\w+/,
// attributes
2022-12-04 23:45:53 +03:00
_root_attribute: mkAttr(($) => $._root_attr_key),
_root_attr_key: ($) =>
2022-12-07 18:52:19 +03:00
choice(
"direction",
$._grid_attr_key,
// reserved but doesn't affected for root
alias(
choice(
"shape",
"label",
"constraint",
"icon",
2023-06-16 00:43:30 +03:00
$.keyword_style,
$._common_style_attr_key,
$._text_attr_key
),
$.reserved
)
2022-12-07 18:52:19 +03:00
),
2022-12-04 23:45:53 +03:00
_shape_attribute: attrAlias(($) =>
choice($._class_attribute, $._base_shape_attribute, $._style_attribute)
),
2022-12-06 12:32:50 +03:00
_class_attribute: mkAttrCont(($) => choice($.class_list, $._class_name))(
($) => $.keyword_class
),
2023-06-16 00:43:30 +03:00
class_list: mkList(($) => $._class_name),
2023-06-16 00:43:30 +03:00
_class_name: ($) => alias($.shape_key, $.class_name),
_base_shape_attribute: either(
mkListAttr(($) => $._shape_list_attr_key),
mkAttr(($) => $._shape_attr_key)
),
2022-12-06 12:32:50 +03:00
_shape_attr_key: ($) =>
prec(
PREC.ATTRIBUTE_KEY,
choice(
2023-02-16 23:28:34 +03:00
"direction",
"shape",
"label",
"link",
"tooltip",
"icon",
"width",
"height",
$._text_attr_key,
$._grid_attr_key
)
2022-12-06 11:57:59 +03:00
),
_shape_list_attr_key: ($) => prec(PREC.ATTRIBUTE_KEY, "constraint"),
2022-12-04 23:45:53 +03:00
_style_attribute: ($) =>
prec(
PREC.ATTRIBUTE,
seq(
2023-06-16 00:43:30 +03:00
$.keyword_style,
choice(
seq($.dot, alias($._inner_style_attribute, $.attribute)),
seq($._colon, alias($._style_attribute_block, $.block))
)
2022-12-06 11:57:59 +03:00
)
2022-12-04 23:45:53 +03:00
),
_style_attribute_block: mkBlock(attrAlias(($) => $._inner_style_attribute)),
2022-12-04 23:45:53 +03:00
_inner_style_attribute: mkAttr(($) => $._style_attr_key),
2022-12-04 23:45:53 +03:00
_grid_attr_key: ($) =>
choice(
"vertical-gap",
"horizontal-gap",
"grid-gap",
"grid-columns",
"grid-rows"
),
2023-06-16 00:43:30 +03:00
_style_attr_key: ($) => choice($._common_style_attr_key, "3d"),
_common_style_attr_key: ($) =>
2022-12-04 23:45:53 +03:00
choice(
"opacity",
"fill",
"fill-pattern",
2022-12-04 23:45:53 +03:00
"stroke",
"stroke-width",
"stroke-dash",
"border-radius",
2023-02-16 23:28:34 +03:00
"double-border",
2023-06-16 05:20:03 +03:00
"font",
2023-02-16 23:28:34 +03:00
"font-size",
2022-12-04 23:45:53 +03:00
"font-color",
"shadow",
"multiple",
"animated",
2023-02-16 23:28:34 +03:00
"link",
"italic",
"bold",
"underline",
"text-transform"
2022-12-04 23:45:53 +03:00
),
_text_shape_attribute: mkAttr(($) => $._text_attr_key),
2022-12-04 23:45:53 +03:00
_text_attr_key: ($) => "near",
_connection_attribute: attrAlias(($) =>
choice($._connection_arrowhead_attribute, $._style_attribute)
),
_connection_arrowhead_attribute: ($) =>
seq(
alias($._connection_arrowhead_attr_key, $.attr_key),
choice(
seq($.dot, alias($._style_attribute, $.attribute)),
seq(
optional(seq($._colon, optional($.label))),
optional(seq(alias($._container_block, $.block)))
)
)
),
_connection_arrowhead_block: mkBlock(($) => $._shape_attribute),
_connection_arrowhead_attr_key: ($) =>
choice("source-arrowhead", "target-arrowhead"),
2022-12-04 18:35:50 +03:00
keyword_underscore: (_) => "_",
2023-06-16 00:43:30 +03:00
keyword_classes: (_) => "classes",
keyword_class: (_) => "class",
keyword_style: (_) => "style",
//
label: ($) => choice($.string, $._unquoted_string),
attr_value_list: mkList(($) => $.attr_value),
2022-12-09 18:49:33 +03:00
attr_value: ($) =>
2023-06-21 23:52:40 +03:00
choice($.boolean, $.integer, $.float, $.string, $._unquoted_string),
2022-12-05 00:26:24 +03:00
// --------------------------------------------
2022-12-04 18:35:37 +03:00
_dash: ($) => token.immediate("-"),
2023-06-21 23:52:40 +03:00
_colon: ($) => token(":"),
2022-12-04 00:07:26 +03:00
arrow: ($) => token(prec(PREC.ARROW, choice(/-+>/, /--+/, /<-+/, /<-+>/))),
dot: ($) => token("."),
_unquoted_string: ($) =>
repeat1(
choice(
$.escape_sequence,
token(
prec(
PREC.UNQUOTED_STRING,
/[^'"`\\|\n\s;{}\[\]][^\\\n;{}\[\]]*[^\\\n\s;{}\[\]]?/
)
)
)
),
2022-12-04 03:36:46 +03:00
2022-12-04 00:07:26 +03:00
string: ($) =>
choice(
seq(
"'",
alias($._unescaped_single_string_fragment, $.string_fragment),
"'"
),
seq(
'"',
repeat(
choice(
alias($._unescaped_double_string_fragment, $.string_fragment),
$.escape_sequence
)
),
'"'
)
),
_unescaped_single_string_fragment: ($) => token.immediate(/[^'\n]+/),
_unescaped_double_string_fragment: ($) => token.immediate(/[^"\\\n]+/),
escape_sequence: ($) =>
token(
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]+}/
)
)
2022-12-04 00:07:26 +03:00
),
2022-12-04 23:45:53 +03:00
2022-12-09 18:49:33 +03:00
boolean: ($) => choice("true", "false"),
integer: ($) => /[0-9]+/,
float: ($) => /[0-9]+\.[0-9]+/,
line_comment: ($) => token(prec(PREC.COMMENT, seq("#", /.*/))),
2022-12-04 23:45:53 +03:00
_eol: ($) => token(prec(PREC.EOL, "\n")),
2023-06-21 18:40:57 +03:00
_end: ($) => choice(";", $._eol),
2022-12-04 00:07:26 +03:00
},
});