@untangle-code/parser-registry 0.0.0-stage → 0.1.1
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,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Python-Regeln, die KEINE Tabelle ausdrueckt.
|
|
3
|
+
*
|
|
4
|
+
* Alles Uebrige — Entscheidungen, Nesting, Kommentare, Halstead-Token,
|
|
5
|
+
* Aufrufe, Scopes — kommt aus `table.ts` durch den gemeinsamen Motor
|
|
6
|
+
* (`shared/table-profile.ts`, `shared/engine.ts`). Hier steht nur, was
|
|
7
|
+
* Python anders macht als jede andere Sprache dieses Projekts:
|
|
8
|
+
*
|
|
9
|
+
* 1. DOCSTRINGS. Python bindet die Beschreibung nicht an einen Kommentar VOR
|
|
10
|
+
* der Definition, sondern an die erste Anweisung IM Rumpf.
|
|
11
|
+
* 2. SICHTBARKEIT. Es gibt keinen Modifikator; sie steht in der
|
|
12
|
+
* Namenskonvention (`_x` geschuetzt, `__x` privat, `__x__` oeffentlich).
|
|
13
|
+
* 3. `__all__`. Was eine Datei exportiert, steht in einer Zuweisung auf
|
|
14
|
+
* Modulebene — also je DATEI, nicht je Knoten. Deshalb wird dieses Profil
|
|
15
|
+
* je Parse gebaut (`AstGrepAdapterHooks.profileForFile`) und nicht einmal
|
|
16
|
+
* je Adapter.
|
|
17
|
+
* 4. METHODE ODER FUNKTION. `def` ist beides; welches, sagt erst der
|
|
18
|
+
* umgebende Knoten. Eine Tabelle bildet Knotenart auf Scope-Art ab und
|
|
19
|
+
* kann das nicht.
|
|
20
|
+
* 5. IMPORTE. Eine Anweisung fuehrt mehrere Ziele mit je eigenem Alias
|
|
21
|
+
* (`from a import b as c, d`) — `ImportRules` kennt genau eines.
|
|
22
|
+
* 6. SIGNATUREN. Die Normalform folgt `signature.ts` (Vorgabewerte fallen
|
|
23
|
+
* weg, Annotationen bleiben) und nicht der Rohtextregel der Tabelle.
|
|
24
|
+
* 7. ZEICHENKETTEN. Ein `string` ist hier KEIN geschlossener Container
|
|
25
|
+
* (siehe `table.ts`); seine Textblaetter muessen deshalb ausdruecklich
|
|
26
|
+
* als Zeichenkettenliterale gelten.
|
|
27
|
+
*/
|
|
28
|
+
import type { LiteralFact, RawImport, SourceFile, SourceRange } from "@untangle-code/domain";
|
|
29
|
+
|
|
30
|
+
import type { DecisionDetectionResult } from "../shared/decisions.js";
|
|
31
|
+
import type { FileTextContext } from "../shared/node-utils.js";
|
|
32
|
+
import { rangeOf } from "../shared/node-utils.js";
|
|
33
|
+
import type { LanguageProfile, ModuleFactSink, ScopeSignature } from "../shared/profile.js";
|
|
34
|
+
import type { ScopeNode } from "../shared/scope-model.js";
|
|
35
|
+
import type { SyntaxNode } from "../shared/syntax.js";
|
|
36
|
+
|
|
37
|
+
import { normalizeHeaderText, normalizeParameterList, normalizeReturnType } from "./signature.js";
|
|
38
|
+
import { PYTHON_STRING_LEAF_KINDS } from "./table.js";
|
|
39
|
+
|
|
40
|
+
const INIT_FILE_RE = /(^|\/)__init__\.pyi?$/;
|
|
41
|
+
const STRING_LEAF_KINDS: ReadonlySet<string> = new Set(PYTHON_STRING_LEAF_KINDS);
|
|
42
|
+
|
|
43
|
+
/** Knotenarten, die einen eigenen Namensraum aufmachen — die Grenze fuer "steht in einer Klasse". */
|
|
44
|
+
const SCOPE_OPENING_KINDS: ReadonlySet<string> = new Set([
|
|
45
|
+
"function_definition",
|
|
46
|
+
"class_definition",
|
|
47
|
+
"lambda",
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// 2. Sichtbarkeit aus der Namenskonvention (PEP 8)
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* `__x__` ist das oeffentliche Protokoll (`__init__`, `__len__`) und bekommt
|
|
56
|
+
* deshalb GAR KEINE Angabe — nicht "public": das Feld ist optional, und
|
|
57
|
+
* "keine Angabe" ist die ehrliche Aussage ueber eine Sprache ohne
|
|
58
|
+
* Sichtbarkeitsmodifikatoren.
|
|
59
|
+
*/
|
|
60
|
+
function pythonVisibility(name: string): string | undefined {
|
|
61
|
+
if (name.startsWith("__") && name.endsWith("__") && name.length > 4) return undefined;
|
|
62
|
+
if (name.startsWith("__")) return "private";
|
|
63
|
+
if (name.startsWith("_")) return "protected";
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// 1. Docstrings
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
/** Der Text eines Zeichenkettenknotens ohne Anfuehrungszeichen (auch dreifache). */
|
|
72
|
+
function stringLiteralText(node: SyntaxNode): string | null {
|
|
73
|
+
if (node.type !== "string") return null;
|
|
74
|
+
for (const child of node.children) {
|
|
75
|
+
if (child.type === "string_content") return child.text;
|
|
76
|
+
}
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Der Docstring eines Scopes: die erste Anweisung des Rumpfes, wenn sie aus
|
|
82
|
+
* nichts als einem Zeichenkettenliteral besteht. Alles andere ist Code, und
|
|
83
|
+
* ein Stueck Code als Beschreibung auszugeben waere eine Erfindung.
|
|
84
|
+
*/
|
|
85
|
+
function docstringRange(
|
|
86
|
+
node: SyntaxNode,
|
|
87
|
+
ctx: FileTextContext,
|
|
88
|
+
bodyField: string,
|
|
89
|
+
): SourceRange | undefined {
|
|
90
|
+
const body = node.childForFieldName(bodyField);
|
|
91
|
+
if (body === null) return undefined;
|
|
92
|
+
const first = body.namedChild(0);
|
|
93
|
+
if (first === null || first.type !== "expression_statement") return undefined;
|
|
94
|
+
const expression = first.namedChild(0);
|
|
95
|
+
if (expression === null || stringLiteralText(expression) === null) return undefined;
|
|
96
|
+
return rangeOf(expression, ctx);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// 3. `__all__`
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Die Namen aus `__all__ = [...]` auf Modulebene, oder `null`, wenn die Datei
|
|
105
|
+
* keines hat. `null` und nicht "leere Menge": ohne `__all__` gilt die
|
|
106
|
+
* Unterstrich-Konvention, mit leerem `__all__` exportiert die Datei nichts.
|
|
107
|
+
* Die beiden Faelle zu vermengen hiesse, jede Datei ohne `__all__` fuer
|
|
108
|
+
* exportlos zu erklaeren.
|
|
109
|
+
*/
|
|
110
|
+
function findDunderAll(root: SyntaxNode): ReadonlySet<string> | null {
|
|
111
|
+
for (const statement of root.children) {
|
|
112
|
+
if (statement.type !== "expression_statement") continue;
|
|
113
|
+
const assignment = statement.namedChild(0);
|
|
114
|
+
if (assignment === null || assignment.type !== "assignment") continue;
|
|
115
|
+
const left = assignment.childForFieldName("left");
|
|
116
|
+
if (left === null || left.type !== "identifier" || left.text !== "__all__") continue;
|
|
117
|
+
const right = assignment.childForFieldName("right");
|
|
118
|
+
if (
|
|
119
|
+
right === null ||
|
|
120
|
+
(right.type !== "list" && right.type !== "tuple" && right.type !== "set")
|
|
121
|
+
) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const names = new Set<string>();
|
|
125
|
+
for (const element of right.children) {
|
|
126
|
+
const text = stringLiteralText(element);
|
|
127
|
+
if (text !== null) names.add(text);
|
|
128
|
+
}
|
|
129
|
+
return names;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Steht dieser Knoten unmittelbar auf Modulebene (auch unter einem Dekorator)? */
|
|
135
|
+
function isTopLevel(node: SyntaxNode): boolean {
|
|
136
|
+
const parent = node.parent;
|
|
137
|
+
if (parent === null) return false;
|
|
138
|
+
if (parent.type === "module") return true;
|
|
139
|
+
return parent.type === "decorated_definition" && parent.parent?.type === "module";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// 4. Methode oder Funktion
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
/** Der naechste umschliessende Knoten, der selbst einen Namensraum oeffnet. */
|
|
147
|
+
function enclosingScopeKind(node: SyntaxNode): string | null {
|
|
148
|
+
let current = node.parent;
|
|
149
|
+
while (current !== null) {
|
|
150
|
+
if (SCOPE_OPENING_KINDS.has(current.type)) return current.type;
|
|
151
|
+
current = current.parent;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// 5. Importe
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
/** Ziel und Alias eines einzelnen Importeintrags (`os.path as osp`, `helper`). */
|
|
161
|
+
function importEntryOf(node: SyntaxNode): { path: string; alias?: string } | null {
|
|
162
|
+
if (node.type === "dotted_name") return { path: node.text };
|
|
163
|
+
if (node.type !== "aliased_import") return null;
|
|
164
|
+
const path = node.childForFieldName("name");
|
|
165
|
+
const alias = node.childForFieldName("alias");
|
|
166
|
+
if (path === null) return null;
|
|
167
|
+
return { path: path.text, ...(alias === null ? {} : { alias: alias.text }) };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* `import a, b as c` — jedes Ziel ist ein eigener Import mit eigenem Pfad.
|
|
172
|
+
* `from m import a, b as c` — EIN Pfad (`m`) mit mehreren gebundenen Namen.
|
|
173
|
+
* Zwei Formen, deshalb zwei Zweige und keine gemeinsame Schleife, die beide
|
|
174
|
+
* halb falsch bedient.
|
|
175
|
+
*/
|
|
176
|
+
function collectPythonImports(
|
|
177
|
+
node: SyntaxNode,
|
|
178
|
+
ctx: FileTextContext,
|
|
179
|
+
sink: ModuleFactSink,
|
|
180
|
+
isInitFile: boolean,
|
|
181
|
+
): void {
|
|
182
|
+
const range = rangeOf(node, ctx);
|
|
183
|
+
// spec.md §6.2: was ein Paketkopf (`__init__.py`) auf oberster Ebene
|
|
184
|
+
// hereinholt, reicht er weiter — das ist ein Re-Export, kein Verbrauch.
|
|
185
|
+
const importKind: RawImport["importKind"] =
|
|
186
|
+
isInitFile && isTopLevel(node) ? "re_export" : "import";
|
|
187
|
+
|
|
188
|
+
if (node.type === "import_statement") {
|
|
189
|
+
for (const child of node.children) {
|
|
190
|
+
const entry = importEntryOf(child);
|
|
191
|
+
if (entry === null) continue;
|
|
192
|
+
sink.imports.push({
|
|
193
|
+
specifier: entry.path,
|
|
194
|
+
importKind,
|
|
195
|
+
range,
|
|
196
|
+
...(entry.alias === undefined ? {} : { importedNames: [entry.alias] }),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const moduleNode = node.childForFieldName("module_name");
|
|
203
|
+
if (moduleNode === null) return;
|
|
204
|
+
const importedNames: string[] = [];
|
|
205
|
+
// Die gebundenen Namen stehen NACH dem Schluesselwort `import`; davor steht
|
|
206
|
+
// der Modulpfad, der genauso aussieht. Ohne diese Schranke stuende
|
|
207
|
+
// `collections` bei `from collections import x` als importierter Name da.
|
|
208
|
+
let afterKeyword = false;
|
|
209
|
+
for (const child of node.children) {
|
|
210
|
+
if (child.type === "import") {
|
|
211
|
+
afterKeyword = true;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!afterKeyword) continue;
|
|
215
|
+
// `from m import *` bindet unbekannt viele Namen. KEINEN zu nennen ist
|
|
216
|
+
// die einzige ehrliche Antwort (domain `analysis.ts`: ein fehlendes Feld
|
|
217
|
+
// heisst "unbekannt").
|
|
218
|
+
const entry = importEntryOf(child);
|
|
219
|
+
if (entry !== null) importedNames.push(entry.alias ?? entry.path);
|
|
220
|
+
}
|
|
221
|
+
sink.imports.push({
|
|
222
|
+
specifier: moduleNode.text,
|
|
223
|
+
importKind,
|
|
224
|
+
range,
|
|
225
|
+
...(importedNames.length > 0 ? { importedNames } : {}),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// 6. Signaturen
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
function headerBeforeBody(node: SyntaxNode, bodyField: string): string {
|
|
234
|
+
const body = node.childForFieldName(bodyField);
|
|
235
|
+
if (body === null) return node.text.split("\n")[0]?.trim() ?? node.text;
|
|
236
|
+
return node.text.slice(0, Math.max(0, body.startIndex - node.startIndex)).trimEnd();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function pythonSignature(node: SyntaxNode, name: string, bodyField: string): ScopeSignature {
|
|
240
|
+
if (node.type === "class_definition") {
|
|
241
|
+
const raw = headerBeforeBody(node, bodyField);
|
|
242
|
+
return { raw, normalized: normalizeHeaderText(raw) };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const parameters = node.childForFieldName("parameters");
|
|
246
|
+
// `lambda_parameters` traegt KEINE Klammern; `parameters` traegt sie. Beide
|
|
247
|
+
// muessen als `(...)` in die Normalform, sonst zerfaellt die Aufteilung.
|
|
248
|
+
const parametersText =
|
|
249
|
+
parameters === null
|
|
250
|
+
? "()"
|
|
251
|
+
: parameters.text.trim().startsWith("(")
|
|
252
|
+
? parameters.text
|
|
253
|
+
: `(${parameters.text})`;
|
|
254
|
+
const returnType = node.childForFieldName("return_type");
|
|
255
|
+
// `async` ist ein anonymes Kind, kein Feld — und es gehoert zur
|
|
256
|
+
// Aufrufschnittstelle: eine Koroutine ist nicht dasselbe wie eine Funktion.
|
|
257
|
+
const isAsync = node.children.some((child) => child.type === "async");
|
|
258
|
+
const head = `${isAsync ? "async " : ""}${name}`;
|
|
259
|
+
const raw = `${head}${parametersText}${returnType === null ? "" : ` -> ${returnType.text}`}`;
|
|
260
|
+
const normalized = `${head}${normalizeParameterList(parametersText)}${normalizeReturnType(
|
|
261
|
+
returnType?.text,
|
|
262
|
+
)}`;
|
|
263
|
+
return { raw, normalized };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Baut das Python-Profil ueber dem Tabellenprofil — EINMAL JE DATEI, weil
|
|
270
|
+
* `__all__` und `__init__.py` Dateieigenschaften sind.
|
|
271
|
+
*/
|
|
272
|
+
export function createPythonProfile(
|
|
273
|
+
base: LanguageProfile,
|
|
274
|
+
root: SyntaxNode,
|
|
275
|
+
file: SourceFile,
|
|
276
|
+
): LanguageProfile {
|
|
277
|
+
const dunderAll = findDunderAll(root);
|
|
278
|
+
const isInitFile = INIT_FILE_RE.test(file.path.replace(/\\/g, "/"));
|
|
279
|
+
const bodyField = "body";
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
...base,
|
|
283
|
+
|
|
284
|
+
// 7. Zeichenkettenblaetter (siehe `table.ts`: `string` ist kein Container).
|
|
285
|
+
simpleLiteralLeafKinds: new Set([...base.simpleLiteralLeafKinds, ...STRING_LEAF_KINDS]),
|
|
286
|
+
simpleLiteralKind(kind: string): LiteralFact["literalKind"] {
|
|
287
|
+
return STRING_LEAF_KINDS.has(kind) ? "string" : base.simpleLiteralKind(kind);
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
// Der Dekorator gehoert zum Symbol: `@app.route(...)` steht ueber der
|
|
291
|
+
// Funktion und beschreibt sie. `chunks.ts` weitet den Bereich darauf aus.
|
|
292
|
+
exportWrapperKinds: new Set(["decorated_definition"]),
|
|
293
|
+
|
|
294
|
+
// 4. `def` ist Funktion, Methode oder Konstruktor.
|
|
295
|
+
scopeKindOf(node: SyntaxNode): ScopeNode["kind"] | null {
|
|
296
|
+
const kind = base.scopeKindOf(node);
|
|
297
|
+
if (kind !== "function" || node.type !== "function_definition") return kind;
|
|
298
|
+
if (enclosingScopeKind(node) !== "class_definition") return "function";
|
|
299
|
+
return base.scopeName(node) === "__init__" ? "constructor" : "method";
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
scopeSignature(node: SyntaxNode, _kind: ScopeNode["kind"], name: string): ScopeSignature {
|
|
303
|
+
return pythonSignature(node, name, bodyField);
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
docRange: (node, ctx) => docstringRange(node, ctx, bodyField),
|
|
307
|
+
|
|
308
|
+
visibility: (_node, name) => pythonVisibility(name),
|
|
309
|
+
|
|
310
|
+
// 3. `__all__` schlaegt die Unterstrich-Konvention. Beides gilt nur fuer
|
|
311
|
+
// Namen auf Modulebene: eine Methode wird nicht aus der Datei exportiert.
|
|
312
|
+
exportedNames(node: SyntaxNode, name: string): string[] {
|
|
313
|
+
if (!isTopLevel(node)) return [];
|
|
314
|
+
if (dunderAll !== null) return dunderAll.has(name) ? [name] : [];
|
|
315
|
+
return name.startsWith("_") ? [] : [name];
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
detectDecisionAndNesting(node: SyntaxNode): DecisionDetectionResult {
|
|
319
|
+
const detected = base.detectDecisionAndNesting(node);
|
|
320
|
+
// `union_pattern` ist FLACH: `case 1 | 2 | 3` ist EIN Knoten mit drei
|
|
321
|
+
// Zweigen, waehrend Rusts `or_pattern` fuer dasselbe zwei verschachtelte
|
|
322
|
+
// Knoten baut. Ohne diese Zahl kostete `1 | 2 | 3` so viel wie `1 | 2`.
|
|
323
|
+
if (node.type !== "union_pattern" || detected.decision === undefined) return detected;
|
|
324
|
+
const alternatives = node.namedChildCount;
|
|
325
|
+
if (alternatives <= 1) return detected;
|
|
326
|
+
return { ...detected, decision: { ...detected.decision, repeat: alternatives - 1 } };
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
collectModuleFacts(node, kind, callerScopeId, ctx, sink): void {
|
|
330
|
+
if (
|
|
331
|
+
kind === "import_statement" ||
|
|
332
|
+
kind === "import_from_statement" ||
|
|
333
|
+
kind === "future_import_statement"
|
|
334
|
+
) {
|
|
335
|
+
collectPythonImports(node, ctx, sink, isInitFile);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
base.collectModuleFacts(node, kind, callerScopeId, ctx, sink);
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature text normalization for Python (spec.md §5.5).
|
|
3
|
+
*
|
|
4
|
+
* `normalizedSignature` feeds `buildSymbolId` and MUST be stable across
|
|
5
|
+
* formatting-only edits. Rules (documented WP-09 decision — Python has no
|
|
6
|
+
* `tree-sitter-python` grammar to reuse `languages/shared/signature.ts`'s
|
|
7
|
+
* `<`/`>`-as-bracket assumption from, which is wrong for Python anyway:
|
|
8
|
+
* `<`/`>` are ONLY comparison operators here, never generic brackets):
|
|
9
|
+
*
|
|
10
|
+
* 1. **Whitespace**: any run of spaces/tabs/newlines collapses to a single
|
|
11
|
+
* space; spacing around `,`, `:` (annotation colon) and `=` (only for
|
|
12
|
+
* an ANNOTATED parameter's default, per PEP 8 — `x: int = 1`, not
|
|
13
|
+
* `x=1`) is canonicalized.
|
|
14
|
+
* 2. **Default values** (`= <expr>`) are STRIPPED from the identity
|
|
15
|
+
* signature, exactly like the TS/JS adapters: a default value is a
|
|
16
|
+
* call-site convenience, not a change to the parameter's name,
|
|
17
|
+
* position, or `*`/`**` role — keeping it would change a symbol's id on
|
|
18
|
+
* a body-only-feeling edit (`def f(a=1)` -> `def f(a=2)` should not look
|
|
19
|
+
* like a new symbol). Unlike TypeScript, Python has no separate
|
|
20
|
+
* "optional marker" syntax (`?`) that would otherwise carry the
|
|
21
|
+
* "caller may omit this" information forward — that information is
|
|
22
|
+
* simply not part of the normalized identity for Python, a documented
|
|
23
|
+
* (not accidental) asymmetry with the TS/JS adapters.
|
|
24
|
+
* 3. **Type annotations** (`: <type>`) are kept verbatim (modulo whitespace
|
|
25
|
+
* rules) — they are part of the observable call contract.
|
|
26
|
+
* 4. **`*args` / `**kwargs` / bare `*` (keyword-only marker) / `/`
|
|
27
|
+
* (positional-only marker, PEP 570)**: their `*`/`**`/`/` marker is kept
|
|
28
|
+
* exactly as written (it changes the call contract), only a following
|
|
29
|
+
* default (impossible for `*args`/`**kwargs`/markers, but harmless to
|
|
30
|
+
* guard) or its annotation spacing is normalized.
|
|
31
|
+
* 5. **Return type** (`-> <type>`) is kept verbatim (modulo whitespace),
|
|
32
|
+
* canonicalized to always have exactly one space on each side of `->`.
|
|
33
|
+
*
|
|
34
|
+
* Known limitation (documented, not a defect, mirroring the TS/JS
|
|
35
|
+
* adapters' own documented limitation): default-value stripping uses a
|
|
36
|
+
* bracket-DEPTH scanner over `( [ {` (never `<`/`>` — see above), not a
|
|
37
|
+
* full expression parser. A default expression containing an unbalanced
|
|
38
|
+
* string literal with a comma/`=` inside is not specially protected either
|
|
39
|
+
* (rare enough in default-value position to accept as a scoping decision).
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const OPEN_BRACKETS = new Set(["(", "[", "{"]);
|
|
43
|
+
const CLOSE_BRACKETS = new Set([")", "]", "}"]);
|
|
44
|
+
|
|
45
|
+
/** Collapses whitespace runs to single spaces and trims. */
|
|
46
|
+
export function normalizeSpacing(text: string): string {
|
|
47
|
+
return text.replace(/\s+/g, " ").trim();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Splits `content` on top-level `,` (depth 0 w.r.t. `( [ {`). */
|
|
51
|
+
export function splitTopLevelComma(content: string): string[] {
|
|
52
|
+
const parts: string[] = [];
|
|
53
|
+
let depth = 0;
|
|
54
|
+
let current = "";
|
|
55
|
+
let inString: string | null = null;
|
|
56
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
57
|
+
const ch = content[i] as string;
|
|
58
|
+
if (inString !== null) {
|
|
59
|
+
current += ch;
|
|
60
|
+
if (ch === "\\") {
|
|
61
|
+
i += 1;
|
|
62
|
+
if (i < content.length) current += content[i];
|
|
63
|
+
} else if (ch === inString) {
|
|
64
|
+
inString = null;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (ch === '"' || ch === "'") {
|
|
69
|
+
inString = ch;
|
|
70
|
+
current += ch;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (OPEN_BRACKETS.has(ch)) depth += 1;
|
|
74
|
+
else if (CLOSE_BRACKETS.has(ch)) depth = Math.max(0, depth - 1);
|
|
75
|
+
if (ch === "," && depth === 0) {
|
|
76
|
+
parts.push(current);
|
|
77
|
+
current = "";
|
|
78
|
+
} else {
|
|
79
|
+
current += ch;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
parts.push(current);
|
|
83
|
+
return parts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Index of the top-level `=` introducing a default value, or -1. Excludes `==`, `!=`, `<=`, `>=`, `:=`. */
|
|
87
|
+
function findTopLevelDefaultEquals(text: string): number {
|
|
88
|
+
let depth = 0;
|
|
89
|
+
let inString: string | null = null;
|
|
90
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
91
|
+
const ch = text[i] as string;
|
|
92
|
+
if (inString !== null) {
|
|
93
|
+
if (ch === "\\") i += 1;
|
|
94
|
+
else if (ch === inString) inString = null;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (ch === '"' || ch === "'") {
|
|
98
|
+
inString = ch;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (OPEN_BRACKETS.has(ch)) {
|
|
102
|
+
depth += 1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (CLOSE_BRACKETS.has(ch)) {
|
|
106
|
+
depth = Math.max(0, depth - 1);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (ch === "=" && depth === 0) {
|
|
110
|
+
const prev = i > 0 ? text[i - 1] : "";
|
|
111
|
+
const next = i + 1 < text.length ? text[i + 1] : "";
|
|
112
|
+
const isCompoundOperator =
|
|
113
|
+
next === "=" ||
|
|
114
|
+
prev === "=" ||
|
|
115
|
+
prev === "!" ||
|
|
116
|
+
prev === "<" ||
|
|
117
|
+
prev === ">" ||
|
|
118
|
+
prev === ":";
|
|
119
|
+
if (!isCompoundOperator) return i;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return -1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Strips one parameter entry's default value (`= <expr>`), keeping any annotation before it. */
|
|
126
|
+
export function stripDefaultValue(entry: string): string {
|
|
127
|
+
const eqIndex = findTopLevelDefaultEquals(entry);
|
|
128
|
+
return eqIndex === -1 ? entry : entry.slice(0, eqIndex).trimEnd();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Canonicalizes one parameter entry's internal spacing: `a :int=1` -> `a: int`. */
|
|
132
|
+
function normalizeParamEntry(entry: string): string {
|
|
133
|
+
const stripped = stripDefaultValue(entry);
|
|
134
|
+
const colonIndex = topLevelColonIndex(stripped);
|
|
135
|
+
if (colonIndex === -1) return normalizeSpacing(stripped);
|
|
136
|
+
const name = normalizeSpacing(stripped.slice(0, colonIndex));
|
|
137
|
+
const annotation = normalizeSpacing(stripped.slice(colonIndex + 1));
|
|
138
|
+
return `${name}: ${annotation}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function topLevelColonIndex(text: string): number {
|
|
142
|
+
let depth = 0;
|
|
143
|
+
let inString: string | null = null;
|
|
144
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
145
|
+
const ch = text[i] as string;
|
|
146
|
+
if (inString !== null) {
|
|
147
|
+
if (ch === "\\") i += 1;
|
|
148
|
+
else if (ch === inString) inString = null;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (ch === '"' || ch === "'") {
|
|
152
|
+
inString = ch;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (OPEN_BRACKETS.has(ch)) depth += 1;
|
|
156
|
+
else if (CLOSE_BRACKETS.has(ch)) depth = Math.max(0, depth - 1);
|
|
157
|
+
else if (ch === ":" && depth === 0) return i;
|
|
158
|
+
}
|
|
159
|
+
return -1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Normalizes a parameter-list text INCLUDING its outer parens, e.g. `( a:int=1, *, b = 2 )` -> `(a: int, *, b)`. */
|
|
163
|
+
export function normalizeParameterList(paramsText: string): string {
|
|
164
|
+
const trimmed = paramsText.trim();
|
|
165
|
+
const hasParens = trimmed.startsWith("(") && trimmed.endsWith(")");
|
|
166
|
+
const inner = hasParens ? trimmed.slice(1, -1) : trimmed;
|
|
167
|
+
const entries = splitTopLevelComma(inner)
|
|
168
|
+
.map((entry) => entry.trim())
|
|
169
|
+
.filter((entry) => entry.length > 0);
|
|
170
|
+
const normalizedEntries = entries.map(normalizeParamEntry);
|
|
171
|
+
return `(${normalizedEntries.join(", ")})`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Normalizes a `-> ReturnType` suffix (or `""` when absent). */
|
|
175
|
+
export function normalizeReturnType(returnTypeRaw: string | undefined): string {
|
|
176
|
+
if (returnTypeRaw === undefined || returnTypeRaw.trim().length === 0) return "";
|
|
177
|
+
return ` -> ${normalizeSpacing(returnTypeRaw)}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Normalizes a class/def header text with no parameter list (e.g. a bare `class Foo:`). */
|
|
181
|
+
export function normalizeHeaderText(headerText: string): string {
|
|
182
|
+
return normalizeSpacing(headerText.replace(/:\s*$/, ""));
|
|
183
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Sprachtabelle fuer Python — die zwoelfte Sprache des ast-grep-Backends.
|
|
3
|
+
*
|
|
4
|
+
* WAS HIER GESCHAH (WP-239): bis Welle 7 stand an dieser Stelle ein
|
|
5
|
+
* HANDGESCHRIEBENER Tokenizer, Parser und AST-Motor ueber 5.604 Zeilen. Sie
|
|
6
|
+
* entstanden nicht aus einer fachlichen Notwendigkeit, sondern weil eine zu
|
|
7
|
+
* enge Auftragsgrenze einem Agenten verbot, `package.json` anzufassen
|
|
8
|
+
* (dokumentiert in `docs/PARITY.md`). Die Grammatik, die es dafuer braucht,
|
|
9
|
+
* gibt es fertig und MIT-lizenziert: `@ast-grep/lang-python`.
|
|
10
|
+
*
|
|
11
|
+
* WAS DIE ABLOESUNG NEBENBEI BEHOBEN HAT — nicht durch eine Korrektur,
|
|
12
|
+
* sondern weil die Klasse von Fehlern verschwindet:
|
|
13
|
+
*
|
|
14
|
+
* - WP-238: `with a() as x, b() as y:` liess im handgeschriebenen Parser die
|
|
15
|
+
* GANZE Datei mit `parseQuality: "failed"` durchfallen. Die Grammatik legt
|
|
16
|
+
* dafuer `with_clause > with_item*` an; es gibt nichts zu erkennen.
|
|
17
|
+
* - WP-115: verschachtelte Ausdruecke in f-String-Formatangaben
|
|
18
|
+
* (`f"{x:{w}.{p}}"`) gingen im Teil-Parse verloren. Hier sind sie
|
|
19
|
+
* gewoehnliche `interpolation`/`format_expression`-Knoten im Baum —
|
|
20
|
+
* deshalb steht `string` NICHT unter `stringContainerKinds` (siehe dort).
|
|
21
|
+
* - WP-226: ein nachgestellter Kommentar vor dem Zeilenende verschluckte im
|
|
22
|
+
* Tokenizer das NEWLINE. Ein `comment` ist hier ein Geschwister wie jedes
|
|
23
|
+
* andere.
|
|
24
|
+
*
|
|
25
|
+
* WAS DIE TABELLE NICHT LEISTET, steht in `profile.ts` daneben: Docstrings,
|
|
26
|
+
* Sichtbarkeit aus der Namenskonvention, `__all__`, die Unterscheidung
|
|
27
|
+
* Funktion/Methode/Konstruktor und die Importformen sind Python-Regeln, die
|
|
28
|
+
* KEINE Tabelle ausdrueckt. Sie stehen als Verfeinerung ueber diesem Profil,
|
|
29
|
+
* nicht als Sonderfaelle im gemeinsamen Motor.
|
|
30
|
+
*
|
|
31
|
+
* DIE KNOTENARTEN SIND NACHGESEHEN, NICHT GERATEN: jede Zeichenkette hier
|
|
32
|
+
* stammt aus einem echten Baum, den `adapters.test.ts` mit demselben
|
|
33
|
+
* Quelltext wieder erzeugt.
|
|
34
|
+
*/
|
|
35
|
+
import type { AstGrepLanguageSource } from "../astgrep-backend/index.js";
|
|
36
|
+
import type { LanguageKindTable } from "../shared/table-profile.js";
|
|
37
|
+
import { DEFAULT_PUNCTUATION } from "../shared/table-profile.js";
|
|
38
|
+
|
|
39
|
+
/** Woher die Grammatik kommt. MIT-lizenziert, siehe `THIRD_PARTY_LICENSES.md`. */
|
|
40
|
+
export const PYTHON_SOURCE: AstGrepLanguageSource = {
|
|
41
|
+
languageName: "python",
|
|
42
|
+
moduleName: "@ast-grep/lang-python",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Zeichenkettenblaetter. Sie stehen NICHT unter `numberLeafKinds` und
|
|
47
|
+
* Verwandten, weil `simpleLiteralKind` daraus "number"/"boolean"/"null"
|
|
48
|
+
* ableitet; `profile.ts` haengt sie an und beantwortet sie mit "string".
|
|
49
|
+
*/
|
|
50
|
+
export const PYTHON_STRING_LEAF_KINDS: readonly string[] = ["string_content", "escape_sequence"];
|
|
51
|
+
|
|
52
|
+
export const PYTHON_TABLE: LanguageKindTable = {
|
|
53
|
+
languageId: "python",
|
|
54
|
+
extensions: [".py", ".pyi"],
|
|
55
|
+
supportLevel: "deep",
|
|
56
|
+
commentKinds: ["comment"],
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* ABSICHTLICH LEER. Ein `string` waere in jeder anderen Sprache ein
|
|
60
|
+
* Container, dessen Teilbaum EIN Operand ist. In Python ist er das nicht:
|
|
61
|
+
* ein f-String traegt `interpolation`-Knoten mit vollwertigen Ausdruecken,
|
|
62
|
+
* und ein Aufruf darin ist ein Aufruf. Wer `string` hier eintraegt, macht
|
|
63
|
+
* WP-115 zum zweiten Mal — nur diesmal ohne Diagnose, weil der Walk gar
|
|
64
|
+
* nicht erst hineinsieht.
|
|
65
|
+
*/
|
|
66
|
+
stringContainerKinds: [],
|
|
67
|
+
|
|
68
|
+
identifierLeafKinds: ["identifier"],
|
|
69
|
+
numberLeafKinds: ["integer", "float"],
|
|
70
|
+
booleanLeafKinds: ["true", "false"],
|
|
71
|
+
nullLeafKinds: ["none"],
|
|
72
|
+
|
|
73
|
+
// `string_start`/`string_end` tragen das Praefix und die Anfuehrungszeichen;
|
|
74
|
+
// sie sind Struktur, kein Operator. Ohne sie stuende `f"` als "Operator" in
|
|
75
|
+
// jeder Halstead-Rechnung.
|
|
76
|
+
punctuationKinds: [...DEFAULT_PUNCTUATION, "string_start", "string_end"],
|
|
77
|
+
|
|
78
|
+
scopes: [
|
|
79
|
+
{ kinds: ["function_definition"], scopeKind: "function" },
|
|
80
|
+
{ kinds: ["class_definition"], scopeKind: "class" },
|
|
81
|
+
// Ein `lambda` ist eine Ausfuehrungseinheit wie eine `def`: spec.md §16.1
|
|
82
|
+
// zaehlt Lambdas mit, und §16.4 verlangt fuer sie ein eigenes
|
|
83
|
+
// Nesting-Ereignis im umgebenden Scope. Beides haengt daran, dass es hier
|
|
84
|
+
// steht.
|
|
85
|
+
{ kinds: ["lambda"], scopeKind: "function" },
|
|
86
|
+
],
|
|
87
|
+
|
|
88
|
+
decisions: {
|
|
89
|
+
// `if_clause` ist BEIDES: der Filter einer Komprehension
|
|
90
|
+
// (`[x for x in a if p(x)]`) und der Waechter eines `case`. Beide sind
|
|
91
|
+
// eine echte Verzweigung, beide zaehlen — und beide heissen so.
|
|
92
|
+
ifKinds: ["if_statement", "if_clause"],
|
|
93
|
+
elseIfKinds: ["elif_clause"],
|
|
94
|
+
// `for_in_clause` ist die `for`-Klausel einer Komprehension. Sie ist eine
|
|
95
|
+
// Schleife, auch wenn sie keine Anweisung ist.
|
|
96
|
+
loopKinds: ["for_statement", "while_statement", "for_in_clause"],
|
|
97
|
+
catchKinds: ["except_clause", "except_group_clause"],
|
|
98
|
+
ternaryKinds: ["conditional_expression"],
|
|
99
|
+
switchKinds: ["match_statement"],
|
|
100
|
+
caseKinds: ["case_clause"],
|
|
101
|
+
// `case _:` zaehlt in Python MIT: anders als ein C-`default` ist `_` ein
|
|
102
|
+
// gewoehnliches Muster in der Reihenfolge, und ein `match` bricht ohne
|
|
103
|
+
// Treffer einfach ab. Deshalb steht hier KEIN `caseExcludeChildKinds`.
|
|
104
|
+
patternAlternativeKinds: ["union_pattern"],
|
|
105
|
+
binaryKinds: ["boolean_operator"],
|
|
106
|
+
operatorField: "operator",
|
|
107
|
+
logicalAndOperators: ["and"],
|
|
108
|
+
logicalOrOperators: ["or"],
|
|
109
|
+
jumpKinds: ["return_statement", "raise_statement", "break_statement", "continue_statement"],
|
|
110
|
+
blockKinds: ["block"],
|
|
111
|
+
consequenceField: "consequence",
|
|
112
|
+
// `elif_clause` UND `else_clause` stehen beide im Feld `alternative`.
|
|
113
|
+
// Genau deshalb ist ein `if` mit `elif` kein Waechter (`guard`), sondern
|
|
114
|
+
// ein `if` — was `adapters.test.ts` festhaelt.
|
|
115
|
+
alternativeField: "alternative",
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Die Importe wertet `profile.ts` aus: Python fuehrt in EINER Anweisung
|
|
119
|
+
// mehrere Ziele mit je eigenem Alias, und das druecken `ImportRules` nicht
|
|
120
|
+
// aus. Die Arten stehen hier, damit das Vorsieb (`needsNode`) sie kennt.
|
|
121
|
+
imports: {
|
|
122
|
+
kinds: ["import_statement", "import_from_statement", "future_import_statement"],
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
calls: {
|
|
126
|
+
kinds: ["call"],
|
|
127
|
+
functionField: "function",
|
|
128
|
+
argumentsField: "arguments",
|
|
129
|
+
memberKinds: ["attribute"],
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
declarationParentKinds: ["function_definition", "class_definition"],
|
|
133
|
+
|
|
134
|
+
bodyField: "body",
|
|
135
|
+
};
|