@willbooster/tree-sitter-c-sharp 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,8 +27,9 @@ The grammar is based upon the Roslyn grammar with changes in order to:
27
27
  Known gaps:
28
28
 
29
29
  - `async`, `var` and `await` cannot be used as identifiers everywhere they are valid
30
- - A pointer dereference can be assigned to only when its operand is a variable, member access, element access,
31
- or another dereference (`*p = 1`, `*(p) = 1`, `*a.b = 1`); `*(int*)p = 1` and `*(p + 1) = 1` are not recognized
30
+ - A pointer dereference can be assigned to only when its operand is a variable, an address, a parenthesized or
31
+ postfix expression, or a cast of one of these or of an invocation (`*p = 1`, `*(p + 1) = 1`, `*(int*)p = 1`,
32
+ `*dst++ = 1`); `*++p = 1` and `*f() = 1` are not recognized
32
33
  - File-based apps preprocessor directives (`#:property`, `#:package`, `#:sdk`, `#:project`) are not yet recognized
33
34
 
34
35
  ## Usage
package/grammar.js CHANGED
@@ -101,6 +101,7 @@ module.exports = grammar({
101
101
  [$._reserved_identifier, $.parameter],
102
102
  [$._simple_name, $.parameter],
103
103
  [$.tuple_element, $.parameter, $.declaration_expression],
104
+ [$.parameter, $.declaration_expression],
104
105
  [$.parameter, $.tuple_element],
105
106
 
106
107
  [$.event_declaration, $.variable_declarator],
@@ -518,8 +519,9 @@ module.exports = grammar({
518
519
  )
519
520
  ),
520
521
 
522
+ // The precedence reads the `ref` of `extension(ref int)` as a modifier rather than a ref_type.
521
523
  receiver_parameter: ($) =>
522
- seq(repeat($._attribute_list), $._parameter_type_with_modifiers, optional(field('name', $.identifier))),
524
+ prec(1, seq(repeat($._attribute_list), $._parameter_type_with_modifiers, optional(field('name', $.identifier)))),
523
525
 
524
526
  extension_body: ($) => seq('{', repeat($._extension_member_declaration), '}', optional(';')),
525
527
 
@@ -615,9 +617,18 @@ module.exports = grammar({
615
617
 
616
618
  parameter_list: ($) => seq('(', sep(choice($.parameter, $._parameter_array), ','), ')'),
617
619
 
620
+ // `ref`, `out`, and `in` stay out of the repeat: reducing the repeat after them would compete with the shift of
621
+ // the next token into an argument (`(ref x)`), whose precedence wins, so `(ref int i) =>` would read `ref int` as a
622
+ // ref_type while a method's `(ref int i)` reads `ref` as a modifier.
618
623
  _parameter_type_with_modifiers: ($) =>
619
624
  seq(
620
- repeat(prec.left(alias(choice('this', 'scoped', 'ref', 'out', 'in', 'readonly'), $.modifier))),
625
+ repeat(prec.left(alias(choice('this', 'scoped', 'readonly'), $.modifier))),
626
+ optional(
627
+ seq(
628
+ alias(choice('ref', 'out', 'in'), $.modifier),
629
+ repeat(prec.left(alias(choice('this', 'readonly'), $.modifier)))
630
+ )
631
+ ),
621
632
  field('type', $.type)
622
633
  ),
623
634
 
@@ -770,7 +781,8 @@ module.exports = grammar({
770
781
  )
771
782
  ),
772
783
 
773
- ref_type: ($) => seq('ref', optional('readonly'), field('type', $.type)),
784
+ // Right-associative so that a parameter's `ref int x` keeps `ref` as a modifier instead of reducing a ref_type.
785
+ ref_type: ($) => prec.right(seq('ref', optional('readonly'), field('type', $.type))),
774
786
 
775
787
  _ref_base_type: ($) =>
776
788
  choice(
@@ -995,9 +1007,10 @@ module.exports = grammar({
995
1007
  _name_invocation_pattern: ($) => seq(field('function', $._name), field('arguments', $.argument_list)),
996
1008
 
997
1009
  // Invocation where function is not a simple name
1010
+ // One above `invocation_expression`, which reads the same tokens where both fit.
998
1011
  _complex_invocation_expression: ($) =>
999
1012
  prec(
1000
- PREC.INVOCATION,
1013
+ PREC.INVOCATION + 1,
1001
1014
  seq(
1002
1015
  field(
1003
1016
  'function',
@@ -1230,7 +1243,7 @@ module.exports = grammar({
1230
1243
  )
1231
1244
  ),
1232
1245
 
1233
- postfix_unary_expression: ($) => prec(PREC.POSTFIX, seq($.expression, choice('++', '--', '!'))),
1246
+ postfix_unary_expression: ($) => prec(PREC.POSTFIX, seq(postfixOperand($), choice('++', '--', '!'))),
1234
1247
 
1235
1248
  prefix_unary_expression: ($) =>
1236
1249
  prec(
@@ -1248,35 +1261,69 @@ module.exports = grammar({
1248
1261
  )
1249
1262
  ),
1250
1263
 
1251
- // Pointer indirection is split by its operand. Dereferencing an lvalue
1252
- // (`*p`, `*(p)`, `**pp`) yields an lvalue, so it is surfaced in
1253
- // `lvalue_expression` and can be assigned to. Dereferencing any other
1254
- // expression (`*(long*)&v`, `*(p + 1)`) is only read: surfacing it in
1255
- // `lvalue_expression` too would let `=` follow every expression that can
1256
- // be its operand, which grows the parser from 8,495 to 13,339 states.
1264
+ // Pointer indirection is split by its operand. The assignable form, surfaced in `lvalue_expression`, takes an
1265
+ // lvalue (`*p`, `*(p)`, `**pp`) or an operand that ends in a closing token or an lvalue: a parenthesized
1266
+ // expression (`*(p + 1)`), a postfix expression (`*p++`), an address (`*&v`), or a cast of an lvalue, a
1267
+ // parenthesized, postfix, or invocation expression, or an address (`*(int*)p`, `*(long*)(p + 4)`, `*(T*)f()`,
1268
+ // `*(long*)&v`). Dereferencing any other expression (`*++p`) is only read: letting any expression be the assignable
1269
+ // form's operand lets `=` follow every expression that can be the operand, which grows the parser by about 4,800
1270
+ // states and nearly doubles the Wasm build, while these operands add about 550.
1257
1271
  //
1258
- // A parenthesized expression followed by `*` is a multiplication, not a
1259
- // cast of a dereference (C# spec §12.9.8 Cast expressions: `(a) * b` is
1260
- // a cast only when `a` cannot be an expression), so a dereference's
1261
- // penalty outweighs the cast's +1. `*(p) + 1` is likewise the sum of a
1262
- // dereference of `(p)` (-2), not a dereference of a cast of `+1`
1263
- // (-4 + 1), so dereferencing a non-lvalue carries the larger penalty.
1264
- _pointer_indirection_expression: ($) => prec.dynamic(-2, prec.right(PREC.UNARY, seq('*', $.lvalue_expression))),
1272
+ // Dynamic precedences: an assignable dereference -2, a read-only one -4, a read-only cast +1, and an assignable cast
1273
+ // -1. C# reads a parenthesized name as a cast only when the token after `)` is `~`, `!`, `(`, an identifier, a
1274
+ // literal, or a keyword (C# spec §12.9.8 Cast expressions), and these values keep that reading: `(a) * b` is a
1275
+ // multiplication (0), not a cast of `*b` (+1 - 2); `*(p) + 1` and `*(p) & x` apply to a dereference of `(p)` (-2),
1276
+ // not to a dereference of a cast of `+1` or `&x` (-4 + 1 or -2 - 1); `*(b)[0]` dereferences an element access
1277
+ // (-2), not a cast of `[0]` (-2 - 1). Where both casts fit, they give the same tree.
1278
+ _pointer_indirection_expression: ($) =>
1279
+ prec.dynamic(
1280
+ -2,
1281
+ prec.right(
1282
+ PREC.UNARY,
1283
+ seq(
1284
+ '*',
1285
+ choice(
1286
+ $.lvalue_expression,
1287
+ $.parenthesized_expression,
1288
+ alias($._address_of_expression, $.prefix_unary_expression),
1289
+ alias($._lvalue_cast_expression, $.cast_expression),
1290
+ $.postfix_unary_expression
1291
+ )
1292
+ )
1293
+ )
1294
+ ),
1295
+
1296
+ _lvalue_cast_expression: ($) =>
1297
+ prec(
1298
+ PREC.CAST,
1299
+ prec.dynamic(
1300
+ -1,
1301
+ seq(
1302
+ '(',
1303
+ field('type', $.type),
1304
+ ')',
1305
+ field(
1306
+ 'value',
1307
+ choice(
1308
+ $.lvalue_expression,
1309
+ $.parenthesized_expression,
1310
+ alias($._address_of_expression, $.prefix_unary_expression),
1311
+ $.postfix_unary_expression,
1312
+ $.invocation_expression
1313
+ )
1314
+ )
1315
+ )
1316
+ )
1317
+ ),
1265
1318
 
1266
1319
  _value_indirection_expression: ($) => prec.dynamic(-4, prec.right(PREC.UNARY, seq('*', $.non_lvalue_expression))),
1267
1320
 
1268
- // Address-of is split out from `prefix_unary_expression` so that
1269
- // `&` can't act as a fallback when the lexer would otherwise
1270
- // emit `&&`. Its operand is restricted to an lvalue (same shape
1271
- // as the spec's `address-of-expression`). This is the fix for
1272
- // tree-sitter#413: `(a) && (b > 0)` was misparsed as
1273
- // `cast(a, &(&(b > 0)))` because the cast state accepted `&` as
1274
- // a unary start and the lexer split `&&` into two `&` tokens.
1275
- // With this split, `&` is only valid before an lvalue, which
1276
- // `(b > 0)` is not — so the cast interpretation can no longer
1277
- // consume the trailing parenthesized expression and `&&` must
1278
- // be emitted as a single token for the binary path to succeed.
1279
- _address_of_expression: ($) => prec.right(PREC.UNARY, seq('&', $.lvalue_expression)),
1321
+ // Address-of is split out from `prefix_unary_expression` and takes only a variable: an lvalue, or an invocation,
1322
+ // which can return a reference (`&span.GetPinnableReference()`). If a cast could take `&` before any expression,
1323
+ // the lexer would split `&&` into two `&` tokens and `(a) && (b > 0)` would read as a cast of `&(&(b > 0))`; as
1324
+ // `(b > 0)` is not a variable, that reading fails and `&&` stays one token.
1325
+ _address_of_expression: ($) =>
1326
+ prec.right(PREC.UNARY, seq('&', choice($.lvalue_expression, $.invocation_expression))),
1280
1327
 
1281
1328
  query_expression: ($) => seq($.from_clause, $._query_body),
1282
1329
 
@@ -1358,7 +1405,7 @@ module.exports = grammar({
1358
1405
  checked_expression: ($) => seq(choice('checked', 'unchecked'), '(', $.expression, ')'),
1359
1406
 
1360
1407
  invocation_expression: ($) =>
1361
- prec(PREC.INVOCATION, seq(field('function', $.expression), field('arguments', $.argument_list))),
1408
+ prec(PREC.INVOCATION, seq(field('function', postfixOperand($)), field('arguments', $.argument_list))),
1362
1409
 
1363
1410
  switch_expression: ($) => prec(PREC.SWITCH, seq($.expression, 'switch', $._switch_expression_body)),
1364
1411
  _switch_expression_body: ($) => seq('{', commaSep($.switch_expression_arm), optional(','), '}'),
@@ -1372,7 +1419,7 @@ module.exports = grammar({
1372
1419
  throw_expression: ($) => seq('throw', $.expression),
1373
1420
 
1374
1421
  element_access_expression: ($) =>
1375
- prec(PREC.POSTFIX, seq(field('expression', $.expression), field('subscript', $.bracketed_argument_list))),
1422
+ prec(PREC.POSTFIX, seq(field('expression', postfixOperand($)), field('subscript', $.bracketed_argument_list))),
1376
1423
 
1377
1424
  interpolated_string_expression: ($) =>
1378
1425
  choice(
@@ -1422,7 +1469,7 @@ module.exports = grammar({
1422
1469
  prec(
1423
1470
  PREC.DOT,
1424
1471
  seq(
1425
- field('expression', choice($.expression, $.predefined_type, $._name)),
1472
+ field('expression', choice(postfixOperand($), $.predefined_type, $._name)),
1426
1473
  choice('.', '->'),
1427
1474
  field('name', $._simple_name)
1428
1475
  )
@@ -1843,6 +1890,26 @@ function preprocIf(suffix, content, precedence = 0, rep = true) {
1843
1890
  };
1844
1891
  }
1845
1892
 
1893
+ /**
1894
+ * The operand of a postfix operator, an invocation, an element access, or a member access. Besides `expression`, it
1895
+ * lists the forms that are operands of `&`, of the assignable `*`, or of the assignable cast: reaching them only
1896
+ * through `expression` would reduce the unary operator or the cast (precedence UNARY or CAST) before `expression`
1897
+ * (precedence 0) lets the postfix operator apply, reading `&a[0]` as `(&a)[0]` and `*(T*)f()[0]` as `(*(T*)f())[0]`.
1898
+ *
1899
+ * @param {GrammarSymbols<string>} $
1900
+ *
1901
+ * @returns {ChoiceRule}
1902
+ */
1903
+ function postfixOperand($) {
1904
+ return choice(
1905
+ $.expression,
1906
+ $.lvalue_expression,
1907
+ $.invocation_expression,
1908
+ $.postfix_unary_expression,
1909
+ $.parenthesized_expression
1910
+ );
1911
+ }
1912
+
1846
1913
  /**
1847
1914
  * Creates a rule to match one or more of the rules separated by a comma
1848
1915
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willbooster/tree-sitter-c-sharp",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "C# grammar for tree-sitter",
5
5
  "keywords": [
6
6
  "incremental",
@@ -1511,6 +1511,14 @@
1511
1511
  {
1512
1512
  "type": "expression",
1513
1513
  "named": true
1514
+ },
1515
+ {
1516
+ "type": "invocation_expression",
1517
+ "named": true
1518
+ },
1519
+ {
1520
+ "type": "postfix_unary_expression",
1521
+ "named": true
1514
1522
  }
1515
1523
  ]
1516
1524
  }
@@ -2316,6 +2324,14 @@
2316
2324
  {
2317
2325
  "type": "expression",
2318
2326
  "named": true
2327
+ },
2328
+ {
2329
+ "type": "invocation_expression",
2330
+ "named": true
2331
+ },
2332
+ {
2333
+ "type": "postfix_unary_expression",
2334
+ "named": true
2319
2335
  }
2320
2336
  ]
2321
2337
  },
@@ -3449,36 +3465,16 @@
3449
3465
  "type": "cast_expression",
3450
3466
  "named": true
3451
3467
  },
3452
- {
3453
- "type": "conditional_access_expression",
3454
- "named": true
3455
- },
3456
- {
3457
- "type": "element_access_expression",
3458
- "named": true
3459
- },
3460
3468
  {
3461
3469
  "type": "expression",
3462
3470
  "named": true
3463
3471
  },
3464
- {
3465
- "type": "generic_name",
3466
- "named": true
3467
- },
3468
- {
3469
- "type": "identifier",
3470
- "named": true
3471
- },
3472
3472
  {
3473
3473
  "type": "invocation_expression",
3474
3474
  "named": true
3475
3475
  },
3476
3476
  {
3477
- "type": "member_access_expression",
3478
- "named": true
3479
- },
3480
- {
3481
- "type": "parenthesized_expression",
3477
+ "type": "postfix_unary_expression",
3482
3478
  "named": true
3483
3479
  },
3484
3480
  {
@@ -3894,11 +3890,11 @@
3894
3890
  "named": true
3895
3891
  },
3896
3892
  {
3897
- "type": "generic_name",
3893
+ "type": "invocation_expression",
3898
3894
  "named": true
3899
3895
  },
3900
3896
  {
3901
- "type": "identifier",
3897
+ "type": "postfix_unary_expression",
3902
3898
  "named": true
3903
3899
  },
3904
3900
  {
@@ -4668,6 +4664,14 @@
4668
4664
  {
4669
4665
  "type": "expression",
4670
4666
  "named": true
4667
+ },
4668
+ {
4669
+ "type": "invocation_expression",
4670
+ "named": true
4671
+ },
4672
+ {
4673
+ "type": "postfix_unary_expression",
4674
+ "named": true
4671
4675
  }
4672
4676
  ]
4673
4677
  }
Binary file
package/tree-sitter.json CHANGED
@@ -19,7 +19,7 @@
19
19
  }
20
20
  ],
21
21
  "metadata": {
22
- "version": "1.0.7",
22
+ "version": "1.0.9",
23
23
  "license": "MIT",
24
24
  "description": "C# grammar for tree-sitter",
25
25
  "authors": [