@willbooster/tree-sitter-c-sharp 2.0.1 → 3.0.0

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
@@ -28,16 +28,12 @@ The grammar is based upon the Roslyn grammar with changes in order to:
28
28
  Known gaps:
29
29
 
30
30
  - `var` and `await` cannot be used as identifiers everywhere they are valid
31
- - A pointer dereference can be assigned to only when its operand is a variable, an address, a parenthesized or
32
- postfix expression, or a cast of one of these or of an invocation (`*p = 1`, `*(p + 1) = 1`, `*(int*)p = 1`,
33
- `*dst++ = 1`); `*++p = 1` and `*f() = 1` are not recognized
34
31
 
35
32
  ## Usage
36
33
 
37
34
  The npm package ships `tree-sitter-c_sharp.wasm` for
38
35
  [@willbooster/web-tree-sitter](https://www.npmjs.com/package/@willbooster/web-tree-sitter), which runs in Node.js, Bun,
39
- browsers, and Cloudflare Workers. Use runtime version 1.1.2 or later: browser loading requires its asynchronous
40
- instantiation support for this grammar. Install the runtime alongside the grammar when using the Wasm parser; it
36
+ browsers, and Cloudflare Workers. Use runtime version 1.3.0 or later for the compact ABI 16 parser. Install the runtime alongside the grammar when using the Wasm parser; it
41
37
  remains an optional peer for consumers that only use the grammar source or queries.
42
38
 
43
39
  In Node.js and Bun, load it from the package:
@@ -83,13 +79,12 @@ The package also ships the queries in `queries/` and the node types in `src/node
83
79
 
84
80
  In Rust, depend on the [crate](https://crates.io/crates/willbooster-tree-sitter-c-sharp) and on
85
81
  [willbooster-tree-sitter](https://crates.io/crates/willbooster-tree-sitter), the runtime this package is tested and
86
- fuzzed with, whose fixes keep incremental reparses consistent with fresh parses (the grammar also loads in the upstream
87
- `tree-sitter` crate 0.27, whose error recovery never ends on some malformed input):
82
+ fuzzed with. The compact ABI 16 parser requires runtime 1.3.0 or later:
88
83
 
89
84
  ```toml
90
85
  [dependencies]
91
- tree-sitter = { package = "willbooster-tree-sitter", version = "1.1.2" }
92
- tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "2" }
86
+ tree-sitter = { package = "willbooster-tree-sitter", version = "1.3.0" }
87
+ tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "3" }
93
88
  ```
94
89
 
95
90
  ```rust
@@ -114,16 +109,28 @@ runtime this package ships. The first run downloads that CLI from its GitHub Rel
114
109
  `cargo` (with the `cmake` that `mise.toml` pins) when the download fails or the release has no binary that runs here. The `tree-sitter-cli` package provides
115
110
  only the types of the grammar DSL that `grammar.js` checks against; its `tree-sitter` binary is upstream's.
116
111
 
112
+ `bun run generate` records a fresh ABI 16 generation profile from the applicable `test/corpus` cases and Git-tracked
113
+ files in `examples/`, then generates compact parser tables. The parser also embeds metadata from `tree-sitter.json`. After changing a grammar,
114
+ `tree-sitter.json`, a corpus case, or a tracked example,
115
+ regenerate and commit `src/`. Stage added or removed examples with `git add -A examples` before generation so the profile uses the intended file list.
116
+ Profiles in `.tmp/generation-profiles/` are temporary and must not be committed. `bun run build-wasm`, `bun run build/ci`,
117
+ and the release build regenerate the parsers before compiling them.
118
+
119
+ The tracked `examples/JsonWriter.cs` and `examples/JsonSerializerInternalWriter.cs` are verbatim MIT-licensed
120
+ snapshots from Newtonsoft.Json commit `52fa3aef1f2cadcd3a3f874251eddc98d3efbbaa`, under `Src/Newtonsoft.Json/`
121
+ and its `Serialization/` directory respectively. Refresh these training inputs independently of the clone pins in
122
+ `script/parse-examples` so changing a test checkout does not change profile training.
123
+
117
124
  `bun run test` runs:
118
125
 
119
126
  - the corpus in `test/corpus`, with the native build and with the Wasm build (the first run downloads the WASI SDK);
120
127
  - an incremental-parsing check (`test/unit/incremental.test.ts`): `script/fuzz-corpus` runs `tree-sitter fuzz`, which
121
128
  edits each corpus case at random, reparses it, undoes the edits, and reparses again. `TREE_SITTER_SEED`,
122
129
  `TREE_SITTER_ITERATIONS`, and `TREE_SITTER_EDITS` run other or more edits;
123
- - a check that the real-world C# files cloned into `examples/` fail to parse exactly as listed in
124
- `script/known-failures.txt`. The first run clones them. The example repositories are pinned to commits in
125
- `script/parse-examples`. After a grammar change or a moved pin alters that list, `script/parse-examples` rewrites
126
- it; review its diff before committing;
130
+ - a check that the C# files under `examples/` that fail to parse are exactly those listed in
131
+ `script/known-failures.txt`. Inputs include Git-tracked generation examples and pinned clones. The first run fetches the pinned repositories listed in
132
+ `script/parse-examples`. After adding, removing, or changing an example, changing the grammar, or moving a pin,
133
+ run `script/parse-examples` and review the failure-list diff before committing;
127
134
  - a performance check (`test/unit/performance.test.ts`) that recovering from an error on each line takes linear time
128
135
  (ten times the lines take about ten times the CPU time, under a ceiling), since consumers parse files while they are
129
136
  being edited. It loads the Wasm build through @willbooster/web-tree-sitter, which `bun run build/ci` rebuilds after
package/grammar.js CHANGED
@@ -49,6 +49,8 @@ module.exports = grammar({
49
49
  name: 'c_sharp',
50
50
 
51
51
  conflicts: ($) => [
52
+ [$._assignment_pointer_cast, $._lvalue_cast_expression],
53
+ [$._assignment_pointer_expression, $._pointer_indirection_expression],
52
54
  [$.modifier, $._constructor_declaration_initializer, $._reserved_identifier],
53
55
  [$._simple_name, $.generic_name],
54
56
  [$._simple_name, $.type_parameter],
@@ -1209,11 +1211,67 @@ module.exports = grammar({
1209
1211
  seq(
1210
1212
  // An invocation can return a reference (`Unsafe.Add(ref x, i) = v`). It ends with `)`, so `=` after it adds few
1211
1213
  // parse states, unlike making any expression assignable.
1212
- field('left', choice($.lvalue_expression, $.invocation_expression)),
1214
+ field(
1215
+ 'left',
1216
+ choice(
1217
+ $.lvalue_expression,
1218
+ $.invocation_expression,
1219
+ alias($._assignment_pointer_expression, $.prefix_unary_expression)
1220
+ )
1221
+ ),
1213
1222
  field('operator', choice('=', '+=', '-=', '*=', '/=', '%=', '&=', '^=', '|=', '<<=', '>>=', '>>>=', '??=')),
1214
1223
  field('right', $.expression)
1215
1224
  ),
1216
1225
 
1226
+ // Keep this alternative confined to assignment targets: sharing it with ordinary dereferences changes hidden
1227
+ // expression-supertype captures in nested reads. -3 prefers the existing lvalue path (-2) when both fit.
1228
+ _assignment_pointer_expression: ($) =>
1229
+ prec.dynamic(
1230
+ -3,
1231
+ prec.right(
1232
+ PREC.UNARY,
1233
+ seq(
1234
+ '*',
1235
+ choice(
1236
+ $.lvalue_expression,
1237
+ $.parenthesized_expression,
1238
+ $.invocation_expression,
1239
+ $.postfix_unary_expression,
1240
+ alias($._assignment_pointer_expression, $.prefix_unary_expression),
1241
+ alias($._pointer_prefix_update, $.prefix_unary_expression),
1242
+ alias($._assignment_pointer_cast, $.cast_expression)
1243
+ )
1244
+ )
1245
+ )
1246
+ ),
1247
+
1248
+ _pointer_prefix_update: ($) =>
1249
+ prec(
1250
+ PREC.UNARY,
1251
+ seq(choice('++', '--'), choice($.lvalue_expression, $.invocation_expression, $.parenthesized_expression))
1252
+ ),
1253
+
1254
+ _assignment_pointer_cast: ($) =>
1255
+ prec(
1256
+ PREC.CAST,
1257
+ seq(
1258
+ '(',
1259
+ field('type', $.type),
1260
+ ')',
1261
+ field(
1262
+ 'value',
1263
+ choice(
1264
+ $.lvalue_expression,
1265
+ $.parenthesized_expression,
1266
+ $.invocation_expression,
1267
+ $.postfix_unary_expression,
1268
+ alias($._assignment_pointer_expression, $.prefix_unary_expression),
1269
+ alias($._pointer_prefix_update, $.prefix_unary_expression)
1270
+ )
1271
+ )
1272
+ )
1273
+ ),
1274
+
1217
1275
  binary_expression: ($) =>
1218
1276
  choice(
1219
1277
  ...[
@@ -1267,14 +1325,9 @@ module.exports = grammar({
1267
1325
  )
1268
1326
  ),
1269
1327
 
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.
1277
- //
1328
+ // Keep the lvalue operand set restricted: a general expression also permits assignment after every operand,
1329
+ // substantially increasing parser states. Additional call and prefix-update operands use the assignment-only
1330
+ // `_assignment_pointer_expression` path to preserve the shared read derivations and their query captures.
1278
1331
  // Dynamic precedences: an assignable dereference -2, a read-only one -4, a read-only cast +1, an assignable cast -1,
1279
1332
  // and a unary `+`, `-`, `^`, or `&` -2. C# reads a parenthesized name as a cast only when the token after `)` is
1280
1333
  // `~`, `!`, `(`, an identifier, a literal, or a keyword (C# spec §12.9.8 Cast expressions), and these values keep
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willbooster/tree-sitter-c-sharp",
3
- "version": "2.0.1",
3
+ "version": "3.0.0",
4
4
  "description": "C# grammar for tree-sitter",
5
5
  "keywords": [
6
6
  "incremental",
@@ -22,10 +22,11 @@
22
22
  "*.wasm"
23
23
  ],
24
24
  "scripts": {
25
- "build-wasm": "script/tree-sitter build --wasm",
26
- "build/ci": "script/tree-sitter generate && script/tree-sitter build --wasm",
25
+ "build-wasm": "script/generate --build-wasm",
26
+ "build/ci": "bun run build-wasm",
27
27
  "cleanup": "bun wb lint --fix --format",
28
28
  "format": "bun wb lint --format",
29
+ "generate": "script/generate",
29
30
  "lint": "bun wb lint",
30
31
  "lint-fix": "bun wb lint --fix",
31
32
  "prepare": "(bun lefthook install || true) && (test ! -f .lefthook/normalize-bun-lockfile.sh || bash .lefthook/normalize-bun-lockfile.sh)",
@@ -47,7 +48,7 @@
47
48
  "@willbooster/oxfmt-config": "1.2.2",
48
49
  "@willbooster/oxlint-config": "1.4.8",
49
50
  "@willbooster/wb": "22.13.1",
50
- "@willbooster/web-tree-sitter": "1.1.2",
51
+ "@willbooster/web-tree-sitter": "1.4.0",
51
52
  "conventional-changelog-conventionalcommits": "10.4.0",
52
53
  "lefthook": "2.1.14",
53
54
  "oxfmt": "0.70.0",
@@ -63,7 +64,7 @@
63
64
  "wrangler": "4.135.0"
64
65
  },
65
66
  "peerDependencies": {
66
- "@willbooster/web-tree-sitter": "^1.1.2"
67
+ "@willbooster/web-tree-sitter": "^1.3.0"
67
68
  },
68
69
  "peerDependenciesMeta": {
69
70
  "@willbooster/web-tree-sitter": {
Binary file
package/tree-sitter.json CHANGED
@@ -19,7 +19,7 @@
19
19
  }
20
20
  ],
21
21
  "metadata": {
22
- "version": "2.0.1",
22
+ "version": "3.0.0",
23
23
  "license": "MIT",
24
24
  "description": "C# grammar for tree-sitter",
25
25
  "authors": [