PyReprism 0.2.0__tar.gz → 1.0.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 (202) hide show
  1. pyreprism-1.0.0/.dockerignore +18 -0
  2. pyreprism-1.0.0/.github/pyreprism-action.sh +58 -0
  3. {pyreprism-0.2.0 → pyreprism-1.0.0}/.gitignore +1 -0
  4. {pyreprism-0.2.0 → pyreprism-1.0.0}/CHANGELOG.md +93 -1
  5. pyreprism-1.0.0/Dockerfile +36 -0
  6. {pyreprism-0.2.0 → pyreprism-1.0.0}/PKG-INFO +43 -11
  7. {pyreprism-0.2.0 → pyreprism-1.0.0}/README.md +184 -6
  8. pyreprism-1.0.0/action.yml +79 -0
  9. {pyreprism-0.2.0 → pyreprism-1.0.0}/docs/intro.rst +34 -8
  10. {pyreprism-0.2.0 → pyreprism-1.0.0}/pyproject.toml +9 -1
  11. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/__init__.py +10 -9
  12. pyreprism-1.0.0/src/PyReprism/_parallel.py +47 -0
  13. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/_tokenops.py +28 -4
  14. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/batch.py +66 -25
  15. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/cli.py +112 -16
  16. pyreprism-1.0.0/src/PyReprism/clones.py +308 -0
  17. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/engines/pygments_engine.py +24 -8
  18. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/__init__.py +64 -19
  19. pyreprism-1.0.0/src/PyReprism/languages/_scanners.py +137 -0
  20. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/actionscript.py +5 -10
  21. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/ada.py +3 -11
  22. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/apacheconf.py +5 -9
  23. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/applescript.py +5 -3
  24. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/arduino.py +0 -23
  25. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/arff.py +0 -18
  26. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/asm6502.py +5 -6
  27. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/aspnet.py +3 -31
  28. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/autohotkey.py +5 -12
  29. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/autoit.py +5 -12
  30. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/base.py +203 -38
  31. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/bash.py +2 -1
  32. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/bro.py +5 -2
  33. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/c.py +1 -1
  34. pyreprism-1.0.0/src/PyReprism/languages/cobol.py +69 -0
  35. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/coffeescript.py +5 -2
  36. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/cpp.py +1 -1
  37. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/crystal.py +4 -12
  38. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/css.py +2 -1
  39. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/docker.py +5 -11
  40. pyreprism-1.0.0/src/PyReprism/languages/dynamic.py +153 -0
  41. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/elixir.py +4 -15
  42. pyreprism-1.0.0/src/PyReprism/languages/erb.py +29 -0
  43. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/erlang.py +0 -11
  44. pyreprism-1.0.0/src/PyReprism/languages/flow.py +23 -0
  45. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/fortran.py +0 -11
  46. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/fsharp.py +0 -11
  47. pyreprism-1.0.0/src/PyReprism/languages/generated.py +158 -0
  48. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/glsl.py +5 -3
  49. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/go.py +7 -1
  50. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/graphql.py +4 -12
  51. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/groovy.py +0 -11
  52. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/hpkp.py +4 -2
  53. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/hsts.py +4 -2
  54. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/ichigojam.py +4 -9
  55. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/icon.py +4 -9
  56. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/java.py +1 -1
  57. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/javascript.py +6 -1
  58. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/jsx.py +6 -21
  59. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/less.py +1 -1
  60. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/livescript.py +5 -2
  61. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/lua.py +5 -2
  62. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/nginx.py +5 -2
  63. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/nsis.py +1 -1
  64. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/objectivec.py +0 -12
  65. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/plsql.py +3 -5
  66. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/properties.py +3 -4
  67. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/pug.py +3 -4
  68. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/python.py +22 -1
  69. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/qore.py +4 -1
  70. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/roboconf.py +3 -4
  71. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/scheme.py +4 -13
  72. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/scss.py +3 -4
  73. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/smalltalk.py +0 -12
  74. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/sql.py +1 -1
  75. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/stylus.py +0 -11
  76. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/tcl.py +4 -2
  77. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/tsx.py +6 -12
  78. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/typescript.py +6 -1
  79. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/yaml.py +3 -4
  80. pyreprism-1.0.0/src/PyReprism/reporters.py +449 -0
  81. pyreprism-1.0.0/src/PyReprism/semantic.py +147 -0
  82. pyreprism-1.0.0/src/PyReprism/syntactic.py +163 -0
  83. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/tokens.py +6 -3
  84. pyreprism-0.2.0/src/PyReprism/languages/erb.py +0 -46
  85. pyreprism-0.2.0/src/PyReprism/languages/flow.py +0 -30
  86. {pyreprism-0.2.0 → pyreprism-1.0.0}/.flake8 +0 -0
  87. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/FUNDING.yaml +0 -0
  88. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  89. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  90. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  91. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/scripts/changelog_notes.py +0 -0
  92. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/scripts/package_version.py +0 -0
  93. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/workflows/ci.yml +0 -0
  94. {pyreprism-0.2.0 → pyreprism-1.0.0}/.github/workflows/publish.yml +0 -0
  95. {pyreprism-0.2.0 → pyreprism-1.0.0}/.pre-commit-hooks.yaml +0 -0
  96. {pyreprism-0.2.0 → pyreprism-1.0.0}/.readthedocs.yaml +0 -0
  97. {pyreprism-0.2.0 → pyreprism-1.0.0}/CONTRIBUTING.md +0 -0
  98. {pyreprism-0.2.0 → pyreprism-1.0.0}/LICENSE +0 -0
  99. {pyreprism-0.2.0 → pyreprism-1.0.0}/RELEASING.md +0 -0
  100. {pyreprism-0.2.0 → pyreprism-1.0.0}/codecov.yml +0 -0
  101. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/__main__.py +0 -0
  102. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/diffs.py +0 -0
  103. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/engines/__init__.py +0 -0
  104. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/engines/base.py +0 -0
  105. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/engines/regex_engine.py +0 -0
  106. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/fingerprints.py +0 -0
  107. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/abap.py +0 -0
  108. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/apl.py +0 -0
  109. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/asciidoc.py +0 -0
  110. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/basic.py +0 -0
  111. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/batch.py +0 -0
  112. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/bison.py +0 -0
  113. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/brainfuck.py +0 -0
  114. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/clike.py +0 -0
  115. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/clojure.py +0 -0
  116. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/csharp.py +0 -0
  117. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/csp.py +0 -0
  118. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/css_extras.py +0 -0
  119. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/d.py +0 -0
  120. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/dart.py +0 -0
  121. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/diff.py +0 -0
  122. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/django.py +0 -0
  123. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/eiffel.py +0 -0
  124. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/gedcom.py +0 -0
  125. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/gherkin.py +0 -0
  126. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/git.py +0 -0
  127. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/haml.py +0 -0
  128. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/handlebars.py +0 -0
  129. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/haskell.py +0 -0
  130. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/haxe.py +0 -0
  131. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/html.py +0 -0
  132. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/inform7.py +0 -0
  133. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/ini.py +0 -0
  134. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/io.py +0 -0
  135. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/j.py +0 -0
  136. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/jolie.py +0 -0
  137. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/json.py +0 -0
  138. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/julia.py +0 -0
  139. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/keyman.py +0 -0
  140. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/kotlin.py +0 -0
  141. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/latex.py +0 -0
  142. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/liquid.py +0 -0
  143. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/lolcode.py +0 -0
  144. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/makefile.py +0 -0
  145. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/markdown.py +0 -0
  146. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/markup.py +0 -0
  147. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/markup_templating.py +0 -0
  148. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/matlab.py +0 -0
  149. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/mel.py +0 -0
  150. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/mizar.py +0 -0
  151. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/monkey.py +0 -0
  152. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/n4js.py +0 -0
  153. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/nasm.py +0 -0
  154. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/nim.py +0 -0
  155. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/nix.py +0 -0
  156. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/ocaml.py +0 -0
  157. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/opencl.py +0 -0
  158. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/oz.py +0 -0
  159. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/parigp.py +0 -0
  160. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/parser.py +0 -0
  161. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/pascal.py +0 -0
  162. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/perl.py +0 -0
  163. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/php.py +0 -0
  164. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/php_extras.py +0 -0
  165. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/powershell.py +0 -0
  166. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/processing.py +0 -0
  167. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/prolog.py +0 -0
  168. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/protobuf.py +0 -0
  169. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/puppet.py +0 -0
  170. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/pure.py +0 -0
  171. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/q.py +0 -0
  172. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/r.py +0 -0
  173. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/reason.py +0 -0
  174. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/registry.py +0 -0
  175. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/renpy.py +0 -0
  176. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/rest.py +0 -0
  177. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/rip.py +0 -0
  178. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/ruby.py +0 -0
  179. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/rust.py +0 -0
  180. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/sas.py +0 -0
  181. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/sass.py +0 -0
  182. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/scala.py +0 -0
  183. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/smarty.py +0 -0
  184. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/soy.py +0 -0
  185. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/swift.py +0 -0
  186. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/textile.py +0 -0
  187. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/twig.py +0 -0
  188. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/vbnet.py +0 -0
  189. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/velocity.py +0 -0
  190. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/verilog.py +0 -0
  191. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/vhdl.py +0 -0
  192. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/vim.py +0 -0
  193. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/visual_basic.py +0 -0
  194. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/wasam.py +0 -0
  195. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/xeora.py +0 -0
  196. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/languages/xojo.py +0 -0
  197. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/metrics.py +0 -0
  198. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/ngrams.py +0 -0
  199. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/py.typed +0 -0
  200. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/utils/__init__.py +0 -0
  201. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/utils/extension.py +0 -0
  202. {pyreprism-0.2.0 → pyreprism-1.0.0}/src/PyReprism/utils/normalizer.py +0 -0
@@ -0,0 +1,18 @@
1
+ # Keep the build context small: only pyproject.toml, LICENSE, docs/intro.rst,
2
+ # and src/ are needed to build the wheel (see Dockerfile).
3
+ .git
4
+ .github
5
+ manuscript
6
+ tests
7
+ venv
8
+ .venv
9
+ docs/*
10
+ !docs/intro.rst
11
+ *.egg-info
12
+ __pycache__
13
+ .pytest_cache
14
+ .mypy_cache
15
+ dist
16
+ build
17
+ *.pyc
18
+ .DS_Store
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # Entry point for the "PyReprism Clone Gate" composite action (see action.yml).
3
+ # Installs PyReprism from the action's own checked-out source (so the CLI matches
4
+ # the pinned ref), runs `pyreprism clones` with the configured inputs, writes the
5
+ # report, and propagates the duplication-gate exit code.
6
+ set -euo pipefail
7
+
8
+ action_path="${GITHUB_ACTION_PATH:-$(cd "$(dirname "$0")/.." && pwd)}"
9
+
10
+ extras="accurate"
11
+ if [ "${INPUT_SEMANTIC:-false}" = "true" ]; then
12
+ extras="accurate,ast,semantic"
13
+ fi
14
+
15
+ venv="${RUNNER_TEMP:-/tmp}/pyreprism-venv"
16
+ echo "::group::Install PyReprism [${extras}]"
17
+ python3 -m venv "$venv"
18
+ "$venv/bin/python" -m pip install --quiet --upgrade pip
19
+ "$venv/bin/python" -m pip install --quiet "${action_path}[${extras}]"
20
+ "$venv/bin/pyreprism" --version
21
+ echo "::endgroup::"
22
+
23
+ # Build the `pyreprism clones` argument list from the inputs.
24
+ args=(clones)
25
+ # Intentionally word-split PATHS and EXTRA_ARGS (space-separated inputs).
26
+ # shellcheck disable=SC2206
27
+ args+=(${INPUT_PATHS:-.})
28
+ args+=(--reporter "${INPUT_REPORTER:-sarif}")
29
+ args+=(--min-tokens "${INPUT_MIN_TOKENS:-50}" --min-lines "${INPUT_MIN_LINES:-5}")
30
+ [ -n "${INPUT_LANG:-}" ] && args+=(--lang "$INPUT_LANG")
31
+ [ "${INPUT_NEAR:-false}" = "true" ] && args+=(--near)
32
+ [ "${INPUT_SEMANTIC:-false}" = "true" ] && args+=(--semantic)
33
+ [ -n "${INPUT_MAX_DUPLICATION:-}" ] && args+=(--max-duplication "$INPUT_MAX_DUPLICATION")
34
+ # shellcheck disable=SC2206
35
+ [ -n "${INPUT_EXTRA_ARGS:-}" ] && args+=(${INPUT_EXTRA_ARGS})
36
+
37
+ out="${INPUT_OUTPUT:-}"
38
+ echo "::group::pyreprism ${args[*]}"
39
+ set +e
40
+ if [ -n "$out" ]; then
41
+ "$venv/bin/pyreprism" "${args[@]}" > "$out"
42
+ code=$?
43
+ cat "$out"
44
+ else
45
+ "$venv/bin/pyreprism" "${args[@]}"
46
+ code=$?
47
+ fi
48
+ set -e
49
+ echo "::endgroup::"
50
+
51
+ if [ -n "$out" ]; then
52
+ echo "report=$out" >> "${GITHUB_OUTPUT:-/dev/null}"
53
+ fi
54
+
55
+ if [ "$code" -ne 0 ]; then
56
+ echo "::error::PyReprism duplication gate failed (exit ${code})."
57
+ fi
58
+ exit "$code"
@@ -169,3 +169,4 @@ __test__.py
169
169
  .DS_Store
170
170
  # Claude Code agent workspace
171
171
  .claude/
172
+ manuscript/replication/
@@ -7,6 +7,97 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.0.0] - 2026-10-12
11
+
12
+ The first stable release. The public API (the top-level functions, the
13
+ language classes, `Token`/`TokenType`, `CodeStats`, and the `batch`, `diffs`,
14
+ `fingerprints`, `ngrams`, `clones`, `reporters`, `syntactic`, and `semantic`
15
+ modules) and the `pyreprism` command line now follow semantic versioning:
16
+ breaking changes only in a new major version. This release adds located clone
17
+ detection with CI reports, optional syntactic and semantic tiers, and parallel
18
+ processing; makes tokenization several times faster; and fixes the lexer
19
+ defects found by validating token classes against tree-sitter parses on a
20
+ project-stratified sample of open-source code.
21
+
22
+ ### Added
23
+ - **Located clones and reports** (`PyReprism.clones`, `PyReprism.reporters`):
24
+ `clones.detect()` finds which line ranges are duplicated between files
25
+ (Rabin-Karp over the normalized token stream, with jscpd-style
26
+ `min_tokens`/`min_lines` floors), optionally adds Type-3 near-duplicate files
27
+ (`near=True`) and Type-4 semantic clones (`semantic=True`), and reports the
28
+ percentage of duplicated lines. Reporters render console, JSON, SARIF (one
29
+ rule id per clone kind), Markdown, and a self-contained HTML page. CLI:
30
+ `pyreprism clones --reporter ... --max-duplication PCT` (non-zero exit above
31
+ the threshold, for CI).
32
+ - **Syntactic tier** (`PyReprism.syntactic`, `pip install "PyReprism[ast]"`):
33
+ tree-sitter ASTs for exact cyclomatic complexity and per-function metrics
34
+ (`cyclomatic()`, `functions()`).
35
+ - **Semantic tier** (`PyReprism.semantic`, `pip install "PyReprism[semantic]"`):
36
+ code embeddings for Type-4 clone detection (`embed()`, `similarity()`, and an
37
+ `EmbeddingIndex` mirroring `FingerprintIndex`).
38
+ - **GitHub Action** (`action.yml`, "PyReprism Clone Gate") and a `Dockerfile`
39
+ for the CLI, to gate pull requests on duplication.
40
+ - Parallel processing: `jobs=` on `batch.analyze`, `batch.transform`, and
41
+ `clones.detect`, and a new `batch.map_files(paths, fn, jobs=...)` that runs any
42
+ per-file function on all cores. `pyreprism scan` and `pyreprism clones` take
43
+ `-j/--jobs` and use one process per CPU by default (`-j 1` for one process).
44
+ Reports are identical for any number of jobs.
45
+ - Secondary file extensions map to their language: `.h` (C); `.cc`, `.cxx`,
46
+ `.hpp`, `.hh`, `.hxx` (C++); `.mjs`, `.cjs`, `.mts`, `.cts`; `.pyi`; `.bash`,
47
+ `.zsh`, `.ksh`; `.yml`; and others. These files were skipped before.
48
+
49
+ ### Changed
50
+ - Faster tokenization (about 2.7x on one core) with identical tokens: each
51
+ language's string, number, identifier, and operator patterns are joined into
52
+ one pattern and compiled once, instead of being tried one by one at every
53
+ position; line numbers are tracked incrementally. `stats` (and `scan`)
54
+ count tokens as they are classified instead of building the token list
55
+ (about 4x faster than before on one core; same results). `code_metrics` computes
56
+ Halstead measures and complexity once instead of twice. Extension lookup no
57
+ longer re-imports every language module (1.4 ms to under 1 µs per file).
58
+ - The Pygments engine is about 3x faster on large files with the same tokens:
59
+ it merged tokens by repeated string concatenation and counted each token's
60
+ line from the start of the file, both quadratic in file size.
61
+ - `Token` is now an immutable `NamedTuple` instead of a frozen dataclass, which
62
+ makes tokens cheaper to create. Fields, attribute access, and immutability
63
+ are unchanged; a `Token` now also compares equal to a plain tuple with the
64
+ same values.
65
+
66
+ ### Fixed
67
+ - `pyreprism --help` stated "160+ built-in languages"; it now says 187, matching
68
+ `pyreprism languages`. A test keeps the two in sync.
69
+ - `pyreprism clones --max-duplication`: when stdout and stderr go to the same log
70
+ (as in CI), the gate message could be written into the middle of the report's
71
+ summary line. stdout is now flushed before the gate message.
72
+ - Defects found by validating token classes against tree-sitter parses:
73
+ - `remove_comments` dropped text that a language's comment regex skips (e.g.
74
+ the `/` in `#include "api/z3.h"`, a `{` in Scala). The base implementation
75
+ now keeps it, and 20 modules that duplicated the old loop inherit it.
76
+ - C, C++, Java, and CSS strings containing `/` were split, so `//` or `/*`
77
+ inside them started a comment (Java text blocks are kept whole too).
78
+ - Python: only a triple-quoted string that starts a line is a docstring
79
+ (comment); `x = """..."""` and call arguments are strings.
80
+ - JavaScript, TypeScript, JSX, TSX: template literals are strings, and
81
+ regular-expression literals no longer start comments.
82
+ - Pygments engine: C preprocessor directives were typed as comments, and
83
+ Python docstrings as strings; both now follow the regex engine's policies.
84
+ - Strings containing comment delimiters (`"a//b"`, `"a#b"`, `'--'`) were split
85
+ in 36 more languages, starting a comment inside the string: ActionScript,
86
+ Ada, AppleScript, Asm6502, ASP.NET, AutoHotkey, AutoIt, Bro, CoffeeScript,
87
+ Crystal, Elixir, Flow, GLSL, GraphQL, HPKP, HSTS, IchigoJam, Icon, Less,
88
+ LiveScript, Lua (including `[[...]]` long strings), Nginx, NSIS, PL/SQL, Qore,
89
+ Roboconf, Scheme (including `#\;` characters), SCSS (including unquoted
90
+ `url(...)`), and SQL. Comments now follow each language's rules in Apache
91
+ config, Dockerfiles, `.properties`, and Pug (own line only), Tcl (where a
92
+ command starts), YAML (after whitespace), and ERB (`<%# %>` tags, not `#` in
93
+ markup). A test checks every language with such a string.
94
+ - Defects found on the project-stratified evaluation sample: Go raw strings
95
+ (`` `...` ``) containing `//` or `/*` became comments; in Bash, `#` inside a
96
+ word (`$#`, `${#a}`, `a#b`) started a comment; a UTF-8 byte-order mark at the
97
+ start of a file was counted as code, so the first line could not be a comment
98
+ line (both engines); and a Python triple-quoted string on the line after an
99
+ opening bracket, comma, or `=` was taken for a docstring.
100
+
10
101
  ## [0.2.0] - 2026-07-08
11
102
 
12
103
  This release adds a static code-analysis toolkit on top of the tokenizer:
@@ -112,7 +203,8 @@ ML-oriented normalization, an optional accurate backend, and batch processing.
112
203
  - Early beta releases: comment removal for an initial set of languages and the
113
204
  `Normalizer` whitespace helper.
114
205
 
115
- [Unreleased]: https://github.com/unlv-evol/PyReprism/compare/v0.2.0...HEAD
206
+ [Unreleased]: https://github.com/unlv-evol/PyReprism/compare/v1.0.0...HEAD
207
+ [1.0.0]: https://github.com/unlv-evol/PyReprism/compare/v0.2.0...v1.0.0
116
208
  [0.2.0]: https://github.com/unlv-evol/PyReprism/compare/v0.1.0...v0.2.0
117
209
  [0.1.0]: https://github.com/unlv-evol/PyReprism/releases/tag/v0.1.0
118
210
  [0.0.4]: https://github.com/unlv-evol/PyReprism/releases/tag/v0.0.4
@@ -0,0 +1,36 @@
1
+ # PyReprism CLI, containerized.
2
+ #
3
+ # docker build -t pyreprism .
4
+ # docker run --rm -v "$PWD:/work" pyreprism clones . --reporter sarif
5
+ # docker run --rm -v "$PWD:/work" pyreprism scan . --json
6
+ #
7
+ # The image ships the 187-language, zero-dependency lexical core plus the
8
+ # optional Pygments backend ([accurate], pure Python). The heavier [ast]
9
+ # (tree-sitter) and [semantic] (torch/transformers) tiers are intentionally
10
+ # left out to keep the image small; derive your own image if you need them.
11
+
12
+ # ---- build stage: produce a wheel from the source tree ----
13
+ FROM python:3.12-slim AS build
14
+ WORKDIR /src
15
+ # Only the files hatchling needs to build the wheel and read metadata.
16
+ COPY pyproject.toml LICENSE ./
17
+ COPY docs/intro.rst ./docs/intro.rst
18
+ COPY src ./src
19
+ RUN pip install --no-cache-dir build \
20
+ && python -m build --wheel --outdir /dist
21
+
22
+ # ---- runtime stage: install the wheel, drop the build tooling ----
23
+ FROM python:3.12-slim
24
+ LABEL org.opencontainers.image.title="PyReprism" \
25
+ org.opencontainers.image.description="Lossless token substrate for polyglot code analysis (CLI)" \
26
+ org.opencontainers.image.source="https://github.com/unlv-evol/PyReprism" \
27
+ org.opencontainers.image.licenses="MIT"
28
+ COPY --from=build /dist/*.whl /tmp/
29
+ RUN pip install --no-cache-dir "$(ls /tmp/*.whl)[accurate]" \
30
+ && rm -rf /tmp/*.whl /root/.cache/pip
31
+ # Run as an unprivileged user; /work is where the caller mounts their code.
32
+ RUN useradd --create-home --uid 1000 app
33
+ USER app
34
+ WORKDIR /work
35
+ ENTRYPOINT ["pyreprism"]
36
+ CMD ["--help"]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: PyReprism
3
- Version: 0.2.0
3
+ Version: 1.0.0
4
4
  Summary: Framework for Source Code Preprocessing
5
5
  Project-URL: Homepage, https://github.com/unlv-evol/PyReprism
6
6
  Project-URL: Documentation, https://pyreprism.readthedocs.io
@@ -28,7 +28,7 @@ License: MIT License
28
28
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
29
  SOFTWARE.
30
30
  License-File: LICENSE
31
- Classifier: Development Status :: 4 - Beta
31
+ Classifier: Development Status :: 5 - Production/Stable
32
32
  Classifier: Environment :: Console
33
33
  Classifier: Intended Audience :: Developers
34
34
  Classifier: Intended Audience :: Science/Research
@@ -44,6 +44,9 @@ Classifier: Programming Language :: Python :: 3.13
44
44
  Requires-Python: >=3.8
45
45
  Provides-Extra: accurate
46
46
  Requires-Dist: pygments; extra == 'accurate'
47
+ Provides-Extra: ast
48
+ Requires-Dist: tree-sitter; extra == 'ast'
49
+ Requires-Dist: tree-sitter-language-pack; extra == 'ast'
47
50
  Provides-Extra: dev
48
51
  Requires-Dist: build; extra == 'dev'
49
52
  Requires-Dist: flake8; extra == 'dev'
@@ -54,6 +57,9 @@ Requires-Dist: twine; extra == 'dev'
54
57
  Provides-Extra: docs
55
58
  Requires-Dist: sphinx; extra == 'docs'
56
59
  Requires-Dist: sphinx-rtd-theme; extra == 'docs'
60
+ Provides-Extra: semantic
61
+ Requires-Dist: torch; extra == 'semantic'
62
+ Requires-Dist: transformers; extra == 'semantic'
57
63
  Description-Content-Type: text/x-rst
58
64
 
59
65
  .. _intro_toplevel:
@@ -64,9 +70,17 @@ Overview
64
70
 
65
71
  **PyReprism** is a Python framework for source-code preprocessing. It lets you
66
72
  **match, extract, count, and remove** comments, strings, numbers, operators,
67
- keywords and other language-specific constructs across **145+ programming
68
- languages and file formats** — through a small high-level API, a command-line
69
- tool, code metrics, ML-oriented normalization, and diff/pull-request analysis.
73
+ keywords and other language-specific constructs across **187+ programming
74
+ languages and file formats** (500+ with the optional Pygments backend) — through a
75
+ small high-level API, a command-line tool, code metrics, ML-oriented
76
+ normalization, and diff/pull-request analysis.
77
+
78
+ Everything is derived from one lossless token stream, and the same substrate
79
+ anchors a **fidelity ladder** of optional-cost tiers: a zero-dependency
80
+ **lexical** tier (default), an optional **syntactic** tier (`tree-sitter ASTs
81
+ <https://pyreprism.readthedocs.io/en/latest/syntactic.html>`__ for precise per-function metrics),
82
+ and an optional **semantic** tier (`code embeddings
83
+ <https://pyreprism.readthedocs.io/en/latest/semantic.html>`__ for Type-4 clone detection).
70
84
 
71
85
  Highlights
72
86
  ==========
@@ -75,11 +89,25 @@ Highlights
75
89
  ``count_*`` / ``match_*`` for each construct, plus a lossless ``tokenize()``.
76
90
  * Code metrics (``stats``) and ML canonicalization (``normalize``) for
77
91
  clone/plagiarism detection and code-embedding pipelines.
78
- * Optional high-accuracy `Pygments`_ backend — with **zero required
79
- dependencies** by default.
80
- * Batch/corpus analysis and unified/``git`` diff processing.
92
+ * Optional high-accuracy `Pygments`_ backend and an on-demand Pygments fallback
93
+ for 500+ languages — with **zero required dependencies** by default.
94
+ * Optional `syntactic (AST) <https://pyreprism.readthedocs.io/en/latest/syntactic.html>`__ and
95
+ `semantic (embedding) <https://pyreprism.readthedocs.io/en/latest/semantic.html>`__ tiers for
96
+ precise per-function metrics and Type-4 clone detection.
97
+ * Located clone detection with console, JSON, SARIF, Markdown, and HTML
98
+ reports and a duplication gate for CI (also as a GitHub Action).
99
+ * Batch/corpus analysis on every core, and unified/``git`` diff processing.
81
100
  * A ``pyreprism`` command-line tool (also ``python -m PyReprism``).
82
101
 
102
+ Stability
103
+ =========
104
+
105
+ PyReprism 1.0 is the first stable release. From 1.0 on, releases follow
106
+ `semantic versioning`_: the public API and the ``pyreprism`` command line change
107
+ incompatibly only in a new major version. Modules whose names start with an
108
+ underscore are internal. Token classifications may still change in minor
109
+ releases when they fix a lexer defect; the changelog lists every such change.
110
+
83
111
  Requirements
84
112
  ============
85
113
 
@@ -92,11 +120,13 @@ Install
92
120
 
93
121
  pip install PyReprism
94
122
 
95
- For the higher-accuracy tokenizer backend:
123
+ The optional tiers are opt-in extras (install only what you need):
96
124
 
97
125
  .. code-block:: shell
98
126
 
99
- pip install "PyReprism[accurate]"
127
+ pip install "PyReprism[accurate]" # Pygments backend (higher lexical fidelity)
128
+ pip install "PyReprism[ast]" # syntactic tier (tree-sitter)
129
+ pip install "PyReprism[semantic]" # semantic tier (code embeddings)
100
130
 
101
131
  Quick start
102
132
  ===========
@@ -131,6 +161,7 @@ Command line
131
161
  pyreprism remove comments file.py
132
162
  pyreprism scan myproject/ --csv # aggregate code metrics over a tree
133
163
  git diff | pyreprism diff --cosmetic # flag comment/whitespace-only changes
164
+ pyreprism clones src/ --max-duplication 5 # located clones; fail above 5%
134
165
 
135
166
  Documentation
136
167
  =============
@@ -156,3 +187,4 @@ See ``CONTRIBUTING.md`` for contribution guidelines.
156
187
 
157
188
  .. _Python: https://www.python.org
158
189
  .. _Pygments: https://pygments.org
190
+ .. _semantic versioning: https://semver.org
@@ -13,12 +13,68 @@
13
13
 
14
14
  # PyReprism
15
15
 
16
- PyReprism is a Python framework that helps researchers and developers the task of source code preprocessing. With PyReprism, you can easily match, extract, count, and remove comments, whitespaces, operators, numbers and other language specific constructs from over **150 programming languages and file extensions**.
16
+ PyReprism is a Python framework that helps researchers and developers the task of source code preprocessing. With PyReprism, you can easily match, extract, count, and remove comments, whitespaces, operators, numbers and other language specific constructs from over **187 programming languages and file extensions** — and **500+** with the optional Pygments backend.
17
+
18
+ Everything is derived from one **lossless token stream**, and the same substrate
19
+ anchors a **fidelity ladder** of three optional-cost tiers: a zero-dependency
20
+ **lexical** tier (default), an optional **syntactic** tier (tree-sitter ASTs for
21
+ precise per-function metrics), and an optional **semantic** tier (code embeddings
22
+ for Type-4 clone detection). The core stays dependency-free; you climb the ladder
23
+ only when a task needs it.
17
24
 
18
25
  ## Install
19
26
  ```shell
20
- pip install PyReprism
27
+ pip install PyReprism # zero-dependency lexical core
28
+
29
+ # optional tiers (install only what you need):
30
+ pip install "PyReprism[accurate]" # Pygments backend (higher lexical fidelity)
31
+ pip install "PyReprism[ast]" # syntactic tier (tree-sitter)
32
+ pip install "PyReprism[semantic]" # semantic tier (code embeddings)
33
+ ```
34
+
35
+ ### Run with Docker
36
+
37
+ No Python install required. The image ships the 187-language lexical core plus
38
+ the Pygments backend (the heavier `[ast]`/`[semantic]` tiers are omitted to keep
39
+ it small). Mount your code at `/work`:
40
+
41
+ ```shell
42
+ docker build -t pyreprism .
43
+
44
+ # fail CI above 5% duplication, emit SARIF for code scanning
45
+ docker run --rm -v "$PWD:/work" pyreprism clones . --reporter sarif --max-duplication 5
46
+
47
+ # per-language metrics as JSON
48
+ docker run --rm -v "$PWD:/work" pyreprism scan . --json
49
+ ```
50
+
51
+ ### GitHub Action
52
+
53
+ Gate pull requests on code duplication in a few lines. The action runs
54
+ `pyreprism clones`, writes a report, and fails the job when duplication exceeds
55
+ your threshold:
56
+
57
+ ```yaml
58
+ - uses: actions/checkout@v4
59
+ - uses: unlv-evol/PyReprism@v1.0.0 # pin to a release tag or commit SHA
60
+ with:
61
+ paths: .
62
+ reporter: sarif
63
+ output: pyreprism-clones.sarif
64
+ max_duplication: 5 # fail the job above 5% duplicated lines
65
+ near: true # also flag Type-3 near-duplicate files
66
+
67
+ # optional: surface the clones in the repo's Security > Code scanning tab
68
+ - uses: github/codeql-action/upload-sarif@v3
69
+ if: always()
70
+ with:
71
+ sarif_file: pyreprism-clones.sarif
21
72
  ```
73
+
74
+ Inputs: `paths`, `reporter` (`console`/`json`/`sarif`/`markdown`/`html`),
75
+ `output`, `max_duplication`, `min_tokens`, `min_lines`, `near`, `semantic`,
76
+ `lang`, and `extra_args`.
77
+
22
78
  ## Quick Usage
23
79
 
24
80
  The simplest entry point is the top-level API. Pass `lang` as a language name
@@ -117,6 +173,52 @@ index.similar_pairs(threshold=0.7) # -> [(file_a, file_b, score), ...]
117
173
  On the CLI: `pyreprism similarity a.py b.py` and
118
174
  `pyreprism clones submissions/ --threshold 0.7`.
119
175
 
176
+ #### Located clones & reports (a jscpd-style duplication detector)
177
+
178
+ Beyond a similarity score, `PyReprism.clones` reports **which line ranges are
179
+ duplicated** between files (Rabin-Karp over the normalized token stream), and
180
+ renders a **unified report** across all three rungs of the fidelity ladder:
181
+
182
+ ```python
183
+ from PyReprism import clones, reporters
184
+
185
+ report = clones.detect("src/",
186
+ min_tokens=50, min_lines=5, # clone-size floor (jscpd-style)
187
+ near=True, # + Type-3 near-duplicate files
188
+ semantic=True) # + Type-4 semantic clones
189
+ report.duplication_pct # % duplicated lines
190
+ for c in report.clones:
191
+ print(c.kind, c.a.path, c.a.start_line, c.b.path, c.b.start_line)
192
+ # kind is 'exact' (T1/2, located), 'near' (T3), or 'semantic' (T4)
193
+
194
+ print(reporters.to_sarif(report)) # GitHub code scanning
195
+ print(reporters.to_markdown(report)) # PR comment
196
+ print(reporters.to_json(report))
197
+ print(reporters.to_html(report)) # self-contained HTML report page
198
+ ```
199
+
200
+ Every clone — **including semantic (Type-4)** ones — flows through the same
201
+ reporters, each kind tagged with its own SARIF rule id
202
+ (`pyreprism/exact-clone`, `.../near-clone`, `.../semantic-clone`), so CI can treat
203
+ lexical and semantic duplication separately. The **HTML reporter** renders a
204
+ self-contained, theme-aware page — a duplication meter with a CI-gate marker,
205
+ per-kind counts, and a filterable card per clone with located line ranges,
206
+ score, rule id, and a highlighted side-by-side snippet. On the CLI:
207
+
208
+ ```shell
209
+ pyreprism clones src/ # located exact clones (console)
210
+ pyreprism clones src/ --near --semantic # add Type-3 and Type-4
211
+ pyreprism clones src/ --reporter sarif > clones.sarif # for GitHub code scanning
212
+ pyreprism clones src/ --reporter html --max-duplication 5 > clones.html # HTML report + CI gate
213
+ pyreprism clones src/ --max-duplication 5 # exit non-zero above 5% (CI gate)
214
+ pyreprism clones src/ -j 1 # one process (default: one per CPU)
215
+ ```
216
+
217
+ Fingerprints and located clones detect Type-1/2/3. For **Type-4** clones (same
218
+ behavior, different code), `--semantic` embeds functions with the optional
219
+ [semantic tier](#higher-fidelity-tiers-syntactic-ast--semantic-embeddings) and
220
+ adds them to the same report.
221
+
120
222
  ### N-grams & code "naturalness"
121
223
 
122
224
  Token n-grams (over token text or, structurally, over token *types*) and an
@@ -173,6 +275,47 @@ installed, else regex). It works on every operation — `remove_*`, `extract_*`,
173
275
  `count_*`, `tokenize`, `normalize`, `stats`, `preprocess` — and on the CLI via
174
276
  `--engine`.
175
277
 
278
+ When Pygments is installed, a language or extension that has no built-in module
279
+ also resolves through an on-demand Pygments fallback, extending coverage to
280
+ **500+** languages (`pr.get_language(".zig")`, `pr.remove_comments(src, lang="solidity")`).
281
+ List what is reachable with `pyreprism languages` (built-in) and
282
+ `pyreprism languages --pygments` (the fallback tier).
283
+
284
+ ### Higher-fidelity tiers: syntactic (AST) & semantic (embeddings)
285
+
286
+ The lexical core is fast and broad but approximate. For tasks that need more, the
287
+ same substrate feeds two optional tiers — install only what you need.
288
+
289
+ **Syntactic tier** (`pip install "PyReprism[ast]"`) parses a real tree-sitter AST
290
+ for exact, **per-function** metrics that a token stream cannot provide:
291
+
292
+ ```python
293
+ from PyReprism import syntactic as ast
294
+
295
+ ast.cyclomatic(src, "java") # precise McCabe complexity (decision nodes)
296
+ ast.functions(src, "python") # -> [FunctionInfo(name, start_line, cyclomatic), ...]
297
+ ```
298
+
299
+ **Semantic tier** (`pip install "PyReprism[semantic]"`) embeds code with a
300
+ code-search transformer to catch **Type-4** clones (same behavior, different code)
301
+ that token fingerprints cannot — it mirrors the `FingerprintIndex` API:
302
+
303
+ ```python
304
+ from PyReprism import semantic
305
+
306
+ semantic.similarity(code_a, code_b) # cosine similarity in embedding space
307
+
308
+ idx = semantic.EmbeddingIndex()
309
+ idx.add_paths("submissions/")
310
+ idx.similar_pairs(threshold=0.8) # semantic near-duplicate pairs
311
+ idx.query(code, k=5) # k most similar indexed snippets
312
+ ```
313
+
314
+ Both tiers degrade gracefully: if the extra isn't installed, `available()` returns
315
+ `False` and the lexical core is unaffected. Use the lexical tier for screening,
316
+ the syntactic tier for precise/per-function metrics, and the semantic tier for
317
+ Type-4 clones.
318
+
176
319
  ### Batch / whole-directory processing
177
320
 
178
321
  Analyze or transform an entire source tree (junk folders like `.git`,
@@ -191,6 +334,20 @@ batch.transform("myproject/", lambda text, lang: lang.remove_comments(text),
191
334
  output="stripped/")
192
335
  ```
193
336
 
337
+ Files are independent, so these calls can use every core: pass `jobs=0` (one
338
+ process per CPU) or `jobs=N` to `batch.analyze`, `batch.transform`,
339
+ `batch.map_files`, and `clones.detect`; results are identical to a serial run.
340
+ With `jobs` other than 1, functions you pass must be picklable (defined at
341
+ module level, not lambdas):
342
+
343
+ ```python
344
+ def metrics_and_copy(text, lang): # any per-file analysis
345
+ return lang.code_metrics(text), lang.normalize(text)
346
+
347
+ for path, lang, (metrics, normalized) in batch.map_files("myproject/", metrics_and_copy, jobs=0):
348
+ ...
349
+ ```
350
+
194
351
  ### Process diffs / pull requests
195
352
 
196
353
  Parse a unified (`git`) diff and analyze the *changed code* per file, language-aware:
@@ -258,7 +415,8 @@ pyreprism normalize file.py # canonicalize for ML
258
415
  pyreprism remove comments file.py --in-place # rewrite in place
259
416
  pyreprism scan myproject/ --csv # aggregate metrics over a tree
260
417
  pyreprism remove comments src/ --output out/ # bulk-transform a directory
261
- pyreprism languages # list supported languages
418
+ pyreprism languages # list built-in languages
419
+ pyreprism languages --pygments # list the Pygments fallback tier
262
420
  ```
263
421
 
264
422
  Equivalently: `python -m PyReprism ...`.
@@ -267,9 +425,29 @@ It can also be wired into [pre-commit](https://pre-commit.com) via the bundled
267
425
  `.pre-commit-hooks.yaml` (`pyreprism-strip-comments`, `pyreprism-normalize-whitespace`).
268
426
  These hooks rewrite files, so scope them with `files:`/`exclude:`.
269
427
 
270
- Read the [docs](https://pyreprism.readthedocs.io) for more usage examples.
271
-
272
- NB: The beta versions of PyReprism is still unstable, but we are working 24/7 to ensure the tool is usable.
428
+ Read the [docs](https://pyreprism.readthedocs.io) for more usage examples.
429
+
430
+ ## Stability and versioning
431
+
432
+ PyReprism 1.0 is the first stable release. From 1.0 on, releases follow
433
+ [semantic versioning](https://semver.org): the public API (the top-level
434
+ functions, the language classes, `Token`/`TokenType`, `CodeStats`, the
435
+ `batch`, `diffs`, `fingerprints`, `ngrams`, `clones`, `reporters`, `syntactic`,
436
+ and `semantic` modules) and the `pyreprism` command line change incompatibly
437
+ only in a new major version. Modules whose names start with an underscore are
438
+ internal. Token classifications may still change in minor releases when they
439
+ fix a lexer defect; the [changelog](CHANGELOG.md) lists every such change.
440
+
441
+ ## Performance
442
+
443
+ Tokenization is a single compiled pattern per language, and directory-level
444
+ work can use every core: `pyreprism scan` and `pyreprism clones` do so by
445
+ default (`-j N` to change it), and the Python API takes `jobs=` (`jobs=0` for
446
+ one process per CPU).
447
+ On an 8-core Apple M3, `pyreprism scan` processes about 2,000 files per second;
448
+ a lexical workflow that replaces cloc, lizard, and jscpd (line counts,
449
+ complexity, and clones over the same files) runs 3.0x faster on one core
450
+ and 5.7x faster on 8 cores than the three tools together.
273
451
 
274
452
  ## How to Contribute
275
453
  We invite you to help us build this tool and make it more extensive. Contribution is open to OSS community.
@@ -0,0 +1,79 @@
1
+ name: 'PyReprism Clone Gate'
2
+ description: >-
3
+ Detect duplicate/clone code across 187 languages and fail CI above a
4
+ duplication threshold. Emits console/JSON/SARIF/Markdown/HTML reports.
5
+ author: 'UNLV EVOL Lab'
6
+ branding:
7
+ icon: 'copy'
8
+ color: 'orange'
9
+
10
+ inputs:
11
+ paths:
12
+ description: 'Paths to scan (space-separated).'
13
+ required: false
14
+ default: '.'
15
+ reporter:
16
+ description: 'Output format: console, json, sarif, markdown, or html.'
17
+ required: false
18
+ default: 'sarif'
19
+ output:
20
+ description: >-
21
+ File (relative to the workspace) to write the report to. Leave empty to
22
+ print to the job log only.
23
+ required: false
24
+ default: 'pyreprism-clones.sarif'
25
+ max_duplication:
26
+ description: >-
27
+ Fail the job if the percentage of duplicated lines exceeds this value.
28
+ Empty disables the gate.
29
+ required: false
30
+ default: ''
31
+ min_tokens:
32
+ description: 'Minimum clone size in tokens.'
33
+ required: false
34
+ default: '50'
35
+ min_lines:
36
+ description: 'Minimum clone size in lines.'
37
+ required: false
38
+ default: '5'
39
+ near:
40
+ description: 'Also report Type-3 near-duplicate files.'
41
+ required: false
42
+ default: 'false'
43
+ semantic:
44
+ description: >-
45
+ Also report Type-4 semantic clones. Installs the heavier semantic/ast
46
+ tiers, which take longer to set up.
47
+ required: false
48
+ default: 'false'
49
+ lang:
50
+ description: 'Force a single language instead of auto-detecting per file.'
51
+ required: false
52
+ default: ''
53
+ extra_args:
54
+ description: 'Additional raw arguments appended to `pyreprism clones`.'
55
+ required: false
56
+ default: ''
57
+
58
+ outputs:
59
+ report:
60
+ description: 'Path to the written report file (empty if none).'
61
+ value: ${{ steps.pyreprism.outputs.report }}
62
+
63
+ runs:
64
+ using: 'composite'
65
+ steps:
66
+ - id: pyreprism
67
+ shell: bash
68
+ run: bash "$GITHUB_ACTION_PATH/.github/pyreprism-action.sh"
69
+ env:
70
+ INPUT_PATHS: ${{ inputs.paths }}
71
+ INPUT_REPORTER: ${{ inputs.reporter }}
72
+ INPUT_OUTPUT: ${{ inputs.output }}
73
+ INPUT_MAX_DUPLICATION: ${{ inputs.max_duplication }}
74
+ INPUT_MIN_TOKENS: ${{ inputs.min_tokens }}
75
+ INPUT_MIN_LINES: ${{ inputs.min_lines }}
76
+ INPUT_NEAR: ${{ inputs.near }}
77
+ INPUT_SEMANTIC: ${{ inputs.semantic }}
78
+ INPUT_LANG: ${{ inputs.lang }}
79
+ INPUT_EXTRA_ARGS: ${{ inputs.extra_args }}