@untangle-code/parser-registry 0.0.0-stage → 0.1.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/LICENSE +661 -0
- package/NOTICE +90 -0
- package/README.md +352 -2
- package/THIRD_PARTY_LICENSES.md +91 -0
- package/dist/boundaries/expression.d.ts +10 -0
- package/dist/boundaries/expression.d.ts.map +1 -0
- package/dist/boundaries/expression.js +95 -0
- package/dist/boundaries/expression.js.map +1 -0
- package/dist/boundaries/extract.d.ts +12 -0
- package/dist/boundaries/extract.d.ts.map +1 -0
- package/dist/boundaries/extract.js +259 -0
- package/dist/boundaries/extract.js.map +1 -0
- package/dist/boundaries/python.d.ts +6 -0
- package/dist/boundaries/python.d.ts.map +1 -0
- package/dist/boundaries/python.js +154 -0
- package/dist/boundaries/python.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/astgrep-backend/adapter.d.ts +101 -0
- package/dist/languages/astgrep-backend/adapter.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/adapter.js +130 -0
- package/dist/languages/astgrep-backend/adapter.js.map +1 -0
- package/dist/languages/astgrep-backend/index.d.ts +29 -0
- package/dist/languages/astgrep-backend/index.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/index.js +28 -0
- package/dist/languages/astgrep-backend/index.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/bash.d.ts +38 -0
- package/dist/languages/astgrep-backend/lang/bash.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/bash.js +42 -0
- package/dist/languages/astgrep-backend/lang/bash.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/c.d.ts +33 -0
- package/dist/languages/astgrep-backend/lang/c.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/c.js +85 -0
- package/dist/languages/astgrep-backend/lang/c.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/cpp.d.ts +27 -0
- package/dist/languages/astgrep-backend/lang/cpp.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/cpp.js +38 -0
- package/dist/languages/astgrep-backend/lang/cpp.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/csharp.d.ts +24 -0
- package/dist/languages/astgrep-backend/lang/csharp.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/csharp.js +102 -0
- package/dist/languages/astgrep-backend/lang/csharp.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/index.d.ts +29 -0
- package/dist/languages/astgrep-backend/lang/index.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/index.js +22 -0
- package/dist/languages/astgrep-backend/lang/index.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/java.d.ts +26 -0
- package/dist/languages/astgrep-backend/lang/java.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/java.js +89 -0
- package/dist/languages/astgrep-backend/lang/java.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/kotlin.d.ts +44 -0
- package/dist/languages/astgrep-backend/lang/kotlin.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/kotlin.js +94 -0
- package/dist/languages/astgrep-backend/lang/kotlin.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/php.d.ts +23 -0
- package/dist/languages/astgrep-backend/lang/php.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/php.js +80 -0
- package/dist/languages/astgrep-backend/lang/php.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/ruby.d.ts +37 -0
- package/dist/languages/astgrep-backend/lang/ruby.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/ruby.js +70 -0
- package/dist/languages/astgrep-backend/lang/ruby.js.map +1 -0
- package/dist/languages/astgrep-backend/lang/swift.d.ts +34 -0
- package/dist/languages/astgrep-backend/lang/swift.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/lang/swift.js +86 -0
- package/dist/languages/astgrep-backend/lang/swift.js.map +1 -0
- package/dist/languages/astgrep-backend/languages.d.ts +55 -0
- package/dist/languages/astgrep-backend/languages.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/languages.js +214 -0
- package/dist/languages/astgrep-backend/languages.js.map +1 -0
- package/dist/languages/astgrep-backend/napi.d.ts +71 -0
- package/dist/languages/astgrep-backend/napi.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/napi.js +176 -0
- package/dist/languages/astgrep-backend/napi.js.map +1 -0
- package/dist/languages/astgrep-backend/syntax.d.ts +88 -0
- package/dist/languages/astgrep-backend/syntax.d.ts.map +1 -0
- package/dist/languages/astgrep-backend/syntax.js +180 -0
- package/dist/languages/astgrep-backend/syntax.js.map +1 -0
- package/dist/languages/index.d.ts +14 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +37 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/javascript/index.d.ts +4 -0
- package/dist/languages/javascript/index.d.ts.map +1 -0
- package/dist/languages/javascript/index.js +7 -0
- package/dist/languages/javascript/index.js.map +1 -0
- package/dist/languages/jsx/index.d.ts +4 -0
- package/dist/languages/jsx/index.d.ts.map +1 -0
- package/dist/languages/jsx/index.js +7 -0
- package/dist/languages/jsx/index.js.map +1 -0
- package/dist/languages/prose/index.d.ts +53 -0
- package/dist/languages/prose/index.d.ts.map +1 -0
- package/dist/languages/prose/index.js +172 -0
- package/dist/languages/prose/index.js.map +1 -0
- package/dist/languages/prose/line-bytes.d.ts +31 -0
- package/dist/languages/prose/line-bytes.d.ts.map +1 -0
- package/dist/languages/prose/line-bytes.js +39 -0
- package/dist/languages/prose/line-bytes.js.map +1 -0
- package/dist/languages/python/create-adapter.d.ts +20 -0
- package/dist/languages/python/create-adapter.d.ts.map +1 -0
- package/dist/languages/python/create-adapter.js +9 -0
- package/dist/languages/python/create-adapter.js.map +1 -0
- package/dist/languages/python/index.d.ts +4 -0
- package/dist/languages/python/index.d.ts.map +1 -0
- package/dist/languages/python/index.js +6 -0
- package/dist/languages/python/index.js.map +1 -0
- package/dist/languages/python/profile.d.ts +36 -0
- package/dist/languages/python/profile.d.ts.map +1 -0
- package/dist/languages/python/profile.js +282 -0
- package/dist/languages/python/profile.js.map +1 -0
- package/dist/languages/python/signature.d.ts +53 -0
- package/dist/languages/python/signature.d.ts.map +1 -0
- package/dist/languages/python/signature.js +188 -0
- package/dist/languages/python/signature.js.map +1 -0
- package/dist/languages/python/table.d.ts +46 -0
- package/dist/languages/python/table.d.ts.map +1 -0
- package/dist/languages/python/table.js +88 -0
- package/dist/languages/python/table.js.map +1 -0
- package/dist/languages/shared/call-detection.d.ts +17 -0
- package/dist/languages/shared/call-detection.d.ts.map +1 -0
- package/dist/languages/shared/call-detection.js +86 -0
- package/dist/languages/shared/call-detection.js.map +1 -0
- package/dist/languages/shared/chunks.d.ts +28 -0
- package/dist/languages/shared/chunks.d.ts.map +1 -0
- package/dist/languages/shared/chunks.js +181 -0
- package/dist/languages/shared/chunks.js.map +1 -0
- package/dist/languages/shared/create-adapter.d.ts +11 -0
- package/dist/languages/shared/create-adapter.d.ts.map +1 -0
- package/dist/languages/shared/create-adapter.js +90 -0
- package/dist/languages/shared/create-adapter.js.map +1 -0
- package/dist/languages/shared/decisions.d.ts +45 -0
- package/dist/languages/shared/decisions.d.ts.map +1 -0
- package/dist/languages/shared/decisions.js +141 -0
- package/dist/languages/shared/decisions.js.map +1 -0
- package/dist/languages/shared/dialect.d.ts +18 -0
- package/dist/languages/shared/dialect.d.ts.map +1 -0
- package/dist/languages/shared/dialect.js +37 -0
- package/dist/languages/shared/dialect.js.map +1 -0
- package/dist/languages/shared/doc-binding.d.ts +26 -0
- package/dist/languages/shared/doc-binding.d.ts.map +1 -0
- package/dist/languages/shared/doc-binding.js +50 -0
- package/dist/languages/shared/doc-binding.js.map +1 -0
- package/dist/languages/shared/engine.d.ts +49 -0
- package/dist/languages/shared/engine.d.ts.map +1 -0
- package/dist/languages/shared/engine.js +317 -0
- package/dist/languages/shared/engine.js.map +1 -0
- package/dist/languages/shared/extract-symbols.d.ts +5 -0
- package/dist/languages/shared/extract-symbols.d.ts.map +1 -0
- package/dist/languages/shared/extract-symbols.js +31 -0
- package/dist/languages/shared/extract-symbols.js.map +1 -0
- package/dist/languages/shared/fingerprint.d.ts +7 -0
- package/dist/languages/shared/fingerprint.d.ts.map +1 -0
- package/dist/languages/shared/fingerprint.js +95 -0
- package/dist/languages/shared/fingerprint.js.map +1 -0
- package/dist/languages/shared/grammar-loader.d.ts +15 -0
- package/dist/languages/shared/grammar-loader.d.ts.map +1 -0
- package/dist/languages/shared/grammar-loader.js +48 -0
- package/dist/languages/shared/grammar-loader.js.map +1 -0
- package/dist/languages/shared/import-detection.d.ts +24 -0
- package/dist/languages/shared/import-detection.d.ts.map +1 -0
- package/dist/languages/shared/import-detection.js +138 -0
- package/dist/languages/shared/import-detection.js.map +1 -0
- package/dist/languages/shared/js-family-profile.d.ts +5 -0
- package/dist/languages/shared/js-family-profile.d.ts.map +1 -0
- package/dist/languages/shared/js-family-profile.js +179 -0
- package/dist/languages/shared/js-family-profile.js.map +1 -0
- package/dist/languages/shared/node-kinds.d.ts +78 -0
- package/dist/languages/shared/node-kinds.d.ts.map +1 -0
- package/dist/languages/shared/node-kinds.js +141 -0
- package/dist/languages/shared/node-kinds.js.map +1 -0
- package/dist/languages/shared/node-utils.d.ts +31 -0
- package/dist/languages/shared/node-utils.d.ts.map +1 -0
- package/dist/languages/shared/node-utils.js +89 -0
- package/dist/languages/shared/node-utils.js.map +1 -0
- package/dist/languages/shared/parse-diagnostics.d.ts +44 -0
- package/dist/languages/shared/parse-diagnostics.d.ts.map +1 -0
- package/dist/languages/shared/parse-diagnostics.js +64 -0
- package/dist/languages/shared/parse-diagnostics.js.map +1 -0
- package/dist/languages/shared/parse-limits.d.ts +24 -0
- package/dist/languages/shared/parse-limits.d.ts.map +1 -0
- package/dist/languages/shared/parse-limits.js +134 -0
- package/dist/languages/shared/parse-limits.js.map +1 -0
- package/dist/languages/shared/profile.d.ts +149 -0
- package/dist/languages/shared/profile.d.ts.map +1 -0
- package/dist/languages/shared/profile.js +9 -0
- package/dist/languages/shared/profile.js.map +1 -0
- package/dist/languages/shared/scope-model.d.ts +32 -0
- package/dist/languages/shared/scope-model.d.ts.map +1 -0
- package/dist/languages/shared/scope-model.js +2 -0
- package/dist/languages/shared/scope-model.js.map +1 -0
- package/dist/languages/shared/scope-naming.d.ts +20 -0
- package/dist/languages/shared/scope-naming.d.ts.map +1 -0
- package/dist/languages/shared/scope-naming.js +118 -0
- package/dist/languages/shared/scope-naming.js.map +1 -0
- package/dist/languages/shared/scope-signature.d.ts +14 -0
- package/dist/languages/shared/scope-signature.d.ts.map +1 -0
- package/dist/languages/shared/scope-signature.js +82 -0
- package/dist/languages/shared/scope-signature.js.map +1 -0
- package/dist/languages/shared/signature.d.ts +55 -0
- package/dist/languages/shared/signature.d.ts.map +1 -0
- package/dist/languages/shared/signature.js +147 -0
- package/dist/languages/shared/signature.js.map +1 -0
- package/dist/languages/shared/syntax.d.ts +115 -0
- package/dist/languages/shared/syntax.d.ts.map +1 -0
- package/dist/languages/shared/syntax.js +2 -0
- package/dist/languages/shared/syntax.js.map +1 -0
- package/dist/languages/shared/table-profile-decisions.d.ts +54 -0
- package/dist/languages/shared/table-profile-decisions.d.ts.map +1 -0
- package/dist/languages/shared/table-profile-decisions.js +245 -0
- package/dist/languages/shared/table-profile-decisions.js.map +1 -0
- package/dist/languages/shared/table-profile-module-facts.d.ts +29 -0
- package/dist/languages/shared/table-profile-module-facts.d.ts.map +1 -0
- package/dist/languages/shared/table-profile-module-facts.js +139 -0
- package/dist/languages/shared/table-profile-module-facts.js.map +1 -0
- package/dist/languages/shared/table-profile-queries.d.ts +27 -0
- package/dist/languages/shared/table-profile-queries.d.ts.map +1 -0
- package/dist/languages/shared/table-profile-queries.js +70 -0
- package/dist/languages/shared/table-profile-queries.js.map +1 -0
- package/dist/languages/shared/table-profile-scopes.d.ts +46 -0
- package/dist/languages/shared/table-profile-scopes.d.ts.map +1 -0
- package/dist/languages/shared/table-profile-scopes.js +180 -0
- package/dist/languages/shared/table-profile-scopes.js.map +1 -0
- package/dist/languages/shared/table-profile.d.ts +302 -0
- package/dist/languages/shared/table-profile.d.ts.map +1 -0
- package/dist/languages/shared/table-profile.js +125 -0
- package/dist/languages/shared/table-profile.js.map +1 -0
- package/dist/languages/shared/tree-sitter-types.d.ts +44 -0
- package/dist/languages/shared/tree-sitter-types.d.ts.map +1 -0
- package/dist/languages/shared/tree-sitter-types.js +2 -0
- package/dist/languages/shared/tree-sitter-types.js.map +1 -0
- package/dist/languages/structured-text/index.d.ts +48 -0
- package/dist/languages/structured-text/index.d.ts.map +1 -0
- package/dist/languages/structured-text/index.js +120 -0
- package/dist/languages/structured-text/index.js.map +1 -0
- package/dist/languages/tsx/index.d.ts +4 -0
- package/dist/languages/tsx/index.d.ts.map +1 -0
- package/dist/languages/tsx/index.js +7 -0
- package/dist/languages/tsx/index.js.map +1 -0
- package/dist/languages/typescript/index.d.ts +4 -0
- package/dist/languages/typescript/index.d.ts.map +1 -0
- package/dist/languages/typescript/index.js +7 -0
- package/dist/languages/typescript/index.js.map +1 -0
- package/dist/languages/vue/index.d.ts +21 -0
- package/dist/languages/vue/index.d.ts.map +1 -0
- package/dist/languages/vue/index.js +234 -0
- package/dist/languages/vue/index.js.map +1 -0
- package/dist/languages/vue/mapping.d.ts +11 -0
- package/dist/languages/vue/mapping.d.ts.map +1 -0
- package/dist/languages/vue/mapping.js +77 -0
- package/dist/languages/vue/mapping.js.map +1 -0
- package/dist/registry.d.ts +24 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +42 -0
- package/dist/registry.js.map +1 -0
- package/dist/text/byte-offset-table.d.ts +27 -0
- package/dist/text/byte-offset-table.d.ts.map +1 -0
- package/dist/text/byte-offset-table.js +39 -0
- package/dist/text/byte-offset-table.js.map +1 -0
- package/package.json +125 -3
- package/src/boundaries/expression.ts +95 -0
- package/src/boundaries/extract.ts +307 -0
- package/src/boundaries/python.ts +181 -0
- package/src/index.ts +51 -0
- package/src/languages/__golden__/javascript-basic.json +32 -0
- package/src/languages/__golden__/tsx-basic.json +19 -0
- package/src/languages/__golden__/typescript-basic.json +37 -0
- package/src/languages/astgrep-backend/GRAMMARS.md +86 -0
- package/src/languages/astgrep-backend/README.md +112 -0
- package/src/languages/astgrep-backend/__golden__/bash-basic.sh +53 -0
- package/src/languages/astgrep-backend/__golden__/c-basic.c +64 -0
- package/src/languages/astgrep-backend/__golden__/cpp-basic.cpp +65 -0
- package/src/languages/astgrep-backend/__golden__/csharp-basic.cs +73 -0
- package/src/languages/astgrep-backend/__golden__/go-basic.go +93 -0
- package/src/languages/astgrep-backend/__golden__/java-basic.java +62 -0
- package/src/languages/astgrep-backend/__golden__/kotlin-basic.kt +58 -0
- package/src/languages/astgrep-backend/__golden__/php-basic.php +72 -0
- package/src/languages/astgrep-backend/__golden__/ruby-basic.rb +62 -0
- package/src/languages/astgrep-backend/__golden__/rust-basic.rs +82 -0
- package/src/languages/astgrep-backend/__golden__/swift-basic.swift +73 -0
- package/src/languages/astgrep-backend/adapter.ts +240 -0
- package/src/languages/astgrep-backend/index.ts +44 -0
- package/src/languages/astgrep-backend/lang/README.md +115 -0
- package/src/languages/astgrep-backend/lang/bash.ts +78 -0
- package/src/languages/astgrep-backend/lang/c.ts +119 -0
- package/src/languages/astgrep-backend/lang/cpp.ts +65 -0
- package/src/languages/astgrep-backend/lang/csharp.ts +125 -0
- package/src/languages/astgrep-backend/lang/index.ts +61 -0
- package/src/languages/astgrep-backend/lang/java.ts +115 -0
- package/src/languages/astgrep-backend/lang/kotlin.ts +136 -0
- package/src/languages/astgrep-backend/lang/php.ts +105 -0
- package/src/languages/astgrep-backend/lang/ruby.ts +105 -0
- package/src/languages/astgrep-backend/lang/swift.ts +118 -0
- package/src/languages/astgrep-backend/languages.ts +238 -0
- package/src/languages/astgrep-backend/napi.ts +261 -0
- package/src/languages/astgrep-backend/syntax.ts +242 -0
- package/src/languages/index.ts +53 -0
- package/src/languages/javascript/index.ts +9 -0
- package/src/languages/jsx/index.ts +9 -0
- package/src/languages/prose/index.ts +270 -0
- package/src/languages/prose/line-bytes.ts +52 -0
- package/src/languages/python/README.md +246 -0
- package/src/languages/python/__golden__/python-basic.json +44 -0
- package/src/languages/python/create-adapter.ts +29 -0
- package/src/languages/python/index.ts +8 -0
- package/src/languages/python/profile.ts +341 -0
- package/src/languages/python/signature.ts +183 -0
- package/src/languages/python/table.ts +135 -0
- package/src/languages/shared/__fixtures__/large-over-32kib.ts.txt +1207 -0
- package/src/languages/shared/call-detection.ts +112 -0
- package/src/languages/shared/chunks.ts +255 -0
- package/src/languages/shared/create-adapter.ts +136 -0
- package/src/languages/shared/decisions.ts +204 -0
- package/src/languages/shared/dialect.ts +55 -0
- package/src/languages/shared/doc-binding.ts +78 -0
- package/src/languages/shared/engine.ts +461 -0
- package/src/languages/shared/extract-symbols.ts +35 -0
- package/src/languages/shared/fingerprint.ts +99 -0
- package/src/languages/shared/grammar-loader.ts +67 -0
- package/src/languages/shared/import-detection.ts +154 -0
- package/src/languages/shared/js-family-profile.ts +246 -0
- package/src/languages/shared/node-kinds.ts +153 -0
- package/src/languages/shared/node-utils.ts +120 -0
- package/src/languages/shared/parse-diagnostics.ts +125 -0
- package/src/languages/shared/parse-limits.ts +147 -0
- package/src/languages/shared/profile.ts +184 -0
- package/src/languages/shared/scope-model.ts +43 -0
- package/src/languages/shared/scope-naming.ts +124 -0
- package/src/languages/shared/scope-signature.ts +108 -0
- package/src/languages/shared/signature.ts +149 -0
- package/src/languages/shared/syntax.ts +116 -0
- package/src/languages/shared/table-profile-decisions.ts +251 -0
- package/src/languages/shared/table-profile-module-facts.ts +177 -0
- package/src/languages/shared/table-profile-queries.ts +85 -0
- package/src/languages/shared/table-profile-scopes.ts +198 -0
- package/src/languages/shared/table-profile.ts +427 -0
- package/src/languages/shared/tree-sitter-types.ts +46 -0
- package/src/languages/structured-text/index.ts +192 -0
- package/src/languages/tsx/index.ts +9 -0
- package/src/languages/typescript/index.ts +9 -0
- package/src/languages/vue/index.ts +312 -0
- package/src/languages/vue/mapping.ts +93 -0
- package/src/registry.ts +61 -0
- package/src/text/byte-offset-table.ts +63 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Der backend-neutrale Baumvertrag dieses Pakets.
|
|
3
|
+
*
|
|
4
|
+
* WARUM ES IHN GIBT: seit WP-220 laufen ZWEI Parser-Backends unter demselben
|
|
5
|
+
* `LanguageAdapter` (domain `analysis.ts`) — `tree-sitter` fuer die
|
|
6
|
+
* JS/TS-Familie und `@ast-grep/napi` fuer die Sprachen, fuer die wir keine
|
|
7
|
+
* eigene Grammatik pflegen wollen. Der vereinheitlichte Walk (`engine.ts`),
|
|
8
|
+
* die Parse-Diagnosen (`parse-diagnostics.ts`), die Fingerabdruecke
|
|
9
|
+
* (`fingerprint.ts`) und der Regionenbaum (`chunks.ts`) sind fuer BEIDE
|
|
10
|
+
* dieselben Zeilen. Sie sprechen deshalb nicht mehr ueber
|
|
11
|
+
* `Parser.SyntaxNode`/`Parser.TreeCursor`, sondern ueber die zwei Formen hier.
|
|
12
|
+
*
|
|
13
|
+
* DIE FORMEN SIND ABSICHTLICH DIE VON TREE-SITTER. `Parser.SyntaxNode` und
|
|
14
|
+
* `Parser.TreeCursor` erfuellen sie STRUKTURELL, ohne einen einzigen Wrapper:
|
|
15
|
+
* der tree-sitter-Weg zahlt fuer diese Abstraktion nichts, kein Objekt, kein
|
|
16
|
+
* Aufruf mehr als vorher (`languages/shared/tree-sitter-types.ts` haelt die
|
|
17
|
+
* Zusage mit einer Zuweisungspruefung fest). Die Kosten traegt der neue Weg:
|
|
18
|
+
* `languages/astgrep-backend/syntax.ts` baut die Form ueber `SgNode` nach, weil
|
|
19
|
+
* ast-grep KEINE Cursor-API hat (gemessen: Faktor 3,74 auf dem Walk). Diese
|
|
20
|
+
* Richtung ist gewollt — der gemessen schnellere Weg bleibt der billige.
|
|
21
|
+
*
|
|
22
|
+
* ZWEI EIGENSCHAFTEN, DIE JEDES BACKEND EINHALTEN MUSS:
|
|
23
|
+
*
|
|
24
|
+
* 1. `startIndex`/`endIndex` und `startPosition.column` sind UTF-16-Offsets,
|
|
25
|
+
* NICHT Bytes — bei tree-sitter ebenso wie bei ast-grep, dessen
|
|
26
|
+
* Typdefinition `Pos.index` faelschlich als "byte offset" beschreibt
|
|
27
|
+
* (nachgemessen an Umlauten und Emoji). Bytes entstehen ausschliesslich
|
|
28
|
+
* in `node-utils.ts#rangeOf`/`rangeOfCursor` ueber die `ByteOffsetTable`.
|
|
29
|
+
*
|
|
30
|
+
* 2. Ein Cursor wird IMMER an der Ausgangsposition zurueckgegeben. Jede
|
|
31
|
+
* Hilfsfunktion, die ihn bewegt, bewegt ihn zurueck.
|
|
32
|
+
*/
|
|
33
|
+
/** Zeile/Spalte eines Knotens; `row` und `column` sind 0-basiert. */
|
|
34
|
+
export interface SyntaxPoint {
|
|
35
|
+
readonly row: number;
|
|
36
|
+
readonly column: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Ein Knoten des Syntaxbaums — die Teilmenge der tree-sitter-Knotenform, die
|
|
41
|
+
* dieses Paket wirklich benutzt. Wer hier etwas hinzufuegt, verpflichtet
|
|
42
|
+
* BEIDE Backends dazu; `languages/astgrep-backend/syntax.ts` muss es dann nachbauen.
|
|
43
|
+
*/
|
|
44
|
+
export interface SyntaxNode {
|
|
45
|
+
readonly type: string;
|
|
46
|
+
readonly text: string;
|
|
47
|
+
/**
|
|
48
|
+
* Benannter Knoten der Grammatik (kein reines Satzzeichen-/Schluesselwort-
|
|
49
|
+
* Token). Beide Backends liefern das ohne Umweg (tree-sitter `isNamed`,
|
|
50
|
+
* ast-grep `isNamed()`); gebraucht wird es dort, wo ein Operator KEIN
|
|
51
|
+
* eigenes Feld hat und nur ueber "das anonyme Kind" zu finden ist.
|
|
52
|
+
*/
|
|
53
|
+
readonly isNamed: boolean;
|
|
54
|
+
/** UTF-16-Offset des ersten Zeichens (siehe Modulkopf, Punkt 1). */
|
|
55
|
+
readonly startIndex: number;
|
|
56
|
+
/** UTF-16-Offset hinter dem letzten Zeichen. */
|
|
57
|
+
readonly endIndex: number;
|
|
58
|
+
readonly startPosition: SyntaxPoint;
|
|
59
|
+
readonly endPosition: SyntaxPoint;
|
|
60
|
+
readonly parent: SyntaxNode | null;
|
|
61
|
+
/**
|
|
62
|
+
* Vorheriges Geschwister — auch ein anonymes. `doc-binding.ts` laeuft
|
|
63
|
+
* darueber rueckwaerts zu Dekoratoren und Doc-Kommentaren; wuerde ein
|
|
64
|
+
* Backend hier nur BENANNTE Geschwister liefern, veraenderte das die
|
|
65
|
+
* Doc-Bindung stillschweigend (ast-grep `prev()` ist deshalb geprueft).
|
|
66
|
+
*/
|
|
67
|
+
readonly previousSibling: SyntaxNode | null;
|
|
68
|
+
readonly children: readonly SyntaxNode[];
|
|
69
|
+
readonly namedChildCount: number;
|
|
70
|
+
namedChild(index: number): SyntaxNode | null;
|
|
71
|
+
childForFieldName(fieldName: string): SyntaxNode | null;
|
|
72
|
+
/** Ein Cursor, der auf DIESEM Knoten steht. */
|
|
73
|
+
walk(): SyntaxCursor;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Der Traversierungszeiger. Bei tree-sitter haelt er die Position nativ und
|
|
78
|
+
* baut je Knoten KEINEN Wrapper (WP-95); bei ast-grep bildet ihn ein
|
|
79
|
+
* expliziter Stapel nach. Der Vertrag ist in beiden Faellen derselbe, und die
|
|
80
|
+
* Besuchsreihenfolge ebenfalls: Vorordnung ueber `gotoFirstChild` /
|
|
81
|
+
* `gotoNextSibling` / `gotoParent`, Rueckordnung ueber `gotoLastChild` /
|
|
82
|
+
* `gotoPreviousSibling` (die Parse-Diagnosen laufen rueckwaerts, siehe
|
|
83
|
+
* `parse-diagnostics.ts`).
|
|
84
|
+
*/
|
|
85
|
+
export interface SyntaxCursor {
|
|
86
|
+
readonly nodeType: string;
|
|
87
|
+
readonly nodeText: string;
|
|
88
|
+
/**
|
|
89
|
+
* Fehlender Knoten der Fehlerkorrektur (tree-sitter `ts_node_is_missing`).
|
|
90
|
+
*
|
|
91
|
+
* ast-grep hat dazu KEIN Gegenstueck. Sein Cursor liefert an dieser Stelle
|
|
92
|
+
* eine HEURISTIK (Knoten der Breite null), und der Adapter sagt das in der
|
|
93
|
+
* Diagnose auch — `languages/astgrep-backend/syntax.ts` und
|
|
94
|
+
* `languages/astgrep-backend/create-adapter.ts`. Wer den Wert liest, muss wissen,
|
|
95
|
+
* dass er je nach Backend gemessen oder geschaetzt ist.
|
|
96
|
+
*/
|
|
97
|
+
readonly nodeIsMissing: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Feldname, unter dem der aktuelle Knoten in seinem Elternknoten steht,
|
|
100
|
+
* oder `undefined`. tree-sitter liefert ihn ohne Zusatzkosten; der
|
|
101
|
+
* ast-grep-Cursor muss ihn ueber die Felder des Elternknotens suchen und
|
|
102
|
+
* kennt deshalb nur die Feldnamen, die das Sprachprofil nennt
|
|
103
|
+
* (`profile.ts#LanguageProfile.fieldNames`).
|
|
104
|
+
*/
|
|
105
|
+
readonly currentFieldName: string | undefined;
|
|
106
|
+
readonly currentNode: SyntaxNode;
|
|
107
|
+
readonly startPosition: SyntaxPoint;
|
|
108
|
+
readonly endPosition: SyntaxPoint;
|
|
109
|
+
readonly startIndex: number;
|
|
110
|
+
readonly endIndex: number;
|
|
111
|
+
gotoParent(): boolean;
|
|
112
|
+
gotoFirstChild(): boolean;
|
|
113
|
+
gotoLastChild(): boolean;
|
|
114
|
+
gotoNextSibling(): boolean;
|
|
115
|
+
gotoPreviousSibling(): boolean;
|
|
116
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/** Entscheidungs- und Nestingauflösung des Tabellenprofils. */
|
|
2
|
+
import type { DecisionKind } from "@untangle-code/domain";
|
|
3
|
+
|
|
4
|
+
import type { DecisionDetectionResult, DetectedDecision } from "./decisions.js";
|
|
5
|
+
import type { SyntaxNode } from "./syntax.js";
|
|
6
|
+
import { firstChildOfKinds, setOf } from "./table-profile-queries.js";
|
|
7
|
+
import type { DecisionRules } from "./table-profile.js";
|
|
8
|
+
|
|
9
|
+
function branching(
|
|
10
|
+
kind: DecisionKind,
|
|
11
|
+
construct: string,
|
|
12
|
+
continuation: boolean,
|
|
13
|
+
): DecisionDetectionResult {
|
|
14
|
+
return {
|
|
15
|
+
decision: { kind, countsForClassic: true, countsForExtended: true },
|
|
16
|
+
nesting: { construct, continuation },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function extendedOnly(kind: DecisionKind, logicalGroupId?: string): DecisionDetectionResult {
|
|
21
|
+
const decision: DetectedDecision = {
|
|
22
|
+
kind,
|
|
23
|
+
countsForClassic: false,
|
|
24
|
+
countsForExtended: true,
|
|
25
|
+
...(logicalGroupId === undefined ? {} : { logicalGroupId }),
|
|
26
|
+
};
|
|
27
|
+
return { decision };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Kompiliert ausschliesslich Kontrollflussregeln einer Sprachtabelle. */
|
|
31
|
+
export class TableDecisionDetector {
|
|
32
|
+
readonly nodeKinds: ReadonlySet<string>;
|
|
33
|
+
|
|
34
|
+
private readonly ifKinds: ReadonlySet<string>;
|
|
35
|
+
private readonly elseIfParentKinds: ReadonlySet<string>;
|
|
36
|
+
private readonly elseIfWrapperKinds: ReadonlySet<string>;
|
|
37
|
+
private readonly elseIfKinds: ReadonlySet<string>;
|
|
38
|
+
private readonly loopKinds: ReadonlySet<string>;
|
|
39
|
+
private readonly catchKinds: ReadonlySet<string>;
|
|
40
|
+
private readonly ternaryKinds: ReadonlySet<string>;
|
|
41
|
+
private readonly switchKinds: ReadonlySet<string>;
|
|
42
|
+
private readonly caseKinds: ReadonlySet<string>;
|
|
43
|
+
private readonly caseExcludeChildKinds: ReadonlySet<string>;
|
|
44
|
+
private readonly guardKinds: ReadonlySet<string>;
|
|
45
|
+
private readonly patternAlternativeKinds: ReadonlySet<string>;
|
|
46
|
+
private readonly shortCircuitKinds: ReadonlySet<string>;
|
|
47
|
+
private readonly anonymousOperatorKinds: ReadonlySet<string>;
|
|
48
|
+
private readonly binaryKinds: ReadonlySet<string>;
|
|
49
|
+
private readonly jumpKinds: ReadonlySet<string>;
|
|
50
|
+
private readonly blockKinds: ReadonlySet<string>;
|
|
51
|
+
private readonly logicalKinds = new Map<string, DecisionKind>();
|
|
52
|
+
private readonly operatorField: string;
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
private readonly rules: DecisionRules,
|
|
56
|
+
private readonly triviaKinds: ReadonlySet<string>,
|
|
57
|
+
) {
|
|
58
|
+
this.ifKinds = setOf(rules.ifKinds);
|
|
59
|
+
this.elseIfParentKinds = setOf(rules.elseIfParentKinds);
|
|
60
|
+
this.elseIfWrapperKinds = setOf(rules.elseIfWrapperKinds);
|
|
61
|
+
this.elseIfKinds = setOf(rules.elseIfKinds);
|
|
62
|
+
this.loopKinds = setOf(rules.loopKinds);
|
|
63
|
+
this.catchKinds = setOf(rules.catchKinds);
|
|
64
|
+
this.ternaryKinds = setOf(rules.ternaryKinds);
|
|
65
|
+
this.switchKinds = setOf(rules.switchKinds);
|
|
66
|
+
this.caseKinds = setOf(rules.caseKinds);
|
|
67
|
+
this.caseExcludeChildKinds = setOf(rules.caseExcludeChildKinds);
|
|
68
|
+
this.guardKinds = setOf(rules.guardKinds);
|
|
69
|
+
this.patternAlternativeKinds = setOf(rules.patternAlternativeKinds);
|
|
70
|
+
this.shortCircuitKinds = setOf(rules.shortCircuitKinds);
|
|
71
|
+
this.anonymousOperatorKinds = setOf(rules.anonymousOperatorKinds);
|
|
72
|
+
this.binaryKinds = setOf(rules.binaryKinds);
|
|
73
|
+
this.jumpKinds = setOf(rules.jumpKinds);
|
|
74
|
+
this.blockKinds = setOf(rules.blockKinds);
|
|
75
|
+
this.operatorField = rules.operatorField ?? "operator";
|
|
76
|
+
this.registerLogicalKinds(rules.logicalAndOperators, "logical_and");
|
|
77
|
+
this.registerLogicalKinds(rules.logicalOrOperators, "logical_or");
|
|
78
|
+
this.registerLogicalKinds(rules.shortCircuitOperators, "short_circuit");
|
|
79
|
+
this.nodeKinds = new Set([
|
|
80
|
+
...this.ifKinds,
|
|
81
|
+
...this.loopKinds,
|
|
82
|
+
...this.catchKinds,
|
|
83
|
+
...this.ternaryKinds,
|
|
84
|
+
...this.switchKinds,
|
|
85
|
+
...this.caseKinds,
|
|
86
|
+
...this.elseIfKinds,
|
|
87
|
+
...this.guardKinds,
|
|
88
|
+
...this.patternAlternativeKinds,
|
|
89
|
+
...this.shortCircuitKinds,
|
|
90
|
+
...this.anonymousOperatorKinds,
|
|
91
|
+
...this.binaryKinds,
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private registerLogicalKinds(operators: readonly string[] | undefined, kind: DecisionKind): void {
|
|
96
|
+
for (const operator of operators ?? []) this.logicalKinds.set(operator, kind);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
detect(node: SyntaxNode): DecisionDetectionResult {
|
|
100
|
+
const kind = node.type;
|
|
101
|
+
if (this.elseIfKinds.has(kind)) return branching("else_if", "if", true);
|
|
102
|
+
if (this.guardKinds.has(kind)) return branching("guard", "if", false);
|
|
103
|
+
if (this.patternAlternativeKinds.has(kind)) return extendedOnly("pattern_alternative");
|
|
104
|
+
if (this.shortCircuitKinds.has(kind)) {
|
|
105
|
+
return extendedOnly("short_circuit", `short-circuit-${String(node.startIndex)}`);
|
|
106
|
+
}
|
|
107
|
+
if (this.ifKinds.has(kind)) return this.detectIf(node);
|
|
108
|
+
if (this.loopKinds.has(kind)) return branching("loop", "loop", false);
|
|
109
|
+
if (this.catchKinds.has(kind)) return branching("catch", "catch", false);
|
|
110
|
+
if (this.ternaryKinds.has(kind)) return branching("ternary", "ternary", false);
|
|
111
|
+
if (this.switchKinds.has(kind))
|
|
112
|
+
return { nesting: { construct: "switch", continuation: false } };
|
|
113
|
+
if (this.caseKinds.has(kind)) return this.detectCase(node);
|
|
114
|
+
if (this.binaryKinds.has(kind) || this.anonymousOperatorKinds.has(kind)) {
|
|
115
|
+
return this.detectLogical(node);
|
|
116
|
+
}
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private detectIf(node: SyntaxNode): DecisionDetectionResult {
|
|
121
|
+
if (this.isElseIf(node)) return branching("else_if", "if", true);
|
|
122
|
+
return branching(this.isGuardClause(node) ? "guard" : "if", "if", false);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private isElseIf(node: SyntaxNode): boolean {
|
|
126
|
+
const parent = node.parent;
|
|
127
|
+
if (parent === null) return false;
|
|
128
|
+
if (this.elseIfParentKinds.has(parent.type)) return true;
|
|
129
|
+
if (this.elseIfWrapperKinds.has(parent.type)) {
|
|
130
|
+
let previous = parent.previousSibling;
|
|
131
|
+
while (previous !== null && this.triviaKinds.has(previous.type)) {
|
|
132
|
+
previous = previous.previousSibling;
|
|
133
|
+
}
|
|
134
|
+
if (previous?.type === "else") return true;
|
|
135
|
+
}
|
|
136
|
+
return this.isDetachedElseIfAfterComments(node, parent);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Kotlin 0.0.7 verliert in `else /* ... *\/ if` das `else` als Baumknoten
|
|
141
|
+
* und beendet das erste `if_expression` vor dem Blockkommentar. Die
|
|
142
|
+
* Korrektur bleibt an AST-Grenzen gebunden: links muss ein mit `if` endender
|
|
143
|
+
* Geschwisterteilbaum stehen, dazwischen mindestens ein echter
|
|
144
|
+
* Kommentarknoten, und nur der exakte Keyword-Spalt `else` darf fehlen.
|
|
145
|
+
*/
|
|
146
|
+
private isDetachedElseIfAfterComments(node: SyntaxNode, parent: SyntaxNode): boolean {
|
|
147
|
+
if (this.rules.recoverDetachedElseIfAfterComments !== true) return false;
|
|
148
|
+
|
|
149
|
+
let right = node;
|
|
150
|
+
let left = node.previousSibling;
|
|
151
|
+
let sawComment = false;
|
|
152
|
+
while (left !== null && this.triviaKinds.has(left.type)) {
|
|
153
|
+
if (!this.isWhitespaceGap(parent, left.endIndex, right.startIndex)) return false;
|
|
154
|
+
sawComment = true;
|
|
155
|
+
right = left;
|
|
156
|
+
left = left.previousSibling;
|
|
157
|
+
}
|
|
158
|
+
if (!sawComment || left === null || !this.endsWithIf(left)) return false;
|
|
159
|
+
|
|
160
|
+
const keywordGap = this.textBetween(parent, left.endIndex, right.startIndex);
|
|
161
|
+
return keywordGap !== null && keywordGap.trim() === "else";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private isWhitespaceGap(parent: SyntaxNode, startIndex: number, endIndex: number): boolean {
|
|
165
|
+
const gap = this.textBetween(parent, startIndex, endIndex);
|
|
166
|
+
return gap !== null && gap.trim().length === 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private textBetween(parent: SyntaxNode, startIndex: number, endIndex: number): string | null {
|
|
170
|
+
const start = startIndex - parent.startIndex;
|
|
171
|
+
const end = endIndex - parent.startIndex;
|
|
172
|
+
if (start < 0 || end < start || end > parent.text.length) return null;
|
|
173
|
+
return parent.text.slice(start, end);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Ob `node` an seiner rechten AST-Grenze in einem `if` endet. */
|
|
177
|
+
private endsWithIf(node: SyntaxNode): boolean {
|
|
178
|
+
const endIndex = node.endIndex;
|
|
179
|
+
const stack: SyntaxNode[] = [node];
|
|
180
|
+
while (stack.length > 0) {
|
|
181
|
+
const current = stack.pop() as SyntaxNode;
|
|
182
|
+
if (current.endIndex !== endIndex) continue;
|
|
183
|
+
if (this.ifKinds.has(current.type)) return true;
|
|
184
|
+
for (const child of current.children) {
|
|
185
|
+
if (child.endIndex === endIndex) stack.push(child);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private isGuardClause(node: SyntaxNode): boolean {
|
|
192
|
+
if (this.rules.consequenceField === undefined || this.jumpKinds.size === 0) return false;
|
|
193
|
+
if (
|
|
194
|
+
this.rules.alternativeField !== undefined &&
|
|
195
|
+
node.childForFieldName(this.rules.alternativeField) !== null
|
|
196
|
+
) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
const consequence = node.childForFieldName(this.rules.consequenceField);
|
|
200
|
+
const sole = consequence === null ? null : this.soleStatementOf(consequence);
|
|
201
|
+
return sole !== null && this.jumpKinds.has(sole.type);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private soleStatementOf(node: SyntaxNode): SyntaxNode | null {
|
|
205
|
+
let current: SyntaxNode | null = node;
|
|
206
|
+
while (current !== null && this.blockKinds.has(current.type)) {
|
|
207
|
+
current = current.namedChildCount === 1 ? current.namedChild(0) : null;
|
|
208
|
+
}
|
|
209
|
+
return current;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private detectCase(node: SyntaxNode): DecisionDetectionResult {
|
|
213
|
+
if (firstChildOfKinds(node, this.caseExcludeChildKinds) !== null) return {};
|
|
214
|
+
const firstText = node.children[0]?.text.trim();
|
|
215
|
+
if (firstText !== undefined && firstText === this.rules.caseExcludeFirstChildText) return {};
|
|
216
|
+
return { decision: { kind: "case", countsForClassic: true, countsForExtended: true } };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private detectLogical(node: SyntaxNode): DecisionDetectionResult {
|
|
220
|
+
const operator = this.operatorOf(node);
|
|
221
|
+
if (operator === undefined) return {};
|
|
222
|
+
const kind = this.logicalKinds.get(operator);
|
|
223
|
+
return kind === undefined ? {} : extendedOnly(kind, this.logicalGroupIdFor(node, operator));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private operatorOf(node: SyntaxNode): string | undefined {
|
|
227
|
+
if (!this.anonymousOperatorKinds.has(node.type)) {
|
|
228
|
+
return node.childForFieldName(this.operatorField)?.text;
|
|
229
|
+
}
|
|
230
|
+
for (const child of node.children) {
|
|
231
|
+
const text = child.text.trim();
|
|
232
|
+
if (this.logicalKinds.has(text)) return text;
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Stabile, positionsbasierte Kennung einer maximalen Operatorkette. */
|
|
238
|
+
private logicalGroupIdFor(node: SyntaxNode, operator: string): string {
|
|
239
|
+
let top = node;
|
|
240
|
+
while (top.parent !== null && this.isLogicalExpression(top.parent)) {
|
|
241
|
+
const parent = top.parent;
|
|
242
|
+
if (this.operatorOf(parent) !== operator) break;
|
|
243
|
+
top = parent;
|
|
244
|
+
}
|
|
245
|
+
return `logical-${String(top.startIndex)}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private isLogicalExpression(node: SyntaxNode): boolean {
|
|
249
|
+
return this.binaryKinds.has(node.type) || this.anonymousOperatorKinds.has(node.type);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/** Import- und Aufruffakten des Tabellenprofils. */
|
|
2
|
+
import type { RawCall, RawImport } from "@untangle-code/domain";
|
|
3
|
+
|
|
4
|
+
import type { FileTextContext } from "./node-utils.js";
|
|
5
|
+
import { rangeOf, unquote } from "./node-utils.js";
|
|
6
|
+
import type { ModuleFactSink } from "./profile.js";
|
|
7
|
+
import type { SyntaxNode } from "./syntax.js";
|
|
8
|
+
import {
|
|
9
|
+
descendantsOfKinds,
|
|
10
|
+
firstChildByKindPriority,
|
|
11
|
+
nameTextsOf,
|
|
12
|
+
setOf,
|
|
13
|
+
} from "./table-profile-queries.js";
|
|
14
|
+
import type { CallRules, ImportRules } from "./table-profile.js";
|
|
15
|
+
|
|
16
|
+
interface Callee {
|
|
17
|
+
calleeName: string;
|
|
18
|
+
qualifier?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ImportParts {
|
|
22
|
+
pathNode: SyntaxNode | null;
|
|
23
|
+
names: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Kompiliert ausschliesslich Import- und Aufrufregeln einer Sprachtabelle. */
|
|
27
|
+
export class TableModuleFactCollector {
|
|
28
|
+
readonly nodeKinds: ReadonlySet<string>;
|
|
29
|
+
|
|
30
|
+
private readonly importKinds: ReadonlySet<string>;
|
|
31
|
+
private readonly importSpecKinds: ReadonlySet<string>;
|
|
32
|
+
private readonly callKinds: ReadonlySet<string>;
|
|
33
|
+
private readonly constructionKinds: ReadonlySet<string>;
|
|
34
|
+
private readonly memberKinds: ReadonlySet<string>;
|
|
35
|
+
private readonly functionField: string;
|
|
36
|
+
private readonly argumentsField: string;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
private readonly imports: ImportRules,
|
|
40
|
+
private readonly calls: CallRules,
|
|
41
|
+
) {
|
|
42
|
+
this.importKinds = setOf(imports.kinds);
|
|
43
|
+
this.importSpecKinds = setOf(imports.specKinds);
|
|
44
|
+
this.callKinds = setOf(calls.kinds);
|
|
45
|
+
this.constructionKinds = setOf(calls.constructionKinds);
|
|
46
|
+
this.memberKinds = setOf(calls.memberKinds);
|
|
47
|
+
this.functionField = calls.functionField ?? "function";
|
|
48
|
+
this.argumentsField = calls.argumentsField ?? "arguments";
|
|
49
|
+
this.nodeKinds = new Set([...this.importKinds, ...this.callKinds, ...this.constructionKinds]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
collect(
|
|
53
|
+
node: SyntaxNode,
|
|
54
|
+
kind: string,
|
|
55
|
+
callerScopeId: string,
|
|
56
|
+
ctx: FileTextContext,
|
|
57
|
+
sink: ModuleFactSink,
|
|
58
|
+
): void {
|
|
59
|
+
if (this.importKinds.has(kind)) {
|
|
60
|
+
this.collectImports(node, ctx, sink);
|
|
61
|
+
} else if (this.callKinds.has(kind) || this.constructionKinds.has(kind)) {
|
|
62
|
+
this.collectCall(node, callerScopeId, ctx, sink);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private collectImports(node: SyntaxNode, ctx: FileTextContext, sink: ModuleFactSink): void {
|
|
67
|
+
const found =
|
|
68
|
+
this.importSpecKinds.size === 0 ? [] : descendantsOfKinds(node, this.importSpecKinds);
|
|
69
|
+
const specs = this.importSpecKinds.size > 0 && found.length > 0 ? found : [node];
|
|
70
|
+
for (const spec of specs) this.collectImportSpec(spec, ctx, sink);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private collectImportSpec(spec: SyntaxNode, ctx: FileTextContext, sink: ModuleFactSink): void {
|
|
74
|
+
const parts = this.importPartsOf(spec);
|
|
75
|
+
const specifier = unquote((parts.pathNode ?? spec).text.trim());
|
|
76
|
+
if (specifier.length === 0) return;
|
|
77
|
+
const rawImport: RawImport = {
|
|
78
|
+
specifier,
|
|
79
|
+
importKind: "import",
|
|
80
|
+
range: rangeOf(spec, ctx),
|
|
81
|
+
// Ohne echte Namensknoten bleibt die Bindung unbekannt.
|
|
82
|
+
...(parts.names.length > 0 ? { importedNames: parts.names } : {}),
|
|
83
|
+
};
|
|
84
|
+
sink.imports.push(rawImport);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private importPartsOf(spec: SyntaxNode): ImportParts {
|
|
88
|
+
let pathNode = this.initialImportPathOf(spec);
|
|
89
|
+
let names =
|
|
90
|
+
this.imports.aliasField === undefined
|
|
91
|
+
? []
|
|
92
|
+
: nameTextsOf(spec.childForFieldName(this.imports.aliasField));
|
|
93
|
+
for (const rule of this.imports.unwrap ?? []) {
|
|
94
|
+
if (pathNode === null || !rule.kinds.includes(pathNode.type)) continue;
|
|
95
|
+
if (rule.aliasField !== undefined) {
|
|
96
|
+
names = [...names, ...nameTextsOf(pathNode.childForFieldName(rule.aliasField))];
|
|
97
|
+
}
|
|
98
|
+
if (rule.namesField !== undefined) {
|
|
99
|
+
names = [...names, ...this.namedChildTexts(pathNode.childForFieldName(rule.namesField))];
|
|
100
|
+
}
|
|
101
|
+
pathNode = pathNode.childForFieldName(rule.pathField) ?? pathNode;
|
|
102
|
+
}
|
|
103
|
+
return { pathNode, names };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private initialImportPathOf(spec: SyntaxNode): SyntaxNode | null {
|
|
107
|
+
if (this.imports.pathField !== undefined) {
|
|
108
|
+
const byField = spec.childForFieldName(this.imports.pathField);
|
|
109
|
+
if (byField !== null) return byField;
|
|
110
|
+
}
|
|
111
|
+
return this.imports.pathKinds === undefined
|
|
112
|
+
? null
|
|
113
|
+
: firstChildByKindPriority(spec, this.imports.pathKinds);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private namedChildTexts(node: SyntaxNode | null): string[] {
|
|
117
|
+
if (node === null) return [];
|
|
118
|
+
const names: string[] = [];
|
|
119
|
+
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
120
|
+
const entry = node.namedChild(index);
|
|
121
|
+
if (entry !== null) names.push(entry.text);
|
|
122
|
+
}
|
|
123
|
+
return names;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private collectCall(
|
|
127
|
+
node: SyntaxNode,
|
|
128
|
+
callerScopeId: string,
|
|
129
|
+
ctx: FileTextContext,
|
|
130
|
+
sink: ModuleFactSink,
|
|
131
|
+
): void {
|
|
132
|
+
if (this.calls.acceptCall?.(node) === false) return;
|
|
133
|
+
const callee = this.calleeOf(node);
|
|
134
|
+
if (callee === null) return;
|
|
135
|
+
const args = node.childForFieldName(this.argumentsField);
|
|
136
|
+
const call: RawCall = {
|
|
137
|
+
calleeName: callee.calleeName,
|
|
138
|
+
...(callee.qualifier === undefined ? {} : { qualifier: callee.qualifier }),
|
|
139
|
+
callerScopeId,
|
|
140
|
+
range: rangeOf(node, ctx),
|
|
141
|
+
...(args === null ? {} : { argumentCount: args.namedChildCount }),
|
|
142
|
+
};
|
|
143
|
+
sink.calls.push(call);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private calleeOf(node: SyntaxNode): Callee | null {
|
|
147
|
+
const target = this.callTargetOf(node);
|
|
148
|
+
if (target === null) return null;
|
|
149
|
+
const receiver =
|
|
150
|
+
this.calls.receiverField === undefined
|
|
151
|
+
? null
|
|
152
|
+
: node.childForFieldName(this.calls.receiverField);
|
|
153
|
+
if (receiver !== null && !this.memberKinds.has(target.type)) {
|
|
154
|
+
return { calleeName: target.text, qualifier: receiver.text };
|
|
155
|
+
}
|
|
156
|
+
return this.memberKinds.has(target.type)
|
|
157
|
+
? this.memberCalleeOf(target)
|
|
158
|
+
: { calleeName: target.text };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private callTargetOf(node: SyntaxNode): SyntaxNode | null {
|
|
162
|
+
const constructedType =
|
|
163
|
+
this.calls.constructedTypeField === undefined
|
|
164
|
+
? null
|
|
165
|
+
: node.childForFieldName(this.calls.constructedTypeField);
|
|
166
|
+
return node.childForFieldName(this.functionField) ?? constructedType ?? node.namedChild(0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private memberCalleeOf(target: SyntaxNode): Callee {
|
|
170
|
+
const parts = target.children.filter((child) => child.type !== ".");
|
|
171
|
+
const calleeName = (parts.at(-1)?.text ?? target.text).replace(/^[.:]+/, "");
|
|
172
|
+
const qualifier = target.text
|
|
173
|
+
.slice(0, Math.max(0, target.text.length - calleeName.length))
|
|
174
|
+
.replace(/[.:]+$/, "");
|
|
175
|
+
return { calleeName, ...(qualifier.length === 0 ? {} : { qualifier }) };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kleine, backend-neutrale Baumabfragen fuer das Tabellenprofil.
|
|
3
|
+
*
|
|
4
|
+
* Sie stehen getrennt von Scope-, Entscheidungs- und Modulregeln, damit jede
|
|
5
|
+
* dieser Verantwortungen dieselbe Iterations- und Reihenfolgelogik benutzt.
|
|
6
|
+
*/
|
|
7
|
+
import type { SyntaxNode } from "./syntax.js";
|
|
8
|
+
|
|
9
|
+
export function setOf(values: readonly string[] | undefined): ReadonlySet<string> {
|
|
10
|
+
return new Set(values ?? []);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Der Text eines Namensknotens, oder nichts — nie ein erfundener Name. */
|
|
14
|
+
export function nameTextsOf(node: SyntaxNode | null): string[] {
|
|
15
|
+
return node === null ? [] : [node.text];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Erster Kindknoten (in Quelltextreihenfolge), dessen Art in `kinds` liegt. */
|
|
19
|
+
export function firstChildOfKinds(node: SyntaxNode, kinds: ReadonlySet<string>): SyntaxNode | null {
|
|
20
|
+
for (const child of node.children) {
|
|
21
|
+
if (kinds.has(child.type)) return child;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Erstes direktes Kind genau einer Art. */
|
|
27
|
+
export function firstChildOfKind(node: SyntaxNode, kind: string): SyntaxNode | null {
|
|
28
|
+
for (const child of node.children) {
|
|
29
|
+
if (child.type === kind) return child;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Erstes Kind nach der REIHENFOLGE DER LISTE, nicht nach Quelltextreihenfolge.
|
|
36
|
+
*
|
|
37
|
+
* WOFUER: `using Sys = System.Text;` hat als Kinder `identifier` (Alias) UND
|
|
38
|
+
* `qualified_name` (Pfad), in dieser Reihenfolge. Die Tabelle nennt deshalb
|
|
39
|
+
* `qualified_name` zuerst und meint damit: erst das Genauere.
|
|
40
|
+
*/
|
|
41
|
+
export function firstChildByKindPriority(
|
|
42
|
+
node: SyntaxNode,
|
|
43
|
+
kinds: readonly string[],
|
|
44
|
+
): SyntaxNode | null {
|
|
45
|
+
for (const kind of kinds) {
|
|
46
|
+
const child = firstChildOfKind(node, kind);
|
|
47
|
+
if (child !== null) return child;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Alle Knoten dieser Art im Teilbaum, in Quelltextreihenfolge (iterativ). */
|
|
53
|
+
export function descendantsOfKinds(node: SyntaxNode, kinds: ReadonlySet<string>): SyntaxNode[] {
|
|
54
|
+
const found: SyntaxNode[] = [];
|
|
55
|
+
const stack: SyntaxNode[] = [node];
|
|
56
|
+
while (stack.length > 0) {
|
|
57
|
+
const current = stack.pop() as SyntaxNode;
|
|
58
|
+
if (current !== node && kinds.has(current.type)) {
|
|
59
|
+
found.push(current);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const children = current.children;
|
|
63
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
64
|
+
stack.push(children[index] as SyntaxNode);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return found.sort((a, b) => a.startIndex - b.startIndex);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Erster passender Nachfahre, ohne den ganzen Teilbaum zu materialisieren. */
|
|
71
|
+
export function firstDescendantOfKinds(
|
|
72
|
+
node: SyntaxNode,
|
|
73
|
+
kinds: ReadonlySet<string>,
|
|
74
|
+
): SyntaxNode | null {
|
|
75
|
+
const stack = [...node.children].reverse();
|
|
76
|
+
while (stack.length > 0) {
|
|
77
|
+
const current = stack.pop() as SyntaxNode;
|
|
78
|
+
if (kinds.has(current.type)) return current;
|
|
79
|
+
const children = current.children;
|
|
80
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
81
|
+
stack.push(children[index] as SyntaxNode);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|