mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-03 05:12:55 +00:00
776 lines
19 KiB
JavaScript
776 lines
19 KiB
JavaScript
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// cSpell: ignore mult prec
|
|
|
|
module.exports = grammar({
|
|
name: "slint",
|
|
|
|
extras: ($) => [/[\s\r\n]+/, $.comment],
|
|
conflicts: ($) => [[$._assignment_value_block], [$.assignment_block]],
|
|
|
|
rules: {
|
|
sourcefile: ($) => repeat($._definition),
|
|
|
|
_definition: ($) =>
|
|
choice($.import_statement, $._exported_type, $._local_type),
|
|
|
|
export: (_) => "export",
|
|
|
|
_local_type: ($) =>
|
|
choice(
|
|
$.struct_definition,
|
|
$.enum_definition,
|
|
$.global_definition,
|
|
$.component_definition,
|
|
),
|
|
|
|
_exported_type: ($) =>
|
|
prec.left(
|
|
seq(
|
|
$.export,
|
|
choice(
|
|
optional(seq("{", commaSep($.export_type), "}")),
|
|
$._local_type,
|
|
),
|
|
),
|
|
),
|
|
|
|
import_statement: ($) =>
|
|
seq(
|
|
"import",
|
|
optional(seq("{", commaSep($.import_type), "}", "from")),
|
|
$.string_value,
|
|
";",
|
|
),
|
|
|
|
export_type: ($) =>
|
|
seq(
|
|
field("local_name", $._type_identifier),
|
|
optional(seq("as", field("export_name", $._type_identifier))),
|
|
),
|
|
|
|
import_type: ($) =>
|
|
seq(
|
|
field("import_name", $.user_type_identifier),
|
|
optional(seq("as", field("local_name", $.user_type_identifier))),
|
|
),
|
|
|
|
component: ($) =>
|
|
seq(
|
|
optional(seq(field("id", $.simple_identifier), ":=")),
|
|
field("type", $.user_type_identifier),
|
|
$.block,
|
|
),
|
|
|
|
component_definition: ($) =>
|
|
seq(
|
|
choice(
|
|
seq(
|
|
// new syntax
|
|
"component",
|
|
field("name", $.user_type_identifier),
|
|
optional(
|
|
seq("inherits", field("base_type", $.user_type_identifier)),
|
|
),
|
|
),
|
|
seq(
|
|
// old syntax
|
|
field("name", $.user_type_identifier),
|
|
":=",
|
|
field("base_type", $.user_type_identifier),
|
|
),
|
|
),
|
|
$.block,
|
|
),
|
|
|
|
_property_type: ($) => seq("<", field("type", $.type), ">"),
|
|
|
|
imperative_block: ($) =>
|
|
seq("{", repeat($._imperative_block_statement), "}"),
|
|
|
|
_imperative_block_statement: ($) =>
|
|
prec(
|
|
1,
|
|
choice(
|
|
seq($.assignment_block, optional(";")),
|
|
seq($.assignment_expr, optional(";")),
|
|
seq($.if_expr, optional(";")),
|
|
$.callback_event,
|
|
$.binding,
|
|
seq($.expression, optional(";")),
|
|
seq("return", optional($.expression), ";"),
|
|
),
|
|
),
|
|
|
|
_binding: ($) =>
|
|
field(
|
|
"binding",
|
|
choice(seq($.imperative_block, optional(";")), seq($.expression, ";")),
|
|
),
|
|
|
|
property: ($) =>
|
|
seq(
|
|
field("visibility", optional($.property_visibility)),
|
|
"property",
|
|
seq(
|
|
$._property_type,
|
|
field("name", $.simple_identifier),
|
|
choice(
|
|
seq(field("binding_op", ":"), $._binding),
|
|
seq(field("binding_op", "<=>"), $.expression, ";"),
|
|
";",
|
|
),
|
|
),
|
|
),
|
|
|
|
binding_alias: ($) =>
|
|
seq(
|
|
field("visibility", optional($.property_visibility)),
|
|
optional("property"),
|
|
field("name", $.simple_identifier),
|
|
"<=>",
|
|
field("alias", $.expression),
|
|
";",
|
|
),
|
|
|
|
binding: ($) => seq(field("name", $.simple_identifier), ":", $._binding),
|
|
|
|
global_block: ($) =>
|
|
seq(
|
|
"{",
|
|
repeat(
|
|
choice(
|
|
$.property,
|
|
$.binding_alias,
|
|
$.callback,
|
|
$.callback_event,
|
|
$.function_definition,
|
|
),
|
|
),
|
|
"}",
|
|
),
|
|
|
|
global_definition: ($) =>
|
|
seq(
|
|
"global",
|
|
field("name", $.user_type_identifier),
|
|
optional(":="), // old syntax!
|
|
$.global_block,
|
|
),
|
|
|
|
struct_block: ($) =>
|
|
seq(
|
|
"{",
|
|
commaSep(
|
|
seq(field("name", $.simple_identifier), ":", field("type", $.type)),
|
|
),
|
|
optional(","),
|
|
"}",
|
|
),
|
|
|
|
struct_definition: ($) =>
|
|
seq(
|
|
"struct",
|
|
field("name", $.user_type_identifier),
|
|
optional(":="), // old syntax!
|
|
$.struct_block,
|
|
),
|
|
|
|
enum_block: ($) =>
|
|
seq(
|
|
"{",
|
|
commaSep(field("name", $.user_type_identifier)),
|
|
optional(","),
|
|
"}",
|
|
),
|
|
|
|
enum_definition: ($) =>
|
|
seq("enum", field("name", $.user_type_identifier), $.enum_block),
|
|
|
|
anon_struct_block: ($) =>
|
|
prec(
|
|
100,
|
|
seq(
|
|
"{",
|
|
commaSep(seq($.simple_identifier, ":", $.expression)),
|
|
optional(","),
|
|
"}",
|
|
),
|
|
),
|
|
|
|
block: ($) => seq("{", repeat($._block_statement), "}"),
|
|
|
|
_block_statement: ($) =>
|
|
choice(
|
|
$.animate_statement,
|
|
$.binding_alias,
|
|
$.callback,
|
|
$.callback_alias,
|
|
$.callback_event,
|
|
$.children_identifier, // No `;` after this one!
|
|
$.changed_callback,
|
|
$.component,
|
|
$.for_loop,
|
|
$.function_definition,
|
|
$.if_statement,
|
|
$.property,
|
|
$.property_assignment,
|
|
$.states_definition,
|
|
$.transitions_definition,
|
|
),
|
|
|
|
property_assignment: ($) =>
|
|
seq(
|
|
field("property", $.simple_identifier),
|
|
":",
|
|
field(
|
|
"value",
|
|
choice(
|
|
seq($.imperative_block, optional(";")),
|
|
seq($.expression, ";"),
|
|
),
|
|
),
|
|
),
|
|
|
|
in_out_transition: ($) =>
|
|
seq(
|
|
choice("in", "out"),
|
|
optional(seq($.expression, ":")),
|
|
"{",
|
|
repeat($.animate_statement),
|
|
"}",
|
|
),
|
|
|
|
transitions_definition: ($) =>
|
|
seq("transitions", "[", repeat($.in_out_transition), "]"),
|
|
|
|
states_definition: ($) =>
|
|
seq(
|
|
"states",
|
|
"[",
|
|
repeat(
|
|
seq(
|
|
field("name", $.simple_identifier),
|
|
"when",
|
|
$.expression,
|
|
":",
|
|
"{",
|
|
repeat(
|
|
choice(
|
|
$.in_out_transition,
|
|
$.assignment_block,
|
|
seq($.assignment_expr, ";"),
|
|
),
|
|
),
|
|
optional($.assignment_expr),
|
|
"}",
|
|
),
|
|
),
|
|
"]",
|
|
),
|
|
|
|
animate_statement: ($) => seq("animate", $.expression, $.animate_body),
|
|
|
|
animate_option_identifier: (_) =>
|
|
choice("delay", "duration", "iteration-count", "direction", "easing"),
|
|
|
|
animate_option: ($) =>
|
|
seq(
|
|
field("option", $.animate_option_identifier),
|
|
":",
|
|
field("expression", $.expression),
|
|
";",
|
|
),
|
|
|
|
animate_body: ($) => seq("{", repeat($.animate_option), "}"),
|
|
|
|
if_expr: ($) =>
|
|
seq(
|
|
"if",
|
|
field("condition", $.expression),
|
|
$.imperative_block,
|
|
optional(seq("else", choice($.if_expr, $.imperative_block))),
|
|
),
|
|
|
|
if_statement: ($) =>
|
|
seq("if", field("condition", $.expression), ":", $.component),
|
|
|
|
for_loop: ($) =>
|
|
seq(
|
|
"for",
|
|
field("identifier", $.simple_indexed_identifier),
|
|
"in",
|
|
field("range", $.for_range),
|
|
":",
|
|
$.component,
|
|
),
|
|
|
|
for_range: ($) => choice($.value_list, $.expression),
|
|
|
|
type_list: ($) => seq("[", commaSep($.type), optional(","), "]"),
|
|
|
|
type: ($) => choice($._type_identifier, $.type_list, $.struct_block),
|
|
|
|
_assignment_setup: ($) =>
|
|
seq(field("name", $.expression), field("op", $.assignment_prec_operator)),
|
|
|
|
_assignment_value_block: ($) => field("value", seq($.block, optional(";"))),
|
|
|
|
assignment_block: ($) =>
|
|
seq($._assignment_setup, $._assignment_value_block, optional(";")),
|
|
|
|
assignment_expr: ($) =>
|
|
prec.right(2, seq($._assignment_setup, field("value", $.expression))),
|
|
|
|
expression: ($) =>
|
|
prec.right(
|
|
choice(
|
|
$.parens_op,
|
|
$.index_op,
|
|
$.tr,
|
|
$.value,
|
|
$.gradient_call,
|
|
$.image_call,
|
|
$.reference_identifier,
|
|
$.simple_identifier,
|
|
$.function_call,
|
|
$.member_access,
|
|
$.unary_expression,
|
|
$.binary_expression,
|
|
$.ternary_expression,
|
|
),
|
|
),
|
|
|
|
parens_op: ($) => seq("(", field("left", $.expression), ")"),
|
|
|
|
index_op: ($) =>
|
|
prec(
|
|
18,
|
|
seq(
|
|
field("left", $.expression),
|
|
"[",
|
|
field("index", $.expression),
|
|
"]",
|
|
),
|
|
),
|
|
|
|
// @tr(...)
|
|
tr: ($) =>
|
|
seq(
|
|
"@tr",
|
|
"(",
|
|
optional(field("context", seq($.string_value, "=>"))),
|
|
field("message", $.string_value),
|
|
optional(
|
|
seq(
|
|
"|",
|
|
field("pipe", $.string_value),
|
|
"%",
|
|
field("percent", $.expression),
|
|
),
|
|
),
|
|
field(
|
|
"arguments",
|
|
optional(seq(",", commaSep1($.expression), optional(","))),
|
|
),
|
|
")",
|
|
),
|
|
|
|
member_access: ($) =>
|
|
prec.left(
|
|
17,
|
|
seq(field("base", $.expression), ".", field("member", $.expression)),
|
|
),
|
|
|
|
unary_expression: ($) =>
|
|
prec.left(
|
|
14,
|
|
seq(field("op", $.unary_prec_operator), field("left", $.expression)),
|
|
),
|
|
|
|
binary_expression: ($) =>
|
|
prec.left(
|
|
1,
|
|
choice(
|
|
$._add_binary_expression,
|
|
$._comparison_binary_expression,
|
|
$._logic_binary_expression,
|
|
$._mult_binary_expression,
|
|
),
|
|
),
|
|
|
|
_add_binary_expression: ($) =>
|
|
prec.left(
|
|
11,
|
|
seq(
|
|
field("left", $.expression),
|
|
field("op", $.add_prec_operator),
|
|
field("right", $.expression),
|
|
),
|
|
),
|
|
|
|
_comparison_binary_expression: ($) =>
|
|
prec.left(
|
|
9,
|
|
seq(
|
|
field("left", $.expression),
|
|
field("op", $.comparison_operator),
|
|
field("right", $.expression),
|
|
),
|
|
),
|
|
|
|
logical_and: (_) => "&&",
|
|
logical_or: (_) => "||",
|
|
|
|
_logic_binary_expression: ($) =>
|
|
choice(
|
|
prec.left(
|
|
4,
|
|
seq(
|
|
field("left", $.expression),
|
|
field("op", $.logical_and),
|
|
field("right", $.expression),
|
|
),
|
|
),
|
|
prec.left(
|
|
3,
|
|
seq(
|
|
field("left", $.expression),
|
|
field("op", $.logical_or),
|
|
field("right", $.expression),
|
|
),
|
|
),
|
|
),
|
|
|
|
_mult_binary_expression: ($) =>
|
|
prec.left(
|
|
12,
|
|
seq(
|
|
field("left", $.expression),
|
|
field("op", $.mult_prec_operator),
|
|
field("right", $.expression),
|
|
),
|
|
),
|
|
|
|
ternary_expression: ($) =>
|
|
prec.left(
|
|
3,
|
|
seq(
|
|
field("condition", $.expression),
|
|
"?",
|
|
field("left", $.expression),
|
|
":",
|
|
field("right", $.expression),
|
|
),
|
|
),
|
|
|
|
callback: ($) =>
|
|
seq(
|
|
optional($.purity),
|
|
"callback",
|
|
field("name", $.simple_identifier),
|
|
optional($._callback_signature),
|
|
optional(seq("->", field("return_type", $._type_identifier))),
|
|
";",
|
|
),
|
|
|
|
purity: (_) => field("value", "pure"),
|
|
|
|
function_visibility: (_) => field("value", choice("public", "private")),
|
|
|
|
function_definition: ($) =>
|
|
seq(
|
|
repeat(choice($.purity, $.function_visibility)),
|
|
"function",
|
|
field("name", $.simple_identifier),
|
|
optional($._function_signature),
|
|
optional(seq("->", field("return_type", $.type))),
|
|
$.imperative_block,
|
|
),
|
|
|
|
callback_alias: ($) =>
|
|
seq(
|
|
optional($.purity),
|
|
"callback",
|
|
field("name", $.simple_identifier),
|
|
"<=>",
|
|
field("alias", $.expression),
|
|
";",
|
|
),
|
|
|
|
callback_event: ($) =>
|
|
seq(
|
|
field("name", choice($.function_call, $.simple_identifier)),
|
|
"=>",
|
|
field("action", $.imperative_block),
|
|
),
|
|
|
|
changed_callback: ($) =>
|
|
seq(
|
|
"changed",
|
|
field("name", $.simple_identifier),
|
|
"=>",
|
|
field("action", $.imperative_block),
|
|
),
|
|
|
|
function_call: ($) =>
|
|
prec(
|
|
17,
|
|
seq(
|
|
field("name", $.simple_identifier),
|
|
field("arguments", $.arguments),
|
|
),
|
|
),
|
|
|
|
gradient_call: ($) =>
|
|
choice(
|
|
seq(
|
|
field("name", $.linear_gradient_identifier),
|
|
"(",
|
|
field(
|
|
"arguments",
|
|
seq(
|
|
field("angle", choice($.angle_value, $.int_value, $.float_value)),
|
|
",",
|
|
field("colors", commaSep2($.gradient_color)),
|
|
optional(","),
|
|
),
|
|
),
|
|
")",
|
|
),
|
|
seq(
|
|
field("name", $.radial_gradient_identifier),
|
|
"(",
|
|
field(
|
|
"arguments",
|
|
seq(
|
|
field("type", $.radial_gradient_kind),
|
|
",",
|
|
field("colors", commaSep2($.gradient_color)),
|
|
optional(","),
|
|
),
|
|
),
|
|
")",
|
|
),
|
|
),
|
|
|
|
gradient_color: ($) => seq($.argument, optional($.percent_value)),
|
|
|
|
image_call: ($) =>
|
|
seq(
|
|
field("name", "@image-url"),
|
|
"(",
|
|
field("image", $.string_value),
|
|
optional(seq(",", "nine-slice", "(", repeat($._int_number), ")")),
|
|
")",
|
|
),
|
|
|
|
typed_identifier: ($) =>
|
|
seq(field("name", $.simple_identifier), ":", field("type", $.type)),
|
|
|
|
_function_signature: ($) =>
|
|
seq(
|
|
"(",
|
|
field(
|
|
"arguments",
|
|
optional(seq(commaSep1($.typed_identifier), optional(","))),
|
|
),
|
|
")",
|
|
),
|
|
|
|
_callback_signature: ($) =>
|
|
prec.left(
|
|
seq(
|
|
"(",
|
|
field("arguments", optional(seq(commaSep1($.type), optional(",")))),
|
|
")",
|
|
),
|
|
),
|
|
|
|
argument: ($) => $.expression,
|
|
|
|
arguments: ($) =>
|
|
prec(
|
|
17,
|
|
seq("(", optional(seq(commaSep1($.argument), optional(","))), ")"),
|
|
),
|
|
|
|
unary_prec_operator: (_) => choice("!", "-", "+"),
|
|
|
|
add_prec_operator: (_) => choice("+", "-"),
|
|
mult_prec_operator: (_) => choice("*", "/"),
|
|
comparison_operator: (_) => choice(">", "<", ">=", "<=", "==", "!="),
|
|
assignment_prec_operator: (_) =>
|
|
prec.left(1, choice("=", ":", "+=", "-=", "*=", "/=")),
|
|
|
|
// This is inspired from tree-sitter-javascript
|
|
// https://github.com/tree-sitter/tree-sitter-javascript/blob/fdeb68ac8d2bd5a78b943528bb68ceda3aade2eb/grammar.js#L866
|
|
/////////////////////////////////////////////////////////////////////
|
|
_string: ($) =>
|
|
choice(
|
|
seq(
|
|
'"',
|
|
repeat(choice($._unescaped_string_fragment, $.escape_sequence)),
|
|
'"',
|
|
),
|
|
),
|
|
|
|
_unescaped_string_fragment: (_) => token.immediate(prec(1, /[^"\\]+/)),
|
|
|
|
escape_sequence: ($) =>
|
|
seq(
|
|
"\\",
|
|
choice(
|
|
/u\{[0-9a-fA-F]+\}/,
|
|
"n",
|
|
"\\",
|
|
'"',
|
|
seq("{", $.expression, "}"),
|
|
),
|
|
),
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
property_visibility: (_) => choice("private", "in", "out", "in-out"),
|
|
|
|
_identifier: (_) => /[a-zA-Z_][a-zA-Z0-9_-]*/,
|
|
simple_identifier: ($) => $._identifier,
|
|
simple_indexed_identifier: ($) =>
|
|
seq(
|
|
field("name", $.simple_identifier),
|
|
optional(seq("[", field("index_var", $.simple_identifier), "]")),
|
|
),
|
|
|
|
builtin_type_identifier: (_) =>
|
|
choice(
|
|
"int",
|
|
"float",
|
|
"bool",
|
|
"string",
|
|
"color",
|
|
"brush",
|
|
"physical-length",
|
|
"length",
|
|
"duration",
|
|
"angle",
|
|
"easing",
|
|
"percent",
|
|
"image",
|
|
"relative-font-size",
|
|
),
|
|
|
|
easing_kind_identifier: ($) =>
|
|
choice(
|
|
"linear",
|
|
"ease-in-quad",
|
|
"ease-out-quad",
|
|
"ease-in-out-quad",
|
|
"ease",
|
|
"ease-in",
|
|
"ease-out",
|
|
"ease-in-out",
|
|
"ease-in-quart",
|
|
"ease-out-quart",
|
|
"ease-in-out-quart",
|
|
"ease-in-quint",
|
|
"ease-out-quint",
|
|
"ease-in-out-quint",
|
|
"ease-in-expo",
|
|
"ease-out-expo",
|
|
"ease-in-out-expo",
|
|
"ease-in-sine",
|
|
"ease-out-sine",
|
|
"ease-in-out-sine",
|
|
"ease-in-back",
|
|
"ease-out-back",
|
|
"ease-in-out-back",
|
|
"ease-in-circ",
|
|
"ease-out-circ",
|
|
"ease-in-out-circ",
|
|
"ease-in-elastic",
|
|
"ease-out-elastic",
|
|
"ease-in-out-elastic",
|
|
"ease-in-bounce",
|
|
"ease-out-bounce",
|
|
"ease-in-out-bounce",
|
|
seq("cubic-bezier", $.arguments),
|
|
),
|
|
|
|
user_type_identifier: ($) => prec(1, $._identifier),
|
|
_type_identifier: ($) =>
|
|
choice($.builtin_type_identifier, $.user_type_identifier),
|
|
|
|
value_list: ($) => seq("[", commaSep($.expression), optional(","), "]"),
|
|
|
|
value: ($) =>
|
|
prec(10, choice($.anon_struct_block, $.value_list, $._basic_value)),
|
|
|
|
children_identifier: (_) => "@children",
|
|
|
|
linear_gradient_identifier: (_) =>
|
|
choice("@linear-gradient", "@linear_gradient"),
|
|
radial_gradient_identifier: (_) =>
|
|
choice("@radial-gradient", "@radial_gradient"),
|
|
radial_gradient_kind: (_) => choice("circle"),
|
|
|
|
reference_identifier: (_) => choice("parent", "root", "self"),
|
|
|
|
_number: ($) => choice($._int_number, $._float_number),
|
|
|
|
_int_number: (_) => /\d+/,
|
|
_float_number: (_) => /(\d*\.\d+|\d+\.\d*)/,
|
|
|
|
int_value: ($) => $._int_number,
|
|
float_value: ($) => $._float_number,
|
|
bool_value: (_) => choice("true", "false"),
|
|
string_value: ($) => $._string,
|
|
color_value: (_) => /#[\da-fA-F]+/,
|
|
// brush_value: ($) => ???,
|
|
physical_length_value: ($) => seq($._number, token.immediate("phx")),
|
|
length_value: ($) =>
|
|
seq($._number, token.immediate(choice("px", "cm", "mm", "in", "pt"))),
|
|
duration_value: ($) => seq($._number, token.immediate(choice("ms", "s"))),
|
|
angle_value: ($) =>
|
|
seq($._number, token.immediate(choice("deg", "grad", "turn", "rad"))),
|
|
// easing_value: ($) => ???,
|
|
percent_value: ($) => seq($._number, token.immediate("%")),
|
|
// image_value: ($) => ???.
|
|
relative_font_size_value: ($) => seq($._number, token.immediate("rem")),
|
|
|
|
_basic_value: ($) =>
|
|
choice(
|
|
$.int_value,
|
|
$.float_value,
|
|
$.bool_value,
|
|
$.string_value,
|
|
$.color_value,
|
|
$.physical_length_value,
|
|
$.length_value,
|
|
$.duration_value,
|
|
$.angle_value,
|
|
$.percent_value,
|
|
$.relative_font_size_value,
|
|
$.easing_kind_identifier,
|
|
),
|
|
|
|
comment: (_) =>
|
|
token(
|
|
choice(
|
|
seq("//", /[^\n\r]*/),
|
|
seq("/*", /[^*]*\*+([^/*][^*]*\*+)*/, "/"),
|
|
),
|
|
),
|
|
},
|
|
});
|
|
|
|
function commaSep(rule) {
|
|
return optional(commaSep1(rule));
|
|
}
|
|
|
|
function commaSep1(rule) {
|
|
return seq(rule, repeat(seq(",", rule)));
|
|
}
|
|
|
|
function commaSep2(rule) {
|
|
return seq(rule, ",", rule, repeat(seq(",", rule)));
|
|
}
|