tree-sitter-vbnet.patch 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  1. diff --git a/grammar.js b/grammar.js
  2. index 21c8523..00bf2b1 100644
  3. --- a/grammar.js
  4. +++ b/grammar.js
  5. @@ -21,10 +21,14 @@ const PRECEDENCE = {
  6. module.exports = grammar({
  7. name: "vbnet",
  8. + word: ($) => $._identifier_token,
  9. +
  10. + externals: ($) => [$._query_clause_continuation, $.xml_literal],
  11. +
  12. extras: $ => [
  13. $.comment,
  14. $.preprocessor_directive,
  15. - /[ \t\v\f]+/,
  16. + /[ \t\v\f\uFEFF]+/,
  17. /\r?\n/,
  18. /_[ \t]*\r?\n/,
  19. ],
  20. @@ -38,17 +42,14 @@ module.exports = grammar({
  21. $._type,
  22. ],
  23. conflicts: ($) => [
  24. + [$.binary_expression, $.assignment_expression, $.inline_assignment_statement],
  25. [$.simple_name, $.generic_name],
  26. [$.await_expression, $.with_expression],
  27. [$._name_reference, $._type],
  28. [$.object_creation_expression, $.array_creation_expression, $.array_type],
  29. - [$.string_literal, $.character_literal],
  30. [$._primary_expression, $.variable_declarator],
  31. - [$.lambda_expression, $.with_expression],
  32. - [$.lambda_expression],
  33. [$.binary_expression, $.assignment_expression],
  34. [$._primary_expression, $.select_element],
  35. - [$.with_expression, $.select_element],
  36. [$.select_clause],
  37. [$._primary_expression, $.group_element],
  38. [$._terminator, $._block_terminator],
  39. @@ -66,7 +67,6 @@ module.exports = grammar({
  40. [$.aggregate_element, $.into_element],
  41. [$.group_by_clause],
  42. [$.else_clause],
  43. - [$.if_statement],
  44. [$.case_else_clause],
  45. [$.case_clause],
  46. [$.elseif_clause],
  47. @@ -75,7 +75,6 @@ module.exports = grammar({
  48. [$._primary_expression, $.simple_name, $.generic_name],
  49. [$._primary_expression, $.variable_declarator, $.labeled_statement],
  50. [$._statement, $.with_statement],
  51. - [$.dictionary_initializer, $.collection_initializer],
  52. [
  53. $.class_declaration,
  54. $.structure_declaration,
  55. @@ -89,7 +88,6 @@ module.exports = grammar({
  56. ],
  57. ],
  58. - word: ($) => $._identifier_token,
  59. rules: {
  60. source_file: ($) =>
  61. @@ -157,13 +155,14 @@ module.exports = grammar({
  62. ),
  63. _terminator: ($) =>
  64. - choice(seq(optional(":"), choice("\n", "\r\n")), $._eof),
  65. + prec.right(choice(seq(optional(":"), choice("\n", "\r\n")), prec(-1, ":"), $._eof)),
  66. - _block_terminator: ($) => prec.right(repeat1(choice("\n", "\r\n", $._eof))),
  67. + _block_terminator: ($) => prec.right(repeat1(choice("\n", "\r\n", ":", $._eof))),
  68. _eof: ($) => token(prec(-10, "$")),
  69. - _identifier_token: ($) => token(prec(-1, /[a-zA-Z_][a-zA-Z0-9_]*/)),
  70. + _identifier_token: ($) =>
  71. + token(prec(-1, /[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Pc}]*[%$&!#@]?/u)),
  72. identifier: ($) =>
  73. choice(
  74. @@ -194,7 +193,6 @@ module.exports = grammar({
  75. ci("MustInherit"),
  76. ci("NotInheritable"),
  77. ci("Overrides"),
  78. - ci("MustOverride"),
  79. ci("NotOverridable"),
  80. ci("Overridable"),
  81. ci("Overloads"),
  82. @@ -225,43 +223,6 @@ module.exports = grammar({
  83. $._statement // Fallback to regular statements
  84. ),
  85. - _non_statement_identifier: $ => choice(
  86. - // Match identifiers that don't start with statement keywords
  87. - token(prec(1, seq(
  88. - negative_lookahead(seq(
  89. - choice(
  90. - /[Tt][Rr][Yy]/,
  91. - /[Ss][Ee][Ll][Ee][Cc][Tt]/,
  92. - /[Ii][Ff]/,
  93. - /[Ww][Hh][Ii][Ll][Ee]/,
  94. - /[Dd][Oo]/,
  95. - /[Ff][Oo][Rr]/,
  96. - /[Uu][Ss][Ii][Nn][Gg]/,
  97. - /[Ww][Ii][Tt][Hh]/,
  98. - /[Tt][Hh][Rr][Oo][Ww]/,
  99. - /[Rr][Ee][Tt][Uu][Rr][Nn]/,
  100. - /[Ee][Xx][Ii][Tt]/,
  101. - /[Cc][Oo][Nn][Tt][Ii][Nn][Uu][Ee]/,
  102. - /[Ss][Tt][Oo][Pp]/,
  103. - /[Ee][Nn][Dd]/,
  104. - /[Gg][Oo][Tt][Oo]/,
  105. - /[Rr][Ee][Ss][Uu][Mm][Ee]/,
  106. - /[Ee][Rr][Rr][Oo][Rr]/,
  107. - /[Oo][Nn]/,
  108. - /[Rr][Ee][Dd][Ii][Mm]/,
  109. - /[Ee][Rr][Aa][Ss][Ee]/,
  110. - /[Ss][Yy][Nn][Cc][Ll][Oo][Cc][Kk]/,
  111. - /[Rr][Aa][Ii][Ss][Ee][Ee][Vv][Ee][Nn][Tt]/,
  112. - /[Aa][Dd][Dd][Hh][Aa][Nn][Dd][Ll][Ee][Rr]/,
  113. - /[Rr][Ee][Mm][Oo][Vv][Ee][Hh][Aa][Nn][Dd][Ll][Ee][Rr]/
  114. - ),
  115. - choice(/\s/, /\r/, /\n/, /$/)
  116. - )),
  117. - /[a-zA-Z_][a-zA-Z0-9_]*/
  118. - ))),
  119. - seq("[", /[^\]]+/, "]") // Bracketed identifier can be anything
  120. - ),
  121. -
  122. local_declaration_modifier: ($) =>
  123. choice(ci("Dim"), ci("Const"), ci("Static")),
  124. @@ -300,6 +261,8 @@ module.exports = grammar({
  125. _primary_expression: ($) =>
  126. choice(
  127. $._literal,
  128. + $.array_literal,
  129. + $.tuple_expression,
  130. $.identifier,
  131. $.parenthesized_expression,
  132. $.generic_invocation_expression, // Add this before regular invocation
  133. @@ -320,17 +283,26 @@ module.exports = grammar({
  134. ),
  135. generic_invocation_expression: ($) =>
  136. - prec(
  137. - PRECEDENCE.INVOCATION + 1,
  138. + prec.right(
  139. + PRECEDENCE.INVOCATION + 1,
  140. seq(
  141. field("function", $._expression),
  142. field("type_arguments", $.type_argument_list),
  143. - field("arguments", $.argument_list)
  144. + optional(field("arguments", $.argument_list))
  145. )
  146. ),
  147. parenthesized_expression: ($) => seq("(", $._expression, ")"),
  148. + tuple_expression: ($) =>
  149. + seq(
  150. + "(",
  151. + choice($._expression, seq(field("name", $.identifier), ":=", $._expression)),
  152. + ",",
  153. + commaSep1(choice($._expression, seq(field("name", $.identifier), ":=", $._expression))),
  154. + ")"
  155. + ),
  156. +
  157. member_access_expression: ($) =>
  158. prec.left(
  159. PRECEDENCE.MEMBER_ACCESS,
  160. @@ -350,20 +322,23 @@ module.exports = grammar({
  161. )
  162. ),
  163. - argument_list: ($) => seq("(", optional(commaSep1($._argument)), ")"),
  164. + argument_list: ($) => seq("(", optional($._arguments), ")"),
  165. - _argument: ($) =>
  166. - choice($._expression, $.named_argument, $.omitted_argument),
  167. + // Argument slots may be omitted between commas (`f(a,, b)`, `f(, x)`) —
  168. + // VB passes Optional parameters positionally by leaving the slot empty.
  169. + _arguments: ($) =>
  170. + seq($._argument, repeat(seq(",", optional($._argument)))),
  171. - omitted_argument: ($) => token(prec(1, ",")),
  172. + _argument: ($) => choice($._expression, $.named_argument),
  173. array_access_expression: ($) =>
  174. prec(
  175. PRECEDENCE.ARRAY_ACCESS,
  176. seq(
  177. field("array", $._expression),
  178. + optional("?"),
  179. "(",
  180. - field("indices", commaSep1($._expression)),
  181. + field("indices", $._arguments),
  182. ")"
  183. )
  184. ),
  185. @@ -471,16 +446,53 @@ module.exports = grammar({
  186. ),
  187. lambda_expression: ($) =>
  188. - seq(
  189. - choice(ci("Function"), ci("Sub")),
  190. - field("parameters", $.lambda_parameter_list),
  191. - field("body", choice($._expression, $._statement_block))
  192. + choice(
  193. + prec.right(
  194. + seq(
  195. + optional(choice(ci("Async"), ci("Iterator"))),
  196. + choice(ci("Function"), ci("Sub")),
  197. + field("parameters", $.lambda_parameter_list),
  198. + field(
  199. + "body",
  200. + choice(
  201. + $._expression,
  202. + alias($.inline_if_statement, $.if_statement),
  203. + alias($.inline_throw_statement, $.throw_statement)
  204. + )
  205. + )
  206. + )
  207. + ),
  208. + seq(
  209. + optional(choice(ci("Async"), ci("Iterator"))),
  210. + ci("Function"),
  211. + field("parameters", $.lambda_parameter_list),
  212. + optional($.as_clause),
  213. + $._block_terminator,
  214. + repeat($._statement),
  215. + ci("End"), ci("Function")
  216. + ),
  217. + seq(
  218. + optional(choice(ci("Async"), ci("Iterator"))),
  219. + ci("Sub"),
  220. + field("parameters", $.lambda_parameter_list),
  221. + $._block_terminator,
  222. + repeat($._statement),
  223. + ci("End"), ci("Sub")
  224. + )
  225. ),
  226. lambda_parameter_list: ($) => seq("(", commaSep($.lambda_parameter), ")"),
  227. lambda_parameter: ($) =>
  228. - seq(field("name", $.identifier), optional($.as_clause)),
  229. + seq(
  230. + optional(field("modifiers", repeat1(choice(ci("ByVal"), ci("ByRef"))))),
  231. + field("name", $.identifier),
  232. + optional(choice(
  233. + $.array_rank_specifier,
  234. + seq("(", field("bounds", commaSep1($._expression)), ")")
  235. + )),
  236. + optional($.as_clause)
  237. + ),
  238. typeof_is_expression: ($) =>
  239. prec.left(
  240. @@ -488,7 +500,7 @@ module.exports = grammar({
  241. seq(
  242. ci("TypeOf"),
  243. field("expression", $._expression),
  244. - ci("Is"),
  245. + choice(seq(ci("Is"), ci("Not")), token(prec(12, ci("IsNot"))), ci("Is")),
  246. field("type", $._type)
  247. )
  248. ),
  249. @@ -504,29 +516,12 @@ module.exports = grammar({
  250. "initializer",
  251. choice(
  252. $.object_initializer,
  253. - $.collection_initializer,
  254. - $.dictionary_initializer // Add this new option
  255. + $.collection_initializer
  256. )
  257. )
  258. )
  259. ),
  260. - dictionary_initializer: ($) =>
  261. - seq(
  262. - ci("From"),
  263. - "{",
  264. - commaSep($.dictionary_element),
  265. - "}"
  266. - ),
  267. -
  268. - dictionary_element: ($) =>
  269. - seq(
  270. - "{",
  271. - field("key", $._expression),
  272. - ",",
  273. - field("value", $._expression),
  274. - "}"
  275. - ),
  276. object_initializer: ($) =>
  277. @@ -551,6 +546,8 @@ module.exports = grammar({
  278. collection_initializer: ($) =>
  279. seq(ci("From"), "{", commaSep($._expression), "}"),
  280. + array_literal: ($) => seq("{", commaSep($._expression), "}"),
  281. +
  282. array_creation_expression: ($) =>
  283. seq(
  284. @@ -646,6 +643,9 @@ module.exports = grammar({
  285. seq(field("object", $._expression), "!", field("member", $.identifier)),
  286. _literal: ($) =>
  287. choice(
  288. + $.xml_literal,
  289. + $.interpolated_string_literal,
  290. + $.date_literal,
  291. $.string_literal,
  292. $.character_literal,
  293. $.integer_literal,
  294. @@ -654,16 +654,49 @@ module.exports = grammar({
  295. $.nothing_literal,
  296. $.date_literal
  297. ),
  298. - string_literal: ($) => seq('"', repeat(choice(/[^"\n]+/, '""')), '"'),
  299. - character_literal: ($) => seq('"', choice(/[^"\n]/, '""'), '"', "c"),
  300. + string_literal: ($) => token(seq('"', repeat(choice(/[^"]/, '""')), '"')),
  301. + interpolated_string_literal: ($) =>
  302. + seq(
  303. + '$"',
  304. + repeat(
  305. + choice(
  306. + token.immediate(prec(101, /[^"{}]+/)),
  307. + token.immediate(prec(101, '""')),
  308. + token.immediate(prec(101, "{{")),
  309. + token.immediate(prec(101, "}}")),
  310. + $.interpolation
  311. + )
  312. + ),
  313. + token.immediate('"')
  314. + ),
  315. + interpolation: ($) =>
  316. + seq(
  317. + token.immediate(prec(101, "{")),
  318. + $._expression,
  319. + optional(seq(",", /-?\d+/)),
  320. + optional(seq(":", /[^}"\r\n]+/)),
  321. + "}"
  322. + ),
  323. + date_literal: ($) => token(prec(2, /#[ \t]*\d[^#\r\n]*#/)),
  324. + character_literal: ($) => token(seq('"', choice(/[^"\r\n]/, '""'), '"', /[cC]/)),
  325. integer_literal: ($) =>
  326. - token(seq(/\d+/, optional(choice("I", "UI", "L", "UL", "S", "US")))),
  327. + token(
  328. + seq(
  329. + choice(
  330. + /\d+(_+\d+)*/,
  331. + /&[Hh][0-9A-Fa-f]+(_+[0-9A-Fa-f]+)*/,
  332. + /&[Oo][0-7]+(_+[0-7]+)*/,
  333. + /&[Bb][01]+(_+[01]+)*/
  334. + ),
  335. + optional(choice(/[Uu][SsIiLl]/, /[SsIiLl]/, "%", "&"))
  336. + )
  337. + ),
  338. floating_point_literal: ($) =>
  339. token(
  340. seq(
  341. choice(seq(/\d+/, ".", /\d+/), seq(".", /\d+/), /\d+/),
  342. optional(/[eE][+-]?\d+/),
  343. - optional(choice("F", "D", "R"))
  344. + optional(/[FfRrDd!#@]/)
  345. )
  346. ),
  347. boolean_literal: ($) => choice(ci("True"), ci("False")),
  348. @@ -672,7 +705,7 @@ module.exports = grammar({
  349. // Names and types
  350. _name_reference: ($) =>
  351. - choice($.simple_name, $.qualified_name, $.global_qualified_name),
  352. + choice($.simple_name, $.generic_name, $.qualified_name, $.global_qualified_name),
  353. simple_name: ($) => $.identifier,
  354. @@ -682,7 +715,7 @@ module.exports = grammar({
  355. seq(
  356. field("qualifier", $._name_reference),
  357. ".",
  358. - field("name", $.simple_name)
  359. + field("name", choice($.simple_name, $.generic_name))
  360. )
  361. ),
  362. global_qualified_name: ($) =>
  363. @@ -694,6 +727,7 @@ module.exports = grammar({
  364. $.simple_name,
  365. $.qualified_name,
  366. $.generic_name,
  367. + $.global_qualified_name,
  368. $.predefined_type,
  369. $.array_type,
  370. $.tuple_type
  371. @@ -720,7 +754,8 @@ module.exports = grammar({
  372. ),
  373. generic_name: ($) => seq(field("name", $.identifier), $.type_argument_list),
  374. - type_argument_list: ($) => seq("(", ci("Of"), commaSep1($._type), ")"),
  375. + type_argument_list: ($) =>
  376. + seq("(", ci("Of"), optional($._type), repeat(seq(",", optional($._type))), ")"),
  377. array_type: ($) =>
  378. seq(field("element_type", $._type), $.array_rank_specifier),
  379. array_rank_specifier: ($) => seq("(", repeat(","), ")"),
  380. @@ -742,10 +777,14 @@ module.exports = grammar({
  381. field("type", $._type)
  382. ),
  383. - as_clause: ($) => seq(
  384. - ci("As"),
  385. - field("declared_type", $._type)
  386. - ),
  387. + as_clause: ($) =>
  388. + seq(
  389. + ci("As"),
  390. + choice(
  391. + $.object_creation_expression,
  392. + field("declared_type", $._type)
  393. + )
  394. + ),
  395. implements_member_clause: ($) =>
  396. seq(ci("Implements"), commaSep1($._name_reference)),
  397. @@ -856,7 +895,7 @@ module.exports = grammar({
  398. enum_member_declaration: ($) =>
  399. seq(
  400. - optional(field("attributes", $.attribute_list)),
  401. + repeat(field("attributes", $.attribute_list)),
  402. field("name", $.identifier),
  403. optional(seq("=", field("value", $._expression))),
  404. $._terminator
  405. @@ -882,6 +921,10 @@ module.exports = grammar({
  406. prec(10, $.inherits_statement),
  407. prec(10, $.implements_statement),
  408. prec(100, $.constructor_declaration),
  409. + prec(150, $.external_method_declaration),
  410. + prec(160, $.custom_event_declaration),
  411. + prec(170, $.abstract_method_declaration),
  412. + prec(171, $.abstract_property_declaration),
  413. // Explicitly add statements that can appear at class level
  414. prec(200, $.try_statement), // Very high precedence
  415. prec(200, $.select_statement), // Very high precedence
  416. @@ -936,33 +979,21 @@ module.exports = grammar({
  417. variable_declarator: ($) =>
  418. seq(
  419. field("name", $.identifier),
  420. + optional("?"),
  421. optional(choice(
  422. // Array bounds specification
  423. seq("(", field("bounds", commaSep1($._expression)), ")"),
  424. // Array rank specification
  425. $.array_rank_specifier
  426. )),
  427. - optional(choice(
  428. - // Regular type declaration
  429. - $.as_clause,
  430. - seq(
  431. - ci("As"),
  432. - ci("New"),
  433. - field("type", $._type),
  434. - optional(field("arguments", $.argument_list)),
  435. - optional(field("initializer", choice(
  436. - $.object_initializer,
  437. - $.collection_initializer
  438. - )))
  439. - )
  440. - )),
  441. + optional($.as_clause),
  442. optional(seq("=", field("initializer", $._expression)))
  443. ),
  444. field_declaration: ($) =>
  445. prec.dynamic(-100, seq(
  446. - optional(field("attributes", $.attribute_list)),
  447. + repeat(field("attributes", $.attribute_list)),
  448. optional(field("modifiers", repeat1($.member_modifier))),
  449. commaSep1($.variable_declarator),
  450. $._terminator
  451. @@ -970,7 +1001,8 @@ module.exports = grammar({
  452. field_variable_declarator: ($) =>
  453. seq(
  454. - field("name", $._non_statement_identifier),
  455. + field("name", $.identifier),
  456. + optional("?"),
  457. optional($.array_rank_specifier),
  458. optional($.as_clause),
  459. optional(seq("=", field("initializer", $._expression)))
  460. @@ -978,12 +1010,13 @@ module.exports = grammar({
  461. property_declaration: ($) =>
  462. seq(
  463. - optional(field("attributes", $.attribute_list)),
  464. + repeat(field("attributes", $.attribute_list)),
  465. optional(field("modifiers", repeat1($.member_modifier))), // Changed
  466. ci("Property"),
  467. field("name", $.identifier),
  468. optional(field("parameters", $.parameter_list)),
  469. - $.as_clause,
  470. + optional($.as_clause),
  471. + optional(seq("=", field("initializer", $._expression))),
  472. optional($.implements_member_clause),
  473. optional(seq("=", field("initializer", $._expression))),
  474. choice(
  475. @@ -1001,7 +1034,7 @@ module.exports = grammar({
  476. property_getter: ($) =>
  477. seq(
  478. - optional(field("attributes", $.attribute_list)),
  479. + repeat(field("attributes", $.attribute_list)),
  480. optional(field("modifiers", repeat1($.member_modifier))),
  481. ci("Get"),
  482. $._block_terminator,
  483. @@ -1013,7 +1046,7 @@ module.exports = grammar({
  484. property_setter: ($) =>
  485. seq(
  486. - optional(field("attributes", $.attribute_list)),
  487. + repeat(field("attributes", $.attribute_list)),
  488. optional(field("modifiers", repeat1($.member_modifier))),
  489. ci("Set"),
  490. optional(seq("(", field("parameter", $.parameter), ")")),
  491. @@ -1028,7 +1061,7 @@ module.exports = grammar({
  492. prec(
  493. 1,
  494. seq(
  495. - optional(field("attributes", $.attribute_list)),
  496. + repeat(field("attributes", $.attribute_list)),
  497. optional(field("modifiers", repeat1($.member_modifier))),
  498. choice(
  499. seq(
  500. @@ -1063,7 +1096,7 @@ module.exports = grammar({
  501. prec(
  502. 100,
  503. seq(
  504. - optional(field("attributes", $.attribute_list)),
  505. + repeat(field("attributes", $.attribute_list)),
  506. optional(field("modifiers", repeat1($.member_modifier))),
  507. $._sub_new,
  508. optional(field("parameters", $.parameter_list)),
  509. @@ -1087,7 +1120,7 @@ module.exports = grammar({
  510. event_declaration: ($) =>
  511. seq(
  512. - optional(field("attributes", $.attribute_list)),
  513. + repeat(field("attributes", $.attribute_list)),
  514. optional(field("modifiers", repeat1($.member_modifier))), // Changed
  515. ci("Event"),
  516. field("name", $.identifier),
  517. @@ -1098,7 +1131,7 @@ module.exports = grammar({
  518. operator_declaration: ($) =>
  519. seq(
  520. - optional(field("attributes", $.attribute_list)),
  521. + repeat(field("attributes", $.attribute_list)),
  522. optional(field("modifiers", repeat1($.member_modifier))), // Changed
  523. ci("Operator"),
  524. field(
  525. @@ -1152,7 +1185,7 @@ module.exports = grammar({
  526. interface_method_declaration: ($) =>
  527. seq(
  528. - optional(field("attributes", $.attribute_list)),
  529. + repeat(field("attributes", $.attribute_list)),
  530. optional(field("modifiers", repeat1($.member_modifier))), // Changed
  531. choice(ci("Sub"), ci("Function")),
  532. field("name", $.identifier),
  533. @@ -1164,7 +1197,7 @@ module.exports = grammar({
  534. interface_property_declaration: ($) =>
  535. seq(
  536. - optional(field("attributes", $.attribute_list)),
  537. + repeat(field("attributes", $.attribute_list)),
  538. optional(field("modifiers", repeat1($.member_modifier))), // Changed
  539. ci("Property"),
  540. field("name", $.identifier),
  541. @@ -1175,7 +1208,7 @@ module.exports = grammar({
  542. interface_event_declaration: ($) =>
  543. seq(
  544. - optional(field("attributes", $.attribute_list)),
  545. + repeat(field("attributes", $.attribute_list)),
  546. optional(field("modifiers", repeat1($.member_modifier))), // Changed
  547. ci("Event"),
  548. field("name", $.identifier),
  549. @@ -1209,7 +1242,7 @@ module.exports = grammar({
  550. parameter: ($) =>
  551. seq(
  552. - optional(field("attributes", $.attribute_list)),
  553. + repeat(field("attributes", $.attribute_list)),
  554. optional(
  555. field(
  556. "modifiers",
  557. @@ -1273,7 +1306,7 @@ module.exports = grammar({
  558. declaration_statement: ($) =>
  559. prec(1, seq( // Add precedence
  560. - optional(field("attributes", $.attribute_list)),
  561. + repeat(field("attributes", $.attribute_list)),
  562. field("modifiers", choice(
  563. $.local_declaration_modifier,
  564. repeat1($.local_declaration_modifier)
  565. @@ -1282,6 +1315,65 @@ module.exports = grammar({
  566. $._terminator
  567. )),
  568. + _inline_statements: ($) =>
  569. + prec.right(seq($._inline_statement, repeat(seq(":", $._inline_statement)))),
  570. +
  571. + _inline_statement: ($) =>
  572. + choice(
  573. + alias($.inline_if_statement, $.if_statement),
  574. + alias($.inline_assignment_statement, $.assignment_statement),
  575. + alias($.inline_return_statement, $.return_statement),
  576. + alias($.inline_throw_statement, $.throw_statement),
  577. + alias($.inline_goto_statement, $.goto_statement),
  578. + alias($.inline_exit_statement, $.exit_statement),
  579. + alias($.inline_continue_statement, $.continue_statement),
  580. + alias($.inline_call_statement, $.call_statement),
  581. + alias($.inline_raiseevent_statement, $.raiseevent_statement),
  582. + alias($.inline_expression_statement, $.expression_statement)
  583. + ),
  584. +
  585. + inline_assignment_statement: ($) =>
  586. + prec.right(
  587. + PRECEDENCE.ASSIGNMENT + 1,
  588. + seq(
  589. + field("left", choice(
  590. + $.identifier,
  591. + $.member_access_expression,
  592. + $.array_access_expression
  593. + )),
  594. + field(
  595. + "operator",
  596. + choice("=", "+=", "-=", "*=", "/=", "\\=", "^=", "&=", "<<=", ">>=")
  597. + ),
  598. + field("right", $._expression)
  599. + )
  600. + ),
  601. + inline_return_statement: ($) => prec.right(1, seq(ci("Return"), optional($._expression))),
  602. + inline_throw_statement: ($) => prec.right(1, seq(ci("Throw"), optional($._expression))),
  603. + inline_goto_statement: ($) => seq(ci("GoTo"), field("label", $.identifier)),
  604. + inline_exit_statement: ($) =>
  605. + seq(
  606. + ci("Exit"),
  607. + choice(ci("Do"), ci("For"), ci("While"), ci("Select"), ci("Sub"), ci("Function"), ci("Property"), ci("Try"))
  608. + ),
  609. + inline_continue_statement: ($) =>
  610. + seq(ci("Continue"), choice(ci("Do"), ci("For"), ci("While"))),
  611. + inline_call_statement: ($) => prec.right(1, seq(ci("Call"), $._expression)),
  612. + inline_raiseevent_statement: ($) =>
  613. + prec.right(1, seq(ci("RaiseEvent"), field("event", $.identifier), optional(field("arguments", $.argument_list)))),
  614. + inline_expression_statement: ($) => prec.right(1, $._expression),
  615. +
  616. + inline_if_statement: ($) =>
  617. + prec.right(
  618. + seq(
  619. + ci("If"),
  620. + field("condition", $._expression),
  621. + ci("Then"),
  622. + field("then_statement", $._inline_statements),
  623. + optional(seq(ci("Else"), field("else_statement", $._inline_statements)))
  624. + )
  625. + ),
  626. +
  627. expression_statement: ($) => seq($._expression, $._terminator),
  628. assignment_statement: ($) =>
  629. @@ -1301,21 +1393,96 @@ module.exports = grammar({
  630. $._terminator
  631. )
  632. ),
  633. + custom_event_declaration: ($) =>
  634. + seq(
  635. + repeat(field("attributes", $.attribute_list)),
  636. + optional(field("modifiers", repeat1($.member_modifier))),
  637. + ci("Custom"),
  638. + ci("Event"),
  639. + field("name", $.identifier),
  640. + $.as_clause,
  641. + optional($.implements_member_clause),
  642. + $._block_terminator,
  643. + repeat($.event_accessor),
  644. + ci("End"), ci("Event"),
  645. + $._terminator
  646. + ),
  647. +
  648. + event_accessor: ($) =>
  649. + seq(
  650. + choice(ci("AddHandler"), ci("RemoveHandler"), ci("RaiseEvent")),
  651. + optional(field("parameters", $.parameter_list)),
  652. + $._block_terminator,
  653. + repeat($._statement),
  654. + ci("End"),
  655. + choice(ci("AddHandler"), ci("RemoveHandler"), ci("RaiseEvent")),
  656. + $._terminator
  657. + ),
  658. +
  659. + _mustoverride_token: ($) => token(prec(11, ci("MustOverride"))),
  660. +
  661. + abstract_method_declaration: ($) =>
  662. + seq(
  663. + repeat(field("attributes", $.attribute_list)),
  664. + optional(field("modifiers", repeat1($.member_modifier))),
  665. + $._mustoverride_token,
  666. + optional(field("modifiers", repeat1($.member_modifier))),
  667. + choice(ci("Sub"), ci("Function")),
  668. + field("name", $.identifier),
  669. + optional(field("type_parameters", $.type_parameter_list)),
  670. + optional(field("parameters", $.parameter_list)),
  671. + optional($.as_clause),
  672. + optional($.implements_member_clause),
  673. + $._terminator
  674. + ),
  675. +
  676. + abstract_property_declaration: ($) =>
  677. + seq(
  678. + repeat(field("attributes", $.attribute_list)),
  679. + optional(field("modifiers", repeat1($.member_modifier))),
  680. + $._mustoverride_token,
  681. + optional(field("modifiers", repeat1($.member_modifier))),
  682. + ci("Property"),
  683. + field("name", $.identifier),
  684. + optional(field("parameters", $.parameter_list)),
  685. + optional($.as_clause),
  686. + optional($.implements_member_clause),
  687. + $._terminator
  688. + ),
  689. +
  690. + external_method_declaration: ($) =>
  691. + seq(
  692. + repeat(field("attributes", $.attribute_list)),
  693. + optional(field("modifiers", repeat1($.member_modifier))),
  694. + ci("Declare"),
  695. + optional(choice(ci("Auto"), ci("Ansi"), ci("Unicode"))),
  696. + choice(ci("Sub"), ci("Function")),
  697. + field("name", $.identifier),
  698. + ci("Lib"),
  699. + field("library", $.string_literal),
  700. + optional(seq(ci("Alias"), field("alias", $.string_literal))),
  701. + optional(field("parameters", $.parameter_list)),
  702. + optional($.as_clause),
  703. + $._terminator
  704. + ),
  705. +
  706. call_statement: ($) => seq(ci("Call"), $._expression, $._terminator),
  707. if_statement: ($) =>
  708. seq(
  709. ci("If"),
  710. field("condition", $._expression),
  711. - ci("Then"),
  712. choice(
  713. - // Single-line if
  714. + // Single-line if: inline statements, optional Else, one terminator
  715. seq(
  716. - field("then_statement", $._statement),
  717. - optional(seq(ci("Else"), field("else_statement", $._statement)))
  718. + ci("Then"),
  719. + field("then_statement", $._inline_statements),
  720. + optional(seq(ci("Else"), field("else_statement", $._inline_statements))),
  721. + $._terminator
  722. ),
  723. // Multi-line if
  724. seq(
  725. + optional(ci("Then")),
  726. $._block_terminator,
  727. field("then_branch", repeat($._statement)),
  728. repeat($.elseif_clause),
  729. @@ -1331,7 +1498,7 @@ module.exports = grammar({
  730. seq(
  731. ci("ElseIf"),
  732. field("condition", $._expression),
  733. - ci("Then"),
  734. + optional(ci("Then")),
  735. $._block_terminator,
  736. field("body", repeat($._statement))
  737. ),
  738. @@ -1445,7 +1612,7 @@ module.exports = grammar({
  739. using_statement: ($) =>
  740. seq(
  741. ci("Using"),
  742. - field("resource", choice($.variable_declarator, $._expression)),
  743. + commaSep1(field("resource", choice($.variable_declarator, $._expression))),
  744. $._block_terminator,
  745. field("body", repeat($._statement)),
  746. ci("End"),
  747. @@ -1590,14 +1757,25 @@ module.exports = grammar({
  748. $._terminator
  749. ), // LINQ Query expressions
  750. query_expression: ($) =>
  751. - seq($.from_clause, repeat($.query_body_clause), $.select_or_group_clause),
  752. - from_clause: ($) => seq(ci("From"), commaSep1($.collection_range_variable)),
  753. + prec.right(
  754. + choice(
  755. + seq(
  756. + $.from_clause,
  757. + repeat(seq(optional($._query_clause_continuation), $.query_body_clause)),
  758. + optional(seq(optional($._query_clause_continuation), $.select_or_group_clause))
  759. + ),
  760. + $.aggregate_clause
  761. + )
  762. + ),
  763. + from_clause: ($) => prec.right(seq(ci("From"), commaSep1($.collection_range_variable))),
  764. collection_range_variable: ($) =>
  765. - seq(
  766. - field("variable", $.identifier),
  767. - optional($.as_clause),
  768. - ci("In"),
  769. - field("collection", $._expression)
  770. + prec.right(
  771. + seq(
  772. + field("variable", $.identifier),
  773. + optional($.as_clause),
  774. + ci("In"),
  775. + field("collection", $._expression)
  776. + )
  777. ),
  778. query_body_clause: ($) =>
  779. choice(
  780. @@ -1606,48 +1784,62 @@ module.exports = grammar({
  781. $.where_clause,
  782. $.join_clause,
  783. $.order_by_clause,
  784. - $.aggregate_clause
  785. - ),
  786. - let_clause: ($) => seq(ci("Let"), commaSep1($.expression_range_variable)),
  787. + $.aggregate_clause,
  788. + $.distinct_clause,
  789. + $.skip_take_clause
  790. + ),
  791. + distinct_clause: ($) => ci("Distinct"),
  792. + skip_take_clause: ($) =>
  793. + prec.right(seq(choice(ci("Skip"), ci("Take")), optional(ci("While")), $._expression)),
  794. + let_clause: ($) => prec.right(seq(ci("Let"), commaSep1($.expression_range_variable))),
  795. expression_range_variable: ($) =>
  796. - seq(
  797. + prec.right(seq(
  798. field("variable", $.identifier),
  799. "=",
  800. field("expression", $._expression)
  801. - ),
  802. - where_clause: ($) => seq(ci("Where"), field("condition", $._expression)),
  803. + )),
  804. + where_clause: ($) =>
  805. + prec.right(seq(ci("Where"), field("condition", $._expression))),
  806. join_clause: ($) =>
  807. - seq(
  808. - ci("Join"),
  809. - $.collection_range_variable,
  810. - ci("On"),
  811. - commaSep1($.join_condition)
  812. + prec.right(
  813. + seq(
  814. + ci("Join"),
  815. + $.collection_range_variable,
  816. + optional($._query_clause_continuation),
  817. + ci("On"),
  818. + commaSep1($.join_condition)
  819. + )
  820. ),
  821. join_condition: ($) =>
  822. - seq(
  823. + prec.right(seq(
  824. field("left", $._expression),
  825. ci("Equals"),
  826. field("right", $._expression)
  827. - ),
  828. - order_by_clause: ($) => seq(ci("Order"), ci("By"), commaSep1($.ordering)),
  829. + )),
  830. + order_by_clause: ($) => prec.right(seq(ci("Order"), ci("By"), commaSep1($.ordering))),
  831. ordering: ($) =>
  832. - seq(
  833. - field("expression", $._expression),
  834. - optional(choice(ci("Ascending"), ci("Descending")))
  835. + prec.right(
  836. + seq(
  837. + field("expression", $._expression),
  838. + optional(choice(ci("Ascending"), ci("Descending")))
  839. + )
  840. ),
  841. aggregate_clause: ($) =>
  842. - seq(
  843. - ci("Aggregate"),
  844. - $.collection_range_variable,
  845. - repeat($.query_body_clause),
  846. - ci("Into"),
  847. - commaSep1($.aggregate_element)
  848. + prec.right(
  849. + seq(
  850. + ci("Aggregate"),
  851. + $.collection_range_variable,
  852. + repeat(seq(optional($._query_clause_continuation), $.query_body_clause)),
  853. + optional($._query_clause_continuation),
  854. + ci("Into"),
  855. + commaSep1($.aggregate_element)
  856. + )
  857. ),
  858. aggregate_element: ($) =>
  859. - seq(
  860. + prec.right(seq(
  861. optional(seq(field("name", $.identifier), "=")),
  862. field("function", $.aggregate_function)
  863. - ),
  864. + )),
  865. aggregate_function: ($) =>
  866. seq(
  867. field(
  868. @@ -1668,42 +1860,44 @@ module.exports = grammar({
  869. select_or_group_clause: ($) => choice($.select_clause, $.group_by_clause),
  870. select_clause: ($) => seq(ci("Select"), commaSep1($.select_element)),
  871. select_element: ($) =>
  872. - choice(
  873. + prec.right(choice(
  874. $._expression,
  875. seq(
  876. field("name", $.identifier),
  877. "=",
  878. field("expression", $._expression)
  879. )
  880. - ),
  881. + )),
  882. group_by_clause: ($) =>
  883. - seq(
  884. - ci("Group"),
  885. - commaSep1($.group_element),
  886. - ci("By"),
  887. - commaSep1($.by_element),
  888. - ci("Into"),
  889. - commaSep1($.into_element)
  890. + prec.right(
  891. + seq(
  892. + ci("Group"),
  893. + commaSep1($.group_element),
  894. + ci("By"),
  895. + commaSep1($.by_element),
  896. + ci("Into"),
  897. + commaSep1($.into_element)
  898. + )
  899. ),
  900. group_element: ($) =>
  901. - choice(
  902. + prec.right(choice(
  903. $._expression,
  904. seq(
  905. field("name", $.identifier),
  906. "=",
  907. field("expression", $._expression)
  908. )
  909. - ),
  910. + )),
  911. by_element: ($) =>
  912. - choice(
  913. + prec.right(choice(
  914. $._expression,
  915. seq(
  916. field("name", $.identifier),
  917. "=",
  918. field("expression", $._expression)
  919. )
  920. - ),
  921. - into_element: ($) => choice($.identifier, $.aggregate_element),
  922. + )),
  923. + into_element: ($) => prec.right(choice($.identifier, $.aggregate_element)),
  924. // Comments
  925. comment: ($) => token(prec(100, choice(
  926. @@ -1714,7 +1908,7 @@ module.exports = grammar({
  927. ))),
  928. // Preprocessor Directives
  929. - preprocessor_directive: ($) => token(seq("#", /[^\r\n]*/)),
  930. + preprocessor_directive: ($) => token(seq("#", /[ \t]*[A-Za-z][^\r\n]*/)),
  931. },
  932. });
  933. diff --git a/src/scanner.c b/src/scanner.c
  934. new file mode 100644
  935. index 0000000..8334fdb
  936. --- /dev/null
  937. +++ b/src/scanner.c
  938. @@ -0,0 +1,303 @@
  939. +#include "tree_sitter/parser.h"
  940. +#include <string.h>
  941. +#include <wctype.h>
  942. +
  943. +// External tokens for the two constructs that are not LR(1)-parseable with
  944. +// tree-sitter's newline-as-extra treatment:
  945. +//
  946. +// 1. QUERY_CLAUSE_CONTINUATION — a newline run that CONTINUES a LINQ query
  947. +// (`From x In xs` ↵ `Where …`). At a clause boundary the parser cannot
  948. +// know from the newline alone whether the query ends (newline = statement
  949. +// terminator) or continues (newline = insignificant, next line starts with
  950. +// a query keyword). The scanner looks past the newline at the next word
  951. +// and only emits the token when it is a query-clause keyword — so the
  952. +// terminator/continuation choice is made by the lexer, not the LR table.
  953. +//
  954. +// 2. XML_LITERAL — a whole VB XML literal (`<Tags><Tag/></Tags>`, including
  955. +// `<%= expr %>` embedded expressions, comments, CDATA) consumed as one
  956. +// opaque token. Only valid where a literal can begin an expression, so a
  957. +// relational `<` (which always FOLLOWS an expression) can never collide.
  958. +
  959. +enum TokenType {
  960. + QUERY_CLAUSE_CONTINUATION,
  961. + XML_LITERAL,
  962. +};
  963. +
  964. +void *tree_sitter_vbnet_external_scanner_create(void) { return NULL; }
  965. +void tree_sitter_vbnet_external_scanner_destroy(void *payload) { (void)payload; }
  966. +unsigned tree_sitter_vbnet_external_scanner_serialize(void *payload, char *buffer) {
  967. + (void)payload; (void)buffer;
  968. + return 0; // stateless
  969. +}
  970. +void tree_sitter_vbnet_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
  971. + (void)payload; (void)buffer; (void)length;
  972. +}
  973. +
  974. +static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
  975. +static inline void skip_ws(TSLexer *lexer) { lexer->advance(lexer, true); }
  976. +
  977. +// Read one alphabetic word (advancing past it), lowercased into buf.
  978. +// Returns its length (0 if the next char is not a letter).
  979. +static int read_word(TSLexer *lexer, char *buf, int cap) {
  980. + int n = 0;
  981. + while (iswalpha((wint_t)lexer->lookahead)) {
  982. + if (n < cap - 1) buf[n] = (char)towlower((wint_t)lexer->lookahead);
  983. + n++;
  984. + advance(lexer);
  985. + }
  986. + buf[n < cap - 1 ? n : cap - 1] = 0;
  987. + return n;
  988. +}
  989. +
  990. +static bool is_query_keyword(const char *w) {
  991. + static const char *kws[] = {
  992. + "where", "select", "order", "group", "join", "let", "skip",
  993. + "take", "distinct", "aggregate", "from", "into", "on", NULL,
  994. + };
  995. + for (int i = 0; kws[i]; i++)
  996. + if (strcmp(w, kws[i]) == 0) return true;
  997. + return false;
  998. +}
  999. +
  1000. +// [ \t\r]* '\n' [ \t\r\n]* followed by a query-clause keyword.
  1001. +// The token covers only the whitespace/newline run (mark_end before the
  1002. +// keyword lookahead), so the clause keyword itself lexes normally after it.
  1003. +static bool scan_query_continuation(TSLexer *lexer) {
  1004. + bool saw_newline = false;
  1005. + for (;;) {
  1006. + int32_t c = lexer->lookahead;
  1007. + if (c == ' ' || c == '\t' || c == '\r') {
  1008. + advance(lexer);
  1009. + } else if (c == '\n') {
  1010. + saw_newline = true;
  1011. + advance(lexer);
  1012. + } else {
  1013. + break;
  1014. + }
  1015. + }
  1016. + if (!saw_newline) return false;
  1017. + lexer->mark_end(lexer);
  1018. +
  1019. + char w[12];
  1020. + int n = read_word(lexer, w, sizeof w);
  1021. + if (n == 0 || n >= (int)(sizeof w)) return false;
  1022. + if (!is_query_keyword(w)) return false;
  1023. +
  1024. + // `Select Case` opens a statement, never a select clause.
  1025. + if (strcmp(w, "select") == 0) {
  1026. + while (lexer->lookahead == ' ' || lexer->lookahead == '\t') advance(lexer);
  1027. + char w2[8];
  1028. + int m = read_word(lexer, w2, sizeof w2);
  1029. + if (m > 0 && m < (int)(sizeof w2) && strcmp(w2, "case") == 0) return false;
  1030. + }
  1031. +
  1032. + lexer->result_symbol = QUERY_CLAUSE_CONTINUATION;
  1033. + return true;
  1034. +}
  1035. +
  1036. +// Consume a VB string inside an embedded `<%= … %>` region ("" escapes).
  1037. +static void consume_vb_string(TSLexer *lexer) {
  1038. + advance(lexer); // opening quote
  1039. + for (;;) {
  1040. + if (lexer->lookahead == 0) return;
  1041. + if (lexer->lookahead == '"') {
  1042. + advance(lexer);
  1043. + if (lexer->lookahead != '"') return; // closing (not an escaped "")
  1044. + // escaped quote: fall through, keep consuming
  1045. + }
  1046. + advance(lexer);
  1047. + }
  1048. +}
  1049. +
  1050. +// Consume `<%= … %>` starting at the '%' (the '<' is already consumed).
  1051. +// Embedded expressions NEST (`<%= From t In ts Select <Tag><%= t.Name %></Tag> %>`,
  1052. +// the staxrip WriteTagfile shape), so track <% / %> depth.
  1053. +static bool consume_embedded_expression(TSLexer *lexer) {
  1054. + advance(lexer); // '%'
  1055. + int depth = 1;
  1056. + for (;;) {
  1057. + int32_t c = lexer->lookahead;
  1058. + if (c == 0) return false;
  1059. + if (c == '"') { consume_vb_string(lexer); continue; }
  1060. + if (c == '<') {
  1061. + advance(lexer);
  1062. + if (lexer->lookahead == '%') { depth++; advance(lexer); }
  1063. + continue;
  1064. + }
  1065. + if (c == '%') {
  1066. + advance(lexer);
  1067. + if (lexer->lookahead == '>') {
  1068. + advance(lexer);
  1069. + if (--depth == 0) return true;
  1070. + }
  1071. + continue;
  1072. + }
  1073. + advance(lexer);
  1074. + }
  1075. +}
  1076. +
  1077. +// Consume `<!-- … -->` / `<![CDATA[ … ]]>` / `<!DOCTYPE …>` starting at '!'.
  1078. +static bool consume_bang_construct(TSLexer *lexer) {
  1079. + advance(lexer); // '!'
  1080. + if (lexer->lookahead == '-') {
  1081. + // comment: to -->
  1082. + int dashes = 0;
  1083. + for (;;) {
  1084. + int32_t c = lexer->lookahead;
  1085. + if (c == 0) return false;
  1086. + if (c == '-') { dashes++; advance(lexer); continue; }
  1087. + if (c == '>' && dashes >= 2) { advance(lexer); return true; }
  1088. + dashes = 0;
  1089. + advance(lexer);
  1090. + }
  1091. + }
  1092. + if (lexer->lookahead == '[') {
  1093. + // CDATA: to ]]>
  1094. + int brackets = 0;
  1095. + for (;;) {
  1096. + int32_t c = lexer->lookahead;
  1097. + if (c == 0) return false;
  1098. + if (c == ']') { brackets++; advance(lexer); continue; }
  1099. + if (c == '>' && brackets >= 2) { advance(lexer); return true; }
  1100. + brackets = 0;
  1101. + advance(lexer);
  1102. + }
  1103. + }
  1104. + // DOCTYPE-ish: to bare >
  1105. + for (;;) {
  1106. + int32_t c = lexer->lookahead;
  1107. + if (c == 0) return false;
  1108. + if (c == '>') { advance(lexer); return true; }
  1109. + advance(lexer);
  1110. + }
  1111. +}
  1112. +
  1113. +// Consume `<? … ?>` starting at the '?'.
  1114. +static bool consume_processing_instruction(TSLexer *lexer) {
  1115. + advance(lexer); // '?'
  1116. + for (;;) {
  1117. + int32_t c = lexer->lookahead;
  1118. + if (c == 0) return false;
  1119. + if (c == '?') {
  1120. + advance(lexer);
  1121. + if (lexer->lookahead == '>') { advance(lexer); return true; }
  1122. + continue;
  1123. + }
  1124. + advance(lexer);
  1125. + }
  1126. +}
  1127. +
  1128. +static bool is_name_start(int32_t c) {
  1129. + return iswalpha((wint_t)c) || c == '_';
  1130. +}
  1131. +
  1132. +static bool scan_xml_literal(TSLexer *lexer) {
  1133. + // Only same-line blanks are skipped: a leading newline must stay unconsumed
  1134. + // so it can serve as a statement/block terminator (a successful scan would
  1135. + // otherwise swallow it as trivia). XML on a continuation line still works:
  1136. + // the newline is consumed as an extra first, then the scanner re-runs.
  1137. + while (lexer->lookahead == ' ' || lexer->lookahead == '\t') skip_ws(lexer);
  1138. + if (lexer->lookahead != '<') return false;
  1139. + advance(lexer);
  1140. + // Only element-start markup opens an XML literal here; comments/PIs as the
  1141. + // outermost construct (`Dim d = <?xml …`) are rare enough to leave alone,
  1142. + // and a bare `<` (comparison) never begins an expression.
  1143. + if (!is_name_start(lexer->lookahead)) return false;
  1144. +
  1145. + int depth = 0; // open (unclosed) elements
  1146. + bool in_tag = true; // inside <...> of an element tag
  1147. + bool closing = false;
  1148. + int32_t quote = 0;
  1149. +
  1150. + for (;;) {
  1151. + int32_t c = lexer->lookahead;
  1152. + if (c == 0) return false; // unterminated: let the normal parser error
  1153. +
  1154. + if (in_tag) {
  1155. + if (quote) {
  1156. + if (c == quote) quote = 0;
  1157. + advance(lexer);
  1158. + continue;
  1159. + }
  1160. + if (c == '"' || c == '\'') { quote = c; advance(lexer); continue; }
  1161. + if (c == '<') {
  1162. + advance(lexer);
  1163. + if (lexer->lookahead == '%') {
  1164. + // attribute value embedded expression: attr=<%= x %>
  1165. + if (!consume_embedded_expression(lexer)) return false;
  1166. + continue;
  1167. + }
  1168. + continue; // stray < in a tag: malformed, keep consuming
  1169. + }
  1170. + if (c == '/') {
  1171. + advance(lexer);
  1172. + if (lexer->lookahead == '>') {
  1173. + // self-closing tag
  1174. + advance(lexer);
  1175. + in_tag = false;
  1176. + if (depth == 0) break; // single self-closed root: done
  1177. + continue;
  1178. + }
  1179. + continue;
  1180. + }
  1181. + if (c == '>') {
  1182. + advance(lexer);
  1183. + if (closing) {
  1184. + depth--;
  1185. + if (depth <= 0) break; // root element closed: done
  1186. + } else {
  1187. + depth++;
  1188. + }
  1189. + in_tag = false;
  1190. + continue;
  1191. + }
  1192. + advance(lexer);
  1193. + continue;
  1194. + }
  1195. +
  1196. + // text content between tags
  1197. + if (c == '<') {
  1198. + advance(lexer);
  1199. + int32_t d = lexer->lookahead;
  1200. + if (d == '/') { advance(lexer); closing = true; in_tag = true; continue; }
  1201. + if (d == '%') {
  1202. + if (!consume_embedded_expression(lexer)) return false;
  1203. + continue;
  1204. + }
  1205. + if (d == '!') {
  1206. + if (!consume_bang_construct(lexer)) return false;
  1207. + continue;
  1208. + }
  1209. + if (d == '?') {
  1210. + if (!consume_processing_instruction(lexer)) return false;
  1211. + continue;
  1212. + }
  1213. + closing = false;
  1214. + in_tag = true; // opening tag
  1215. + continue;
  1216. + }
  1217. + advance(lexer);
  1218. + }
  1219. +
  1220. + lexer->mark_end(lexer);
  1221. + lexer->result_symbol = XML_LITERAL;
  1222. + return true;
  1223. +}
  1224. +
  1225. +bool tree_sitter_vbnet_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
  1226. + (void)payload;
  1227. + int32_t c = lexer->lookahead;
  1228. + bool ws = (c == ' ' || c == '\t' || c == '\n' || c == '\r');
  1229. +
  1230. + // A scan attempt advances the lexer, so exactly ONE attempt runs per
  1231. + // invocation — never chain a second attempt after a failed first (its
  1232. + // reads would start mid-stream). The leading character picks the branch;
  1233. + // a false return discards the attempt entirely.
  1234. + if (valid_symbols[QUERY_CLAUSE_CONTINUATION] && ws) {
  1235. + return scan_query_continuation(lexer);
  1236. + }
  1237. + if (valid_symbols[XML_LITERAL] && (c == '<' || ws)) {
  1238. + return scan_xml_literal(lexer);
  1239. + }
  1240. + return false;
  1241. +}