demangle 0.3.0__tar.gz

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.
Files changed (247) hide show
  1. demangle-0.3.0/.gitignore +20 -0
  2. demangle-0.3.0/ARCHITECTURE.md +210 -0
  3. demangle-0.3.0/CHANGELOG.md +3691 -0
  4. demangle-0.3.0/CONFORMANCE.md +499 -0
  5. demangle-0.3.0/CONTRIBUTING.md +157 -0
  6. demangle-0.3.0/LICENSE +21 -0
  7. demangle-0.3.0/NOTICE +178 -0
  8. demangle-0.3.0/PKG-INFO +382 -0
  9. demangle-0.3.0/README.md +356 -0
  10. demangle-0.3.0/ROADMAP.md +1064 -0
  11. demangle-0.3.0/SECURITY.md +75 -0
  12. demangle-0.3.0/benchmarks/baseline.json +38 -0
  13. demangle-0.3.0/benchmarks/bench.py +433 -0
  14. demangle-0.3.0/docs/ARCHITECTURE.md +1 -0
  15. demangle-0.3.0/docs/CHANGELOG.md +1 -0
  16. demangle-0.3.0/docs/CONFORMANCE.md +1 -0
  17. demangle-0.3.0/docs/CONTRIBUTING.md +1 -0
  18. demangle-0.3.0/docs/ROADMAP.md +1 -0
  19. demangle-0.3.0/docs/SECURITY.md +1 -0
  20. demangle-0.3.0/docs/adding-a-scheme.md +122 -0
  21. demangle-0.3.0/docs/analysing-a-binary.md +166 -0
  22. demangle-0.3.0/docs/index.md +1 -0
  23. demangle-0.3.0/docs/reference/api.md +204 -0
  24. demangle-0.3.0/docs/reference/core.md +99 -0
  25. demangle-0.3.0/docs/reference/schemes.md +302 -0
  26. demangle-0.3.0/docs/specs/itanium-grammar.txt +289 -0
  27. demangle-0.3.0/docs/testing.md +212 -0
  28. demangle-0.3.0/mkdocs.yml +96 -0
  29. demangle-0.3.0/pyproject.toml +181 -0
  30. demangle-0.3.0/src/demangle/__init__.py +103 -0
  31. demangle-0.3.0/src/demangle/_signature.py +623 -0
  32. demangle-0.3.0/src/demangle/api.py +749 -0
  33. demangle-0.3.0/src/demangle/cli.py +521 -0
  34. demangle-0.3.0/src/demangle/core/__init__.py +20 -0
  35. demangle-0.3.0/src/demangle/core/ast.py +899 -0
  36. demangle-0.3.0/src/demangle/core/builder.py +160 -0
  37. demangle-0.3.0/src/demangle/core/cache.py +70 -0
  38. demangle-0.3.0/src/demangle/core/decorations.py +73 -0
  39. demangle-0.3.0/src/demangle/core/errors.py +75 -0
  40. demangle-0.3.0/src/demangle/core/limits.py +50 -0
  41. demangle-0.3.0/src/demangle/core/plugin.py +98 -0
  42. demangle-0.3.0/src/demangle/core/reader.py +247 -0
  43. demangle-0.3.0/src/demangle/core/registry.py +221 -0
  44. demangle-0.3.0/src/demangle/core/spelling.py +470 -0
  45. demangle-0.3.0/src/demangle/core/style.py +192 -0
  46. demangle-0.3.0/src/demangle/filter.py +226 -0
  47. demangle-0.3.0/src/demangle/py.typed +0 -0
  48. demangle-0.3.0/src/demangle/schemes/__init__.py +7 -0
  49. demangle-0.3.0/src/demangle/schemes/ada/__init__.py +175 -0
  50. demangle-0.3.0/src/demangle/schemes/ada/_parser.py +313 -0
  51. demangle-0.3.0/src/demangle/schemes/ada/nodes.py +89 -0
  52. demangle-0.3.0/src/demangle/schemes/codewarrior/__init__.py +215 -0
  53. demangle-0.3.0/src/demangle/schemes/codewarrior/_parser.py +583 -0
  54. demangle-0.3.0/src/demangle/schemes/codewarrior/nodes.py +131 -0
  55. demangle-0.3.0/src/demangle/schemes/codewarrior/options.py +33 -0
  56. demangle-0.3.0/src/demangle/schemes/d/__init__.py +118 -0
  57. demangle-0.3.0/src/demangle/schemes/d/_parser.py +1616 -0
  58. demangle-0.3.0/src/demangle/schemes/d/nodes.py +93 -0
  59. demangle-0.3.0/src/demangle/schemes/delphi/__init__.py +79 -0
  60. demangle-0.3.0/src/demangle/schemes/delphi/_parser.py +897 -0
  61. demangle-0.3.0/src/demangle/schemes/delphi/nodes.py +128 -0
  62. demangle-0.3.0/src/demangle/schemes/gnuv2/__init__.py +325 -0
  63. demangle-0.3.0/src/demangle/schemes/gnuv2/_parser.py +2487 -0
  64. demangle-0.3.0/src/demangle/schemes/gnuv2/nodes.py +145 -0
  65. demangle-0.3.0/src/demangle/schemes/gnuv2/options.py +54 -0
  66. demangle-0.3.0/src/demangle/schemes/go/__init__.py +127 -0
  67. demangle-0.3.0/src/demangle/schemes/go/_parser.py +278 -0
  68. demangle-0.3.0/src/demangle/schemes/go/nodes.py +138 -0
  69. demangle-0.3.0/src/demangle/schemes/itanium/__init__.py +61 -0
  70. demangle-0.3.0/src/demangle/schemes/itanium/options.py +332 -0
  71. demangle-0.3.0/src/demangle/schemes/itanium/parser.py +5456 -0
  72. demangle-0.3.0/src/demangle/schemes/itanium/substitutions.py +362 -0
  73. demangle-0.3.0/src/demangle/schemes/itanium/tables.py +376 -0
  74. demangle-0.3.0/src/demangle/schemes/jni/__init__.py +96 -0
  75. demangle-0.3.0/src/demangle/schemes/jni/_parser.py +226 -0
  76. demangle-0.3.0/src/demangle/schemes/jni/nodes.py +91 -0
  77. demangle-0.3.0/src/demangle/schemes/msvc/__init__.py +291 -0
  78. demangle-0.3.0/src/demangle/schemes/msvc/_parser.py +2024 -0
  79. demangle-0.3.0/src/demangle/schemes/msvc/nodes.py +450 -0
  80. demangle-0.3.0/src/demangle/schemes/msvc/options.py +140 -0
  81. demangle-0.3.0/src/demangle/schemes/nim/__init__.py +81 -0
  82. demangle-0.3.0/src/demangle/schemes/nim/_parser.py +470 -0
  83. demangle-0.3.0/src/demangle/schemes/nim/nodes.py +81 -0
  84. demangle-0.3.0/src/demangle/schemes/objc/__init__.py +119 -0
  85. demangle-0.3.0/src/demangle/schemes/objc/_parser.py +823 -0
  86. demangle-0.3.0/src/demangle/schemes/objc/nodes.py +122 -0
  87. demangle-0.3.0/src/demangle/schemes/pascal/__init__.py +85 -0
  88. demangle-0.3.0/src/demangle/schemes/pascal/_parser.py +428 -0
  89. demangle-0.3.0/src/demangle/schemes/pascal/nodes.py +131 -0
  90. demangle-0.3.0/src/demangle/schemes/rust/__init__.py +244 -0
  91. demangle-0.3.0/src/demangle/schemes/rust/_dispatch.py +85 -0
  92. demangle-0.3.0/src/demangle/schemes/rust/_legacy.py +266 -0
  93. demangle-0.3.0/src/demangle/schemes/rust/_v0.py +1867 -0
  94. demangle-0.3.0/src/demangle/schemes/rust/nodes.py +194 -0
  95. demangle-0.3.0/src/demangle/schemes/rust/options.py +36 -0
  96. demangle-0.3.0/src/demangle/schemes/swift/__init__.py +247 -0
  97. demangle-0.3.0/src/demangle/schemes/swift/_demangler.py +3224 -0
  98. demangle-0.3.0/src/demangle/schemes/swift/_node.py +644 -0
  99. demangle-0.3.0/src/demangle/schemes/swift/_old_demangler.py +1525 -0
  100. demangle-0.3.0/src/demangle/schemes/swift/_printer.py +2737 -0
  101. demangle-0.3.0/src/demangle/schemes/swift/_punycode.py +116 -0
  102. demangle-0.3.0/src/demangle/schemes/swift/nodes.py +143 -0
  103. demangle-0.3.0/src/demangle/schemes/swift/options.py +155 -0
  104. demangle-0.3.0/src/demangle/schemes/swift/resolve.py +577 -0
  105. demangle-0.3.0/src/demangle/schemes/swift/symbolic.py +201 -0
  106. demangle-0.3.0/tests/__init__.py +0 -0
  107. demangle-0.3.0/tests/conformance/ada-libiberty.txt +45 -0
  108. demangle-0.3.0/tests/conformance/ada-real-world.txt +1449 -0
  109. demangle-0.3.0/tests/conformance/codewarrior-cwdemangle.txt +60 -0
  110. demangle-0.3.0/tests/conformance/d-libiberty.txt +382 -0
  111. demangle-0.3.0/tests/conformance/d-real-world.txt +1268 -0
  112. demangle-0.3.0/tests/conformance/delphi-constructs.txt +93 -0
  113. demangle-0.3.0/tests/conformance/delphi-real-world.txt +79 -0
  114. demangle-0.3.0/tests/conformance/delphi-refusals.txt +28 -0
  115. demangle-0.3.0/tests/conformance/delphi-tdump.txt +11374 -0
  116. demangle-0.3.0/tests/conformance/gnuv2-libiberty.txt +671 -0
  117. demangle-0.3.0/tests/conformance/gnuv2-real-world.txt +12674 -0
  118. demangle-0.3.0/tests/conformance/go-real-world.txt +1532 -0
  119. demangle-0.3.0/tests/conformance/itanium-libcxxabi.txt.gz +0 -0
  120. demangle-0.3.0/tests/conformance/itanium-libstdcxx.txt +5923 -0
  121. demangle-0.3.0/tests/conformance/itanium-real-world-gnu.txt +325 -0
  122. demangle-0.3.0/tests/conformance/itanium-real-world.txt +332 -0
  123. demangle-0.3.0/tests/conformance/itanium-reference-defects.txt +273 -0
  124. demangle-0.3.0/tests/conformance/itanium-regressions.txt +43 -0
  125. demangle-0.3.0/tests/conformance/itanium-types-llvm.txt +1079 -0
  126. demangle-0.3.0/tests/conformance/itanium-types.txt +1079 -0
  127. demangle-0.3.0/tests/conformance/jni-real-world.txt +59 -0
  128. demangle-0.3.0/tests/conformance/msvc-arm64ec.txt +615 -0
  129. demangle-0.3.0/tests/conformance/msvc-boost.txt +5854 -0
  130. demangle-0.3.0/tests/conformance/msvc-clang.txt +174 -0
  131. demangle-0.3.0/tests/conformance/msvc-dbghelp.txt +1084 -0
  132. demangle-0.3.0/tests/conformance/msvc-llvm-corpus.txt +629 -0
  133. demangle-0.3.0/tests/conformance/msvc-name-only.txt +498 -0
  134. demangle-0.3.0/tests/conformance/msvc-reference-defects.txt +56 -0
  135. demangle-0.3.0/tests/conformance/msvc-suppressions.txt +1258 -0
  136. demangle-0.3.0/tests/conformance/msvc-type-descriptors.txt +111 -0
  137. demangle-0.3.0/tests/conformance/nim-lossy.txt +17 -0
  138. demangle-0.3.0/tests/conformance/nim-real-world.txt +2123 -0
  139. demangle-0.3.0/tests/conformance/objc-lossy.txt +34 -0
  140. demangle-0.3.0/tests/conformance/objc-real-world.txt +2675 -0
  141. demangle-0.3.0/tests/conformance/objc-refusals.txt +116 -0
  142. demangle-0.3.0/tests/conformance/pascal-real-world.txt +3907 -0
  143. demangle-0.3.0/tests/conformance/pascal-refusals.txt +759 -0
  144. demangle-0.3.0/tests/conformance/rust-real-world.txt +5329 -0
  145. demangle-0.3.0/tests/conformance/rust-toolchain.txt +404 -0
  146. demangle-0.3.0/tests/conformance/rustc-upstream.txt +59 -0
  147. demangle-0.3.0/tests/conformance/swift-real-world.txt +8500 -0
  148. demangle-0.3.0/tests/conformance/swift-reference-defects.txt +40 -0
  149. demangle-0.3.0/tests/conformance/swift-refusals.txt +74 -0
  150. demangle-0.3.0/tests/conformance/swift-simplified.txt +224 -0
  151. demangle-0.3.0/tests/conformance/swift-symbolic.txt +279 -0
  152. demangle-0.3.0/tests/conformance/swift-upstream.txt +520 -0
  153. demangle-0.3.0/tests/conftest.py +67 -0
  154. demangle-0.3.0/tests/corpus/msvc.txt +620 -0
  155. demangle-0.3.0/tests/test_ada.py +320 -0
  156. demangle-0.3.0/tests/test_address_of.py +133 -0
  157. demangle-0.3.0/tests/test_api.py +347 -0
  158. demangle-0.3.0/tests/test_api_surface.py +473 -0
  159. demangle-0.3.0/tests/test_architecture.py +445 -0
  160. demangle-0.3.0/tests/test_cli.py +576 -0
  161. demangle-0.3.0/tests/test_codewarrior.py +286 -0
  162. demangle-0.3.0/tests/test_concurrency.py +207 -0
  163. demangle-0.3.0/tests/test_conformance.py +834 -0
  164. demangle-0.3.0/tests/test_core.py +366 -0
  165. demangle-0.3.0/tests/test_d.py +1391 -0
  166. demangle-0.3.0/tests/test_delphi.py +321 -0
  167. demangle-0.3.0/tests/test_docs.py +243 -0
  168. demangle-0.3.0/tests/test_expressions.py +2241 -0
  169. demangle-0.3.0/tests/test_gnu_expressions.py +963 -0
  170. demangle-0.3.0/tests/test_gnuv2.py +585 -0
  171. demangle-0.3.0/tests/test_go.py +379 -0
  172. demangle-0.3.0/tests/test_itanium_apple_auto.py +126 -0
  173. demangle-0.3.0/tests/test_itanium_closure_prefix.py +270 -0
  174. demangle-0.3.0/tests/test_jni.py +262 -0
  175. demangle-0.3.0/tests/test_limits.py +397 -0
  176. demangle-0.3.0/tests/test_local_names.py +173 -0
  177. demangle-0.3.0/tests/test_msvc.py +1776 -0
  178. demangle-0.3.0/tests/test_msvc_arm64ec.py +102 -0
  179. demangle-0.3.0/tests/test_msvc_ast.py +162 -0
  180. demangle-0.3.0/tests/test_msvc_descriptors.py +186 -0
  181. demangle-0.3.0/tests/test_msvc_options.py +483 -0
  182. demangle-0.3.0/tests/test_nim.py +323 -0
  183. demangle-0.3.0/tests/test_objc.py +290 -0
  184. demangle-0.3.0/tests/test_parity.py +168 -0
  185. demangle-0.3.0/tests/test_pascal.py +340 -0
  186. demangle-0.3.0/tests/test_readme.py +233 -0
  187. demangle-0.3.0/tests/test_robustness.py +533 -0
  188. demangle-0.3.0/tests/test_rust.py +368 -0
  189. demangle-0.3.0/tests/test_rust_ast.py +149 -0
  190. demangle-0.3.0/tests/test_rust_consts.py +241 -0
  191. demangle-0.3.0/tests/test_signature.py +446 -0
  192. demangle-0.3.0/tests/test_special_names.py +310 -0
  193. demangle-0.3.0/tests/test_substitution_parameters.py +373 -0
  194. demangle-0.3.0/tests/test_swift.py +803 -0
  195. demangle-0.3.0/tests/test_swift_simplified.py +172 -0
  196. demangle-0.3.0/tests/test_swift_symbolic.py +443 -0
  197. demangle-0.3.0/tests/test_types.py +1055 -0
  198. demangle-0.3.0/tools/corpus_sources/features.cpp +96 -0
  199. demangle-0.3.0/tools/corpus_sources/go/go.mod +3 -0
  200. demangle-0.3.0/tools/corpus_sources/go/main.go +18 -0
  201. demangle-0.3.0/tools/corpus_sources/go/plain/lib.go +3 -0
  202. demangle-0.3.0/tools/corpus_sources/go/v2.5/lib.go +37 -0
  203. demangle-0.3.0/tools/corpus_sources/go/weird.pkg.name/lib.go +5 -0
  204. demangle-0.3.0/tools/corpus_sources/modern.cpp +153 -0
  205. demangle-0.3.0/tools/corpus_sources/modern23.cpp +75 -0
  206. demangle-0.3.0/tools/corpus_sources/msvc/modern.cpp +158 -0
  207. demangle-0.3.0/tools/corpus_sources/msvc/msvc.cpp +235 -0
  208. demangle-0.3.0/tools/corpus_sources/reference_defects/README.md +37 -0
  209. demangle-0.3.0/tools/corpus_sources/reference_defects/auto_marshall.cpp +39 -0
  210. demangle-0.3.0/tools/corpus_sources/reference_defects/closure_prefix.cpp +18 -0
  211. demangle-0.3.0/tools/corpus_sources/reference_defects/division.cpp +4 -0
  212. demangle-0.3.0/tools/corpus_sources/reference_defects/generic_lambda.cpp +10 -0
  213. demangle-0.3.0/tools/corpus_sources/reference_defects/inheriting_constructor.cpp +27 -0
  214. demangle-0.3.0/tools/corpus_sources/reference_defects/insort.cpp +28 -0
  215. demangle-0.3.0/tools/corpus_sources/reference_defects/insort_composite.cpp +31 -0
  216. demangle-0.3.0/tools/corpus_sources/reference_defects/member_template_lambda.cpp +29 -0
  217. demangle-0.3.0/tools/corpus_sources/reference_defects/prepare_execution.cpp +27 -0
  218. demangle-0.3.0/tools/corpus_sources/reference_defects/template_template_param.cpp +25 -0
  219. demangle-0.3.0/tools/corpus_sources/reference_defects/value_init_new.cpp +4 -0
  220. demangle-0.3.0/tools/corpus_sources/rust/consts.rs +41 -0
  221. demangle-0.3.0/tools/corpus_sources/rust/features.rs +562 -0
  222. demangle-0.3.0/tools/corpus_sources/rust/library.rs +322 -0
  223. demangle-0.3.0/tools/corpus_sources/rust/types.rs +200 -0
  224. demangle-0.3.0/tools/cplus-dem-reference/README.md +79 -0
  225. demangle-0.3.0/tools/cplus-dem-reference/SHA256SUMS +17 -0
  226. demangle-0.3.0/tools/cplus-dem-reference/build.sh +57 -0
  227. demangle-0.3.0/tools/cplus-dem-reference/main.c +54 -0
  228. demangle-0.3.0/tools/differential.py +453 -0
  229. demangle-0.3.0/tools/enumerate.py +1576 -0
  230. demangle-0.3.0/tools/generate_ada_corpus.py +115 -0
  231. demangle-0.3.0/tools/generate_corpus.py +192 -0
  232. demangle-0.3.0/tools/generate_d_corpus.py +91 -0
  233. demangle-0.3.0/tools/generate_delphi_corpus.py +256 -0
  234. demangle-0.3.0/tools/generate_go_corpus.py +177 -0
  235. demangle-0.3.0/tools/generate_msvc_dbghelp_corpus.py +594 -0
  236. demangle-0.3.0/tools/generate_rust_corpus.py +173 -0
  237. demangle-0.3.0/tools/invariants.py +169 -0
  238. demangle-0.3.0/tools/mutate.py +484 -0
  239. demangle-0.3.0/tools/probe_substitutions.py +53 -0
  240. demangle-0.3.0/tools/rustc-demangle-reference/Cargo.lock +16 -0
  241. demangle-0.3.0/tools/rustc-demangle-reference/Cargo.toml +11 -0
  242. demangle-0.3.0/tools/rustc-demangle-reference/README.md +16 -0
  243. demangle-0.3.0/tools/rustc-demangle-reference/src/main.rs +32 -0
  244. demangle-0.3.0/tools/sweep_rust_chars.py +113 -0
  245. demangle-0.3.0/tools/swift-demangle-reference/README.md +102 -0
  246. demangle-0.3.0/tools/swift-demangle-reference/build.sh +74 -0
  247. demangle-0.3.0/tools/swift-demangle-reference/main.cpp +59 -0
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .ty_cache/
12
+ site/
13
+ .coverage
14
+ htmlcov/
15
+ *.o
16
+ tools/rustc-demangle-reference/target/
17
+ tools/swift-demangle-reference/swift/
18
+ tools/swift-demangle-reference/build/
19
+ tools/cplus-dem-reference/gcc/
20
+ tools/cplus-dem-reference/build/
@@ -0,0 +1,210 @@
1
+ # Architecture
2
+
3
+ `demangle` turns a mangled symbol name back into the declaration a programmer wrote.
4
+ It supports several unrelated mangling schemes, is written in pure Python with no
5
+ dependencies, and is meant to be embedded in tools that process millions of symbols.
6
+
7
+ Those three facts pull in different directions, and the architecture exists to resolve
8
+ them.
9
+
10
+ ## The central problem
11
+
12
+ A demangler has two plausible shapes and each is wrong on its own.
13
+
14
+ **Shape one: parse straight to a string.** Fast, small, and what nearly every existing
15
+ demangler does. But the caller gets an opaque string. A tool that wants the namespace,
16
+ the template arguments, or the parameter types has to re-parse the output with a
17
+ regular expression -- and C++ declaration syntax is not a language you can pull apart
18
+ with regular expressions.
19
+
20
+ **Shape two: parse to an abstract syntax tree.** Structured and inspectable, but it
21
+ allocates an object per grammar node. In Python that cost is not theoretical: on a
22
+ binary with 400,000 symbols it is the difference between seconds and minutes, and most
23
+ callers only ever wanted the string.
24
+
25
+ ## The resolution: parsers write to a builder
26
+
27
+ No parser in this codebase constructs its own output. Each one is written against the
28
+ `Builder` protocol in `core/builder.py` and calls methods like `builder.pointer(inner)`
29
+ or `builder.template(name, args)` as it recognises grammar productions. What those
30
+ calls *produce* is the builder's business, not the parser's.
31
+
32
+ Two builders ship:
33
+
34
+ | Builder | Produces | Use |
35
+ |---|---|---|
36
+ | `SpellingBuilder` | `Spelling` pairs, immediately concatenable | `demangle()` -- the hot path |
37
+ | `AstBuilder` | `Node` trees | `parse()` -- structured access |
38
+
39
+ The parser is written once and stays honest about the grammar; the cost model is chosen
40
+ by the caller. Adding a third backend -- emitting JSON, or a token stream for a
41
+ syntax highlighter -- means writing one class and touching no parser.
42
+
43
+ This is the single most important thing to understand about the codebase. A change that
44
+ makes a parser build strings directly, however locally convenient, breaks it.
45
+
46
+ ### Expressions
47
+
48
+ Expressions were the last place the rule did not hold: the parser assembled
49
+ `f"sizeof ({...})"` itself, so an expression inside a type reached the tree as one opaque
50
+ node. They now go through `Builder.expression(form, parts)`, where `parts` interleaves the
51
+ production's fixed text with its operands' handles and `form` names the shape — `binary`,
52
+ `conditional`, `call`, `sizeof`.
53
+
54
+ One method rather than one per operator. Fifteen methods would be fifteen things every
55
+ future builder must implement, and would still not cover the next operator someone
56
+ mangles. The parser already owns operator *spelling* — it comes from `tables.py`, which
57
+ exists to be checked against the ABI — so what is left for the builder to decide is
58
+ structure, and `form` plus operands is that.
59
+
60
+ Brackets are parts like any other. Whether an operand needs them is a precedence question
61
+ only the parser can answer, so it arrives settled; a consumer walking the tree sees a
62
+ `paren` expression wrapping the operand rather than punctuation glued into a string.
63
+
64
+ Rust reaches the same place by a different route, and the difference is worth knowing.
65
+ Its printer emits one linear stream of fragments and a *sink* decides what to do with
66
+ them: `TextSink` concatenates, `TreeSink` remembers where each production began and
67
+ ended. So the tree is not a second traversal that has to be kept in step with the text —
68
+ it is the same traversal with its boundaries kept, and a tree renders to what
69
+ `demangle()` returns by construction rather than by test. Rust can do this because its
70
+ spelling is strictly left to right; a C-family declarator, which wraps the name it
71
+ declares, cannot be recovered from a linear stream and needs the builder proper.
72
+
73
+ ### When a scheme needs its own spelling
74
+
75
+ `core/spelling.py` implements *C-family* declarator placement. A scheme whose output
76
+ looks like a C declaration uses it and gets the hard part for free. A scheme whose output
77
+ does not — MSVC, where the calling convention sits inside the parentheses and the spacing
78
+ rules differ — supplies its own node kinds and renderer instead, subclassing `core.ast.Node`
79
+ so `walk()`, `find()` and `spell()` keep working. `AstBuilder` is shared regardless.
80
+
81
+ That is the extension point, not a workaround: forcing every scheme through one
82
+ renderer would mean MSVC-shaped branches inside `core`.
83
+
84
+ ## Layout
85
+
86
+ ```
87
+ demangle/
88
+ api.py demangle(), parse(), detect() -- the public surface
89
+ _signature.py signature(): the parts of a name rather than its spelling
90
+ filter.py demangling the symbols out of text that is not only symbols
91
+ cli.py the `demangle` command
92
+ core/
93
+ reader.py a bounds-checked cursor; the only input primitive parsers use
94
+ builder.py the Builder protocol -- the contract between parser and output
95
+ spelling.py SpellingBuilder: C++ declarator placement, the fast path
96
+ ast.py AstBuilder and the Node hierarchy
97
+ errors.py the exception hierarchy
98
+ limits.py the bounds a parser reads before it recurses or emits
99
+ style.py named styles, and composing one for a single call
100
+ decorations.py what a symbol table adds around a name, which belongs to no scheme
101
+ plugin.py LanguagePlugin: the contract a scheme implements
102
+ cache.py bounded memoisation
103
+ registry.py language discovery, including third-party plugins
104
+ schemes/
105
+ itanium/ the Itanium C++ ABI (GCC, Clang, and everything that follows them)
106
+ msvc/ Microsoft's scheme, as UnDecorateSymbolName reverses it
107
+ rust/ Rust legacy (_ZN) and v0 (_R)
108
+ swift/ Swift, both the current mangling and Swift 3's
109
+ d/ D, as GNU binutils reverses it
110
+ go/ Go package paths and receivers
111
+ nim/ Nim, whose symbols are ordinary C identifiers
112
+ objc/ Objective-C
113
+ pascal/ Free Pascal
114
+ delphi/ Borland/Embarcadero Delphi and C++Builder
115
+ gnuv2/ pre-Itanium C++: g++ before 3.0, cfront/ARM, Lucid, HP aCC, EDG
116
+ codewarrior/ Metrowerks CodeWarrior, the other pre-Itanium C++ mangling
117
+ ada/ Ada, as GNAT encodes it
118
+ jni/ the C function a Java `native` method is called through
119
+ ```
120
+
121
+ `core` never imports from `schemes`; `schemes/*` never import from each other. Both are
122
+ enforced by a test — including a test that the enforcement itself fires on a constructed
123
+ violation, because the first version of the rule had a hole in it and looked fine.
124
+
125
+ One exception, and it is in the test: `core/style.py` names the built-in option objects
126
+ inside a function body, so the import is lazy and cycle-free.
127
+
128
+ ## Why declarator placement is its own module
129
+
130
+ C++ does not spell a type before the name; it spells it *around* the name. The type
131
+ "pointer to function taking char, returning int" is written `int (*)(char)`, and a
132
+ variable of that type is `int (*f)(char)` -- the name lands in a hole in the middle.
133
+
134
+ So a type under construction is not a string but a pair of strings, `left` and `right`,
135
+ and rendering is `left + declarator + right`. Every constructor in `core/spelling.py`
136
+ exists to keep that hole in the correct place through pointers, arrays, references and
137
+ nested function types. It is the part of a demangler that is most often subtly wrong,
138
+ so it is isolated, independently testable, and shared by every scheme whose output looks
139
+ like a C declaration.
140
+
141
+ ## Substitutions are load-bearing, not an optimisation
142
+
143
+ The Itanium scheme compresses repeated components into back-references: `S_`, `S0_`,
144
+ `S1_`. The numbering is implicit -- it depends entirely on which components the
145
+ *encoder* considered substitutable, and in what order. Add one entry the specification
146
+ does not, or miss one it does, and every later back-reference in the name resolves to
147
+ the wrong component.
148
+
149
+ The rules are not folklore; they are ABI section 5.1.10, and `schemes/itanium/
150
+ substitutions.py` implements them as a separate, directly testable component with the
151
+ specification quoted at each decision. Rust v0 has its own back-reference scheme with
152
+ different rules, kept in its own module for the same reason.
153
+
154
+ ## Adding a language
155
+
156
+ A language is a module under `schemes/` exposing a `LanguagePlugin`. Nothing in `core`
157
+ knows the list of schemes at import time; `core/registry.py` finds built-ins lazily and third-party
158
+ plugins through the `demangle.languages` entry-point group, so a separate distribution
159
+ can add a language without a patch to this one.
160
+
161
+ A plugin declares:
162
+
163
+ - `name` -- the identifier used in the API and on the command line
164
+ - `detect(name)` -- a cheap, allocation-free test for "is this plausibly mine?"
165
+ - `parse(name, builder)` -- the parser, written against the builder protocol
166
+
167
+ `detect` must be cheap because `demangle()` on an unknown name calls every registered
168
+ `detect` before giving up, and that path runs on every non-mangled symbol in a binary --
169
+ which, in a typical binary, is most of them.
170
+
171
+ ## Performance
172
+
173
+ Design rules, in the order they matter:
174
+
175
+ 1. **The common call must not allocate an AST.** Hence the builder protocol.
176
+ 2. **Detection precedes parsing.** A one-character prefix test rejects the majority of
177
+ real symbols before any parser starts.
178
+ 3. **Results are memoised.** Symbol tables repeat names heavily -- the same
179
+ `std::allocator<char>` appears thousands of times in one binary. Caches are bounded
180
+ so a long-running process cannot grow without limit.
181
+ 4. **`__slots__` on every hot class.** Node and Spelling instances are created in the
182
+ millions.
183
+ 5. **No regular expressions in a parse loop.** The parsers are character dispatch.
184
+
185
+ `benchmarks/` measures all of this against real symbol corpora, and the numbers are
186
+ part of the release checklist rather than a thing to check when someone complains.
187
+
188
+ ## Correctness
189
+
190
+ Correctness is defined against the reference implementations, not against our own
191
+ reading of the specifications. Each scheme has one: `llvm-cxxfilt` and GNU `c++filt` for
192
+ Itanium, `llvm-undname` for MSVC, the `rustc-demangle` crate for Rust, `c++filt
193
+ --format=dlang` for D and `--format=gnat` for Ada, Embarcadero's own unmangler for
194
+ Delphi, and -- where a distribution ships nothing that reads the mangling -- a reference
195
+ built here from the compiler's own sources, for Swift, pre-Itanium C++ and CodeWarrior.
196
+ Go, Nim, Free Pascal, Objective-C and JNI have no reference anywhere, and are held to a
197
+ property instead: re-mangling what was read has to reproduce the symbol.
198
+ [CONFORMANCE.md](CONFORMANCE.md) records what each is measured against and what the
199
+ measurement says.
200
+
201
+ `tests/conformance/` holds frozen corpora with the reference output recorded next to
202
+ each name, and the pass counts are pinned as exact numbers so that a change in either
203
+ direction has to be a deliberate edit rather than something that slips through. The
204
+ harness in `tools/` regenerates those corpora and can run a live differential against
205
+ the reference binaries when they are installed.
206
+
207
+ The contract at the boundary is deliberately narrow: `demangle()` never raises and
208
+ returns its input unchanged when it cannot do better, because a wrong expansion is
209
+ worse than a mangled name -- it matches neither spelling. Callers who need to know the
210
+ difference use `parse()`, which raises.