|
|
@@ -0,0 +1,1293 @@
|
|
|
+diff --git a/grammar.js b/grammar.js
|
|
|
+index 21c8523..00bf2b1 100644
|
|
|
+--- a/grammar.js
|
|
|
++++ b/grammar.js
|
|
|
+@@ -21,10 +21,14 @@ const PRECEDENCE = {
|
|
|
+
|
|
|
+ module.exports = grammar({
|
|
|
+ name: "vbnet",
|
|
|
++ word: ($) => $._identifier_token,
|
|
|
++
|
|
|
++ externals: ($) => [$._query_clause_continuation, $.xml_literal],
|
|
|
++
|
|
|
+ extras: $ => [
|
|
|
+ $.comment,
|
|
|
+ $.preprocessor_directive,
|
|
|
+- /[ \t\v\f]+/,
|
|
|
++ /[ \t\v\f\uFEFF]+/,
|
|
|
+ /\r?\n/,
|
|
|
+ /_[ \t]*\r?\n/,
|
|
|
+ ],
|
|
|
+@@ -38,17 +42,14 @@ module.exports = grammar({
|
|
|
+ $._type,
|
|
|
+ ],
|
|
|
+ conflicts: ($) => [
|
|
|
++ [$.binary_expression, $.assignment_expression, $.inline_assignment_statement],
|
|
|
+ [$.simple_name, $.generic_name],
|
|
|
+ [$.await_expression, $.with_expression],
|
|
|
+ [$._name_reference, $._type],
|
|
|
+ [$.object_creation_expression, $.array_creation_expression, $.array_type],
|
|
|
+- [$.string_literal, $.character_literal],
|
|
|
+ [$._primary_expression, $.variable_declarator],
|
|
|
+- [$.lambda_expression, $.with_expression],
|
|
|
+- [$.lambda_expression],
|
|
|
+ [$.binary_expression, $.assignment_expression],
|
|
|
+ [$._primary_expression, $.select_element],
|
|
|
+- [$.with_expression, $.select_element],
|
|
|
+ [$.select_clause],
|
|
|
+ [$._primary_expression, $.group_element],
|
|
|
+ [$._terminator, $._block_terminator],
|
|
|
+@@ -66,7 +67,6 @@ module.exports = grammar({
|
|
|
+ [$.aggregate_element, $.into_element],
|
|
|
+ [$.group_by_clause],
|
|
|
+ [$.else_clause],
|
|
|
+- [$.if_statement],
|
|
|
+ [$.case_else_clause],
|
|
|
+ [$.case_clause],
|
|
|
+ [$.elseif_clause],
|
|
|
+@@ -75,7 +75,6 @@ module.exports = grammar({
|
|
|
+ [$._primary_expression, $.simple_name, $.generic_name],
|
|
|
+ [$._primary_expression, $.variable_declarator, $.labeled_statement],
|
|
|
+ [$._statement, $.with_statement],
|
|
|
+- [$.dictionary_initializer, $.collection_initializer],
|
|
|
+ [
|
|
|
+ $.class_declaration,
|
|
|
+ $.structure_declaration,
|
|
|
+@@ -89,7 +88,6 @@ module.exports = grammar({
|
|
|
+ ],
|
|
|
+ ],
|
|
|
+
|
|
|
+- word: ($) => $._identifier_token,
|
|
|
+
|
|
|
+ rules: {
|
|
|
+ source_file: ($) =>
|
|
|
+@@ -157,13 +155,14 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ _terminator: ($) =>
|
|
|
+- choice(seq(optional(":"), choice("\n", "\r\n")), $._eof),
|
|
|
++ prec.right(choice(seq(optional(":"), choice("\n", "\r\n")), prec(-1, ":"), $._eof)),
|
|
|
+
|
|
|
+- _block_terminator: ($) => prec.right(repeat1(choice("\n", "\r\n", $._eof))),
|
|
|
++ _block_terminator: ($) => prec.right(repeat1(choice("\n", "\r\n", ":", $._eof))),
|
|
|
+
|
|
|
+ _eof: ($) => token(prec(-10, "$")),
|
|
|
+
|
|
|
+- _identifier_token: ($) => token(prec(-1, /[a-zA-Z_][a-zA-Z0-9_]*/)),
|
|
|
++ _identifier_token: ($) =>
|
|
|
++ token(prec(-1, /[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Pc}]*[%$&!#@]?/u)),
|
|
|
+
|
|
|
+ identifier: ($) =>
|
|
|
+ choice(
|
|
|
+@@ -194,7 +193,6 @@ module.exports = grammar({
|
|
|
+ ci("MustInherit"),
|
|
|
+ ci("NotInheritable"),
|
|
|
+ ci("Overrides"),
|
|
|
+- ci("MustOverride"),
|
|
|
+ ci("NotOverridable"),
|
|
|
+ ci("Overridable"),
|
|
|
+ ci("Overloads"),
|
|
|
+@@ -225,43 +223,6 @@ module.exports = grammar({
|
|
|
+ $._statement // Fallback to regular statements
|
|
|
+ ),
|
|
|
+
|
|
|
+- _non_statement_identifier: $ => choice(
|
|
|
+- // Match identifiers that don't start with statement keywords
|
|
|
+- token(prec(1, seq(
|
|
|
+- negative_lookahead(seq(
|
|
|
+- choice(
|
|
|
+- /[Tt][Rr][Yy]/,
|
|
|
+- /[Ss][Ee][Ll][Ee][Cc][Tt]/,
|
|
|
+- /[Ii][Ff]/,
|
|
|
+- /[Ww][Hh][Ii][Ll][Ee]/,
|
|
|
+- /[Dd][Oo]/,
|
|
|
+- /[Ff][Oo][Rr]/,
|
|
|
+- /[Uu][Ss][Ii][Nn][Gg]/,
|
|
|
+- /[Ww][Ii][Tt][Hh]/,
|
|
|
+- /[Tt][Hh][Rr][Oo][Ww]/,
|
|
|
+- /[Rr][Ee][Tt][Uu][Rr][Nn]/,
|
|
|
+- /[Ee][Xx][Ii][Tt]/,
|
|
|
+- /[Cc][Oo][Nn][Tt][Ii][Nn][Uu][Ee]/,
|
|
|
+- /[Ss][Tt][Oo][Pp]/,
|
|
|
+- /[Ee][Nn][Dd]/,
|
|
|
+- /[Gg][Oo][Tt][Oo]/,
|
|
|
+- /[Rr][Ee][Ss][Uu][Mm][Ee]/,
|
|
|
+- /[Ee][Rr][Rr][Oo][Rr]/,
|
|
|
+- /[Oo][Nn]/,
|
|
|
+- /[Rr][Ee][Dd][Ii][Mm]/,
|
|
|
+- /[Ee][Rr][Aa][Ss][Ee]/,
|
|
|
+- /[Ss][Yy][Nn][Cc][Ll][Oo][Cc][Kk]/,
|
|
|
+- /[Rr][Aa][Ii][Ss][Ee][Ee][Vv][Ee][Nn][Tt]/,
|
|
|
+- /[Aa][Dd][Dd][Hh][Aa][Nn][Dd][Ll][Ee][Rr]/,
|
|
|
+- /[Rr][Ee][Mm][Oo][Vv][Ee][Hh][Aa][Nn][Dd][Ll][Ee][Rr]/
|
|
|
+- ),
|
|
|
+- choice(/\s/, /\r/, /\n/, /$/)
|
|
|
+- )),
|
|
|
+- /[a-zA-Z_][a-zA-Z0-9_]*/
|
|
|
+- ))),
|
|
|
+- seq("[", /[^\]]+/, "]") // Bracketed identifier can be anything
|
|
|
+- ),
|
|
|
+-
|
|
|
+ local_declaration_modifier: ($) =>
|
|
|
+ choice(ci("Dim"), ci("Const"), ci("Static")),
|
|
|
+
|
|
|
+@@ -300,6 +261,8 @@ module.exports = grammar({
|
|
|
+ _primary_expression: ($) =>
|
|
|
+ choice(
|
|
|
+ $._literal,
|
|
|
++ $.array_literal,
|
|
|
++ $.tuple_expression,
|
|
|
+ $.identifier,
|
|
|
+ $.parenthesized_expression,
|
|
|
+ $.generic_invocation_expression, // Add this before regular invocation
|
|
|
+@@ -320,17 +283,26 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ generic_invocation_expression: ($) =>
|
|
|
+- prec(
|
|
|
+- PRECEDENCE.INVOCATION + 1,
|
|
|
++ prec.right(
|
|
|
++ PRECEDENCE.INVOCATION + 1,
|
|
|
+ seq(
|
|
|
+ field("function", $._expression),
|
|
|
+ field("type_arguments", $.type_argument_list),
|
|
|
+- field("arguments", $.argument_list)
|
|
|
++ optional(field("arguments", $.argument_list))
|
|
|
+ )
|
|
|
+ ),
|
|
|
+
|
|
|
+ parenthesized_expression: ($) => seq("(", $._expression, ")"),
|
|
|
+
|
|
|
++ tuple_expression: ($) =>
|
|
|
++ seq(
|
|
|
++ "(",
|
|
|
++ choice($._expression, seq(field("name", $.identifier), ":=", $._expression)),
|
|
|
++ ",",
|
|
|
++ commaSep1(choice($._expression, seq(field("name", $.identifier), ":=", $._expression))),
|
|
|
++ ")"
|
|
|
++ ),
|
|
|
++
|
|
|
+ member_access_expression: ($) =>
|
|
|
+ prec.left(
|
|
|
+ PRECEDENCE.MEMBER_ACCESS,
|
|
|
+@@ -350,20 +322,23 @@ module.exports = grammar({
|
|
|
+ )
|
|
|
+ ),
|
|
|
+
|
|
|
+- argument_list: ($) => seq("(", optional(commaSep1($._argument)), ")"),
|
|
|
++ argument_list: ($) => seq("(", optional($._arguments), ")"),
|
|
|
+
|
|
|
+- _argument: ($) =>
|
|
|
+- choice($._expression, $.named_argument, $.omitted_argument),
|
|
|
++ // Argument slots may be omitted between commas (`f(a,, b)`, `f(, x)`) —
|
|
|
++ // VB passes Optional parameters positionally by leaving the slot empty.
|
|
|
++ _arguments: ($) =>
|
|
|
++ seq($._argument, repeat(seq(",", optional($._argument)))),
|
|
|
+
|
|
|
+- omitted_argument: ($) => token(prec(1, ",")),
|
|
|
++ _argument: ($) => choice($._expression, $.named_argument),
|
|
|
+
|
|
|
+ array_access_expression: ($) =>
|
|
|
+ prec(
|
|
|
+ PRECEDENCE.ARRAY_ACCESS,
|
|
|
+ seq(
|
|
|
+ field("array", $._expression),
|
|
|
++ optional("?"),
|
|
|
+ "(",
|
|
|
+- field("indices", commaSep1($._expression)),
|
|
|
++ field("indices", $._arguments),
|
|
|
+ ")"
|
|
|
+ )
|
|
|
+ ),
|
|
|
+@@ -471,16 +446,53 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ lambda_expression: ($) =>
|
|
|
+- seq(
|
|
|
+- choice(ci("Function"), ci("Sub")),
|
|
|
+- field("parameters", $.lambda_parameter_list),
|
|
|
+- field("body", choice($._expression, $._statement_block))
|
|
|
++ choice(
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ optional(choice(ci("Async"), ci("Iterator"))),
|
|
|
++ choice(ci("Function"), ci("Sub")),
|
|
|
++ field("parameters", $.lambda_parameter_list),
|
|
|
++ field(
|
|
|
++ "body",
|
|
|
++ choice(
|
|
|
++ $._expression,
|
|
|
++ alias($.inline_if_statement, $.if_statement),
|
|
|
++ alias($.inline_throw_statement, $.throw_statement)
|
|
|
++ )
|
|
|
++ )
|
|
|
++ )
|
|
|
++ ),
|
|
|
++ seq(
|
|
|
++ optional(choice(ci("Async"), ci("Iterator"))),
|
|
|
++ ci("Function"),
|
|
|
++ field("parameters", $.lambda_parameter_list),
|
|
|
++ optional($.as_clause),
|
|
|
++ $._block_terminator,
|
|
|
++ repeat($._statement),
|
|
|
++ ci("End"), ci("Function")
|
|
|
++ ),
|
|
|
++ seq(
|
|
|
++ optional(choice(ci("Async"), ci("Iterator"))),
|
|
|
++ ci("Sub"),
|
|
|
++ field("parameters", $.lambda_parameter_list),
|
|
|
++ $._block_terminator,
|
|
|
++ repeat($._statement),
|
|
|
++ ci("End"), ci("Sub")
|
|
|
++ )
|
|
|
+ ),
|
|
|
+
|
|
|
+ lambda_parameter_list: ($) => seq("(", commaSep($.lambda_parameter), ")"),
|
|
|
+
|
|
|
+ lambda_parameter: ($) =>
|
|
|
+- seq(field("name", $.identifier), optional($.as_clause)),
|
|
|
++ seq(
|
|
|
++ optional(field("modifiers", repeat1(choice(ci("ByVal"), ci("ByRef"))))),
|
|
|
++ field("name", $.identifier),
|
|
|
++ optional(choice(
|
|
|
++ $.array_rank_specifier,
|
|
|
++ seq("(", field("bounds", commaSep1($._expression)), ")")
|
|
|
++ )),
|
|
|
++ optional($.as_clause)
|
|
|
++ ),
|
|
|
+
|
|
|
+ typeof_is_expression: ($) =>
|
|
|
+ prec.left(
|
|
|
+@@ -488,7 +500,7 @@ module.exports = grammar({
|
|
|
+ seq(
|
|
|
+ ci("TypeOf"),
|
|
|
+ field("expression", $._expression),
|
|
|
+- ci("Is"),
|
|
|
++ choice(seq(ci("Is"), ci("Not")), token(prec(12, ci("IsNot"))), ci("Is")),
|
|
|
+ field("type", $._type)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+@@ -504,29 +516,12 @@ module.exports = grammar({
|
|
|
+ "initializer",
|
|
|
+ choice(
|
|
|
+ $.object_initializer,
|
|
|
+- $.collection_initializer,
|
|
|
+- $.dictionary_initializer // Add this new option
|
|
|
++ $.collection_initializer
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ),
|
|
|
+
|
|
|
+- dictionary_initializer: ($) =>
|
|
|
+- seq(
|
|
|
+- ci("From"),
|
|
|
+- "{",
|
|
|
+- commaSep($.dictionary_element),
|
|
|
+- "}"
|
|
|
+- ),
|
|
|
+-
|
|
|
+- dictionary_element: ($) =>
|
|
|
+- seq(
|
|
|
+- "{",
|
|
|
+- field("key", $._expression),
|
|
|
+- ",",
|
|
|
+- field("value", $._expression),
|
|
|
+- "}"
|
|
|
+- ),
|
|
|
+
|
|
|
+
|
|
|
+ object_initializer: ($) =>
|
|
|
+@@ -551,6 +546,8 @@ module.exports = grammar({
|
|
|
+ collection_initializer: ($) =>
|
|
|
+ seq(ci("From"), "{", commaSep($._expression), "}"),
|
|
|
+
|
|
|
++ array_literal: ($) => seq("{", commaSep($._expression), "}"),
|
|
|
++
|
|
|
+
|
|
|
+ array_creation_expression: ($) =>
|
|
|
+ seq(
|
|
|
+@@ -646,6 +643,9 @@ module.exports = grammar({
|
|
|
+ seq(field("object", $._expression), "!", field("member", $.identifier)),
|
|
|
+ _literal: ($) =>
|
|
|
+ choice(
|
|
|
++ $.xml_literal,
|
|
|
++ $.interpolated_string_literal,
|
|
|
++ $.date_literal,
|
|
|
+ $.string_literal,
|
|
|
+ $.character_literal,
|
|
|
+ $.integer_literal,
|
|
|
+@@ -654,16 +654,49 @@ module.exports = grammar({
|
|
|
+ $.nothing_literal,
|
|
|
+ $.date_literal
|
|
|
+ ),
|
|
|
+- string_literal: ($) => seq('"', repeat(choice(/[^"\n]+/, '""')), '"'),
|
|
|
+- character_literal: ($) => seq('"', choice(/[^"\n]/, '""'), '"', "c"),
|
|
|
++ string_literal: ($) => token(seq('"', repeat(choice(/[^"]/, '""')), '"')),
|
|
|
++ interpolated_string_literal: ($) =>
|
|
|
++ seq(
|
|
|
++ '$"',
|
|
|
++ repeat(
|
|
|
++ choice(
|
|
|
++ token.immediate(prec(101, /[^"{}]+/)),
|
|
|
++ token.immediate(prec(101, '""')),
|
|
|
++ token.immediate(prec(101, "{{")),
|
|
|
++ token.immediate(prec(101, "}}")),
|
|
|
++ $.interpolation
|
|
|
++ )
|
|
|
++ ),
|
|
|
++ token.immediate('"')
|
|
|
++ ),
|
|
|
++ interpolation: ($) =>
|
|
|
++ seq(
|
|
|
++ token.immediate(prec(101, "{")),
|
|
|
++ $._expression,
|
|
|
++ optional(seq(",", /-?\d+/)),
|
|
|
++ optional(seq(":", /[^}"\r\n]+/)),
|
|
|
++ "}"
|
|
|
++ ),
|
|
|
++ date_literal: ($) => token(prec(2, /#[ \t]*\d[^#\r\n]*#/)),
|
|
|
++ character_literal: ($) => token(seq('"', choice(/[^"\r\n]/, '""'), '"', /[cC]/)),
|
|
|
+ integer_literal: ($) =>
|
|
|
+- token(seq(/\d+/, optional(choice("I", "UI", "L", "UL", "S", "US")))),
|
|
|
++ token(
|
|
|
++ seq(
|
|
|
++ choice(
|
|
|
++ /\d+(_+\d+)*/,
|
|
|
++ /&[Hh][0-9A-Fa-f]+(_+[0-9A-Fa-f]+)*/,
|
|
|
++ /&[Oo][0-7]+(_+[0-7]+)*/,
|
|
|
++ /&[Bb][01]+(_+[01]+)*/
|
|
|
++ ),
|
|
|
++ optional(choice(/[Uu][SsIiLl]/, /[SsIiLl]/, "%", "&"))
|
|
|
++ )
|
|
|
++ ),
|
|
|
+ floating_point_literal: ($) =>
|
|
|
+ token(
|
|
|
+ seq(
|
|
|
+ choice(seq(/\d+/, ".", /\d+/), seq(".", /\d+/), /\d+/),
|
|
|
+ optional(/[eE][+-]?\d+/),
|
|
|
+- optional(choice("F", "D", "R"))
|
|
|
++ optional(/[FfRrDd!#@]/)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ boolean_literal: ($) => choice(ci("True"), ci("False")),
|
|
|
+@@ -672,7 +705,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ // Names and types
|
|
|
+ _name_reference: ($) =>
|
|
|
+- choice($.simple_name, $.qualified_name, $.global_qualified_name),
|
|
|
++ choice($.simple_name, $.generic_name, $.qualified_name, $.global_qualified_name),
|
|
|
+
|
|
|
+ simple_name: ($) => $.identifier,
|
|
|
+
|
|
|
+@@ -682,7 +715,7 @@ module.exports = grammar({
|
|
|
+ seq(
|
|
|
+ field("qualifier", $._name_reference),
|
|
|
+ ".",
|
|
|
+- field("name", $.simple_name)
|
|
|
++ field("name", choice($.simple_name, $.generic_name))
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ global_qualified_name: ($) =>
|
|
|
+@@ -694,6 +727,7 @@ module.exports = grammar({
|
|
|
+ $.simple_name,
|
|
|
+ $.qualified_name,
|
|
|
+ $.generic_name,
|
|
|
++ $.global_qualified_name,
|
|
|
+ $.predefined_type,
|
|
|
+ $.array_type,
|
|
|
+ $.tuple_type
|
|
|
+@@ -720,7 +754,8 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ generic_name: ($) => seq(field("name", $.identifier), $.type_argument_list),
|
|
|
+- type_argument_list: ($) => seq("(", ci("Of"), commaSep1($._type), ")"),
|
|
|
++ type_argument_list: ($) =>
|
|
|
++ seq("(", ci("Of"), optional($._type), repeat(seq(",", optional($._type))), ")"),
|
|
|
+ array_type: ($) =>
|
|
|
+ seq(field("element_type", $._type), $.array_rank_specifier),
|
|
|
+ array_rank_specifier: ($) => seq("(", repeat(","), ")"),
|
|
|
+@@ -742,10 +777,14 @@ module.exports = grammar({
|
|
|
+ field("type", $._type)
|
|
|
+ ),
|
|
|
+
|
|
|
+- as_clause: ($) => seq(
|
|
|
+- ci("As"),
|
|
|
+- field("declared_type", $._type)
|
|
|
+- ),
|
|
|
++ as_clause: ($) =>
|
|
|
++ seq(
|
|
|
++ ci("As"),
|
|
|
++ choice(
|
|
|
++ $.object_creation_expression,
|
|
|
++ field("declared_type", $._type)
|
|
|
++ )
|
|
|
++ ),
|
|
|
+
|
|
|
+ implements_member_clause: ($) =>
|
|
|
+ seq(ci("Implements"), commaSep1($._name_reference)),
|
|
|
+@@ -856,7 +895,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ enum_member_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ field("name", $.identifier),
|
|
|
+ optional(seq("=", field("value", $._expression))),
|
|
|
+ $._terminator
|
|
|
+@@ -882,6 +921,10 @@ module.exports = grammar({
|
|
|
+ prec(10, $.inherits_statement),
|
|
|
+ prec(10, $.implements_statement),
|
|
|
+ prec(100, $.constructor_declaration),
|
|
|
++ prec(150, $.external_method_declaration),
|
|
|
++ prec(160, $.custom_event_declaration),
|
|
|
++ prec(170, $.abstract_method_declaration),
|
|
|
++ prec(171, $.abstract_property_declaration),
|
|
|
+ // Explicitly add statements that can appear at class level
|
|
|
+ prec(200, $.try_statement), // Very high precedence
|
|
|
+ prec(200, $.select_statement), // Very high precedence
|
|
|
+@@ -936,33 +979,21 @@ module.exports = grammar({
|
|
|
+ variable_declarator: ($) =>
|
|
|
+ seq(
|
|
|
+ field("name", $.identifier),
|
|
|
++ optional("?"),
|
|
|
+ optional(choice(
|
|
|
+ // Array bounds specification
|
|
|
+ seq("(", field("bounds", commaSep1($._expression)), ")"),
|
|
|
+ // Array rank specification
|
|
|
+ $.array_rank_specifier
|
|
|
+ )),
|
|
|
+- optional(choice(
|
|
|
+- // Regular type declaration
|
|
|
+- $.as_clause,
|
|
|
+- seq(
|
|
|
+- ci("As"),
|
|
|
+- ci("New"),
|
|
|
+- field("type", $._type),
|
|
|
+- optional(field("arguments", $.argument_list)),
|
|
|
+- optional(field("initializer", choice(
|
|
|
+- $.object_initializer,
|
|
|
+- $.collection_initializer
|
|
|
+- )))
|
|
|
+- )
|
|
|
+- )),
|
|
|
++ optional($.as_clause),
|
|
|
+ optional(seq("=", field("initializer", $._expression)))
|
|
|
+ ),
|
|
|
+
|
|
|
+
|
|
|
+ field_declaration: ($) =>
|
|
|
+ prec.dynamic(-100, seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
+ commaSep1($.variable_declarator),
|
|
|
+ $._terminator
|
|
|
+@@ -970,7 +1001,8 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ field_variable_declarator: ($) =>
|
|
|
+ seq(
|
|
|
+- field("name", $._non_statement_identifier),
|
|
|
++ field("name", $.identifier),
|
|
|
++ optional("?"),
|
|
|
+ optional($.array_rank_specifier),
|
|
|
+ optional($.as_clause),
|
|
|
+ optional(seq("=", field("initializer", $._expression)))
|
|
|
+@@ -978,12 +1010,13 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ property_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))), // Changed
|
|
|
+ ci("Property"),
|
|
|
+ field("name", $.identifier),
|
|
|
+ optional(field("parameters", $.parameter_list)),
|
|
|
+- $.as_clause,
|
|
|
++ optional($.as_clause),
|
|
|
++ optional(seq("=", field("initializer", $._expression))),
|
|
|
+ optional($.implements_member_clause),
|
|
|
+ optional(seq("=", field("initializer", $._expression))),
|
|
|
+ choice(
|
|
|
+@@ -1001,7 +1034,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ property_getter: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
+ ci("Get"),
|
|
|
+ $._block_terminator,
|
|
|
+@@ -1013,7 +1046,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ property_setter: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
+ ci("Set"),
|
|
|
+ optional(seq("(", field("parameter", $.parameter), ")")),
|
|
|
+@@ -1028,7 +1061,7 @@ module.exports = grammar({
|
|
|
+ prec(
|
|
|
+ 1,
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
+ choice(
|
|
|
+ seq(
|
|
|
+@@ -1063,7 +1096,7 @@ module.exports = grammar({
|
|
|
+ prec(
|
|
|
+ 100,
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
+ $._sub_new,
|
|
|
+ optional(field("parameters", $.parameter_list)),
|
|
|
+@@ -1087,7 +1120,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ event_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))), // Changed
|
|
|
+ ci("Event"),
|
|
|
+ field("name", $.identifier),
|
|
|
+@@ -1098,7 +1131,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ operator_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))), // Changed
|
|
|
+ ci("Operator"),
|
|
|
+ field(
|
|
|
+@@ -1152,7 +1185,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ interface_method_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))), // Changed
|
|
|
+ choice(ci("Sub"), ci("Function")),
|
|
|
+ field("name", $.identifier),
|
|
|
+@@ -1164,7 +1197,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ interface_property_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))), // Changed
|
|
|
+ ci("Property"),
|
|
|
+ field("name", $.identifier),
|
|
|
+@@ -1175,7 +1208,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ interface_event_declaration: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(field("modifiers", repeat1($.member_modifier))), // Changed
|
|
|
+ ci("Event"),
|
|
|
+ field("name", $.identifier),
|
|
|
+@@ -1209,7 +1242,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ parameter: ($) =>
|
|
|
+ seq(
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ optional(
|
|
|
+ field(
|
|
|
+ "modifiers",
|
|
|
+@@ -1273,7 +1306,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ declaration_statement: ($) =>
|
|
|
+ prec(1, seq( // Add precedence
|
|
|
+- optional(field("attributes", $.attribute_list)),
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
+ field("modifiers", choice(
|
|
|
+ $.local_declaration_modifier,
|
|
|
+ repeat1($.local_declaration_modifier)
|
|
|
+@@ -1282,6 +1315,65 @@ module.exports = grammar({
|
|
|
+ $._terminator
|
|
|
+ )),
|
|
|
+
|
|
|
++ _inline_statements: ($) =>
|
|
|
++ prec.right(seq($._inline_statement, repeat(seq(":", $._inline_statement)))),
|
|
|
++
|
|
|
++ _inline_statement: ($) =>
|
|
|
++ choice(
|
|
|
++ alias($.inline_if_statement, $.if_statement),
|
|
|
++ alias($.inline_assignment_statement, $.assignment_statement),
|
|
|
++ alias($.inline_return_statement, $.return_statement),
|
|
|
++ alias($.inline_throw_statement, $.throw_statement),
|
|
|
++ alias($.inline_goto_statement, $.goto_statement),
|
|
|
++ alias($.inline_exit_statement, $.exit_statement),
|
|
|
++ alias($.inline_continue_statement, $.continue_statement),
|
|
|
++ alias($.inline_call_statement, $.call_statement),
|
|
|
++ alias($.inline_raiseevent_statement, $.raiseevent_statement),
|
|
|
++ alias($.inline_expression_statement, $.expression_statement)
|
|
|
++ ),
|
|
|
++
|
|
|
++ inline_assignment_statement: ($) =>
|
|
|
++ prec.right(
|
|
|
++ PRECEDENCE.ASSIGNMENT + 1,
|
|
|
++ seq(
|
|
|
++ field("left", choice(
|
|
|
++ $.identifier,
|
|
|
++ $.member_access_expression,
|
|
|
++ $.array_access_expression
|
|
|
++ )),
|
|
|
++ field(
|
|
|
++ "operator",
|
|
|
++ choice("=", "+=", "-=", "*=", "/=", "\\=", "^=", "&=", "<<=", ">>=")
|
|
|
++ ),
|
|
|
++ field("right", $._expression)
|
|
|
++ )
|
|
|
++ ),
|
|
|
++ inline_return_statement: ($) => prec.right(1, seq(ci("Return"), optional($._expression))),
|
|
|
++ inline_throw_statement: ($) => prec.right(1, seq(ci("Throw"), optional($._expression))),
|
|
|
++ inline_goto_statement: ($) => seq(ci("GoTo"), field("label", $.identifier)),
|
|
|
++ inline_exit_statement: ($) =>
|
|
|
++ seq(
|
|
|
++ ci("Exit"),
|
|
|
++ choice(ci("Do"), ci("For"), ci("While"), ci("Select"), ci("Sub"), ci("Function"), ci("Property"), ci("Try"))
|
|
|
++ ),
|
|
|
++ inline_continue_statement: ($) =>
|
|
|
++ seq(ci("Continue"), choice(ci("Do"), ci("For"), ci("While"))),
|
|
|
++ inline_call_statement: ($) => prec.right(1, seq(ci("Call"), $._expression)),
|
|
|
++ inline_raiseevent_statement: ($) =>
|
|
|
++ prec.right(1, seq(ci("RaiseEvent"), field("event", $.identifier), optional(field("arguments", $.argument_list)))),
|
|
|
++ inline_expression_statement: ($) => prec.right(1, $._expression),
|
|
|
++
|
|
|
++ inline_if_statement: ($) =>
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ ci("If"),
|
|
|
++ field("condition", $._expression),
|
|
|
++ ci("Then"),
|
|
|
++ field("then_statement", $._inline_statements),
|
|
|
++ optional(seq(ci("Else"), field("else_statement", $._inline_statements)))
|
|
|
++ )
|
|
|
++ ),
|
|
|
++
|
|
|
+ expression_statement: ($) => seq($._expression, $._terminator),
|
|
|
+
|
|
|
+ assignment_statement: ($) =>
|
|
|
+@@ -1301,21 +1393,96 @@ module.exports = grammar({
|
|
|
+ $._terminator
|
|
|
+ )
|
|
|
+ ),
|
|
|
++ custom_event_declaration: ($) =>
|
|
|
++ seq(
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
++ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
++ ci("Custom"),
|
|
|
++ ci("Event"),
|
|
|
++ field("name", $.identifier),
|
|
|
++ $.as_clause,
|
|
|
++ optional($.implements_member_clause),
|
|
|
++ $._block_terminator,
|
|
|
++ repeat($.event_accessor),
|
|
|
++ ci("End"), ci("Event"),
|
|
|
++ $._terminator
|
|
|
++ ),
|
|
|
++
|
|
|
++ event_accessor: ($) =>
|
|
|
++ seq(
|
|
|
++ choice(ci("AddHandler"), ci("RemoveHandler"), ci("RaiseEvent")),
|
|
|
++ optional(field("parameters", $.parameter_list)),
|
|
|
++ $._block_terminator,
|
|
|
++ repeat($._statement),
|
|
|
++ ci("End"),
|
|
|
++ choice(ci("AddHandler"), ci("RemoveHandler"), ci("RaiseEvent")),
|
|
|
++ $._terminator
|
|
|
++ ),
|
|
|
++
|
|
|
++ _mustoverride_token: ($) => token(prec(11, ci("MustOverride"))),
|
|
|
++
|
|
|
++ abstract_method_declaration: ($) =>
|
|
|
++ seq(
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
++ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
++ $._mustoverride_token,
|
|
|
++ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
++ choice(ci("Sub"), ci("Function")),
|
|
|
++ field("name", $.identifier),
|
|
|
++ optional(field("type_parameters", $.type_parameter_list)),
|
|
|
++ optional(field("parameters", $.parameter_list)),
|
|
|
++ optional($.as_clause),
|
|
|
++ optional($.implements_member_clause),
|
|
|
++ $._terminator
|
|
|
++ ),
|
|
|
++
|
|
|
++ abstract_property_declaration: ($) =>
|
|
|
++ seq(
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
++ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
++ $._mustoverride_token,
|
|
|
++ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
++ ci("Property"),
|
|
|
++ field("name", $.identifier),
|
|
|
++ optional(field("parameters", $.parameter_list)),
|
|
|
++ optional($.as_clause),
|
|
|
++ optional($.implements_member_clause),
|
|
|
++ $._terminator
|
|
|
++ ),
|
|
|
++
|
|
|
++ external_method_declaration: ($) =>
|
|
|
++ seq(
|
|
|
++ repeat(field("attributes", $.attribute_list)),
|
|
|
++ optional(field("modifiers", repeat1($.member_modifier))),
|
|
|
++ ci("Declare"),
|
|
|
++ optional(choice(ci("Auto"), ci("Ansi"), ci("Unicode"))),
|
|
|
++ choice(ci("Sub"), ci("Function")),
|
|
|
++ field("name", $.identifier),
|
|
|
++ ci("Lib"),
|
|
|
++ field("library", $.string_literal),
|
|
|
++ optional(seq(ci("Alias"), field("alias", $.string_literal))),
|
|
|
++ optional(field("parameters", $.parameter_list)),
|
|
|
++ optional($.as_clause),
|
|
|
++ $._terminator
|
|
|
++ ),
|
|
|
++
|
|
|
+ call_statement: ($) => seq(ci("Call"), $._expression, $._terminator),
|
|
|
+
|
|
|
+ if_statement: ($) =>
|
|
|
+ seq(
|
|
|
+ ci("If"),
|
|
|
+ field("condition", $._expression),
|
|
|
+- ci("Then"),
|
|
|
+ choice(
|
|
|
+- // Single-line if
|
|
|
++ // Single-line if: inline statements, optional Else, one terminator
|
|
|
+ seq(
|
|
|
+- field("then_statement", $._statement),
|
|
|
+- optional(seq(ci("Else"), field("else_statement", $._statement)))
|
|
|
++ ci("Then"),
|
|
|
++ field("then_statement", $._inline_statements),
|
|
|
++ optional(seq(ci("Else"), field("else_statement", $._inline_statements))),
|
|
|
++ $._terminator
|
|
|
+ ),
|
|
|
+ // Multi-line if
|
|
|
+ seq(
|
|
|
++ optional(ci("Then")),
|
|
|
+ $._block_terminator,
|
|
|
+ field("then_branch", repeat($._statement)),
|
|
|
+ repeat($.elseif_clause),
|
|
|
+@@ -1331,7 +1498,7 @@ module.exports = grammar({
|
|
|
+ seq(
|
|
|
+ ci("ElseIf"),
|
|
|
+ field("condition", $._expression),
|
|
|
+- ci("Then"),
|
|
|
++ optional(ci("Then")),
|
|
|
+ $._block_terminator,
|
|
|
+ field("body", repeat($._statement))
|
|
|
+ ),
|
|
|
+@@ -1445,7 +1612,7 @@ module.exports = grammar({
|
|
|
+ using_statement: ($) =>
|
|
|
+ seq(
|
|
|
+ ci("Using"),
|
|
|
+- field("resource", choice($.variable_declarator, $._expression)),
|
|
|
++ commaSep1(field("resource", choice($.variable_declarator, $._expression))),
|
|
|
+ $._block_terminator,
|
|
|
+ field("body", repeat($._statement)),
|
|
|
+ ci("End"),
|
|
|
+@@ -1590,14 +1757,25 @@ module.exports = grammar({
|
|
|
+ $._terminator
|
|
|
+ ), // LINQ Query expressions
|
|
|
+ query_expression: ($) =>
|
|
|
+- seq($.from_clause, repeat($.query_body_clause), $.select_or_group_clause),
|
|
|
+- from_clause: ($) => seq(ci("From"), commaSep1($.collection_range_variable)),
|
|
|
++ prec.right(
|
|
|
++ choice(
|
|
|
++ seq(
|
|
|
++ $.from_clause,
|
|
|
++ repeat(seq(optional($._query_clause_continuation), $.query_body_clause)),
|
|
|
++ optional(seq(optional($._query_clause_continuation), $.select_or_group_clause))
|
|
|
++ ),
|
|
|
++ $.aggregate_clause
|
|
|
++ )
|
|
|
++ ),
|
|
|
++ from_clause: ($) => prec.right(seq(ci("From"), commaSep1($.collection_range_variable))),
|
|
|
+ collection_range_variable: ($) =>
|
|
|
+- seq(
|
|
|
+- field("variable", $.identifier),
|
|
|
+- optional($.as_clause),
|
|
|
+- ci("In"),
|
|
|
+- field("collection", $._expression)
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ field("variable", $.identifier),
|
|
|
++ optional($.as_clause),
|
|
|
++ ci("In"),
|
|
|
++ field("collection", $._expression)
|
|
|
++ )
|
|
|
+ ),
|
|
|
+ query_body_clause: ($) =>
|
|
|
+ choice(
|
|
|
+@@ -1606,48 +1784,62 @@ module.exports = grammar({
|
|
|
+ $.where_clause,
|
|
|
+ $.join_clause,
|
|
|
+ $.order_by_clause,
|
|
|
+- $.aggregate_clause
|
|
|
+- ),
|
|
|
+- let_clause: ($) => seq(ci("Let"), commaSep1($.expression_range_variable)),
|
|
|
++ $.aggregate_clause,
|
|
|
++ $.distinct_clause,
|
|
|
++ $.skip_take_clause
|
|
|
++ ),
|
|
|
++ distinct_clause: ($) => ci("Distinct"),
|
|
|
++ skip_take_clause: ($) =>
|
|
|
++ prec.right(seq(choice(ci("Skip"), ci("Take")), optional(ci("While")), $._expression)),
|
|
|
++ let_clause: ($) => prec.right(seq(ci("Let"), commaSep1($.expression_range_variable))),
|
|
|
+ expression_range_variable: ($) =>
|
|
|
+- seq(
|
|
|
++ prec.right(seq(
|
|
|
+ field("variable", $.identifier),
|
|
|
+ "=",
|
|
|
+ field("expression", $._expression)
|
|
|
+- ),
|
|
|
+- where_clause: ($) => seq(ci("Where"), field("condition", $._expression)),
|
|
|
++ )),
|
|
|
++ where_clause: ($) =>
|
|
|
++ prec.right(seq(ci("Where"), field("condition", $._expression))),
|
|
|
+ join_clause: ($) =>
|
|
|
+- seq(
|
|
|
+- ci("Join"),
|
|
|
+- $.collection_range_variable,
|
|
|
+- ci("On"),
|
|
|
+- commaSep1($.join_condition)
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ ci("Join"),
|
|
|
++ $.collection_range_variable,
|
|
|
++ optional($._query_clause_continuation),
|
|
|
++ ci("On"),
|
|
|
++ commaSep1($.join_condition)
|
|
|
++ )
|
|
|
+ ),
|
|
|
+ join_condition: ($) =>
|
|
|
+- seq(
|
|
|
++ prec.right(seq(
|
|
|
+ field("left", $._expression),
|
|
|
+ ci("Equals"),
|
|
|
+ field("right", $._expression)
|
|
|
+- ),
|
|
|
+- order_by_clause: ($) => seq(ci("Order"), ci("By"), commaSep1($.ordering)),
|
|
|
++ )),
|
|
|
++ order_by_clause: ($) => prec.right(seq(ci("Order"), ci("By"), commaSep1($.ordering))),
|
|
|
+ ordering: ($) =>
|
|
|
+- seq(
|
|
|
+- field("expression", $._expression),
|
|
|
+- optional(choice(ci("Ascending"), ci("Descending")))
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ field("expression", $._expression),
|
|
|
++ optional(choice(ci("Ascending"), ci("Descending")))
|
|
|
++ )
|
|
|
+ ),
|
|
|
+ aggregate_clause: ($) =>
|
|
|
+- seq(
|
|
|
+- ci("Aggregate"),
|
|
|
+- $.collection_range_variable,
|
|
|
+- repeat($.query_body_clause),
|
|
|
+- ci("Into"),
|
|
|
+- commaSep1($.aggregate_element)
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ ci("Aggregate"),
|
|
|
++ $.collection_range_variable,
|
|
|
++ repeat(seq(optional($._query_clause_continuation), $.query_body_clause)),
|
|
|
++ optional($._query_clause_continuation),
|
|
|
++ ci("Into"),
|
|
|
++ commaSep1($.aggregate_element)
|
|
|
++ )
|
|
|
+ ),
|
|
|
+ aggregate_element: ($) =>
|
|
|
+- seq(
|
|
|
++ prec.right(seq(
|
|
|
+ optional(seq(field("name", $.identifier), "=")),
|
|
|
+ field("function", $.aggregate_function)
|
|
|
+- ),
|
|
|
++ )),
|
|
|
+ aggregate_function: ($) =>
|
|
|
+ seq(
|
|
|
+ field(
|
|
|
+@@ -1668,42 +1860,44 @@ module.exports = grammar({
|
|
|
+ select_or_group_clause: ($) => choice($.select_clause, $.group_by_clause),
|
|
|
+ select_clause: ($) => seq(ci("Select"), commaSep1($.select_element)),
|
|
|
+ select_element: ($) =>
|
|
|
+- choice(
|
|
|
++ prec.right(choice(
|
|
|
+ $._expression,
|
|
|
+ seq(
|
|
|
+ field("name", $.identifier),
|
|
|
+ "=",
|
|
|
+ field("expression", $._expression)
|
|
|
+ )
|
|
|
+- ),
|
|
|
++ )),
|
|
|
+ group_by_clause: ($) =>
|
|
|
+- seq(
|
|
|
+- ci("Group"),
|
|
|
+- commaSep1($.group_element),
|
|
|
+- ci("By"),
|
|
|
+- commaSep1($.by_element),
|
|
|
+- ci("Into"),
|
|
|
+- commaSep1($.into_element)
|
|
|
++ prec.right(
|
|
|
++ seq(
|
|
|
++ ci("Group"),
|
|
|
++ commaSep1($.group_element),
|
|
|
++ ci("By"),
|
|
|
++ commaSep1($.by_element),
|
|
|
++ ci("Into"),
|
|
|
++ commaSep1($.into_element)
|
|
|
++ )
|
|
|
+ ),
|
|
|
+ group_element: ($) =>
|
|
|
+- choice(
|
|
|
++ prec.right(choice(
|
|
|
+ $._expression,
|
|
|
+ seq(
|
|
|
+ field("name", $.identifier),
|
|
|
+ "=",
|
|
|
+ field("expression", $._expression)
|
|
|
+ )
|
|
|
+- ),
|
|
|
++ )),
|
|
|
+ by_element: ($) =>
|
|
|
+- choice(
|
|
|
++ prec.right(choice(
|
|
|
+ $._expression,
|
|
|
+ seq(
|
|
|
+ field("name", $.identifier),
|
|
|
+ "=",
|
|
|
+ field("expression", $._expression)
|
|
|
+ )
|
|
|
+- ),
|
|
|
+- into_element: ($) => choice($.identifier, $.aggregate_element),
|
|
|
++ )),
|
|
|
++ into_element: ($) => prec.right(choice($.identifier, $.aggregate_element)),
|
|
|
+
|
|
|
+ // Comments
|
|
|
+ comment: ($) => token(prec(100, choice(
|
|
|
+@@ -1714,7 +1908,7 @@ module.exports = grammar({
|
|
|
+ ))),
|
|
|
+
|
|
|
+ // Preprocessor Directives
|
|
|
+- preprocessor_directive: ($) => token(seq("#", /[^\r\n]*/)),
|
|
|
++ preprocessor_directive: ($) => token(seq("#", /[ \t]*[A-Za-z][^\r\n]*/)),
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+diff --git a/src/scanner.c b/src/scanner.c
|
|
|
+new file mode 100644
|
|
|
+index 0000000..8334fdb
|
|
|
+--- /dev/null
|
|
|
++++ b/src/scanner.c
|
|
|
+@@ -0,0 +1,303 @@
|
|
|
++#include "tree_sitter/parser.h"
|
|
|
++#include <string.h>
|
|
|
++#include <wctype.h>
|
|
|
++
|
|
|
++// External tokens for the two constructs that are not LR(1)-parseable with
|
|
|
++// tree-sitter's newline-as-extra treatment:
|
|
|
++//
|
|
|
++// 1. QUERY_CLAUSE_CONTINUATION — a newline run that CONTINUES a LINQ query
|
|
|
++// (`From x In xs` ↵ `Where …`). At a clause boundary the parser cannot
|
|
|
++// know from the newline alone whether the query ends (newline = statement
|
|
|
++// terminator) or continues (newline = insignificant, next line starts with
|
|
|
++// a query keyword). The scanner looks past the newline at the next word
|
|
|
++// and only emits the token when it is a query-clause keyword — so the
|
|
|
++// terminator/continuation choice is made by the lexer, not the LR table.
|
|
|
++//
|
|
|
++// 2. XML_LITERAL — a whole VB XML literal (`<Tags><Tag/></Tags>`, including
|
|
|
++// `<%= expr %>` embedded expressions, comments, CDATA) consumed as one
|
|
|
++// opaque token. Only valid where a literal can begin an expression, so a
|
|
|
++// relational `<` (which always FOLLOWS an expression) can never collide.
|
|
|
++
|
|
|
++enum TokenType {
|
|
|
++ QUERY_CLAUSE_CONTINUATION,
|
|
|
++ XML_LITERAL,
|
|
|
++};
|
|
|
++
|
|
|
++void *tree_sitter_vbnet_external_scanner_create(void) { return NULL; }
|
|
|
++void tree_sitter_vbnet_external_scanner_destroy(void *payload) { (void)payload; }
|
|
|
++unsigned tree_sitter_vbnet_external_scanner_serialize(void *payload, char *buffer) {
|
|
|
++ (void)payload; (void)buffer;
|
|
|
++ return 0; // stateless
|
|
|
++}
|
|
|
++void tree_sitter_vbnet_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
|
|
|
++ (void)payload; (void)buffer; (void)length;
|
|
|
++}
|
|
|
++
|
|
|
++static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
|
|
|
++static inline void skip_ws(TSLexer *lexer) { lexer->advance(lexer, true); }
|
|
|
++
|
|
|
++// Read one alphabetic word (advancing past it), lowercased into buf.
|
|
|
++// Returns its length (0 if the next char is not a letter).
|
|
|
++static int read_word(TSLexer *lexer, char *buf, int cap) {
|
|
|
++ int n = 0;
|
|
|
++ while (iswalpha((wint_t)lexer->lookahead)) {
|
|
|
++ if (n < cap - 1) buf[n] = (char)towlower((wint_t)lexer->lookahead);
|
|
|
++ n++;
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++ buf[n < cap - 1 ? n : cap - 1] = 0;
|
|
|
++ return n;
|
|
|
++}
|
|
|
++
|
|
|
++static bool is_query_keyword(const char *w) {
|
|
|
++ static const char *kws[] = {
|
|
|
++ "where", "select", "order", "group", "join", "let", "skip",
|
|
|
++ "take", "distinct", "aggregate", "from", "into", "on", NULL,
|
|
|
++ };
|
|
|
++ for (int i = 0; kws[i]; i++)
|
|
|
++ if (strcmp(w, kws[i]) == 0) return true;
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++// [ \t\r]* '\n' [ \t\r\n]* followed by a query-clause keyword.
|
|
|
++// The token covers only the whitespace/newline run (mark_end before the
|
|
|
++// keyword lookahead), so the clause keyword itself lexes normally after it.
|
|
|
++static bool scan_query_continuation(TSLexer *lexer) {
|
|
|
++ bool saw_newline = false;
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == ' ' || c == '\t' || c == '\r') {
|
|
|
++ advance(lexer);
|
|
|
++ } else if (c == '\n') {
|
|
|
++ saw_newline = true;
|
|
|
++ advance(lexer);
|
|
|
++ } else {
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (!saw_newline) return false;
|
|
|
++ lexer->mark_end(lexer);
|
|
|
++
|
|
|
++ char w[12];
|
|
|
++ int n = read_word(lexer, w, sizeof w);
|
|
|
++ if (n == 0 || n >= (int)(sizeof w)) return false;
|
|
|
++ if (!is_query_keyword(w)) return false;
|
|
|
++
|
|
|
++ // `Select Case` opens a statement, never a select clause.
|
|
|
++ if (strcmp(w, "select") == 0) {
|
|
|
++ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') advance(lexer);
|
|
|
++ char w2[8];
|
|
|
++ int m = read_word(lexer, w2, sizeof w2);
|
|
|
++ if (m > 0 && m < (int)(sizeof w2) && strcmp(w2, "case") == 0) return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ lexer->result_symbol = QUERY_CLAUSE_CONTINUATION;
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++// Consume a VB string inside an embedded `<%= … %>` region ("" escapes).
|
|
|
++static void consume_vb_string(TSLexer *lexer) {
|
|
|
++ advance(lexer); // opening quote
|
|
|
++ for (;;) {
|
|
|
++ if (lexer->lookahead == 0) return;
|
|
|
++ if (lexer->lookahead == '"') {
|
|
|
++ advance(lexer);
|
|
|
++ if (lexer->lookahead != '"') return; // closing (not an escaped "")
|
|
|
++ // escaped quote: fall through, keep consuming
|
|
|
++ }
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++// Consume `<%= … %>` starting at the '%' (the '<' is already consumed).
|
|
|
++// Embedded expressions NEST (`<%= From t In ts Select <Tag><%= t.Name %></Tag> %>`,
|
|
|
++// the staxrip WriteTagfile shape), so track <% / %> depth.
|
|
|
++static bool consume_embedded_expression(TSLexer *lexer) {
|
|
|
++ advance(lexer); // '%'
|
|
|
++ int depth = 1;
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == 0) return false;
|
|
|
++ if (c == '"') { consume_vb_string(lexer); continue; }
|
|
|
++ if (c == '<') {
|
|
|
++ advance(lexer);
|
|
|
++ if (lexer->lookahead == '%') { depth++; advance(lexer); }
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ if (c == '%') {
|
|
|
++ advance(lexer);
|
|
|
++ if (lexer->lookahead == '>') {
|
|
|
++ advance(lexer);
|
|
|
++ if (--depth == 0) return true;
|
|
|
++ }
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++// Consume `<!-- … -->` / `<![CDATA[ … ]]>` / `<!DOCTYPE …>` starting at '!'.
|
|
|
++static bool consume_bang_construct(TSLexer *lexer) {
|
|
|
++ advance(lexer); // '!'
|
|
|
++ if (lexer->lookahead == '-') {
|
|
|
++ // comment: to -->
|
|
|
++ int dashes = 0;
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == 0) return false;
|
|
|
++ if (c == '-') { dashes++; advance(lexer); continue; }
|
|
|
++ if (c == '>' && dashes >= 2) { advance(lexer); return true; }
|
|
|
++ dashes = 0;
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (lexer->lookahead == '[') {
|
|
|
++ // CDATA: to ]]>
|
|
|
++ int brackets = 0;
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == 0) return false;
|
|
|
++ if (c == ']') { brackets++; advance(lexer); continue; }
|
|
|
++ if (c == '>' && brackets >= 2) { advance(lexer); return true; }
|
|
|
++ brackets = 0;
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ // DOCTYPE-ish: to bare >
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == 0) return false;
|
|
|
++ if (c == '>') { advance(lexer); return true; }
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++// Consume `<? … ?>` starting at the '?'.
|
|
|
++static bool consume_processing_instruction(TSLexer *lexer) {
|
|
|
++ advance(lexer); // '?'
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == 0) return false;
|
|
|
++ if (c == '?') {
|
|
|
++ advance(lexer);
|
|
|
++ if (lexer->lookahead == '>') { advance(lexer); return true; }
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++static bool is_name_start(int32_t c) {
|
|
|
++ return iswalpha((wint_t)c) || c == '_';
|
|
|
++}
|
|
|
++
|
|
|
++static bool scan_xml_literal(TSLexer *lexer) {
|
|
|
++ // Only same-line blanks are skipped: a leading newline must stay unconsumed
|
|
|
++ // so it can serve as a statement/block terminator (a successful scan would
|
|
|
++ // otherwise swallow it as trivia). XML on a continuation line still works:
|
|
|
++ // the newline is consumed as an extra first, then the scanner re-runs.
|
|
|
++ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') skip_ws(lexer);
|
|
|
++ if (lexer->lookahead != '<') return false;
|
|
|
++ advance(lexer);
|
|
|
++ // Only element-start markup opens an XML literal here; comments/PIs as the
|
|
|
++ // outermost construct (`Dim d = <?xml …`) are rare enough to leave alone,
|
|
|
++ // and a bare `<` (comparison) never begins an expression.
|
|
|
++ if (!is_name_start(lexer->lookahead)) return false;
|
|
|
++
|
|
|
++ int depth = 0; // open (unclosed) elements
|
|
|
++ bool in_tag = true; // inside <...> of an element tag
|
|
|
++ bool closing = false;
|
|
|
++ int32_t quote = 0;
|
|
|
++
|
|
|
++ for (;;) {
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ if (c == 0) return false; // unterminated: let the normal parser error
|
|
|
++
|
|
|
++ if (in_tag) {
|
|
|
++ if (quote) {
|
|
|
++ if (c == quote) quote = 0;
|
|
|
++ advance(lexer);
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ if (c == '"' || c == '\'') { quote = c; advance(lexer); continue; }
|
|
|
++ if (c == '<') {
|
|
|
++ advance(lexer);
|
|
|
++ if (lexer->lookahead == '%') {
|
|
|
++ // attribute value embedded expression: attr=<%= x %>
|
|
|
++ if (!consume_embedded_expression(lexer)) return false;
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ continue; // stray < in a tag: malformed, keep consuming
|
|
|
++ }
|
|
|
++ if (c == '/') {
|
|
|
++ advance(lexer);
|
|
|
++ if (lexer->lookahead == '>') {
|
|
|
++ // self-closing tag
|
|
|
++ advance(lexer);
|
|
|
++ in_tag = false;
|
|
|
++ if (depth == 0) break; // single self-closed root: done
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ if (c == '>') {
|
|
|
++ advance(lexer);
|
|
|
++ if (closing) {
|
|
|
++ depth--;
|
|
|
++ if (depth <= 0) break; // root element closed: done
|
|
|
++ } else {
|
|
|
++ depth++;
|
|
|
++ }
|
|
|
++ in_tag = false;
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ advance(lexer);
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++
|
|
|
++ // text content between tags
|
|
|
++ if (c == '<') {
|
|
|
++ advance(lexer);
|
|
|
++ int32_t d = lexer->lookahead;
|
|
|
++ if (d == '/') { advance(lexer); closing = true; in_tag = true; continue; }
|
|
|
++ if (d == '%') {
|
|
|
++ if (!consume_embedded_expression(lexer)) return false;
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ if (d == '!') {
|
|
|
++ if (!consume_bang_construct(lexer)) return false;
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ if (d == '?') {
|
|
|
++ if (!consume_processing_instruction(lexer)) return false;
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ closing = false;
|
|
|
++ in_tag = true; // opening tag
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ advance(lexer);
|
|
|
++ }
|
|
|
++
|
|
|
++ lexer->mark_end(lexer);
|
|
|
++ lexer->result_symbol = XML_LITERAL;
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++bool tree_sitter_vbnet_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
|
|
|
++ (void)payload;
|
|
|
++ int32_t c = lexer->lookahead;
|
|
|
++ bool ws = (c == ' ' || c == '\t' || c == '\n' || c == '\r');
|
|
|
++
|
|
|
++ // A scan attempt advances the lexer, so exactly ONE attempt runs per
|
|
|
++ // invocation — never chain a second attempt after a failed first (its
|
|
|
++ // reads would start mid-stream). The leading character picks the branch;
|
|
|
++ // a false return discards the attempt entirely.
|
|
|
++ if (valid_symbols[QUERY_CLAUSE_CONTINUATION] && ws) {
|
|
|
++ return scan_query_continuation(lexer);
|
|
|
++ }
|
|
|
++ if (valid_symbols[XML_LITERAL] && (c == '<' || ws)) {
|
|
|
++ return scan_xml_literal(lexer);
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|