@willbooster/tree-sitter-c-sharp 1.0.8 → 1.0.10

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
@@ -83,6 +83,7 @@ module.exports = grammar({
83
83
  [$.constant_pattern, $.lvalue_expression, $._name],
84
84
 
85
85
  [$.type, $._name_invocation_pattern, $.recursive_pattern],
86
+ [$.type, $.recursive_pattern],
86
87
  [$.attribute, $.type, $._name_invocation_pattern, $.recursive_pattern],
87
88
 
88
89
  [$.parenthesized_pattern, $._parenthesized_pattern_with_designation],
@@ -1007,9 +1008,10 @@ module.exports = grammar({
1007
1008
  _name_invocation_pattern: ($) => seq(field('function', $._name), field('arguments', $.argument_list)),
1008
1009
 
1009
1010
  // Invocation where function is not a simple name
1011
+ // One above `invocation_expression`, which reads the same tokens where both fit.
1010
1012
  _complex_invocation_expression: ($) =>
1011
1013
  prec(
1012
- PREC.INVOCATION,
1014
+ PREC.INVOCATION + 1,
1013
1015
  seq(
1014
1016
  field(
1015
1017
  'function',
@@ -1081,10 +1083,15 @@ module.exports = grammar({
1081
1083
  prec.dynamic(1, seq($.positional_pattern_clause, $._simple_designation)),
1082
1084
  // positional pattern without variable designation (no type prefix)
1083
1085
  $.positional_pattern_clause,
1084
- // other type followed by pattern clauses (type is required here to avoid ambiguity)
1086
+ // other type followed by a property pattern clause
1087
+ seq(field('type', $.type), $.property_pattern_clause, optional($._simple_designation)),
1088
+ // A positional clause follows only a name (above), a predefined type, or an array type. Letting any type take
1089
+ // one reads `Name(A.B)` once more, through `type`; after a syntax error, the extra GLR versions then exceed
1090
+ // the runtime's limit, and which reading survives depends on the versions alive, not on dynamic precedence.
1085
1091
  seq(
1086
- field('type', $.type),
1087
- choice(seq($.positional_pattern_clause, optional($.property_pattern_clause)), $.property_pattern_clause),
1092
+ field('type', choice($.predefined_type, $.array_type)),
1093
+ $.positional_pattern_clause,
1094
+ optional($.property_pattern_clause),
1088
1095
  optional($._simple_designation)
1089
1096
  ),
1090
1097
  // no type, just pattern clauses (no variable designation to avoid conflict with above)
@@ -1242,7 +1249,7 @@ module.exports = grammar({
1242
1249
  )
1243
1250
  ),
1244
1251
 
1245
- postfix_unary_expression: ($) => prec(PREC.POSTFIX, seq($.expression, choice('++', '--', '!'))),
1252
+ postfix_unary_expression: ($) => prec(PREC.POSTFIX, seq(postfixOperand($), choice('++', '--', '!'))),
1246
1253
 
1247
1254
  prefix_unary_expression: ($) =>
1248
1255
  prec(
@@ -1260,35 +1267,69 @@ module.exports = grammar({
1260
1267
  )
1261
1268
  ),
1262
1269
 
1263
- // Pointer indirection is split by its operand. Dereferencing an lvalue
1264
- // (`*p`, `*(p)`, `**pp`) yields an lvalue, so it is surfaced in
1265
- // `lvalue_expression` and can be assigned to. Dereferencing any other
1266
- // expression (`*(long*)&v`, `*(p + 1)`) is only read: surfacing it in
1267
- // `lvalue_expression` too would let `=` follow every expression that can
1268
- // be its operand, which grows the parser from 8,495 to 13,339 states.
1270
+ // Pointer indirection is split by its operand. The assignable form, surfaced in `lvalue_expression`, takes an
1271
+ // lvalue (`*p`, `*(p)`, `**pp`) or an operand that ends in a closing token or an lvalue: a parenthesized
1272
+ // expression (`*(p + 1)`), a postfix expression (`*p++`), an address (`*&v`), or a cast of an lvalue, a
1273
+ // parenthesized, postfix, or invocation expression, or an address (`*(int*)p`, `*(long*)(p + 4)`, `*(T*)f()`,
1274
+ // `*(long*)&v`). Dereferencing any other expression (`*++p`) is only read: letting any expression be the assignable
1275
+ // form's operand lets `=` follow every expression that can be the operand, which grows the parser by about 4,800
1276
+ // states and nearly doubles the Wasm build, while these operands add about 550.
1269
1277
  //
1270
- // A parenthesized expression followed by `*` is a multiplication, not a
1271
- // cast of a dereference (C# spec §12.9.8 Cast expressions: `(a) * b` is
1272
- // a cast only when `a` cannot be an expression), so a dereference's
1273
- // penalty outweighs the cast's +1. `*(p) + 1` is likewise the sum of a
1274
- // dereference of `(p)` (-2), not a dereference of a cast of `+1`
1275
- // (-4 + 1), so dereferencing a non-lvalue carries the larger penalty.
1276
- _pointer_indirection_expression: ($) => prec.dynamic(-2, prec.right(PREC.UNARY, seq('*', $.lvalue_expression))),
1278
+ // Dynamic precedences: an assignable dereference -2, a read-only one -4, a read-only cast +1, and an assignable cast
1279
+ // -1. C# reads a parenthesized name as a cast only when the token after `)` is `~`, `!`, `(`, an identifier, a
1280
+ // literal, or a keyword (C# spec §12.9.8 Cast expressions), and these values keep that reading: `(a) * b` is a
1281
+ // multiplication (0), not a cast of `*b` (+1 - 2); `*(p) + 1` and `*(p) & x` apply to a dereference of `(p)` (-2),
1282
+ // not to a dereference of a cast of `+1` or `&x` (-4 + 1 or -2 - 1); `*(b)[0]` dereferences an element access
1283
+ // (-2), not a cast of `[0]` (-2 - 1). Where both casts fit, they give the same tree.
1284
+ _pointer_indirection_expression: ($) =>
1285
+ prec.dynamic(
1286
+ -2,
1287
+ prec.right(
1288
+ PREC.UNARY,
1289
+ seq(
1290
+ '*',
1291
+ choice(
1292
+ $.lvalue_expression,
1293
+ $.parenthesized_expression,
1294
+ alias($._address_of_expression, $.prefix_unary_expression),
1295
+ alias($._lvalue_cast_expression, $.cast_expression),
1296
+ $.postfix_unary_expression
1297
+ )
1298
+ )
1299
+ )
1300
+ ),
1301
+
1302
+ _lvalue_cast_expression: ($) =>
1303
+ prec(
1304
+ PREC.CAST,
1305
+ prec.dynamic(
1306
+ -1,
1307
+ seq(
1308
+ '(',
1309
+ field('type', $.type),
1310
+ ')',
1311
+ field(
1312
+ 'value',
1313
+ choice(
1314
+ $.lvalue_expression,
1315
+ $.parenthesized_expression,
1316
+ alias($._address_of_expression, $.prefix_unary_expression),
1317
+ $.postfix_unary_expression,
1318
+ $.invocation_expression
1319
+ )
1320
+ )
1321
+ )
1322
+ )
1323
+ ),
1277
1324
 
1278
1325
  _value_indirection_expression: ($) => prec.dynamic(-4, prec.right(PREC.UNARY, seq('*', $.non_lvalue_expression))),
1279
1326
 
1280
- // Address-of is split out from `prefix_unary_expression` so that
1281
- // `&` can't act as a fallback when the lexer would otherwise
1282
- // emit `&&`. Its operand is restricted to an lvalue (same shape
1283
- // as the spec's `address-of-expression`). This is the fix for
1284
- // tree-sitter#413: `(a) && (b > 0)` was misparsed as
1285
- // `cast(a, &(&(b > 0)))` because the cast state accepted `&` as
1286
- // a unary start and the lexer split `&&` into two `&` tokens.
1287
- // With this split, `&` is only valid before an lvalue, which
1288
- // `(b > 0)` is not — so the cast interpretation can no longer
1289
- // consume the trailing parenthesized expression and `&&` must
1290
- // be emitted as a single token for the binary path to succeed.
1291
- _address_of_expression: ($) => prec.right(PREC.UNARY, seq('&', $.lvalue_expression)),
1327
+ // Address-of is split out from `prefix_unary_expression` and takes only a variable: an lvalue, or an invocation,
1328
+ // which can return a reference (`&span.GetPinnableReference()`). If a cast could take `&` before any expression,
1329
+ // the lexer would split `&&` into two `&` tokens and `(a) && (b > 0)` would read as a cast of `&(&(b > 0))`; as
1330
+ // `(b > 0)` is not a variable, that reading fails and `&&` stays one token.
1331
+ _address_of_expression: ($) =>
1332
+ prec.right(PREC.UNARY, seq('&', choice($.lvalue_expression, $.invocation_expression))),
1292
1333
 
1293
1334
  query_expression: ($) => seq($.from_clause, $._query_body),
1294
1335
 
@@ -1370,7 +1411,7 @@ module.exports = grammar({
1370
1411
  checked_expression: ($) => seq(choice('checked', 'unchecked'), '(', $.expression, ')'),
1371
1412
 
1372
1413
  invocation_expression: ($) =>
1373
- prec(PREC.INVOCATION, seq(field('function', $.expression), field('arguments', $.argument_list))),
1414
+ prec(PREC.INVOCATION, seq(field('function', postfixOperand($)), field('arguments', $.argument_list))),
1374
1415
 
1375
1416
  switch_expression: ($) => prec(PREC.SWITCH, seq($.expression, 'switch', $._switch_expression_body)),
1376
1417
  _switch_expression_body: ($) => seq('{', commaSep($.switch_expression_arm), optional(','), '}'),
@@ -1384,7 +1425,7 @@ module.exports = grammar({
1384
1425
  throw_expression: ($) => seq('throw', $.expression),
1385
1426
 
1386
1427
  element_access_expression: ($) =>
1387
- prec(PREC.POSTFIX, seq(field('expression', $.expression), field('subscript', $.bracketed_argument_list))),
1428
+ prec(PREC.POSTFIX, seq(field('expression', postfixOperand($)), field('subscript', $.bracketed_argument_list))),
1388
1429
 
1389
1430
  interpolated_string_expression: ($) =>
1390
1431
  choice(
@@ -1434,7 +1475,7 @@ module.exports = grammar({
1434
1475
  prec(
1435
1476
  PREC.DOT,
1436
1477
  seq(
1437
- field('expression', choice($.expression, $.predefined_type, $._name)),
1478
+ field('expression', choice(postfixOperand($), $.predefined_type, $._name)),
1438
1479
  choice('.', '->'),
1439
1480
  field('name', $._simple_name)
1440
1481
  )
@@ -1855,6 +1896,26 @@ function preprocIf(suffix, content, precedence = 0, rep = true) {
1855
1896
  };
1856
1897
  }
1857
1898
 
1899
+ /**
1900
+ * The operand of a postfix operator, an invocation, an element access, or a member access. Besides `expression`, it
1901
+ * lists the forms that are operands of `&`, of the assignable `*`, or of the assignable cast: reaching them only
1902
+ * through `expression` would reduce the unary operator or the cast (precedence UNARY or CAST) before `expression`
1903
+ * (precedence 0) lets the postfix operator apply, reading `&a[0]` as `(&a)[0]` and `*(T*)f()[0]` as `(*(T*)f())[0]`.
1904
+ *
1905
+ * @param {GrammarSymbols<string>} $
1906
+ *
1907
+ * @returns {ChoiceRule}
1908
+ */
1909
+ function postfixOperand($) {
1910
+ return choice(
1911
+ $.expression,
1912
+ $.lvalue_expression,
1913
+ $.invocation_expression,
1914
+ $.postfix_unary_expression,
1915
+ $.parenthesized_expression
1916
+ );
1917
+ }
1918
+
1858
1919
  /**
1859
1920
  * Creates a rule to match one or more of the rules separated by a comma
1860
1921
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willbooster/tree-sitter-c-sharp",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
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.8",
22
+ "version": "1.0.10",
23
23
  "license": "MIT",
24
24
  "description": "C# grammar for tree-sitter",
25
25
  "authors": [