mirror of
https://github.com/github/codeql.git
synced 2026-08-02 00:13:00 +02:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2114 lines
73 KiB
JavaScript
2114 lines
73 KiB
JavaScript
"use strict";
|
|
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2021 alex-pinkus
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
const PRECS = {
|
|
multiplication: 11,
|
|
addition: 10,
|
|
infix_operations: 9,
|
|
nil_coalescing: 8,
|
|
check: 7,
|
|
prefix_operations: 7,
|
|
comparison: 6,
|
|
postfix_operations: 6,
|
|
equality: 5,
|
|
conjunction: 4,
|
|
disjunction: 3,
|
|
block: 2,
|
|
loop: 1,
|
|
keypath: 1,
|
|
parameter_pack: 1,
|
|
control_transfer: 0,
|
|
as: -1,
|
|
tuple: -1,
|
|
if: -1,
|
|
switch: -1,
|
|
do: -1,
|
|
fully_open_range: -1,
|
|
range: -1,
|
|
navigation: -1,
|
|
expr: -1,
|
|
ty: -1,
|
|
call: -2,
|
|
ternary: -2,
|
|
try: -2,
|
|
call_suffix: -2,
|
|
range_suffix: -2,
|
|
ternary_binary_suffix: -2,
|
|
await: -2,
|
|
assignment: -3,
|
|
comment: -3,
|
|
lambda: -3,
|
|
regex: -4,
|
|
};
|
|
|
|
const DYNAMIC_PRECS = {
|
|
call: 1,
|
|
};
|
|
|
|
const DEC_DIGITS = token(sep1(/[0-9]+/, /_+/));
|
|
const HEX_DIGITS = token(sep1(/[0-9a-fA-F]+/, /_+/));
|
|
const OCT_DIGITS = token(sep1(/[0-7]+/, /_+/));
|
|
const BIN_DIGITS = token(sep1(/[01]+/, /_+/));
|
|
const REAL_EXPONENT = token(seq(/[eE]/, optional(/[+-]/), DEC_DIGITS));
|
|
const HEX_REAL_EXPONENT = token(seq(/[pP]/, optional(/[+-]/), DEC_DIGITS));
|
|
|
|
var LEXICAL_IDENTIFIER;
|
|
|
|
if (tree_sitter_version_supports_emoji()) {
|
|
LEXICAL_IDENTIFIER =
|
|
/[_\p{XID_Start}\p{Emoji}&&[^0-9#*]](\p{EMod}|\x{FE0F}\x{20E3}?)?([_\p{XID_Continue}\p{Emoji}\x{200D}](\p{EMod}|\x{FE0F}\x{20E3}?)?)*/;
|
|
} else {
|
|
LEXICAL_IDENTIFIER = /[_\p{XID_Start}][_\p{XID_Continue}]*/;
|
|
}
|
|
|
|
module.exports = grammar({
|
|
name: "swift",
|
|
supertypes: ($) => [
|
|
$.expression,
|
|
$.unannotated_type,
|
|
$.global_declaration,
|
|
$.type_level_declaration,
|
|
$.local_declaration,
|
|
$.protocol_member_declaration,
|
|
],
|
|
conflicts: ($) => [
|
|
// @Type(... could either be an annotation constructor invocation or an annotated expression
|
|
[$.attribute],
|
|
[$._attribute_argument],
|
|
// Is `foo { ... }` a constructor invocation or function invocation?
|
|
[$.simple_user_type, $.expression],
|
|
// To support nested types A.B not being interpreted as `(navigation_expression ... (type_identifier)) (navigation_suffix)`
|
|
[$.user_type],
|
|
// How to tell the difference between Foo.bar(with:and:), and Foo.bar(with: smth, and: other)? You need GLR
|
|
[$.value_argument],
|
|
// { (foo, bar) ...
|
|
[$.expression, $.lambda_parameter],
|
|
[$._primary_expression, $.lambda_parameter],
|
|
// (foo) where foo could be a binding pattern or a tuple expression item.
|
|
[$._binding_pattern_with_expr, $.tuple_expression_item],
|
|
// After a `{` in a function or switch context, it's ambigous whether we're starting a set of local statements or
|
|
// applying some modifiers to a capture or pattern.
|
|
[$.modifiers],
|
|
// `+(...)` is ambigously either "call the function produced by a reference to the operator `+`" or "use the unary
|
|
// operator `+` on the result of the parenthetical expression."
|
|
[$._additive_operator, $._prefix_unary_operator],
|
|
[$.referenceable_operator, $._prefix_unary_operator],
|
|
// `{ [self, b, c] ...` could be a capture list or an array literal depending on what else happens.
|
|
[$.capture_list_item, $.expression],
|
|
[$.capture_list_item, $.expression, $.simple_user_type],
|
|
[$._primary_expression, $.capture_list_item],
|
|
// a ? b : c () could be calling c(), or it could be calling a function that's produced by the result of
|
|
// `(a ? b : c)`. We have a small hack to force it to be the former of these by intentionally introducing a
|
|
// conflict.
|
|
[$.call_suffix, $.expr_hack_at_ternary_binary_call_suffix],
|
|
// try {expression} is a bit magic and applies quite broadly: `try foo()` and `try foo { }` show that this is right
|
|
// associative, and `try foo ? bar() : baz` even more so. But it doesn't always win: something like
|
|
// `if try foo { } ...` should award its braces to the `if`. In order to make this actually happen, we need to parse
|
|
// all the options and pick the best one that doesn't error out.
|
|
[$.try_expression, $._unary_expression],
|
|
[$.try_expression, $.expression],
|
|
// await {expression} has the same special cases as `try`.
|
|
[$.await_expression, $._unary_expression],
|
|
[$.await_expression, $.expression],
|
|
// In a computed property, when you see an @attribute, it's not yet clear if that's going to be for a
|
|
// locally-declared class or a getter / setter specifier.
|
|
[
|
|
$._local_property_declaration,
|
|
$._local_typealias_declaration,
|
|
$._local_function_declaration,
|
|
$._local_class_declaration,
|
|
$.computed_getter,
|
|
$.computed_modify,
|
|
$.computed_setter,
|
|
],
|
|
// The `class` modifier is legal in many of the same positions that a class declaration itself would be.
|
|
[$._bodyless_function_declaration, $.property_modifier],
|
|
[$.init_declaration, $.property_modifier],
|
|
// Patterns, man
|
|
[$._navigable_type_expression, $.case_pattern],
|
|
[$._no_expr_pattern_already_bound, $._binding_pattern_no_expr],
|
|
|
|
// On encountering a closure starting with `{ @Foo ...`, we don't yet know if that attribute applies to the closure
|
|
// type or to a declaration within the closure. What a mess! We just have to hope that if we keep going, only one of
|
|
// those will parse (because there will be an `in` or a `let`).
|
|
[
|
|
$._lambda_type_declaration,
|
|
$._local_property_declaration,
|
|
$._local_typealias_declaration,
|
|
$._local_function_declaration,
|
|
$._local_class_declaration,
|
|
],
|
|
|
|
// We want `foo() { }` to be treated as one function call, but we _also_ want `if foo() { ... }` to be treated as a
|
|
// full if-statement. This means we have to treat it as a conflict rather than purely a left or right associative
|
|
// construct, and let the parser realize that the second expression won't parse properly with the `{ ... }` as a
|
|
// lambda.
|
|
[$.constructor_suffix],
|
|
[$.call_suffix],
|
|
|
|
// `actor` is allowed to be an identifier, even though it is also a locally permitted declaration. If we encounter
|
|
// it, the only way to know what it's meant to be is to keep going.
|
|
[$._modifierless_class_declaration, $.property_modifier],
|
|
[$._fn_call_lambda_arguments],
|
|
|
|
// `borrowing` and `consuming` are legal as identifiers, but are also legal modifiers
|
|
[$.parameter_modifiers],
|
|
|
|
// These are keywords sometimes, but simple identifiers other times, and it just depends on the rest of their usage.
|
|
[$._contextual_simple_identifier, $._modifierless_class_declaration],
|
|
[$._contextual_simple_identifier, $.property_behavior_modifier],
|
|
[$._contextual_simple_identifier, $.parameter_modifier],
|
|
[$._contextual_simple_identifier, $.type_parameter_pack],
|
|
[$._contextual_simple_identifier, $.type_pack_expansion],
|
|
[$._contextual_simple_identifier, $.visibility_modifier],
|
|
],
|
|
extras: ($) => [
|
|
$.comment,
|
|
$.multiline_comment,
|
|
/\s+/, // Whitespace
|
|
],
|
|
externals: ($) => [
|
|
// Comments and raw strings are parsed in a custom scanner because they require us to carry forward state to
|
|
// maintain symmetry. For instance, parsing a multiline comment requires us to increment a counter whenever we see
|
|
// `/*`, and decrement it whenever we see `*/`. A standard grammar would only be able to exit the comment at the
|
|
// first `*/` (like C does). Similarly, when you start a string with `##"`, you're required to include the same
|
|
// number of `#` symbols to end it.
|
|
$._multiline_comment,
|
|
$.raw_str_part,
|
|
$.raw_str_continuing_indicator,
|
|
$.raw_str_end_part,
|
|
// Because Swift doesn't have explicit semicolons, we also do some whitespace handling in a custom scanner. Line
|
|
// breaks are _sometimes_ meaningful as the end of a statement: try to write `let foo: Foo let bar: Bar`, for
|
|
// instance and the compiler will complain, but add either a newline or a semicolon and it's fine. We borrow the
|
|
// idea from the Kotlin grammar that a newline is sometimes a "semicolon". By including `\n` in both `_semi` and
|
|
// an anonymous `whitespace` extras, we _should_ be able to let the parser decide if a newline is meaningful. If the
|
|
// parser sees something like `foo.bar(1\n)`, it knows that a "semicolon" would not be valid there, so it parses
|
|
// that as whitespace. On the other hand, `let foo: Foo\n let bar: Bar` has a meaningful newline.
|
|
// Unfortunately, we can't simply stop at that. There are some expressions and statements that remain valid if you
|
|
// end them early, but are expected to be parsed across multiple lines. One particular nefarious example is a
|
|
// function declaration, where you might have something like `func foo<A>(args: A) -> Foo throws where A: Hashable`.
|
|
// This would still be a valid declaration even if it ended after the `)`, the `Foo`, or the `throws`, so a grammar
|
|
// that simply interprets a newline as "sometimes a semi" would parse those incorrectly.
|
|
// To solve that case, our custom scanner must do a bit of extra lookahead itself. If we're about to generate a
|
|
// `_semi`, we advance a bit further to see if the next non-whitespace token would be one of these other operators.
|
|
// If so, we ignore the `_semi` and just produce the operator; if not, we produce the `_semi` and let the rest of
|
|
// the grammar sort it out. This isn't perfect, but it works well enough most of the time.
|
|
$._implicit_semi,
|
|
$._explicit_semi,
|
|
// Every one of the below operators will suppress a `_semi` if we encounter it after a newline.
|
|
$._arrow_operator_custom,
|
|
$._dot_custom,
|
|
$._conjunction_operator_custom,
|
|
$._disjunction_operator_custom,
|
|
$._nil_coalescing_operator_custom,
|
|
$._eq_custom,
|
|
$._eq_eq_custom,
|
|
$._plus_then_ws,
|
|
$._minus_then_ws,
|
|
$._bang_custom,
|
|
$._throws_keyword,
|
|
$._rethrows_keyword,
|
|
$.default_keyword,
|
|
$.where_keyword,
|
|
$["else"],
|
|
$.catch_keyword,
|
|
$._as_custom,
|
|
$._as_quest_custom,
|
|
$._as_bang_custom,
|
|
$._async_keyword_custom,
|
|
$._custom_operator,
|
|
$._hash_symbol_custom,
|
|
$._directive_if,
|
|
$._directive_elseif,
|
|
$._directive_else,
|
|
$._directive_endif,
|
|
|
|
// Fake operator that will never get triggered, but follows the sequence of characters for `try!`. Tracked by the
|
|
// custom scanner so that it can avoid triggering `$.bang` for that case.
|
|
$._fake_try_bang,
|
|
],
|
|
inline: ($) => [$._locally_permitted_modifiers],
|
|
rules: {
|
|
////////////////////////////////
|
|
// File Structure
|
|
////////////////////////////////
|
|
source_file: ($) =>
|
|
seq(
|
|
optional(field("shebang", $.shebang_line)),
|
|
optional(
|
|
seq(
|
|
field("statement", $._top_level_statement),
|
|
repeat(seq($._semi, field("statement", $._top_level_statement))),
|
|
optional($._semi)
|
|
)
|
|
)
|
|
),
|
|
_semi: ($) => choice($._implicit_semi, $._explicit_semi),
|
|
shebang_line: ($) => seq($._hash_symbol, "!", /[^\r\n]*/),
|
|
////////////////////////////////
|
|
// Lexical Structure - https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html
|
|
////////////////////////////////
|
|
comment: ($) => token(prec(PRECS.comment, seq("//", /.*/))),
|
|
// Named wrapper for the unnamed `_multiline_comment` external token, so
|
|
// that multi-line comments still appear in the AST (e.g. as extras between
|
|
// top-level statements) without being extracted as class body members when
|
|
// used only to separate those members.
|
|
multiline_comment: ($) => $._multiline_comment,
|
|
// Identifiers
|
|
simple_identifier: ($) =>
|
|
choice(
|
|
LEXICAL_IDENTIFIER,
|
|
/`[^\r\n` ]*`/,
|
|
/\$[0-9]+/,
|
|
token(seq("$", LEXICAL_IDENTIFIER)),
|
|
$._contextual_simple_identifier
|
|
),
|
|
// Keywords that were added after they were already legal as identifiers. `tree-sitter` will prefer exact matches
|
|
// when parsing so unless we explicitly say that these are legal, the parser will interpret them as their keyword.
|
|
_contextual_simple_identifier: ($) =>
|
|
choice(
|
|
"actor",
|
|
"async",
|
|
"each",
|
|
"lazy",
|
|
"repeat",
|
|
"package",
|
|
$._parameter_ownership_modifier
|
|
),
|
|
identifier: ($) => sep1(field("part", $.simple_identifier), $._dot),
|
|
// Literals
|
|
_basic_literal: ($) =>
|
|
choice(
|
|
$.integer_literal,
|
|
$.hex_literal,
|
|
$.oct_literal,
|
|
$.bin_literal,
|
|
$.real_literal,
|
|
$.boolean_literal,
|
|
$._string_literal,
|
|
$.regex_literal,
|
|
"nil"
|
|
),
|
|
real_literal: ($) =>
|
|
token(
|
|
choice(
|
|
seq(DEC_DIGITS, REAL_EXPONENT),
|
|
seq(optional(DEC_DIGITS), ".", DEC_DIGITS, optional(REAL_EXPONENT)),
|
|
seq(
|
|
"0x",
|
|
HEX_DIGITS,
|
|
optional(seq(".", HEX_DIGITS)),
|
|
HEX_REAL_EXPONENT
|
|
)
|
|
)
|
|
),
|
|
integer_literal: ($) => token(seq(optional(/[1-9]/), DEC_DIGITS)),
|
|
hex_literal: ($) => token(seq("0", /[xX]/, HEX_DIGITS)),
|
|
oct_literal: ($) => token(seq("0", /[oO]/, OCT_DIGITS)),
|
|
bin_literal: ($) => token(seq("0", /[bB]/, BIN_DIGITS)),
|
|
boolean_literal: ($) => choice("true", "false"),
|
|
// String literals
|
|
_string_literal: ($) =>
|
|
choice(
|
|
$.line_string_literal,
|
|
$.multi_line_string_literal,
|
|
$.raw_string_literal
|
|
),
|
|
line_string_literal: ($) =>
|
|
seq(
|
|
'"',
|
|
repeat(choice(field("text", $._line_string_content), $._interpolation)),
|
|
'"'
|
|
),
|
|
_line_string_content: ($) => choice($.line_str_text, $.str_escaped_char),
|
|
line_str_text: ($) => /[^\\"]+/,
|
|
str_escaped_char: ($) =>
|
|
choice($._escaped_identifier, $._uni_character_literal),
|
|
_uni_character_literal: ($) => seq("\\", "u", /\{[0-9a-fA-F]+\}/),
|
|
multi_line_string_literal: ($) =>
|
|
seq(
|
|
'"""',
|
|
repeat(
|
|
choice(field("text", $._multi_line_string_content), $._interpolation)
|
|
),
|
|
'"""'
|
|
),
|
|
raw_string_literal: ($) =>
|
|
seq(
|
|
repeat(
|
|
seq(
|
|
field("text", $.raw_str_part),
|
|
field("interpolation", $.raw_str_interpolation),
|
|
field("continuing", optional($.raw_str_continuing_indicator))
|
|
)
|
|
),
|
|
field("text", $.raw_str_end_part)
|
|
),
|
|
raw_str_interpolation: ($) =>
|
|
seq(field("start", $.raw_str_interpolation_start), $._interpolation_contents, ")"),
|
|
raw_str_interpolation_start: ($) => /\\#*\(/,
|
|
_multi_line_string_content: ($) =>
|
|
choice($.multi_line_str_text, $.str_escaped_char, '"'),
|
|
_interpolation: ($) => seq("\\(", $._interpolation_contents, ")"),
|
|
_interpolation_contents: ($) =>
|
|
sep1Opt(
|
|
field(
|
|
"interpolation",
|
|
alias($.value_argument, $.interpolated_expression)
|
|
),
|
|
","
|
|
),
|
|
_escaped_identifier: ($) => /\\[0\\tnr"'\n]/,
|
|
multi_line_str_text: ($) => /[^\\"]+/,
|
|
// Based on https://gitlab.com/woolsweater/tree-sitter-swifter/-/blob/3d47c85bd47ce54cdf2023a9c0e01eb90adfcc1d/grammar.js#L1019
|
|
// But required modifications to hit all of the cases in SE-354
|
|
regex_literal: ($) =>
|
|
choice(
|
|
$._extended_regex_literal,
|
|
$._multiline_regex_literal,
|
|
$._oneline_regex_literal
|
|
),
|
|
|
|
_extended_regex_literal: ($) =>
|
|
seq($._hash_symbol, /\/((\/[^#])|[^\n])+\/#/),
|
|
|
|
_multiline_regex_literal: ($) =>
|
|
seq($._hash_symbol, /\/\n/, /(\/[^#]|[^/])*?\n\/#/),
|
|
|
|
_oneline_regex_literal: ($) =>
|
|
token(
|
|
prec(
|
|
PRECS.regex,
|
|
seq(
|
|
"/",
|
|
token.immediate(/[^ \t\n]?[^/\n]*[^ \t\n/]/),
|
|
token.immediate("/")
|
|
)
|
|
)
|
|
),
|
|
////////////////////////////////
|
|
// Types - https://docs.swift.org/swift-book/ReferenceManual/Types.html
|
|
////////////////////////////////
|
|
type_annotation: ($) =>
|
|
seq(":", field("type", $._possibly_implicitly_unwrapped_type)),
|
|
_possibly_implicitly_unwrapped_type: ($) =>
|
|
choice($.type, $.implicitly_unwrapped_type),
|
|
implicitly_unwrapped_type: ($) =>
|
|
seq(field("name", $.type), token.immediate("!")),
|
|
type: ($) =>
|
|
prec.right(
|
|
PRECS.ty,
|
|
seq(field("modifiers", optional($.type_modifiers)), field("name", $.unannotated_type))
|
|
),
|
|
unannotated_type: ($) =>
|
|
prec.right(
|
|
PRECS.ty,
|
|
choice(
|
|
$.user_type,
|
|
$.tuple_type,
|
|
$.function_type,
|
|
$.array_type,
|
|
$.dictionary_type,
|
|
$.optional_type,
|
|
$.metatype,
|
|
$.opaque_type,
|
|
$.existential_type,
|
|
$.protocol_composition_type,
|
|
$.type_parameter_pack,
|
|
$.type_pack_expansion,
|
|
$.suppressed_constraint
|
|
)
|
|
),
|
|
// The grammar just calls this whole thing a `type-identifier` but that's a bit confusing.
|
|
user_type: ($) => sep1(field("part", $.simple_user_type), $._dot),
|
|
simple_user_type: ($) =>
|
|
prec.right(
|
|
PRECS.ty,
|
|
seq(
|
|
field("name", alias($.simple_identifier, $.type_identifier)),
|
|
field("arguments", optional($.type_arguments))
|
|
)
|
|
),
|
|
tuple_type: ($) =>
|
|
choice(
|
|
seq(
|
|
"(",
|
|
optional(sep1Opt(field("element", $.tuple_type_item), ",")),
|
|
")"
|
|
),
|
|
field("element", alias($.parenthesized_type, $.tuple_type_item))
|
|
),
|
|
tuple_type_item: ($) =>
|
|
prec(
|
|
PRECS.expr,
|
|
seq(
|
|
optional($._tuple_type_item_identifier),
|
|
field("modifiers", optional($.parameter_modifiers)),
|
|
field("type", $.type)
|
|
)
|
|
),
|
|
_tuple_type_item_identifier: ($) =>
|
|
prec(
|
|
PRECS.expr,
|
|
seq(
|
|
optional(field("external_name", $.wildcard_pattern)),
|
|
field("name", $.simple_identifier),
|
|
":"
|
|
)
|
|
),
|
|
function_type: ($) =>
|
|
seq(
|
|
field("params", choice($.tuple_type, $.unannotated_type)),
|
|
field("async", optional($._async_keyword)),
|
|
field("throws", optional(choice($.throws_clause, $.throws))),
|
|
$._arrow_operator,
|
|
field("return_type", $.type)
|
|
),
|
|
array_type: ($) => seq("[", field("element", $.type), "]"),
|
|
dictionary_type: ($) =>
|
|
seq("[", field("key", $.type), ":", field("value", $.type), "]"),
|
|
optional_type: ($) =>
|
|
prec.left(
|
|
seq(
|
|
field(
|
|
"wrapped",
|
|
choice($.user_type, $.tuple_type, $.array_type, $.dictionary_type)
|
|
),
|
|
repeat1(alias($._immediate_quest, "?"))
|
|
)
|
|
),
|
|
metatype: ($) => seq(field("name", $.unannotated_type), ".", choice("Type", "Protocol")),
|
|
_quest: ($) => "?",
|
|
_immediate_quest: ($) => token.immediate("?"),
|
|
opaque_type: ($) => prec.right(seq("some", field("name", $.unannotated_type))),
|
|
existential_type: ($) => prec.right(seq("any", field("name", $.unannotated_type))),
|
|
type_parameter_pack: ($) => prec.left(seq("each", field("name", $.unannotated_type))),
|
|
type_pack_expansion: ($) => prec.left(seq("repeat", field("name", $.unannotated_type))),
|
|
protocol_composition_type: ($) =>
|
|
prec.left(
|
|
seq(
|
|
field("type", $.unannotated_type),
|
|
repeat1(seq("&", prec.right(field("type", $.unannotated_type))))
|
|
)
|
|
),
|
|
suppressed_constraint: ($) =>
|
|
prec.right(
|
|
seq(
|
|
"~",
|
|
field("suppressed", alias($.simple_identifier, $.type_identifier))
|
|
)
|
|
),
|
|
////////////////////////////////
|
|
// Expressions - https://docs.swift.org/swift-book/ReferenceManual/Expressions.html
|
|
////////////////////////////////
|
|
expression: ($) =>
|
|
prec(
|
|
PRECS.expr,
|
|
choice(
|
|
$.simple_identifier,
|
|
$._unary_expression,
|
|
$._binary_expression,
|
|
$.ternary_expression,
|
|
$._primary_expression,
|
|
$.if_statement,
|
|
$.switch_statement,
|
|
$.assignment,
|
|
$.value_parameter_pack,
|
|
$.value_pack_expansion,
|
|
$.optional_chain_marker
|
|
)
|
|
),
|
|
optional_chain_marker: ($) =>
|
|
seq(field("expr", $.expression), alias($._immediate_quest, "?")),
|
|
// Unary expressions
|
|
_unary_expression: ($) =>
|
|
choice(
|
|
$.postfix_expression,
|
|
$.call_expression,
|
|
$.macro_invocation,
|
|
$.constructor_expression,
|
|
$.navigation_expression,
|
|
$.prefix_expression,
|
|
$.as_expression,
|
|
$.selector_expression,
|
|
$.open_start_range_expression,
|
|
$.open_end_range_expression,
|
|
$.directive,
|
|
$.diagnostic
|
|
),
|
|
postfix_expression: ($) =>
|
|
prec.left(
|
|
PRECS.postfix_operations,
|
|
seq(
|
|
field("target", $.expression),
|
|
field("operation", $._postfix_unary_operator)
|
|
)
|
|
),
|
|
constructor_expression: ($) =>
|
|
prec(
|
|
PRECS.call,
|
|
seq(
|
|
field(
|
|
"constructed_type",
|
|
choice($.array_type, $.dictionary_type, $.user_type)
|
|
),
|
|
field("suffix", $.constructor_suffix)
|
|
)
|
|
),
|
|
parenthesized_type: ($) =>
|
|
seq(
|
|
"(",
|
|
field("type", choice($.opaque_type, $.existential_type, $.dictionary_type)),
|
|
")"
|
|
),
|
|
navigation_expression: ($) =>
|
|
prec.left(
|
|
PRECS.navigation,
|
|
seq(
|
|
field(
|
|
"target",
|
|
choice(
|
|
$._navigable_type_expression,
|
|
$.expression,
|
|
$.parenthesized_type
|
|
)
|
|
),
|
|
field("suffix", $.navigation_suffix)
|
|
)
|
|
),
|
|
_navigable_type_expression: ($) =>
|
|
choice($.user_type, $.array_type, $.dictionary_type),
|
|
open_start_range_expression: ($) =>
|
|
prec.right(
|
|
PRECS.range,
|
|
seq(
|
|
$._range_operator,
|
|
prec.right(PRECS.range_suffix, field("end", $.expression))
|
|
)
|
|
),
|
|
_range_operator: ($) =>
|
|
choice($._open_ended_range_operator, $._three_dot_operator),
|
|
open_end_range_expression: ($) =>
|
|
prec.right(
|
|
PRECS.range,
|
|
seq(field("start", $.expression), $._three_dot_operator)
|
|
),
|
|
prefix_expression: ($) =>
|
|
prec.left(
|
|
PRECS.prefix_operations,
|
|
seq(
|
|
field("operation", $._prefix_unary_operator),
|
|
field(
|
|
"target",
|
|
choice(
|
|
$.expression,
|
|
alias(choice("async", "if", "switch"), $.expression)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
as_expression: ($) =>
|
|
prec.left(
|
|
PRECS.as,
|
|
seq(field("expr", $.expression), field("operator", $.as_operator), field("type", $.type))
|
|
),
|
|
selector_expression: ($) =>
|
|
seq(
|
|
$._hash_symbol,
|
|
"selector",
|
|
"(",
|
|
optional(choice("getter:", "setter:")),
|
|
field("expr", $.expression),
|
|
")"
|
|
),
|
|
// Binary expressions
|
|
_binary_expression: ($) =>
|
|
choice(
|
|
$.multiplicative_expression,
|
|
$.additive_expression,
|
|
$.range_expression,
|
|
$.infix_expression,
|
|
$.nil_coalescing_expression,
|
|
$.check_expression,
|
|
$.equality_expression,
|
|
$.comparison_expression,
|
|
$.conjunction_expression,
|
|
$.disjunction_expression,
|
|
$.bitwise_operation
|
|
),
|
|
multiplicative_expression: ($) =>
|
|
prec.left(
|
|
PRECS.multiplication,
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._multiplicative_operator),
|
|
field("rhs", $.expression)
|
|
)
|
|
),
|
|
additive_expression: ($) =>
|
|
prec.left(
|
|
PRECS.addition,
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._additive_operator),
|
|
field("rhs", $.expression)
|
|
)
|
|
),
|
|
range_expression: ($) =>
|
|
prec.right(
|
|
PRECS.range,
|
|
seq(
|
|
field("start", $.expression),
|
|
field("op", $._range_operator),
|
|
field("end", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
infix_expression: ($) =>
|
|
prec.left(
|
|
PRECS.infix_operations,
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $.custom_operator),
|
|
field("rhs", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
nil_coalescing_expression: ($) =>
|
|
prec.right(
|
|
PRECS.nil_coalescing,
|
|
seq(
|
|
field("value", $.expression),
|
|
$._nil_coalescing_operator,
|
|
field("if_nil", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
check_expression: ($) =>
|
|
prec.left(
|
|
PRECS.check,
|
|
seq(
|
|
field("target", $.expression),
|
|
field("op", $._is_operator),
|
|
field("type", $.type)
|
|
)
|
|
),
|
|
comparison_expression: ($) =>
|
|
prec.left(
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._comparison_operator),
|
|
field("rhs", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
equality_expression: ($) =>
|
|
prec.left(
|
|
PRECS.equality,
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._equality_operator),
|
|
field("rhs", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
conjunction_expression: ($) =>
|
|
prec.left(
|
|
PRECS.conjunction,
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._conjunction_operator),
|
|
field("rhs", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
disjunction_expression: ($) =>
|
|
prec.left(
|
|
PRECS.disjunction,
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._disjunction_operator),
|
|
field("rhs", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
bitwise_operation: ($) =>
|
|
prec.left(
|
|
seq(
|
|
field("lhs", $.expression),
|
|
field("op", $._bitwise_binary_operator),
|
|
field("rhs", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
custom_operator: ($) => choice(token(/[\/]+[*]+/), $._custom_operator),
|
|
// Suffixes
|
|
navigation_suffix: ($) =>
|
|
seq(
|
|
$._dot,
|
|
field("suffix", choice($.simple_identifier, $.integer_literal))
|
|
),
|
|
call_suffix: ($) =>
|
|
prec(
|
|
PRECS.call_suffix,
|
|
choice(
|
|
field("arguments", $.value_arguments),
|
|
prec.dynamic(-1, $._fn_call_lambda_arguments), // Prefer to treat `foo() { }` as one call not two
|
|
seq(field("arguments", $.value_arguments), $._fn_call_lambda_arguments)
|
|
)
|
|
),
|
|
constructor_suffix: ($) =>
|
|
prec(
|
|
PRECS.call_suffix,
|
|
choice(
|
|
field("arguments", alias($._constructor_value_arguments, $.value_arguments)),
|
|
prec.dynamic(-1, $._fn_call_lambda_arguments), // As above
|
|
seq(
|
|
field("arguments", alias($._constructor_value_arguments, $.value_arguments)),
|
|
$._fn_call_lambda_arguments
|
|
)
|
|
)
|
|
),
|
|
_constructor_value_arguments: ($) =>
|
|
seq("(", optional(sep1Opt(field("argument", $.value_argument), ",")), ")"),
|
|
_fn_call_lambda_arguments: ($) =>
|
|
sep1(field("lambda", $.lambda_literal), seq(field("name", $.simple_identifier), ":")),
|
|
type_arguments: ($) => prec.left(seq("<", sep1Opt(field("argument", $.type), ","), ">")),
|
|
value_arguments: ($) =>
|
|
seq(
|
|
choice(
|
|
seq("(", optional(sep1Opt(field("argument", $.value_argument), ",")), ")"),
|
|
seq("[", optional(sep1Opt(field("argument", $.value_argument), ",")), "]")
|
|
)
|
|
),
|
|
value_argument_label: ($) =>
|
|
prec.left(
|
|
field("name", choice(
|
|
$.simple_identifier,
|
|
// We don't rely on $._contextual_simple_identifier here because
|
|
// these don't usually fall into that category.
|
|
alias("if", $.simple_identifier),
|
|
alias("switch", $.simple_identifier)
|
|
))
|
|
),
|
|
value_argument: ($) =>
|
|
prec.left(
|
|
seq(
|
|
field("type_modifiers", optional($.type_modifiers)),
|
|
choice(
|
|
repeat1(
|
|
seq(field("reference_specifier", $.value_argument_label), ":")
|
|
),
|
|
seq(
|
|
optional(seq(field("name", $.value_argument_label), ":")),
|
|
field("value", $.expression)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
try_expression: ($) =>
|
|
prec.right(
|
|
PRECS["try"],
|
|
seq(
|
|
field("operator", $.try_operator),
|
|
field(
|
|
"expr",
|
|
choice(
|
|
// Prefer direct calls, e.g. `try foo()`, over indirect like `try a ? b() : c`. This allows us to have
|
|
// left associativity for the direct calls, which is technically wrong but is the only way to resolve the
|
|
// ambiguity of `if foo { ... }` in the correct direction.
|
|
prec.right(-2, $.expression),
|
|
prec.left(0, $._binary_expression),
|
|
prec.left(0, $.call_expression),
|
|
// Similarly special case the ternary expression, where `try` may come earlier than it is actually needed.
|
|
// When the parser just encounters some identifier after a `try`, it should prefer the `call_expression` (so
|
|
// this should be lower in priority than that), but when we encounter an ambiguous expression that might be
|
|
// either `try (foo() ? ...)` or `(try foo()) ? ...`, we should prefer the former. We accomplish that by
|
|
// giving it a _static precedence_ of -1 but a _dynamic precedence_ of 1.
|
|
prec.dynamic(1, prec.left(-1, $.ternary_expression))
|
|
)
|
|
)
|
|
)
|
|
),
|
|
await_expression: ($) =>
|
|
prec.right(
|
|
PRECS.await,
|
|
seq(
|
|
$._await_operator,
|
|
field(
|
|
"expr",
|
|
choice(
|
|
// Prefer direct calls over indirect (same as with `try`).
|
|
prec.right(-2, $.expression),
|
|
prec.left(0, $.call_expression),
|
|
// Special case ternary to `await` the whole thing (same as with `try`).
|
|
prec.dynamic(1, prec.left(-1, $.ternary_expression))
|
|
)
|
|
)
|
|
)
|
|
),
|
|
_await_operator: ($) => alias("await", "await"),
|
|
ternary_expression: ($) =>
|
|
prec.right(
|
|
PRECS.ternary,
|
|
seq(
|
|
field("condition", $.expression),
|
|
$._quest,
|
|
field("if_true", $.expression),
|
|
":",
|
|
field("if_false", $._expr_hack_at_ternary_binary_suffix)
|
|
)
|
|
),
|
|
_expr_hack_at_ternary_binary_suffix: ($) =>
|
|
prec.left(
|
|
PRECS.ternary_binary_suffix,
|
|
choice(
|
|
$.expression,
|
|
alias($.expr_hack_at_ternary_binary_call, $.call_expression)
|
|
)
|
|
),
|
|
expr_hack_at_ternary_binary_call: ($) =>
|
|
seq(
|
|
field("function", $.expression),
|
|
field("suffix", alias($.expr_hack_at_ternary_binary_call_suffix, $.call_suffix))
|
|
),
|
|
expr_hack_at_ternary_binary_call_suffix: ($) =>
|
|
prec(PRECS.call_suffix, field("arguments", $.value_arguments)),
|
|
call_expression: ($) =>
|
|
prec(
|
|
PRECS.call,
|
|
prec.dynamic(DYNAMIC_PRECS.call, seq(field("function", $.expression), field("suffix", $.call_suffix)))
|
|
),
|
|
macro_invocation: ($) =>
|
|
prec(
|
|
PRECS.call,
|
|
prec.dynamic(
|
|
DYNAMIC_PRECS.call,
|
|
seq(
|
|
$._hash_symbol,
|
|
field("name", $.simple_identifier),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
field("suffix", $.call_suffix)
|
|
)
|
|
)
|
|
),
|
|
_primary_expression: ($) =>
|
|
choice(
|
|
$.tuple_expression,
|
|
$._basic_literal,
|
|
$.lambda_literal,
|
|
$.special_literal,
|
|
$.playground_literal,
|
|
$.array_literal,
|
|
$.dictionary_literal,
|
|
$.self_expression,
|
|
$.super_expression,
|
|
$.try_expression,
|
|
$.await_expression,
|
|
$.referenceable_operator,
|
|
$.key_path_expression,
|
|
$.key_path_string_expression,
|
|
prec.right(
|
|
PRECS.fully_open_range,
|
|
alias($._three_dot_operator, $.fully_open_range)
|
|
)
|
|
),
|
|
tuple_expression: ($) =>
|
|
prec.right(
|
|
PRECS.tuple,
|
|
seq(
|
|
"(",
|
|
sep1Opt(field("element", $.tuple_expression_item), ","),
|
|
")"
|
|
)
|
|
),
|
|
tuple_expression_item: ($) =>
|
|
seq(
|
|
optional(seq(field("name", $.simple_identifier), ":")),
|
|
field("value", $.expression)
|
|
),
|
|
array_literal: ($) =>
|
|
seq("[", optional(sep1Opt(field("element", $.expression), ",")), "]"),
|
|
dictionary_literal: ($) =>
|
|
seq(
|
|
"[",
|
|
choice(":", sep1Opt(field("element", $.dictionary_literal_item), ",")),
|
|
optional(","),
|
|
"]"
|
|
),
|
|
dictionary_literal_item: ($) =>
|
|
seq(field("key", $.expression), ":", field("value", $.expression)),
|
|
special_literal: ($) =>
|
|
seq(
|
|
$._hash_symbol,
|
|
choice(
|
|
"file",
|
|
"fileID",
|
|
"filePath",
|
|
"line",
|
|
"column",
|
|
"function",
|
|
"dsohandle"
|
|
)
|
|
),
|
|
playground_literal: ($) =>
|
|
seq(
|
|
$._hash_symbol,
|
|
field("kind", choice("colorLiteral", "fileLiteral", "imageLiteral")),
|
|
"(",
|
|
sep1Opt(field("argument", $.playground_literal_argument), ","),
|
|
")"
|
|
),
|
|
playground_literal_argument: ($) =>
|
|
seq(field("name", $.simple_identifier), ":", field("value", $.expression)),
|
|
lambda_literal: ($) =>
|
|
prec.left(
|
|
PRECS.lambda,
|
|
seq(
|
|
choice("{", "^{"),
|
|
optional($._lambda_type_declaration),
|
|
optional($._statements),
|
|
"}"
|
|
)
|
|
),
|
|
_lambda_type_declaration: ($) =>
|
|
seq(
|
|
repeat(field("attribute", $.attribute)),
|
|
prec(PRECS.expr, optional(field("captures", $.capture_list))),
|
|
optional(field("type", $.lambda_function_type)),
|
|
"in"
|
|
),
|
|
capture_list: ($) => seq("[", sep1Opt(field("item", $.capture_list_item), ","), "]"),
|
|
capture_list_item: ($) =>
|
|
choice(
|
|
field("name", $.self_expression),
|
|
prec(
|
|
PRECS.expr,
|
|
seq(
|
|
field("ownership", optional($.ownership_modifier)),
|
|
field("name", $.simple_identifier),
|
|
optional(seq($._equal_sign, field("value", $.expression)))
|
|
)
|
|
)
|
|
),
|
|
lambda_function_type: ($) =>
|
|
prec(
|
|
PRECS.expr,
|
|
seq(
|
|
choice(
|
|
field("params", $.lambda_function_type_parameters),
|
|
seq("(", field("params", optional($.lambda_function_type_parameters)), ")")
|
|
),
|
|
field("async", optional($._async_keyword)),
|
|
field("throws", optional(choice($.throws_clause, $.throws))),
|
|
optional(
|
|
seq(
|
|
$._arrow_operator,
|
|
field("return_type", $._possibly_implicitly_unwrapped_type)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
lambda_function_type_parameters: ($) => sep1Opt(field("parameter", $.lambda_parameter), ","),
|
|
lambda_parameter: ($) =>
|
|
seq(
|
|
choice(
|
|
field("name", $.self_expression),
|
|
prec(PRECS.expr, field("name", $.simple_identifier)),
|
|
prec(
|
|
PRECS.expr,
|
|
seq(
|
|
optional(field("external_name", $.simple_identifier)),
|
|
field("name", $.simple_identifier),
|
|
":",
|
|
field("modifiers", optional($.parameter_modifiers)),
|
|
field("type", $._possibly_implicitly_unwrapped_type)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
self_expression: ($) => "self",
|
|
super_expression: ($) => seq("super"),
|
|
_else_options: ($) => choice(field("else_branch", $.block), field("else_branch", $.if_statement)),
|
|
if_statement: ($) =>
|
|
prec.right(
|
|
PRECS["if"],
|
|
seq(
|
|
"if",
|
|
sep1(field("condition", $.if_condition), ","),
|
|
field("body", $.block),
|
|
optional(seq(alias($["else"], "else"), $._else_options))
|
|
)
|
|
),
|
|
if_condition: ($) =>
|
|
field("kind", choice($.if_let_binding, $.expression, $.availability_condition)),
|
|
if_let_binding: ($) =>
|
|
seq(
|
|
$._direct_or_indirect_binding,
|
|
optional(seq($._equal_sign, field("value", $.expression))),
|
|
field("where", optional($.where_clause))
|
|
),
|
|
guard_statement: ($) =>
|
|
prec.right(
|
|
PRECS["if"],
|
|
seq(
|
|
"guard",
|
|
sep1(field("condition", $.if_condition), ","),
|
|
alias($["else"], "else"),
|
|
field("body", $.block)
|
|
)
|
|
),
|
|
switch_statement: ($) =>
|
|
prec.right(
|
|
PRECS["switch"],
|
|
seq(
|
|
"switch",
|
|
field("expr", $.expression),
|
|
"{",
|
|
repeat(field("entry", $.switch_entry)),
|
|
"}"
|
|
)
|
|
),
|
|
switch_entry: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
choice(
|
|
seq(
|
|
"case",
|
|
field("pattern", $.switch_pattern),
|
|
field("where", optional($.where_clause)),
|
|
repeat(seq(",", field("pattern", $.switch_pattern)))
|
|
),
|
|
field("default", $.default_keyword)
|
|
),
|
|
":",
|
|
$._statements,
|
|
optional("fallthrough")
|
|
),
|
|
switch_pattern: ($) => field("pattern", alias($._binding_pattern_with_expr, $.pattern)),
|
|
do_statement: ($) =>
|
|
prec.right(PRECS["do"], seq("do", field("body", $.block), repeat(field("catch", $.catch_block)))),
|
|
catch_block: ($) =>
|
|
seq(
|
|
field("keyword", $.catch_keyword),
|
|
field("error", optional(alias($._binding_pattern_no_expr, $.pattern))),
|
|
field("where", optional($.where_clause)),
|
|
field("body", $.block)
|
|
),
|
|
where_clause: ($) => prec.left(seq(field("keyword", $.where_keyword), field("expr", $.expression))),
|
|
key_path_expression: ($) =>
|
|
prec.right(
|
|
PRECS.keypath,
|
|
seq(
|
|
"\\",
|
|
field("type", optional(
|
|
choice($.simple_user_type, $.array_type, $.dictionary_type)
|
|
)),
|
|
repeat(seq(".", field("component", $.key_path_component)))
|
|
)
|
|
),
|
|
key_path_string_expression: ($) =>
|
|
prec.left(seq($._hash_symbol, "keyPath", "(", field("expr", $.expression), ")")),
|
|
key_path_component: ($) =>
|
|
prec.left(
|
|
choice(
|
|
seq(field("name", $.simple_identifier), repeat(field("postfix", $.key_path_postfix))),
|
|
repeat1(field("postfix", $.key_path_postfix))
|
|
)
|
|
),
|
|
key_path_postfix: ($) =>
|
|
choice(
|
|
"?",
|
|
field("force_unwrap", $.bang),
|
|
"self",
|
|
seq("[", optional(sep1(field("argument", $.value_argument), ",")), "]")
|
|
),
|
|
try_operator: ($) =>
|
|
prec.right(
|
|
seq("try", choice(optional($._try_operator_type), $._fake_try_bang))
|
|
),
|
|
_try_operator_type: ($) =>
|
|
choice(token.immediate("!"), token.immediate("?")),
|
|
_assignment_and_operator: ($) =>
|
|
choice("+=", "-=", "*=", "/=", "%=", $._equal_sign),
|
|
_equality_operator: ($) => choice("!=", "!==", $._eq_eq, "==="),
|
|
_comparison_operator: ($) => choice("<", ">", "<=", ">="),
|
|
_three_dot_operator: ($) => alias("...", "..."), // Weird alias to satisfy highlight queries
|
|
_open_ended_range_operator: ($) => alias("..<", "..<"),
|
|
_is_operator: ($) => "is",
|
|
_additive_operator: ($) =>
|
|
choice(
|
|
alias($._plus_then_ws, "+"),
|
|
alias($._minus_then_ws, "-"),
|
|
"+",
|
|
"-"
|
|
),
|
|
// The `/` operator conflicts with a regex literal (which itself appears to conflict with a
|
|
// comment, for some reason), so we must give it equivalent token precedence.
|
|
_multiplicative_operator: ($) =>
|
|
choice("*", alias(token(prec(PRECS.regex, "/")), "/"), "%"),
|
|
as_operator: ($) => choice($._as, $._as_quest, $._as_bang),
|
|
_prefix_unary_operator: ($) =>
|
|
prec.right(
|
|
choice(
|
|
"++",
|
|
"--",
|
|
"-",
|
|
"+",
|
|
$.bang,
|
|
"&",
|
|
"~",
|
|
$._dot,
|
|
$.custom_operator
|
|
)
|
|
),
|
|
_bitwise_binary_operator: ($) => choice("&", "|", "^", "<<", ">>"),
|
|
_postfix_unary_operator: ($) => choice("++", "--", $.bang),
|
|
directly_assignable_expression: ($) => field("expr", $.expression),
|
|
|
|
////////////////////////////////
|
|
// Statements - https://docs.swift.org/swift-book/ReferenceManual/Statements.html
|
|
////////////////////////////////
|
|
_statements: ($) =>
|
|
prec.left(
|
|
// Left precedence is required in switch statements
|
|
seq(
|
|
field("statement", $._local_statement),
|
|
repeat(seq($._semi, field("statement", $._local_statement))),
|
|
optional($._semi)
|
|
)
|
|
),
|
|
_local_statement: ($) =>
|
|
choice(
|
|
$.expression,
|
|
$.local_declaration,
|
|
$._labeled_statement,
|
|
$.control_transfer_statement
|
|
),
|
|
_top_level_statement: ($) =>
|
|
choice(
|
|
$.expression,
|
|
$.global_declaration,
|
|
$._labeled_statement,
|
|
$._throw_statement
|
|
),
|
|
block: ($) => prec(PRECS.block, seq("{", optional($._statements), "}")),
|
|
_labeled_statement: ($) =>
|
|
seq(
|
|
optional($.statement_label),
|
|
choice(
|
|
$.for_statement,
|
|
$.while_statement,
|
|
$.repeat_while_statement,
|
|
$.do_statement,
|
|
$.if_statement,
|
|
$.guard_statement,
|
|
$.switch_statement
|
|
)
|
|
),
|
|
statement_label: ($) => token(/[a-zA-Z_][a-zA-Z_0-9]*:/),
|
|
for_statement: ($) =>
|
|
prec(
|
|
PRECS.loop,
|
|
seq(
|
|
"for",
|
|
field("try", optional($.try_operator)),
|
|
optional($._await_operator),
|
|
field("item", alias($._binding_pattern_no_expr, $.pattern)),
|
|
field("type", optional($.type_annotation)),
|
|
"in",
|
|
field("collection", $._for_statement_collection),
|
|
field("where", optional($.where_clause)),
|
|
field("body", $.block)
|
|
)
|
|
),
|
|
_for_statement_collection: ($) =>
|
|
// If this expression has "await", this triggers some special-cased logic to prefer function calls. We prefer
|
|
// the opposite, though, since function calls may contain trailing code blocks, which are undesirable here.
|
|
//
|
|
// To fix that, we simply undo the special casing by defining our own `await_expression`.
|
|
choice($.expression, alias($.for_statement_await, $.await_expression)),
|
|
for_statement_await: ($) => seq($._await_operator, field("expr", $.expression)),
|
|
|
|
while_statement: ($) =>
|
|
prec(
|
|
PRECS.loop,
|
|
seq(
|
|
"while",
|
|
sep1(field("condition", $.if_condition), ","),
|
|
field("body", $.block)
|
|
)
|
|
),
|
|
repeat_while_statement: ($) =>
|
|
prec(
|
|
PRECS.loop,
|
|
seq(
|
|
"repeat",
|
|
field("body", $.block),
|
|
// Make sure we make it to the `while` before assuming this is a parameter pack.
|
|
repeat($._implicit_semi),
|
|
"while",
|
|
sep1(field("condition", $.if_condition), ",")
|
|
)
|
|
),
|
|
control_transfer_statement: ($) =>
|
|
choice(
|
|
prec.right(
|
|
PRECS.control_transfer,
|
|
seq(field("kind", $.throw_keyword), field("result", $.expression))
|
|
),
|
|
prec.right(
|
|
PRECS.control_transfer,
|
|
seq(
|
|
field("kind", $._optionally_valueful_control_keyword),
|
|
field("result", optional($.expression))
|
|
)
|
|
)
|
|
),
|
|
_throw_statement: ($) => seq($.throw_keyword, $.expression),
|
|
throw_keyword: ($) => "throw",
|
|
_optionally_valueful_control_keyword: ($) =>
|
|
choice("return", "continue", "break", "yield"),
|
|
assignment: ($) =>
|
|
prec.left(
|
|
PRECS.assignment,
|
|
seq(
|
|
field("target", $.directly_assignable_expression),
|
|
field("operator", $._assignment_and_operator),
|
|
field("result", $.expression)
|
|
)
|
|
),
|
|
value_parameter_pack: ($) =>
|
|
prec.left(PRECS.parameter_pack, seq("each", field("expr", $.expression))),
|
|
value_pack_expansion: ($) =>
|
|
prec.left(PRECS.parameter_pack, seq("repeat", field("expr", $.expression))),
|
|
availability_condition: ($) =>
|
|
seq(
|
|
$._hash_symbol,
|
|
choice("available", "unavailable"),
|
|
"(",
|
|
sep1Opt($._availability_argument, ","),
|
|
")"
|
|
),
|
|
_availability_argument: ($) =>
|
|
choice(seq(field("platform", $.identifier), sep1(field("version", $.integer_literal), ".")), "*"),
|
|
////////////////////////////////
|
|
// Declarations - https://docs.swift.org/swift-book/ReferenceManual/Declarations.html
|
|
////////////////////////////////
|
|
global_declaration: ($) =>
|
|
choice(
|
|
$.import_declaration,
|
|
$.property_declaration,
|
|
$.typealias_declaration,
|
|
$.function_declaration,
|
|
$.init_declaration,
|
|
$.class_declaration,
|
|
$.protocol_declaration,
|
|
$.operator_declaration,
|
|
$.precedence_group_declaration,
|
|
$.associatedtype_declaration,
|
|
$.macro_declaration
|
|
),
|
|
type_level_declaration: ($) =>
|
|
choice(
|
|
$.import_declaration,
|
|
$.property_declaration,
|
|
$.typealias_declaration,
|
|
$.function_declaration,
|
|
$.init_declaration,
|
|
$.class_declaration,
|
|
$.protocol_declaration,
|
|
$.deinit_declaration,
|
|
$.subscript_declaration,
|
|
$.operator_declaration,
|
|
$.precedence_group_declaration,
|
|
$.associatedtype_declaration
|
|
),
|
|
local_declaration: ($) =>
|
|
choice(
|
|
alias($._local_property_declaration, $.property_declaration),
|
|
alias($._local_typealias_declaration, $.typealias_declaration),
|
|
alias($._local_function_declaration, $.function_declaration),
|
|
alias($._local_class_declaration, $.class_declaration)
|
|
),
|
|
_local_property_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($._locally_permitted_modifiers)),
|
|
$._modifierless_property_declaration
|
|
),
|
|
_local_typealias_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($._locally_permitted_modifiers)),
|
|
$._modifierless_typealias_declaration
|
|
),
|
|
_local_function_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($._locally_permitted_modifiers)),
|
|
$._modifierless_function_declaration
|
|
),
|
|
_local_class_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($._locally_permitted_modifiers)),
|
|
$._modifierless_class_declaration
|
|
),
|
|
import_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
"import",
|
|
optional($._import_kind),
|
|
field("name", $.identifier)
|
|
),
|
|
_import_kind: ($) =>
|
|
choice(
|
|
"typealias",
|
|
"struct",
|
|
"class",
|
|
"enum",
|
|
"protocol",
|
|
"let",
|
|
"var",
|
|
"func"
|
|
),
|
|
protocol_property_declaration: ($) =>
|
|
prec.right(
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
field("name", alias($._binding_kind_and_pattern, $.pattern)),
|
|
field("type", optional($.type_annotation)),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
field("requirements", $.protocol_property_requirements)
|
|
)
|
|
),
|
|
protocol_property_requirements: ($) =>
|
|
seq("{", repeat(field("accessor", choice($.getter_specifier, $.setter_specifier))), "}"),
|
|
property_declaration: ($) =>
|
|
seq(field("modifiers", optional($.modifiers)), $._modifierless_property_declaration),
|
|
_modifierless_property_declaration: ($) =>
|
|
prec.right(
|
|
seq(
|
|
$._possibly_async_binding_pattern_kind,
|
|
sep1(field("declarator", $.property_binding), ",")
|
|
)
|
|
),
|
|
property_binding: ($) =>
|
|
prec.left(
|
|
seq(
|
|
field("name", alias($._no_expr_pattern_already_bound, $.pattern)),
|
|
field("type", optional($.type_annotation)),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
optional(
|
|
choice(
|
|
$._expression_with_willset_didset,
|
|
$._expression_without_willset_didset,
|
|
field("observers", $.willset_didset_block),
|
|
field("computed_value", $.computed_property)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
_expression_with_willset_didset: ($) =>
|
|
prec.dynamic(
|
|
1,
|
|
seq(
|
|
$._equal_sign,
|
|
field("value", $.expression),
|
|
field("observers", $.willset_didset_block)
|
|
)
|
|
),
|
|
_expression_without_willset_didset: ($) =>
|
|
seq($._equal_sign, field("value", $.expression)),
|
|
willset_didset_block: ($) =>
|
|
choice(
|
|
seq("{", field("willset", $.willset_clause), field("didset", optional($.didset_clause)), "}"),
|
|
seq("{", field("didset", $.didset_clause), field("willset", optional($.willset_clause)), "}")
|
|
),
|
|
willset_clause: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
"willSet",
|
|
optional(seq("(", field("parameter", $.simple_identifier), ")")),
|
|
field("body", $.block)
|
|
),
|
|
didset_clause: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
"didSet",
|
|
optional(seq("(", field("parameter", $.simple_identifier), ")")),
|
|
field("body", $.block)
|
|
),
|
|
typealias_declaration: ($) =>
|
|
seq(field("modifiers", optional($.modifiers)), $._modifierless_typealias_declaration),
|
|
_modifierless_typealias_declaration: ($) =>
|
|
seq(
|
|
"typealias",
|
|
field("name", alias($.simple_identifier, $.type_identifier)),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
$._equal_sign,
|
|
field("value", $.type)
|
|
),
|
|
function_declaration: ($) =>
|
|
prec.right(
|
|
seq($._bodyless_function_declaration, field("body", $.block))
|
|
),
|
|
_modifierless_function_declaration: ($) =>
|
|
prec.right(
|
|
seq(
|
|
$._modifierless_function_declaration_no_body,
|
|
field("body", $.block)
|
|
)
|
|
),
|
|
_bodyless_function_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
optional("class"), // XXX: This should be possible in non-last position, but that creates parsing ambiguity
|
|
$._modifierless_function_declaration_no_body
|
|
),
|
|
_modifierless_function_declaration_no_body: ($) =>
|
|
prec.right(
|
|
seq(
|
|
$._non_constructor_function_decl,
|
|
field("type_parameters", optional($.type_parameters)),
|
|
$._function_value_parameters,
|
|
field("async", optional($._async_keyword)),
|
|
field("throws", optional(choice($.throws_clause, $.throws))),
|
|
optional(
|
|
seq(
|
|
$._arrow_operator,
|
|
field("return_type", $._possibly_implicitly_unwrapped_type)
|
|
)
|
|
),
|
|
field("type_constraints", optional($.type_constraints))
|
|
)
|
|
),
|
|
macro_declaration: ($) =>
|
|
seq(
|
|
$._macro_head,
|
|
field("name", $.simple_identifier),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
$._macro_signature,
|
|
optional(field("definition", $.macro_definition)),
|
|
field("type_constraints", optional($.type_constraints))
|
|
),
|
|
_macro_head: ($) => seq(field("modifiers", optional($.modifiers)), "macro"),
|
|
_macro_signature: ($) =>
|
|
seq(
|
|
$._function_value_parameters,
|
|
optional(seq($._arrow_operator, field("return_type", $.unannotated_type)))
|
|
),
|
|
macro_definition: ($) =>
|
|
seq(
|
|
$._equal_sign,
|
|
field("body", choice($.expression, $.external_macro_definition))
|
|
),
|
|
|
|
external_macro_definition: ($) =>
|
|
seq($._hash_symbol, "externalMacro", field("arguments", $.value_arguments)),
|
|
|
|
class_declaration: ($) =>
|
|
seq(field("modifiers", optional($.modifiers)), $._modifierless_class_declaration),
|
|
_modifierless_class_declaration: ($) =>
|
|
prec.right(
|
|
choice(
|
|
seq(
|
|
field("declaration_kind", choice("class", "struct", "actor")),
|
|
field("name", alias($.simple_identifier, $.type_identifier)),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
optional(seq(":", $._inheritance_specifiers)),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
field("body", $.class_body)
|
|
),
|
|
seq(
|
|
field("declaration_kind", "extension"),
|
|
field("name", $.unannotated_type),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
optional(seq(":", $._inheritance_specifiers)),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
field("body", $.class_body)
|
|
),
|
|
seq(
|
|
optional("indirect"),
|
|
field("declaration_kind", "enum"),
|
|
field("name", alias($.simple_identifier, $.type_identifier)),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
optional(seq(":", $._inheritance_specifiers)),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
field("body", $.enum_class_body)
|
|
)
|
|
)
|
|
),
|
|
class_body: ($) => seq("{", optional($._class_member_declarations), "}"),
|
|
_inheritance_specifiers: ($) =>
|
|
prec.left(sep1($._annotated_inheritance_specifier, choice(",", "&"))),
|
|
_annotated_inheritance_specifier: ($) =>
|
|
seq(repeat(field("attribute", $.attribute)), field("inherits", $.inheritance_specifier)),
|
|
inheritance_specifier: ($) =>
|
|
prec.left(
|
|
field(
|
|
"inherits_from",
|
|
choice($.user_type, $.function_type, $.suppressed_constraint)
|
|
)
|
|
),
|
|
type_parameters: ($) =>
|
|
seq(
|
|
"<",
|
|
sep1Opt(field("parameter", $.type_parameter), ","),
|
|
field("constraints", optional($.type_constraints)),
|
|
">"
|
|
),
|
|
type_parameter: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.type_parameter_modifiers)),
|
|
field("name", $._type_parameter_possibly_packed),
|
|
optional(seq(":", field("type", $.type)))
|
|
),
|
|
_type_parameter_possibly_packed: ($) =>
|
|
choice(
|
|
alias($.simple_identifier, $.type_identifier),
|
|
$.type_parameter_pack
|
|
),
|
|
|
|
type_constraints: ($) =>
|
|
prec.right(seq(field("keyword", $.where_keyword), sep1Opt(field("constraint", $.type_constraint), ","))),
|
|
type_constraint: ($) =>
|
|
field("constraint", choice($.inheritance_constraint, $.equality_constraint)),
|
|
inheritance_constraint: ($) =>
|
|
seq(
|
|
repeat(field("attribute", $.attribute)),
|
|
field("constrained_type", $._constrained_type),
|
|
":",
|
|
field("inherits_from", $._possibly_implicitly_unwrapped_type)
|
|
),
|
|
equality_constraint: ($) =>
|
|
seq(
|
|
repeat(field("attribute", $.attribute)),
|
|
field("constrained_type", $._constrained_type),
|
|
choice($._equal_sign, $._eq_eq),
|
|
field("must_equal", $.type)
|
|
),
|
|
_constrained_type: ($) => choice($.identifier, $.nested_type_identifier),
|
|
nested_type_identifier: ($) =>
|
|
seq(
|
|
field("base", $.unannotated_type),
|
|
optional(seq(".", sep1(field("member", $.simple_identifier), ".")))
|
|
),
|
|
_class_member_separator: ($) => choice($._semi, $._multiline_comment),
|
|
_class_member_declarations: ($) =>
|
|
seq(
|
|
sep1(field("member", $.type_level_declaration), $._class_member_separator),
|
|
optional($._class_member_separator)
|
|
),
|
|
_function_value_parameters: ($) =>
|
|
repeat1(
|
|
seq("(", optional(sep1Opt(field("parameter", $.function_parameter), ",")), ")")
|
|
),
|
|
function_parameter: ($) =>
|
|
seq(
|
|
field("attribute", optional($.attribute)),
|
|
field("parameter", $.parameter),
|
|
optional(seq($._equal_sign, field("default_value", $.expression)))
|
|
),
|
|
parameter: ($) =>
|
|
seq(
|
|
optional(field("external_name", $.simple_identifier)),
|
|
field("name", $.simple_identifier),
|
|
":",
|
|
field("modifiers", optional($.parameter_modifiers)),
|
|
field("type", $._possibly_implicitly_unwrapped_type),
|
|
optional($._three_dot_operator)
|
|
),
|
|
_non_constructor_function_decl: ($) =>
|
|
seq(
|
|
"func",
|
|
field("name", choice($.simple_identifier, $.referenceable_operator))
|
|
),
|
|
referenceable_operator: ($) =>
|
|
field("operator", choice(
|
|
$.custom_operator,
|
|
$._comparison_operator,
|
|
$._additive_operator,
|
|
$._multiplicative_operator,
|
|
$._equality_operator,
|
|
$._assignment_and_operator,
|
|
"++",
|
|
"--",
|
|
$.bang,
|
|
"~",
|
|
"|",
|
|
"^",
|
|
"<<",
|
|
">>",
|
|
"&"
|
|
)),
|
|
// Hide the fact that certain symbols come from the custom scanner by aliasing them to their
|
|
// string variants. This keeps us from having to see them in the syntax tree (which would be
|
|
// noisy) but allows callers to refer to them as nodes by their text form like with any
|
|
// operator.
|
|
_equal_sign: ($) => alias($._eq_custom, "="),
|
|
_eq_eq: ($) => alias($._eq_eq_custom, "=="),
|
|
_dot: ($) => alias($._dot_custom, "."),
|
|
_arrow_operator: ($) => alias($._arrow_operator_custom, "->"),
|
|
_conjunction_operator: ($) => alias($._conjunction_operator_custom, "&&"),
|
|
_disjunction_operator: ($) => alias($._disjunction_operator_custom, "||"),
|
|
_nil_coalescing_operator: ($) =>
|
|
alias($._nil_coalescing_operator_custom, "??"),
|
|
_as: ($) => alias($._as_custom, "as"),
|
|
_as_quest: ($) => alias($._as_quest_custom, "as?"),
|
|
_as_bang: ($) => alias($._as_bang_custom, "as!"),
|
|
_hash_symbol: ($) => alias($._hash_symbol_custom, "#"),
|
|
bang: ($) => choice($._bang_custom, "!"),
|
|
_async_keyword: ($) => alias($._async_keyword_custom, "async"),
|
|
_async_modifier: ($) => token("async"),
|
|
throws: ($) => choice($._throws_keyword, $._rethrows_keyword),
|
|
throws_clause: ($) =>
|
|
seq($._throws_keyword, "(", field("type", $.unannotated_type), ")"),
|
|
enum_class_body: ($) =>
|
|
seq("{", repeat(field("member", choice($.enum_entry, $.type_level_declaration))), "}"),
|
|
enum_entry: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
optional("indirect"),
|
|
"case",
|
|
sep1(field("case", $.enum_case_entry), ","),
|
|
optional(";")
|
|
),
|
|
enum_case_entry: ($) =>
|
|
seq(
|
|
field("name", $.simple_identifier),
|
|
optional($._enum_entry_suffix)
|
|
),
|
|
_enum_entry_suffix: ($) =>
|
|
choice(
|
|
field("data_contents", $.enum_type_parameters),
|
|
seq($._equal_sign, field("raw_value", $.expression))
|
|
),
|
|
enum_type_parameters: ($) =>
|
|
seq(
|
|
"(",
|
|
optional(sep1(field("parameter", $.enum_type_parameter), ",")),
|
|
")"
|
|
),
|
|
enum_type_parameter: ($) =>
|
|
seq(
|
|
optional(
|
|
seq(optional(field("external_name", $.wildcard_pattern)), field("name", $.simple_identifier), ":")
|
|
),
|
|
field("type", $.type),
|
|
optional(seq($._equal_sign, field("default_value", $.expression)))
|
|
),
|
|
protocol_declaration: ($) =>
|
|
prec.right(
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
"protocol",
|
|
field("name", alias($.simple_identifier, $.type_identifier)),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
optional(seq(":", $._inheritance_specifiers)),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
field("body", $.protocol_body)
|
|
)
|
|
),
|
|
protocol_body: ($) =>
|
|
seq("{", optional($._protocol_member_declarations), "}"),
|
|
_protocol_member_declarations: ($) =>
|
|
seq(sep1(field("member", $.protocol_member_declaration), $._semi), optional($._semi)),
|
|
protocol_member_declaration: ($) =>
|
|
choice(
|
|
$.protocol_function_declaration,
|
|
$.init_declaration,
|
|
$.deinit_declaration,
|
|
$.protocol_property_declaration,
|
|
$.typealias_declaration,
|
|
$.associatedtype_declaration,
|
|
$.subscript_declaration
|
|
),
|
|
protocol_function_declaration: ($) =>
|
|
seq(
|
|
$._bodyless_function_declaration,
|
|
optional(field("body", $.block))
|
|
),
|
|
init_declaration: ($) =>
|
|
prec.right(
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
optional("class"),
|
|
"init",
|
|
optional(choice($._quest, field("bang", $.bang))),
|
|
field("type_parameters", optional($.type_parameters)),
|
|
$._function_value_parameters,
|
|
field("async", optional($._async_keyword)),
|
|
field("throws", optional(choice($.throws_clause, $.throws))),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
optional(field("body", $.block))
|
|
)
|
|
),
|
|
deinit_declaration: ($) =>
|
|
prec.right(
|
|
seq(field("modifiers", optional($.modifiers)), "deinit", field("body", $.block))
|
|
),
|
|
subscript_declaration: ($) =>
|
|
prec.right(
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
"subscript",
|
|
field("type_parameters", optional($.type_parameters)),
|
|
$._function_value_parameters,
|
|
optional(
|
|
seq(
|
|
$._arrow_operator,
|
|
field("return_type", $._possibly_implicitly_unwrapped_type)
|
|
)
|
|
),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
field("body", $.computed_property)
|
|
)
|
|
),
|
|
computed_property: ($) =>
|
|
seq(
|
|
"{",
|
|
choice(
|
|
optional($._statements),
|
|
repeat(
|
|
field("accessor", choice($.computed_getter, $.computed_setter, $.computed_modify))
|
|
)
|
|
),
|
|
"}"
|
|
),
|
|
computed_getter: ($) =>
|
|
seq(repeat(field("attribute", $.attribute)), field("specifier", $.getter_specifier), optional(field("body", $.block))),
|
|
computed_modify: ($) =>
|
|
seq(repeat(field("attribute", $.attribute)), field("specifier", $.modify_specifier), optional(field("body", $.block))),
|
|
computed_setter: ($) =>
|
|
seq(
|
|
repeat(field("attribute", $.attribute)),
|
|
field("specifier", $.setter_specifier),
|
|
optional(seq("(", field("parameter", $.simple_identifier), ")")),
|
|
optional(field("body", $.block))
|
|
),
|
|
getter_specifier: ($) =>
|
|
seq(field("mutation", optional($.mutation_modifier)), "get", optional($._getter_effects)),
|
|
setter_specifier: ($) => seq(field("mutation", optional($.mutation_modifier)), "set"),
|
|
modify_specifier: ($) => seq(field("mutation", optional($.mutation_modifier)), "_modify"),
|
|
_getter_effects: ($) =>
|
|
repeat1(field("effect", choice(alias($._async_keyword, $.async_keyword), $.throws_clause, $.throws))),
|
|
operator_declaration: ($) =>
|
|
seq(
|
|
field("kind", choice("prefix", "infix", "postfix")),
|
|
"operator",
|
|
field("name", $.referenceable_operator),
|
|
optional(seq(":", field("precedence_group", $.simple_identifier))),
|
|
field("body", optional($.deprecated_operator_declaration_body))
|
|
),
|
|
// The Swift compiler no longer accepts these, but some very old code still uses it.
|
|
deprecated_operator_declaration_body: ($) =>
|
|
seq("{", repeat(field("entry", choice($.simple_identifier, $._basic_literal))), "}"),
|
|
precedence_group_declaration: ($) =>
|
|
seq(
|
|
"precedencegroup",
|
|
field("name", $.simple_identifier),
|
|
"{",
|
|
field("attributes", optional($.precedence_group_attributes)),
|
|
"}"
|
|
),
|
|
precedence_group_attributes: ($) => repeat1(field("attribute", $.precedence_group_attribute)),
|
|
precedence_group_attribute: ($) =>
|
|
seq(
|
|
field("name", $.simple_identifier),
|
|
":",
|
|
field("value", choice($.simple_identifier, $.boolean_literal))
|
|
),
|
|
associatedtype_declaration: ($) =>
|
|
seq(
|
|
field("modifiers", optional($.modifiers)),
|
|
"associatedtype",
|
|
field("name", alias($.simple_identifier, $.type_identifier)),
|
|
optional(seq(":", field("must_inherit", $.type))),
|
|
field("type_constraints", optional($.type_constraints)),
|
|
optional(seq($._equal_sign, field("default_value", $.type)))
|
|
),
|
|
////////////////////////////////
|
|
// Attributes - https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
|
|
////////////////////////////////
|
|
attribute: ($) =>
|
|
seq(
|
|
"@",
|
|
field("name", $.user_type),
|
|
// attribute arguments are a mess of special cases, maybe this is good enough?
|
|
optional(seq("(", sep1Opt($._attribute_argument, ","), ")"))
|
|
),
|
|
_attribute_argument: ($) =>
|
|
choice(
|
|
// labeled function parameters, used in custom property wrappers
|
|
seq(field("argument_name", $.simple_identifier), ":", field("argument", $.expression)),
|
|
// Unlabeled function parameters, simple identifiers, or `*`
|
|
field("argument", $.expression),
|
|
// References to param names (used in `@objc(foo:bar:)`)
|
|
repeat1(seq(field("param_ref", $.simple_identifier), ":")),
|
|
// Version restrictions (iOS 3.4.5, Swift 5.0.0)
|
|
seq(repeat1(field("platform", $.simple_identifier)), sep1(field("version", $.integer_literal), "."))
|
|
),
|
|
////////////////////////////////
|
|
// Patterns - https://docs.swift.org/swift-book/ReferenceManual/Patterns.html
|
|
////////////////////////////////
|
|
_universally_allowed_pattern: ($) =>
|
|
choice(
|
|
$.wildcard_pattern,
|
|
$.tuple_pattern,
|
|
$.type_casting_pattern,
|
|
$.case_pattern
|
|
),
|
|
_bound_identifier: ($) => field("bound_identifier", $.simple_identifier),
|
|
|
|
_binding_pattern_no_expr: ($) =>
|
|
seq(
|
|
field("kind", choice(
|
|
$._universally_allowed_pattern,
|
|
$.binding_pattern,
|
|
$._bound_identifier
|
|
)),
|
|
optional($._quest)
|
|
),
|
|
_no_expr_pattern_already_bound: ($) =>
|
|
seq(
|
|
field("kind", choice($._universally_allowed_pattern, $._bound_identifier)),
|
|
optional($._quest)
|
|
),
|
|
_binding_pattern_with_expr: ($) =>
|
|
seq(
|
|
field("kind", choice(
|
|
$._universally_allowed_pattern,
|
|
$.binding_pattern,
|
|
$.expression
|
|
)),
|
|
optional($._quest)
|
|
),
|
|
_non_binding_pattern_with_expr: ($) =>
|
|
seq(
|
|
field("kind", choice($._universally_allowed_pattern, $.expression)),
|
|
optional($._quest)
|
|
),
|
|
_direct_or_indirect_binding: ($) =>
|
|
seq(
|
|
choice(
|
|
field("pattern", alias($._binding_kind_and_pattern, $.pattern)),
|
|
seq("case", field("pattern", alias($._binding_pattern_no_expr, $.pattern)))
|
|
),
|
|
field("type", optional($.type_annotation))
|
|
),
|
|
value_binding_pattern: ($) => field("mutability", choice("var", "let")),
|
|
_possibly_async_binding_pattern_kind: ($) =>
|
|
seq(optional($._async_modifier), field("binding", $.value_binding_pattern)),
|
|
_binding_kind_and_pattern: ($) =>
|
|
seq(
|
|
$._possibly_async_binding_pattern_kind,
|
|
$._no_expr_pattern_already_bound
|
|
),
|
|
wildcard_pattern: ($) => "_",
|
|
tuple_pattern_item: ($) =>
|
|
choice(
|
|
seq(
|
|
field("name", $.simple_identifier),
|
|
":",
|
|
field("pattern", alias($._binding_pattern_with_expr, $.pattern))
|
|
),
|
|
field("pattern", alias($._binding_pattern_with_expr, $.pattern))
|
|
),
|
|
tuple_pattern: ($) => seq("(", sep1Opt(field("item", $.tuple_pattern_item), ","), ")"),
|
|
case_pattern: ($) =>
|
|
seq(
|
|
optional("case"),
|
|
optional(field("type", $.user_type)), // XXX this should just be _type but that creates ambiguity
|
|
$._dot,
|
|
field("name", $.simple_identifier),
|
|
optional(field("arguments", $.tuple_pattern))
|
|
),
|
|
type_casting_pattern: ($) =>
|
|
choice(
|
|
seq("is", field("type", $.type)),
|
|
seq(field("pattern", alias($._binding_pattern_no_expr, $.pattern)), $._as, field("type", $.type))
|
|
),
|
|
binding_pattern: ($) =>
|
|
seq(
|
|
seq(optional("case"), field("binding", $.value_binding_pattern)),
|
|
field("pattern", alias($._no_expr_pattern_already_bound, $.pattern))
|
|
),
|
|
|
|
// ==========
|
|
// Modifiers
|
|
// ==========
|
|
modifiers: ($) =>
|
|
repeat1(
|
|
prec.left(
|
|
field("modifier", choice($._non_local_scope_modifier, $._locally_permitted_modifiers))
|
|
)
|
|
),
|
|
_locally_permitted_modifiers: ($) =>
|
|
repeat1(choice($.attribute, $._locally_permitted_modifier)),
|
|
parameter_modifiers: ($) => repeat1(field("modifier", $.parameter_modifier)),
|
|
_modifier: ($) =>
|
|
choice($._non_local_scope_modifier, $._locally_permitted_modifier),
|
|
_non_local_scope_modifier: ($) =>
|
|
choice(
|
|
$.member_modifier,
|
|
$.visibility_modifier,
|
|
$.function_modifier,
|
|
$.mutation_modifier,
|
|
$.property_modifier,
|
|
$.parameter_modifier
|
|
),
|
|
_locally_permitted_modifier: ($) =>
|
|
choice(
|
|
$.ownership_modifier,
|
|
$.inheritance_modifier,
|
|
$.property_behavior_modifier
|
|
),
|
|
property_behavior_modifier: ($) => "lazy",
|
|
type_modifiers: ($) => repeat1(field("attribute", $.attribute)),
|
|
member_modifier: ($) =>
|
|
choice("override", "convenience", "required", "nonisolated"),
|
|
visibility_modifier: ($) =>
|
|
seq(
|
|
choice(
|
|
"public",
|
|
"private",
|
|
"internal",
|
|
"fileprivate",
|
|
"open",
|
|
"package"
|
|
),
|
|
optional(seq("(", "set", ")"))
|
|
),
|
|
type_parameter_modifiers: ($) => repeat1(field("attribute", $.attribute)),
|
|
function_modifier: ($) => choice("infix", "postfix", "prefix"),
|
|
mutation_modifier: ($) => choice("mutating", "nonmutating"),
|
|
property_modifier: ($) =>
|
|
choice("static", "dynamic", "optional", "class", "distributed"),
|
|
inheritance_modifier: ($) => choice("final"),
|
|
parameter_modifier: ($) =>
|
|
choice(
|
|
"inout",
|
|
"@escaping",
|
|
"@autoclosure",
|
|
$._parameter_ownership_modifier
|
|
),
|
|
ownership_modifier: ($) =>
|
|
choice("weak", "unowned", "unowned(safe)", "unowned(unsafe)"),
|
|
_parameter_ownership_modifier: ($) => choice("borrowing", "consuming"),
|
|
use_site_target: ($) =>
|
|
seq(
|
|
choice(
|
|
"property",
|
|
"get",
|
|
"set",
|
|
"receiver",
|
|
"param",
|
|
"setparam",
|
|
"delegate"
|
|
),
|
|
":"
|
|
),
|
|
directive: ($) =>
|
|
prec.right(
|
|
PRECS.comment,
|
|
choice(
|
|
seq(alias($._directive_if, "#if"), field("condition", $.compilation_condition)),
|
|
seq(alias($._directive_elseif, "#elseif"), field("condition", $.compilation_condition)),
|
|
seq(alias($._directive_else, "#else")),
|
|
seq(alias($._directive_endif, "#endif"))
|
|
)
|
|
),
|
|
compilation_condition: ($) =>
|
|
prec.right(
|
|
choice(
|
|
seq("os", "(", field("name", $.simple_identifier), ")"),
|
|
seq("arch", "(", field("name", $.simple_identifier), ")"),
|
|
seq(
|
|
"swift",
|
|
"(",
|
|
$._comparison_operator,
|
|
sep1(field("version", $.integer_literal), "."),
|
|
")"
|
|
),
|
|
seq(
|
|
"compiler",
|
|
"(",
|
|
$._comparison_operator,
|
|
sep1(field("version", $.integer_literal), "."),
|
|
")"
|
|
),
|
|
seq("canImport", "(", sep1(field("name", $.simple_identifier), "."), ")"),
|
|
seq("targetEnvironment", "(", field("name", $.simple_identifier), ")"),
|
|
field("value", $.boolean_literal),
|
|
field("name", $.simple_identifier),
|
|
seq("(", field("inner", $.compilation_condition), ")"),
|
|
seq("!", field("operand", $.compilation_condition)),
|
|
seq(
|
|
field("lhs", $.compilation_condition),
|
|
$._conjunction_operator,
|
|
field("rhs", $.compilation_condition)
|
|
),
|
|
seq(
|
|
field("lhs", $.compilation_condition),
|
|
$._disjunction_operator,
|
|
field("rhs", $.compilation_condition)
|
|
)
|
|
)
|
|
),
|
|
diagnostic: ($) =>
|
|
prec(
|
|
PRECS.comment,
|
|
seq(
|
|
$._hash_symbol,
|
|
choice(
|
|
// Using regexes here, rather than actually validating the string literal, because complex string literals
|
|
// cannot be used inside `token()` and we need that to ensure we get the right precedence.
|
|
seq(/error([^\r\n]*)/),
|
|
seq(/warning([^\r\n]*)/),
|
|
seq(/sourceLocation([^\r\n]*)/)
|
|
)
|
|
)
|
|
),
|
|
// Dumping ground for any nodes that used to exist in the grammar, but have since been removed for whatever
|
|
// reason.
|
|
// Neovim applies updates non-atomically to the parser and the queries. Meanwhile, `tree-sitter` rejects any query
|
|
// that contains any unrecognized nodes. Putting those two facts together, we see that we must never remove nodes
|
|
// that once existed.
|
|
unused_for_backward_compatibility: ($) =>
|
|
choice(alias("unused1", "try?"), alias("unused2", "try!")),
|
|
},
|
|
});
|
|
function sep1(rule, separator) {
|
|
return seq(rule, repeat(seq(separator, rule)));
|
|
}
|
|
function sep1Opt(rule, separator) {
|
|
return seq(rule, repeat(seq(separator, rule)), optional(separator));
|
|
}
|
|
|
|
function tree_sitter_version_supports_emoji() {
|
|
try {
|
|
return (
|
|
TREE_SITTER_CLI_VERSION_MAJOR > 0 ||
|
|
TREE_SITTER_CLI_VERSION_MINOR > 20 ||
|
|
TREE_SITTER_CLI_VERSION_PATCH >= 5
|
|
);
|
|
} catch (err) {
|
|
if (err instanceof ReferenceError) {
|
|
return false;
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|