pyxllib 0.3.96__py3-none-any.whl → 0.3.200__py3-none-any.whl

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 (358) hide show
  1. pyxllib/__init__.py +21 -21
  2. pyxllib/algo/__init__.py +8 -8
  3. pyxllib/algo/disjoint.py +54 -54
  4. pyxllib/algo/geo.py +541 -529
  5. pyxllib/algo/intervals.py +964 -964
  6. pyxllib/algo/matcher.py +389 -311
  7. pyxllib/algo/newbie.py +166 -166
  8. pyxllib/algo/pupil.py +629 -461
  9. pyxllib/algo/shapelylib.py +67 -67
  10. pyxllib/algo/specialist.py +241 -240
  11. pyxllib/algo/stat.py +494 -458
  12. pyxllib/algo/treelib.py +149 -149
  13. pyxllib/algo/unitlib.py +66 -66
  14. {pyxlpr → pyxllib/autogui}/__init__.py +5 -5
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/{ext/autogui → autogui}/autogui.py +852 -823
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/{ext/autogui → autogui}/virtualkey.py +102 -102
  20. pyxllib/autogui/wechat.py +827 -0
  21. pyxllib/autogui/wechat_msg.py +421 -0
  22. pyxllib/autogui/wxautolib.py +84 -0
  23. pyxllib/cv/__init__.py +5 -5
  24. pyxllib/cv/expert.py +267 -267
  25. pyxllib/cv/imfile.py +159 -159
  26. pyxllib/cv/imhash.py +39 -39
  27. pyxllib/cv/pupil.py +9 -9
  28. pyxllib/cv/rgbfmt.py +1525 -1525
  29. pyxllib/cv/slidercaptcha.py +137 -0
  30. pyxllib/cv/trackbartools.py +251 -251
  31. pyxllib/cv/xlcvlib.py +1040 -1040
  32. pyxllib/cv/xlpillib.py +423 -423
  33. pyxllib/data/echarts.py +240 -129
  34. pyxllib/data/jsonlib.py +89 -0
  35. pyxllib/data/oss.py +72 -72
  36. pyxllib/data/pglib.py +1127 -643
  37. pyxllib/data/sqlite.py +568 -341
  38. pyxllib/data/sqllib.py +297 -297
  39. pyxllib/ext/JLineViewer.py +505 -492
  40. pyxllib/ext/__init__.py +6 -6
  41. pyxllib/ext/demolib.py +246 -246
  42. pyxllib/ext/drissionlib.py +277 -0
  43. pyxllib/ext/kq5034lib.py +12 -1606
  44. pyxllib/ext/old.py +663 -663
  45. pyxllib/ext/qt.py +449 -449
  46. pyxllib/ext/robustprocfile.py +497 -0
  47. pyxllib/ext/seleniumlib.py +76 -76
  48. pyxllib/ext/tk.py +173 -173
  49. pyxllib/ext/unixlib.py +827 -826
  50. pyxllib/ext/utools.py +351 -338
  51. pyxllib/ext/webhook.py +124 -101
  52. pyxllib/ext/win32lib.py +40 -40
  53. pyxllib/ext/wjxlib.py +88 -0
  54. pyxllib/ext/wpsapi.py +124 -0
  55. pyxllib/ext/xlwork.py +9 -0
  56. pyxllib/ext/yuquelib.py +1105 -173
  57. pyxllib/file/__init__.py +17 -17
  58. pyxllib/file/docxlib.py +761 -761
  59. pyxllib/file/gitlib.py +309 -309
  60. pyxllib/file/libreoffice.py +165 -0
  61. pyxllib/file/movielib.py +148 -139
  62. pyxllib/file/newbie.py +10 -10
  63. pyxllib/file/onenotelib.py +1469 -1469
  64. pyxllib/file/packlib/__init__.py +330 -293
  65. pyxllib/file/packlib/zipfile.py +2441 -2441
  66. pyxllib/file/pdflib.py +426 -426
  67. pyxllib/file/pupil.py +185 -185
  68. pyxllib/file/specialist/__init__.py +685 -685
  69. pyxllib/file/specialist/dirlib.py +799 -799
  70. pyxllib/file/specialist/download.py +193 -186
  71. pyxllib/file/specialist/filelib.py +2829 -2618
  72. pyxllib/file/xlsxlib.py +3131 -2976
  73. pyxllib/file/xlsyncfile.py +341 -0
  74. pyxllib/prog/__init__.py +5 -5
  75. pyxllib/prog/cachetools.py +64 -0
  76. pyxllib/prog/deprecatedlib.py +233 -233
  77. pyxllib/prog/filelock.py +42 -0
  78. pyxllib/prog/ipyexec.py +253 -253
  79. pyxllib/prog/multiprogs.py +940 -0
  80. pyxllib/prog/newbie.py +451 -444
  81. pyxllib/prog/pupil.py +1197 -1128
  82. pyxllib/prog/sitepackages.py +33 -33
  83. pyxllib/prog/specialist/__init__.py +391 -217
  84. pyxllib/prog/specialist/bc.py +203 -200
  85. pyxllib/prog/specialist/browser.py +497 -488
  86. pyxllib/prog/specialist/common.py +347 -347
  87. pyxllib/prog/specialist/datetime.py +199 -131
  88. pyxllib/prog/specialist/tictoc.py +240 -241
  89. pyxllib/prog/specialist/xllog.py +180 -180
  90. pyxllib/prog/xlosenv.py +108 -101
  91. pyxllib/stdlib/__init__.py +17 -17
  92. pyxllib/stdlib/tablepyxl/__init__.py +10 -10
  93. pyxllib/stdlib/tablepyxl/style.py +303 -303
  94. pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
  95. pyxllib/text/__init__.py +8 -8
  96. pyxllib/text/ahocorasick.py +39 -39
  97. pyxllib/text/airscript.js +744 -0
  98. pyxllib/text/charclasslib.py +121 -109
  99. pyxllib/text/jiebalib.py +267 -264
  100. pyxllib/text/jinjalib.py +32 -0
  101. pyxllib/text/jsa_ai_prompt.md +271 -0
  102. pyxllib/text/jscode.py +922 -767
  103. pyxllib/text/latex/__init__.py +158 -158
  104. pyxllib/text/levenshtein.py +303 -303
  105. pyxllib/text/nestenv.py +1215 -1215
  106. pyxllib/text/newbie.py +300 -288
  107. pyxllib/text/pupil/__init__.py +8 -8
  108. pyxllib/text/pupil/common.py +1121 -1095
  109. pyxllib/text/pupil/xlalign.py +326 -326
  110. pyxllib/text/pycode.py +47 -47
  111. pyxllib/text/specialist/__init__.py +8 -8
  112. pyxllib/text/specialist/common.py +112 -112
  113. pyxllib/text/specialist/ptag.py +186 -186
  114. pyxllib/text/spellchecker.py +172 -172
  115. pyxllib/text/templates/echart_base.html +11 -0
  116. pyxllib/text/templates/highlight_code.html +17 -0
  117. pyxllib/text/templates/latex_editor.html +103 -0
  118. pyxllib/text/vbacode.py +17 -17
  119. pyxllib/text/xmllib.py +747 -685
  120. pyxllib/xl.py +42 -38
  121. pyxllib/xlcv.py +17 -17
  122. pyxllib-0.3.200.dist-info/METADATA +48 -0
  123. pyxllib-0.3.200.dist-info/RECORD +126 -0
  124. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +1 -2
  125. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info/licenses}/LICENSE +190 -190
  126. pyxllib/ext/autogui/__init__.py +0 -8
  127. pyxllib-0.3.96.dist-info/METADATA +0 -51
  128. pyxllib-0.3.96.dist-info/RECORD +0 -333
  129. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  130. pyxlpr/ai/__init__.py +0 -5
  131. pyxlpr/ai/clientlib.py +0 -1281
  132. pyxlpr/ai/specialist.py +0 -286
  133. pyxlpr/ai/torch_app.py +0 -172
  134. pyxlpr/ai/xlpaddle.py +0 -655
  135. pyxlpr/ai/xltorch.py +0 -705
  136. pyxlpr/data/__init__.py +0 -11
  137. pyxlpr/data/coco.py +0 -1325
  138. pyxlpr/data/datacls.py +0 -365
  139. pyxlpr/data/datasets.py +0 -200
  140. pyxlpr/data/gptlib.py +0 -1291
  141. pyxlpr/data/icdar/__init__.py +0 -96
  142. pyxlpr/data/icdar/deteval.py +0 -377
  143. pyxlpr/data/icdar/icdar2013.py +0 -341
  144. pyxlpr/data/icdar/iou.py +0 -340
  145. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  146. pyxlpr/data/imtextline.py +0 -473
  147. pyxlpr/data/labelme.py +0 -866
  148. pyxlpr/data/removeline.py +0 -179
  149. pyxlpr/data/specialist.py +0 -57
  150. pyxlpr/eval/__init__.py +0 -85
  151. pyxlpr/paddleocr.py +0 -776
  152. pyxlpr/ppocr/__init__.py +0 -15
  153. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  154. pyxlpr/ppocr/data/__init__.py +0 -135
  155. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  156. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  157. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  158. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  159. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  160. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  161. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  162. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  163. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  164. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  165. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  166. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  167. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  168. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  169. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  170. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  171. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  172. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  173. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  174. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  175. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  176. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  177. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  178. pyxlpr/ppocr/losses/__init__.py +0 -61
  179. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  180. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  181. pyxlpr/ppocr/losses/center_loss.py +0 -88
  182. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  183. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  184. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  185. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  186. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  187. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  188. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  189. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  190. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  191. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  192. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  193. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  194. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  195. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  196. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  197. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  198. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  199. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  200. pyxlpr/ppocr/metrics/__init__.py +0 -44
  201. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  202. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  203. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  204. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  205. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  206. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  207. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  208. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  209. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  210. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  211. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  212. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  213. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  214. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  215. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  216. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  217. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  218. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  219. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  220. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  221. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  222. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  223. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  224. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  225. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  226. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  227. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  228. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  229. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  230. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  231. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  232. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  233. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  234. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  235. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  236. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  237. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  238. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  239. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  240. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  241. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  242. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  243. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  244. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  245. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  246. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  247. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  248. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  249. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  250. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  251. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  252. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  253. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  254. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  255. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  256. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  257. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  258. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  259. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  260. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  261. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  262. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  263. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  264. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  265. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  266. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  267. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  268. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  269. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  270. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  271. pyxlpr/ppocr/tools/__init__.py +0 -14
  272. pyxlpr/ppocr/tools/eval.py +0 -83
  273. pyxlpr/ppocr/tools/export_center.py +0 -77
  274. pyxlpr/ppocr/tools/export_model.py +0 -129
  275. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  276. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  277. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  278. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  279. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  280. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  281. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  282. pyxlpr/ppocr/tools/infer_det.py +0 -134
  283. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  284. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  285. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  286. pyxlpr/ppocr/tools/infer_table.py +0 -107
  287. pyxlpr/ppocr/tools/program.py +0 -596
  288. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  289. pyxlpr/ppocr/tools/train.py +0 -163
  290. pyxlpr/ppocr/tools/xlprog.py +0 -748
  291. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  292. pyxlpr/ppocr/utils/__init__.py +0 -24
  293. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  294. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  295. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  296. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  297. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  298. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  299. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  300. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  301. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  302. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  303. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  304. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  305. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  306. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  307. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  308. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  309. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  310. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  311. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  312. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  313. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  314. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  315. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  316. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  317. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  318. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  319. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  320. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  321. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  322. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  323. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  324. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  325. pyxlpr/ppocr/utils/dict90.txt +0 -90
  326. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  327. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  328. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  329. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  330. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  331. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  332. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  333. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  334. pyxlpr/ppocr/utils/gen_label.py +0 -81
  335. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  336. pyxlpr/ppocr/utils/iou.py +0 -54
  337. pyxlpr/ppocr/utils/logging.py +0 -69
  338. pyxlpr/ppocr/utils/network.py +0 -84
  339. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  340. pyxlpr/ppocr/utils/profiler.py +0 -110
  341. pyxlpr/ppocr/utils/save_load.py +0 -150
  342. pyxlpr/ppocr/utils/stats.py +0 -72
  343. pyxlpr/ppocr/utils/utility.py +0 -80
  344. pyxlpr/ppstructure/__init__.py +0 -13
  345. pyxlpr/ppstructure/predict_system.py +0 -187
  346. pyxlpr/ppstructure/table/__init__.py +0 -13
  347. pyxlpr/ppstructure/table/eval_table.py +0 -72
  348. pyxlpr/ppstructure/table/matcher.py +0 -192
  349. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  350. pyxlpr/ppstructure/table/predict_table.py +0 -221
  351. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  352. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  353. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  354. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  355. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  356. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  357. pyxlpr/ppstructure/utility.py +0 -71
  358. pyxlpr/xlai.py +0 -10
pyxllib/text/jscode.py CHANGED
@@ -1,767 +1,922 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽,梁奕本(js去注释部分)
4
- # @Email : 877362867@qq.com, https://lyeebn.gitee.io/technology-shop/HeyBoss.html
5
- # @Date : 2023/10/20
6
-
7
- from collections import Counter
8
- import re
9
- import textwrap
10
-
11
- try:
12
- import jsbeautifier
13
- except ModuleNotFoundError:
14
- pass
15
-
16
-
17
- def __1_删注释功能():
18
- """
19
- 用编译语法解析方式分析,清理JS中的注释,支持嵌套
20
-
21
- Usage:
22
- 1、A simple function
23
- from pyxllib.text.jscode import dropJScomment
24
- dropJScomment(jsSourceCodeAsString)
25
-
26
- 2、Object
27
- from pyxllib.text.jscode import JSParser
28
- js = JSParser(jsSourceCode)
29
- jsc = js.clearComment()
30
- """
31
-
32
-
33
- def 删注释周围留空(c, arr): # 应急
34
- arr.reverse()
35
- for i in arr:
36
- # 先用这个快速处理,有空再优化: https://blog.csdn.net/cooco369/article/details/82994932
37
- # c = c[:i].rstrip() + '%' + c[i:].lstrip() # debug,定位
38
- 有回车 = False
39
- l = r = 上一回车处 = i
40
- r = i + 1
41
- len_c = len(c)
42
- while len_c > l > 0:
43
- l -= 1
44
- ci = c[l]
45
- if ci == '\n': # \r已统一为\n
46
- 有回车 = True
47
- elif ci not in '\t \v': # TODO 中文空格行不行
48
- break
49
- while r < len_c:
50
- ci = c[r]
51
- if ci in '\n\r': # \r已统一为\n
52
- 有回车 = True
53
- 上一回车处 = r # 保持缩进,但有 Bug 灵异,难道是 ?
54
- elif ci not in '\t \v': # TODO 中文空格行不行
55
- break
56
- r += 1
57
- # print(r)
58
- c = c[:l + 1] + ('\n' if 有回车 else '') + c[上一回车处:] # 有必要多留个空格吗
59
- # 已知 BUG:连续注释后的缩进无法保持,但不影响 JS 代码逻辑就是了
60
- return c
61
-
62
-
63
- def 回溯区分正则除号(c):
64
- # 此函数参数需要提前处理:高偶合,非内聚
65
- # 原理:除号是二元运算符,其前面必有一个量:数值(可以是变量名或字符串字面量),这不太好穷举
66
- # 而正则为一字面量,前面可能必为某种运算符 = + ,或特殊符号:& | 逻辑? 括号 ( , 参数,[ { 对象 : ,或 ; 语句结束符,回车。有个坑:折行要注意。
67
- # 摆脱对正则的依赖,变成无第三无依赖
68
- # True 为 正则, False 为除号
69
- i = len(c)
70
- while i > 0:
71
- i -= 1
72
- ci = c[i]
73
- if ci in '\t \v':
74
- continue # 暂时无法给出结论
75
- elif ci in '\n\r': # 折行,暂时无法给出结论,其实可以与如上合并,区别可能是回车前可能是已省略的分号
76
- # return 回溯区分正则除号(c, i=i) # 经验证,js 中 转义回车仅限于字符串内,运算符后是可折行的, \ 反而语法错误
77
- continue # 经考虑,还是不必递归了
78
- elif ci in '{[(=,;?:&|!*~%^': # todo ++ -- ,注:~ 为非预期操作,但因类型转换语法允许 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_operators
79
- return True # 正则
80
- elif ci == '+': # todo ++ --
81
- i -= 1
82
- if i > 0 and c[i] == '+': return False # 除号 # ++: / 前 为变量 为除尘
83
- return True # 正则
84
- elif ci == '-': # 要么 变量--,要反报错
85
- i -= 1
86
- if i > 0 and c[i] == '-': return False # 除号 # ++: / 前 为变量 为除尘
87
- return True # raise BaseException('/ 前单 -') # 实测因类型转换,两边转数值类型,最多是 NaN,而不至于报错
88
- else:
89
- return False # 除号,可能是变量,什么的
90
- return True # 正则
91
-
92
-
93
- class JSParser():
94
- def 普通引号(self, 号='"'): # 号:开始结束定界符,下同
95
- while self.indexPointer < self.jsCodeLength: # 正常会提前 return 见下
96
- self.indexPointer += 1
97
- si = self.jsSourceCode[self.indexPointer]
98
- self.jsWithoutComment += si # 这应该是以下所有情况包括 else 都要的
99
- if si == 号:
100
- return
101
- elif si == '\\': # 转义,提前吃掉, \n'"`
102
- # 即 r += '\\'
103
- self.indexPointer += 1
104
- if self.jsSourceCode[
105
- self.indexPointer + 1] == 号: # 超标报错正好,对应其语法错误,TODO 以后完善,实测 JS, 如果在引号中回车前没\反是其语法错误;就当源文件语法吧
106
- pass
107
- self.jsWithoutComment += self.jsSourceCode[self.indexPointer] # 转义后的字符好像都是要吃掉的,不用 if,待深入思考
108
- elif si == '\n':
109
- raise BaseException('原稿语法有误》缺右字符串定界:\a' + 号)
110
-
111
- def 反引号(self, 号='`'):
112
- while self.indexPointer < self.jsCodeLength: # 正常会提前 return 见下
113
- self.indexPointer += 1
114
- si = self.jsSourceCode[self.indexPointer]
115
- self.jsWithoutComment += si # 这应该是以下所有情况包括 else 都要的
116
- if si == 号:
117
- return
118
- elif si == '\\':
119
- # 即 r += '\\'
120
- self.indexPointer += 1
121
- if self.jsSourceCode[
122
- self.indexPointer + 1] == 号: # 超标报错正好,对应其语法错误,TODO 以后完善,实测 JS, 如果在引号中回车前没\反是其语法错误;就当源文件语法吧
123
- pass
124
- self.jsWithoutComment += self.jsSourceCode[self.indexPointer] # 转义后的字符好像都是要吃掉的,不用 if,待深入思考
125
- elif si == '$' and self.jsSourceCode[
126
- self.indexPointer + 1] == '{': # 重要区别 。经实验 ${ 后必有 } 否则报错:g = `2${2+3 7`
127
- self.indexPointer += 1
128
- self.jsWithoutComment += '{'
129
- self.反引号嵌套表达式允许多行注释()
130
- pass
131
-
132
- def 反引号嵌套表达式允许多行注释(self): # 适合钻牛角尖, TODO 堆栈 结构练手? 这好像可以 递归 main 了,差个 } return 吧
133
- while self.indexPointer < self.jsCodeLength: # 正常会提前 return 见下
134
- self.indexPointer += 1
135
- si = self.jsSourceCode[self.indexPointer]
136
- if si == '}':
137
- self.jsWithoutComment += si
138
- return
139
- elif si in '"\'': # 吃撑了的嵌套骚操作 si == '"' or si == "'"
140
- self.jsWithoutComment += si
141
- self.普通引号(号=si)
142
- elif si == "`": # `,还能嵌套 String.raw` 算了,不玩了
143
- self.jsWithoutComment += si
144
- self.反引号()
145
- elif si == '/': # 以下这一块逻辑 main
146
- self.反斜线然后呢()
147
- # if s[idx+1] == '*': # 注意这里,嵌套了注释!超标正好报错,有效的 JS 代码 这里不会超标
148
- # idx += 1
149
- # 多行注释()
150
- # elif s[idx+1] == '/':
151
- # idx += 1
152
- # 单行注释()
153
- # else:
154
- # r += s[idx]
155
- else:
156
- self.jsWithoutComment += si
157
-
158
- def 反斜线然后呢(self): # 共用于【main】与 【反引号嵌套表达式允许多行注释】中的表达式
159
- 偷看 = self.jsSourceCode[self.indexPointer + 1]
160
- if 偷看 == '/':
161
- self.单行注释()
162
- self.jsWithoutComment += self.注释占位
163
- elif 偷看 == '*':
164
- self.jsWithoutComment += self.注释占位
165
- self.indexPointer += 1
166
- self.多行注释()
167
- elif 回溯区分正则除号(
168
- self.jsWithoutComment): # bool(re.search('[\n\r(\[\{=+,;&?|]([ \t]*|\\[\n\r])*[ \t]*$', self.jsWithoutComment)): # def 区分正则或除号(): ←
169
- self.jsWithoutComment += '/'
170
- self.正则()
171
- else:
172
- self.jsWithoutComment += '/'
173
- # 除号不必特殊处理吧()
174
- # TODO,重要【严重】有没有可能是除号:q = 1 / 2 + /2/ // 除号、正则 、注释并存
175
- # 如果是除号,那之前应该有数值 \d),不好判断,考虑到 JS 有些隐性的类型转换,如:'6' / 3
176
- # 如果是正则,往前回溯应该必有 = 或 + ,也可能在函数作为参数 (甚至可能还有变态 通过 \ 加回车,在正则前折行) re 如下
177
-
178
- def 源反引号(self, 号='`'):
179
- return self.反引号(号=号) # TODO 先借用,也就差个 \ ,其它有什么区别待考虑
180
- pass
181
-
182
- def 单行注释(self):
183
- # while (s[idx] != '\n' or s[idx] != '\r') and idx < 长 :
184
- while (self.jsSourceCode[self.indexPointer] not in '\n\r') and self.indexPointer < self.jsCodeLength:
185
- self.indexPointer += 1
186
- self.注释location.append(len(self.jsWithoutComment))
187
- self.jsWithoutComment += '\n' # 补回车于删注释点后,不能反了
188
-
189
- def 多行注释(self):
190
- self.indexPointer += 1 # 要吗?加过没
191
- while not (self.jsSourceCode[self.indexPointer] == '*' and self.jsSourceCode[
192
- self.indexPointer + 1] == '/'): self.indexPointer += 1
193
- self.indexPointer += 1
194
- # r += '\n'
195
- self.注释location.append(len(self.jsWithoutComment))
196
-
197
- def 正则(self): # 正常的 JS 代码中 正则中无回车符
198
- while True: # not (jsSourceCode[self.indexPointer + 1] == '/')
199
- self.indexPointer += 1 # TODO 放哪里
200
- si = self.jsSourceCode[self.indexPointer]
201
- self.jsWithoutComment += si
202
- if si == '/':
203
- return
204
- elif si in '\n\r':
205
- raise BaseException('正则还能折行?你是哪个老师教的')
206
- elif self.jsSourceCode[self.indexPointer] == '\\':
207
- self.indexPointer += 1 # TODO 放哪里
208
- self.jsWithoutComment += self.jsSourceCode[self.indexPointer]
209
-
210
- def __init__(self, jsSourceCode):
211
- # 就传源码吧,文件打开让用户自己去做,不然还得判断是文件名还是字符串,还得判断文件是否存在,还得依赖 os 包
212
- self.jsSourceCode = jsSourceCode.replace('\r', '\n') + '\n ' # 防超标,为啥 .strip() 会异常
213
- self.jsCodeLength = len(jsSourceCode)
214
- self.indexPointer = -1 # 游标指针
215
- self.注释占位 = '' # 生僻占位?以便后期删其前 \s
216
- self.注释location = [] # 记录当时 len(self.jsWithoutComment),改天写吧
217
- self.jsWithoutComment = False # init 先执行,所以不能执行调此类的其它函数,全写到一个这个函数里也麻烦,缩进了两级,如果要用方法可能重复执行,故用缓存法,
218
-
219
- def clearComment(self): # 难道这个函数要在放类外吗(以便 init 能调用)
220
- if self.jsWithoutComment: return self.jsWithoutComment # 已经求值过就用缓存,避免如下代码重复执行。这里一个变量两用:初始 bool 类型 False ,之后存清注释的str结果,类型改变,如果要让 GPT 改为 C++ 可能要注意一下,除了这里,别的地文都没有动态类型
221
- # 仅第一次计算,
222
- self.jsWithoutComment = ''
223
- while self.indexPointer < self.jsCodeLength: # 要注意在哪里加 1 ,统一在处理开头处加吧,让当前处理的指标与相应字符初始一致,特殊情况再特殊加
224
- self.indexPointer += 1
225
- si = self.jsSourceCode[self.indexPointer]
226
- if si == '/': # 正则、单/多行注释、除号,idea:注释前可能有多余的 \s,删注释时,可以留个 unicode 记号,到时用正则删
227
- self.反斜线然后呢()
228
- continue # 其后的情况都要 r += si, TODO 合并
229
- elif si in '"\'': # ' " →1 si == '"' or si == "'"
230
- self.jsWithoutComment += si
231
- self.普通引号(号=si)
232
- elif si == "`": # `
233
- self.jsWithoutComment += si
234
- self.反引号()
235
- elif si == "S" and self.jsCodeLength - self.indexPointer > 11 and self.jsSourceCode[
236
- self.indexPointer:self.indexPointer + 11] == 'String.raw`': # String.raw` 元字符串
237
- self.jsWithoutComment += si
238
- self.indexPointer += 10;
239
- self.jsWithoutComment += 'tring.raw`' # 分两步,以便上面那个能和其它情景合并
240
- self.源反引号()
241
- elif si == "\n": # 压缩连续回车(空行)寻找一回车位置,这可能出现 BUG 吗?慎重,危险,不能出现于字符串,注释等中
242
- tmp = self.indexPointer
243
- while tmp < self.jsCodeLength:
244
- tmp += 1
245
- if self.jsSourceCode[tmp] == '\n':
246
- self.indexPointer = tmp
247
- elif self.jsSourceCode[tmp] in '\t ':
248
- break
249
- else:
250
- break
251
- self.jsWithoutComment += si
252
- else:
253
- self.jsWithoutComment += si
254
- # 格式化字符串好像没什么特殊的
255
- pass
256
- self.jsWithoutComment = 删注释周围留空(self.jsWithoutComment, self.注释location).strip()
257
- # print(self.注释location)
258
- return self.jsWithoutComment
259
-
260
-
261
- def remove_js_comments(jsSourceCode): # 对外接口,将本来用得两行代码封装为一行
262
- js = JSParser(jsSourceCode)
263
- return js.clearComment()
264
-
265
-
266
- def __2_类js的as处理功能():
267
- pass
268
-
269
-
270
- airscript_head = r"""
271
- // 0 基础组件代码(可以放在功能代码之前,也能放在最后面)
272
-
273
- // 根据提供的 pattern 在 range 中寻找 cell
274
- // 如果没有提供 range,默认在 ActiveSheet.UsedRange 中寻找
275
- function findCell(pattern, range = ActiveSheet.UsedRange) {
276
- const cell = range.Find(pattern, range, xlValues, xlWhole)
277
- return cell
278
- }
279
-
280
- function levenshteinDistance(a, b) {
281
- const matrix = [];
282
-
283
- let i;
284
- for (i = 0; i <= b.length; i++) {
285
- matrix[i] = [i];
286
- }
287
-
288
- let j;
289
- for (j = 0; j <= a.length; j++) {
290
- matrix[0][j] = j;
291
- }
292
-
293
- for (i = 1; i <= b.length; i++) {
294
- for (j = 1; j <= a.length; j++) {
295
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
296
- matrix[i][j] = matrix[i - 1][j - 1];
297
- } else {
298
- matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1));
299
- }
300
- }
301
- }
302
-
303
- return matrix[b.length][a.length];
304
- }
305
-
306
- // 根据提供的 pattern range 中寻找 column
307
- // 如果没有提供 range,默认在 ActiveSheet.UsedRange 中寻找
308
- function findColumn(pattern, range = ActiveSheet.UsedRange) {
309
- let cell = findCell(pattern, range); // 首先尝试精确匹配
310
- if (!cell) { // 如果精确匹配失败,尝试模糊匹配
311
- let minDistance = Infinity;
312
- let minDistanceColumn;
313
- for (let i = 1; i <= range.Columns.Count; i++) {
314
- let columnName = range.Cells(1, i).Value;
315
- let distance = levenshteinDistance(pattern, columnName);
316
- if (distance < minDistance) {
317
- minDistance = distance;
318
- minDistanceColumn = i;
319
- }
320
- }
321
- return minDistanceColumn;
322
- }
323
- if (cell) { return cell.Column }
324
- }
325
-
326
- // 根据提供的 pattern range 中寻找 row
327
- // 如果没有提供 range,默认在 ActiveSheet.UsedRange 中寻找
328
- function findRow(pattern, range = ActiveSheet.UsedRange) {
329
- const cell = findCell(pattern, range)
330
- if (cell) { return cell.Row }
331
- }
332
-
333
- // 判断一个 cells 集合是否为空
334
- function isEmpty(cells) {
335
- for (let i = 1; i <= cells.Count; i++) {
336
- if (cells.Item(i).Text) {
337
- return false;
338
- }
339
- }
340
- return true;
341
- }
342
-
343
- // 获取实际使用的区域
344
- function getUsedRange(maxRows = 500, maxColumns = 100, startFromA1 = true) {
345
- /* 允许通过"表格上下文"信息,调整这里数据行的上限500行,或者列上限100列
346
- 注意,如果分析预设的表格数据在这个限定参数内可以不改
347
- 只有表格未知,或者明确数据量超过设置时,需要重新调整这里的参数
348
- 调整的时候千万不要故意凑的刚刚好,可以设置一定的冗余区间
349
- 比如数据说有4101条,那么这里阈值设置为5000也是可以的,比较保险。
350
- */
351
-
352
- // 默认获得的区间,有可能是有冗余的空行,所以还要进一步优化
353
- let usedRange = ActiveSheet.UsedRange;
354
-
355
- let lastRow = Math.min(usedRange.Rows.Count, maxRows);
356
- let lastColumn = Math.min(usedRange.Columns.Count, maxColumns);
357
-
358
- let firstRow = 1;
359
- let firstColumn = 1;
360
-
361
- // 找到最后一个非空行
362
- for (; lastRow >= firstRow; lastRow--) {
363
- if (!isEmpty(usedRange.Rows(lastRow).Cells)) {
364
- break;
365
- }
366
- }
367
-
368
- // 找到最后一个非空列
369
- for (; lastColumn >= firstColumn; lastColumn--) {
370
- if (!isEmpty(usedRange.Columns(lastColumn).Cells)) {
371
- break;
372
- }
373
- }
374
-
375
- // 如果表格不是从"A1"开始,找到第一个非空行和非空列
376
- if (!startFromA1) {
377
- for (; firstRow <= lastRow; firstRow++) {
378
- if (!isEmpty(usedRange.Rows(firstRow).Cells)) {
379
- break;
380
- }
381
- }
382
-
383
- for (; firstColumn <= lastColumn; firstColumn++) {
384
- if (!isEmpty(usedRange.Columns(firstColumn).Cells)) {
385
- break;
386
- }
387
- }
388
- }
389
-
390
- // 创建一个新的 Range 对象,它只包含非空的行和列
391
- let newUsedRange = ActiveSheet.Range(
392
- usedRange.Cells(firstRow, firstColumn),
393
- usedRange.Cells(lastRow, lastColumn)
394
- );
395
-
396
- return newUsedRange; // 返回新的实际数据区域
397
- }
398
-
399
- // 将 Excel 日期转换为 JavaScript 日期
400
- function xlDateToJSDate(xlDate) {
401
- return new Date((xlDate - 25569) * 24 * 3600 * 1000);
402
- }
403
-
404
- // 判断日期是否在本周
405
- function isCurrentWeek(date) {
406
- const today = new Date();
407
- today.setHours(0, 0, 0, 0); // 把时间设为午夜以准确地比较日期
408
- const firstDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay()));
409
- const lastDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay() + 6));
410
- return date >= firstDayOfWeek && date <= lastDayOfWeek;
411
- }
412
-
413
- // 判断日期是否在当前月份
414
- function isCurrentMonth(date) {
415
- const currentDate = new Date();
416
- currentDate.setHours(0, 0, 0, 0); // 把时间设为午夜stdcode以准确地比较日期
417
- return date.getMonth() === currentDate.getMonth() && date.getFullYear() === currentDate.getFullYear();
418
- }
419
-
420
- // 判断日期是否在下周
421
- function isNextWeek(date) {
422
- const today = new Date();
423
- today.setHours(0, 0, 0, 0); // 把时间设为午夜以准确地比较日期
424
- const nextWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
425
- return date > today && date <= nextWeek;
426
- }
427
-
428
- // 判断日期是否在下个月
429
- function isNextMonth(date) {
430
- const today = new Date();
431
- today.setHours(0, 0, 0, 0); // 把时间设为午夜以准确地比较日期
432
- const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
433
- const endDateOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0);
434
- return date >= nextMonth && date <= endDateOfNextMonth;
435
- }
436
- """.strip()
437
-
438
-
439
- class AirScriptCodeFixer:
440
- @classmethod
441
- def fix_colors(cls, code_text):
442
- # 1 一些错误的颜色设置方法
443
- if re.search(r'(?<!\.)\b(Color.\w+)\b', code_text):
444
- return 0, code_text
445
-
446
- # 2 不能像vba那样,直接对颜色设置一个数值
447
- match = re.search(r'\.Color\s*=\s*(\d+)', code_text)
448
- if match:
449
- color_number = int(match.group(1))
450
- red = color_number % 256
451
- green = (color_number // 256) % 256
452
- blue = (color_number // 256 // 256) % 256
453
- rgb_format = f'RGB({red}, {green}, {blue})'
454
- code_text = code_text[:match.start(1)] + rgb_format + code_text[match.end(1):]
455
-
456
- # 3 一些错误的颜色设置方法,进行修正
457
- configs = {
458
- '红色': 'RGB(255, 0, 0)',
459
- '黄色': 'RGB(255, 255, 0)',
460
- '绿色': 'RGB(0, 255, 0)',
461
- '蓝色': 'RGB(0, 0, 255)',
462
- '灰色': 'RGB(128, 128, 128)',
463
- 'red': 'RGB(255, 0, 0)',
464
- 'yellow': 'RGB(255, 255, 0)',
465
- 'green': 'RGB(0, 255, 0)',
466
- 'blue': 'RGB(0, 0, 255)',
467
- 'black': 'RGB(0, 0, 0)',
468
- 'gray': 'RGB(128, 128, 128)',
469
- 'grey': 'RGB(128, 128, 128)',
470
- 'purple': 'RGB(128, 0, 128)',
471
- 'pink': 'RGB(255, 192, 203)',
472
- 'orange': 'RGB(255, 128, 0)',
473
- }
474
-
475
- def replace_color_fmt(m):
476
- t1, t2 = m.groups()
477
- t2 = t2.strip('"\'').lower()
478
- if t2 in configs:
479
- return f'{t1}{configs[t2]}'
480
- elif m2 := re.search(r'[a-fA-F0-9]{6}', t2):
481
- res = f'{t1}RGB({int(m2.group(0)[:2], 16)}, ' \
482
- f'{int(m2.group(0)[2:4], 16)}, ' \
483
- f'{int(m2.group(0)[4:], 16)})'
484
- return res
485
- return t1 + m.group(2)
486
-
487
- text = re.sub(r'''(\bColor\s*=\s*)(['"].+?['"])''', replace_color_fmt, code_text)
488
-
489
- # 4 经过优化仍无法修正的颜色问题
490
- if re.search(r'''\bColor\s*=\s*['"]''', text):
491
- # global count_target
492
- # ms = re.findall(r'''\bColor\s*=\s*(['"].+)''', text)
493
- # for m in ms:
494
- # count_target[m] += 1
495
- return 0, text
496
-
497
- return 1, text
498
-
499
- @classmethod
500
- def fix_miscellaneous(cls, code_text):
501
- """ 修复其他各种杂项问题 """
502
- text = code_text
503
-
504
- # Cannot convert a Symbol value to a string, 一般是对Excel对象使用'+='运算报错
505
- text = re.sub(r'(\s+)((?:.+)Value2?)\s+(?:\+=)\s+(.+)', r'\1\2 = \2 + \3', text) # 531条
506
-
507
- # 各种错误的接口调用形式
508
- text = text.replace('.Range.Find(', '.Find(') # 8条
509
-
510
- # sort接口问题
511
- text = re.sub(r'(\.Sort\(.*?,\s+)(-1|0|false)\)', r'\g<1>2)', text) # 328条
512
-
513
- # 做数据有效性的时候,有时候会有重复的引号嵌套
514
- text = re.sub(r'''(Formula\d:\s*')"(.+?)"''', r'\1\2', text)
515
-
516
- # 230907周四19:56,枚举值不用放在字符串中
517
- text = re.sub(r'''(['"`])(xlCellTypeVisible)\1''', r'\2', text)
518
-
519
- # 231106周一18:42,range的使用规范性
520
- text = re.sub(r'Range\(("|\')([A-Z]+|\d+)("|\')\)', r'Range(\1\2:\2\1)', text)
521
-
522
- return 1, text
523
-
524
- @classmethod
525
- def delete_error_record(cls, code_text):
526
- return 1, code_text
527
-
528
- @classmethod
529
- def check_assistant_content(cls, code_text):
530
- text = code_text
531
-
532
- global count_target
533
- pieces = re.findall(r'[a-zA-Z_\d\.]+\.Columns', text)
534
- count_target += Counter([x.strip() for x in pieces])
535
-
536
- # Columns前一般用ActiveSheet就行了
537
-
538
- return 1, text
539
-
540
- @classmethod
541
- def simplify_advtools(cls, code_text):
542
- """ 移除高级工具函数代码,用其他更简洁的方式取代 """
543
- text = code_text
544
- text = text.replace('getUsedRange()', 'ActiveSheet.UsedRange')
545
- text = re.sub(r'''findCell\(((['"]).+?\2)(, [a-zA-Z]+)?\)''',
546
- r'ActiveSheet.UsedRange.Find(\1)', text)
547
- text = re.sub(r'''findColumn\(((['"]).+?\2)(, [a-zA-Z]+)?\)''',
548
- r'ActiveSheet.UsedRange.Find(\1).Column', text)
549
- text = re.sub(r'''findRow\(((['"]).+?\2(, [a-zA-Z]+)?)\)''',
550
- r'ActiveSheet.UsedRange.Find(\1).Row', text)
551
-
552
- return 1, text
553
-
554
- @classmethod
555
- def simplify_code(cls, code_text, indent=4):
556
- """ 代码简化,去掉一些冗余写法
557
-
558
- 包括代码美化,默认缩进是4,但在训练阶段,建议默认缩进是2,
559
- """
560
- # 1 代码精简
561
- code_text = re.sub(r'Application\.(WorksheetFunction|ActiveWorkbook|ActiveSheet|Sheets|Range|Workbook)', r'\1', code_text)
562
- code_text = re.sub(r'Workbook\.(Sheets)', r'\1', code_text)
563
- code_text = re.sub(r'ActiveSheet\.(Range|Rows|Columns|Cells)', r'\1', code_text)
564
- code_text = re.sub(r'(\w+)\.(Row|Column)\s*\+\s*\1\.\2s\.Count\s*-\s*1', r'\1.\2End', code_text)
565
- code_text = re.sub(r'\bvar\b', 'let', code_text)
566
- code_text = code_text.replace('Sheets.Item(', 'Sheets(')
567
- code_text = re.sub(r'Application.Enum.\w+.(\w+)', r'\1', code_text)
568
-
569
- # 2 代码美化
570
- opts = jsbeautifier.default_options()
571
- opts.indent_size = indent
572
- code_text = jsbeautifier.beautify(code_text, opts)
573
-
574
- return 1, code_text.strip()
575
-
576
- @classmethod
577
- def simplify_code2(cls, code_text, indent=4):
578
- """ 有些规则可能在标注数据中想留着,但训练的时候想删除,则可以调用这个进一步级别的简化 """
579
- _, code_text = cls.simplify_code(code_text, indent)
580
- return code_text
581
-
582
- @classmethod
583
- def fix_stdcode(cls, code_text):
584
- """ 更智能的,缺什么组件才补什么组件 """
585
- # 1 检查依赖补充
586
- text = code_text
587
- _, text = cls.simplify_advtools(text)
588
-
589
- defined_vars = set(re.findall(r'(?:<=^|\b)(?:var|let|const|function)\s+(\w+)(?:\s+|\()', text))
590
- used_vars = set(re.findall(r'(?<!\.)\b(\w+)\b', text))
591
-
592
- # 2 提取js中的函数
593
- def extract_functions(code_string):
594
- pattern = r"(function\s+(\w+).+?^\})"
595
- matches = re.findall(pattern, code_string, re.MULTILINE | re.DOTALL)
596
- return {name: func for func, name in matches}
597
-
598
- js_funcs = extract_functions(airscript_head)
599
-
600
- # 3 补充缺失的定义
601
- pre_additional_code = []
602
- for name, code in {'xlDateToJSDate': '',
603
- 'isCurrentWeek': '',
604
- 'isCurrentMonth': '',
605
- 'isNextWeek': '',
606
- 'isNextMonth': '',
607
- 'usedRange': 'const usedRange = ActiveSheet.UsedRange;',
608
- 'headerRows': 'const headerRows = usedRange.Rows("1:1");',
609
- 'firstDataRow': 'const firstDataRow = headerRows.RowEnd + 1;',
610
- 'lastDataRow': 'const lastRow = usedRange.RowEnd;',
611
- }.items():
612
- if name in used_vars and name not in defined_vars:
613
- if name in js_funcs:
614
- code = js_funcs[name]
615
- if code:
616
- pre_additional_code.append(code)
617
- used_vars.remove(name)
618
- else: # 有未定义就使用的变量,这条数据不要了
619
- return 0, text
620
- else:
621
- # 还得再检查一波是不是有叫'xxxColumn'的变量未定义被使用
622
- logo = True
623
- for name in used_vars:
624
- if name.endswith('Column') and name not in defined_vars:
625
- logo = False
626
- break
627
- if logo and pre_additional_code:
628
- text = '\n'.join(pre_additional_code) + '\n' + text
629
- return 1, text
630
-
631
- @classmethod
632
- def pre_proc(cls, code_text):
633
- code_text = re.sub(r'^\\n', '', code_text, flags=re.MULTILINE)
634
- return 1, code_text
635
-
636
- @classmethod
637
- def fix_loc_head(cls, code_text):
638
- """ 修复定位头 """
639
- m1 = re.search(r'//\s*1([\.\s]+)定位', code_text)
640
- m2 = re.search(r'//\s*2([\.\s]+)业务功能', code_text)
641
- if not m1 and m2:
642
- code_text = '// 1' + m2.group(1) + '定位\n' + code_text
643
- return 1, code_text
644
-
645
- @classmethod
646
- def remove_stdcode(cls, code_text):
647
- """ 删除开头固定的组件头代码 """
648
- code_text = re.sub(r'(.*?)(//\s*1[\.\s]+定位)', r'\2', code_text, flags=re.DOTALL)
649
- code_text = re.sub(r'// 0 基础组件代码(可以放在功能代码之前,也能放在最后面).+?$', '', code_text, flags=re.DOTALL)
650
- return 1, code_text
651
-
652
- @classmethod
653
- def fix_texts(cls, code_text):
654
- """ 修复文本中出现的关键词,描述 """
655
- s = code_text
656
- s = s.replace('<表格结构信息描述>', '表格摘要')
657
- s = s.replace('<孩子:表格摘要>', '表格摘要')
658
- return 1, s
659
-
660
- @classmethod
661
- def fix_base(cls, code_text):
662
- text = code_text
663
- for func in [
664
- cls.simplify_code,
665
- cls.fix_colors,
666
- cls.fix_miscellaneous,
667
- cls.advanced_remove_comments_regex,
668
- ]:
669
- status, text = func(text)
670
- if not status:
671
- return status, text
672
- return status, text
673
-
674
- @classmethod
675
- def fix_base2(cls, code_text):
676
- text = code_text
677
- for func in [
678
- cls.simplify_code,
679
- cls.fix_colors,
680
- cls.fix_miscellaneous,
681
- ]:
682
- status, text = func(text)
683
- if not status:
684
- return status, text
685
- return status, text
686
-
687
- @classmethod
688
- def fix_all(cls, code_text):
689
- old_text = code_text
690
- text = code_text
691
- for func in [
692
- cls.simplify_code,
693
- cls.fix_colors,
694
- cls.fix_miscellaneous,
695
- cls.fix_stdcode,
696
- # cls.advanced_remove_comments_regex,
697
- ]:
698
- status, text = func(text)
699
- if not status:
700
- return status, text
701
- # if text != old_text:
702
- # bcompare(old_text, text)
703
- # dprint()
704
- return status, text
705
-
706
- @classmethod
707
- def format_hanging_indent(cls, text):
708
- r""" 优化悬挂缩进的文本排版
709
-
710
- :param str text: 输入文本
711
- :return str: 优化后的文本
712
-
713
- >>> AirScriptCodeFixer.format_hanging_indent('const usedRange = getUsedRange();\\n const headerRows = usedRange.Rows(\'1:1\');')
714
- 'const usedRange = getUsedRange();\\nconst headerRows = usedRange.Rows(\'1:1\');'
715
- """
716
- lines = text.strip().split('\n') # 去掉前后空行并分割成行
717
- first_line = lines.pop(0) # 取出第1行
718
- remaining_text = '\n'.join(lines) # 剩余行合并为一个字符串
719
- dedented_text = textwrap.dedent(remaining_text) # 对剩余行进行反缩进处理
720
-
721
- return 1, first_line + '\n' + dedented_text # 将处理后的剩余行和第1行拼接回去
722
-
723
- @classmethod
724
- def remove_comments_regex(cls, js_code):
725
- """ 这个代码功能并不严谨,只是一个临时快速方案 """
726
- js_code = re.sub(r'^\s*/\*.*?\*/\n?', '', js_code, flags=re.DOTALL | re.MULTILINE)
727
- js_code = re.sub(r'^\s*//.*\n?', '', js_code, flags=re.MULTILINE)
728
-
729
- # Removing multi-line comments
730
- js_code = re.sub(r'\s*/\*.*?\*/', '', js_code, flags=re.DOTALL)
731
- # Removing single-line comments
732
- js_code = re.sub(r'\s*//.*', '', js_code)
733
- return js_code
734
-
735
- @classmethod
736
- def advanced_remove_comments_regex(cls, js_code):
737
- # Regex to match strings, either single or double quoted
738
- string_pattern = r'(?:"[^"\\]*(?:\\.[^"\\]*)*"|\'[^\'\\]*(?:\\.[^\'\\]*)*\')'
739
-
740
- # Combined regex pattern to match strings or single/multi-line comments
741
- pattern = r'|'.join([
742
- string_pattern, # match strings first to avoid removing content inside them
743
- r'\/\/[^\n]*', # single line comments
744
- r'\/\*.*?\*\/' # multi-line comments
745
- ])
746
-
747
- def replacer(match):
748
- # If the matched text is a string, return it unchanged
749
- if match.group(0).startswith(('"', "'")):
750
- return match.group(0)
751
- # Otherwise, it's a comment, so return an empty string
752
- return ''
753
-
754
- # Use re.sub with the replacer function
755
- return 1, re.sub(pattern, replacer, js_code, flags=re.DOTALL)
756
-
757
- @classmethod
758
- def remove_js_comment(cls, js_code):
759
- try:
760
- js_code2 = remove_js_comments(js_code)
761
- except BaseException as e:
762
- js_code2 = cls.remove_comments_regex(js_code)
763
- return 1, js_code2
764
-
765
-
766
- if __name__ == '__main__':
767
- pass
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽,梁奕本(js去注释部分)
4
+ # @Email : 877362867@qq.com, https://lyeebn.gitee.io/technology-shop/HeyBoss.html
5
+ # @Date : 2023/10/20
6
+
7
+ from collections import Counter
8
+ import re
9
+ import textwrap
10
+ import os
11
+
12
+ from jinja2 import Template
13
+
14
+ try:
15
+ import jsbeautifier
16
+ except ModuleNotFoundError:
17
+ pass
18
+
19
+ from pyxllib.file.specialist import XlPath
20
+ from pyxllib.prog.cachetools import xlcache
21
+
22
+
23
+ def __1_删注释功能():
24
+ """
25
+ 用编译语法解析方式分析,清理JS中的注释,支持嵌套
26
+
27
+ Usage:
28
+ 1、A simple function
29
+ from pyxllib.text.jscode import dropJScomment
30
+ dropJScomment(jsSourceCodeAsString)
31
+
32
+ 2、Object
33
+ from pyxllib.text.jscode import JSParser
34
+ js = JSParser(jsSourceCode)
35
+ jsc = js.clearComment()
36
+ """
37
+
38
+
39
+ def 删注释周围留空(c, arr): # 应急
40
+ arr.reverse()
41
+ for i in arr:
42
+ # 先用这个快速处理,有空再优化: https://blog.csdn.net/cooco369/article/details/82994932
43
+ # c = c[:i].rstrip() + '%' + c[i:].lstrip() # debug,定位
44
+ 有回车 = False
45
+ l = r = 上一回车处 = i
46
+ r = i + 1
47
+ len_c = len(c)
48
+ while len_c > l > 0:
49
+ l -= 1
50
+ ci = c[l]
51
+ if ci == '\n': # \r已统一为\n
52
+ 有回车 = True
53
+ elif ci not in '\t \v': # TODO 中文空格行不行
54
+ break
55
+ while r < len_c:
56
+ ci = c[r]
57
+ if ci in '\n\r': # \r已统一为\n
58
+ 有回车 = True
59
+ 上一回车处 = r # 保持缩进,但有 Bug 灵异,难道是 ?
60
+ elif ci not in '\t \v': # TODO 中文空格行不行
61
+ break
62
+ r += 1
63
+ # print(r)
64
+ c = c[:l + 1] + ('\n' if 有回车 else '') + c[上一回车处:] # 有必要多留个空格吗
65
+ # 已知 BUG:连续注释后的缩进无法保持,但不影响 JS 代码逻辑就是了
66
+ return c
67
+
68
+
69
+ def 回溯区分正则除号(c):
70
+ # 此函数参数需要提前处理:高偶合,非内聚
71
+ # 原理:除号是二元运算符,其前面必有一个量:数值(可以是变量名或字符串字面量),这不太好穷举
72
+ # 而正则为一字面量,前面可能必为某种运算符 = + ,或特殊符号:& | 逻辑? 括号 ( , 参数,[ { 对象 : ,或 ; 语句结束符,回车。有个坑:折行要注意。
73
+ # 摆脱对正则的依赖,变成无第三无依赖
74
+ # True 为 正则, False 为除号
75
+ i = len(c)
76
+ while i > 0:
77
+ i -= 1
78
+ ci = c[i]
79
+ if ci in '\t \v':
80
+ continue # 暂时无法给出结论
81
+ elif ci in '\n\r': # 折行,暂时无法给出结论,其实可以与如上合并,区别可能是回车前可能是已省略的分号
82
+ # return 回溯区分正则除号(c, i=i) # 经验证,js 转义回车仅限于字符串内,运算符后是可折行的, \ 反而语法错误
83
+ continue # 经考虑,还是不必递归了
84
+ elif ci in '{[(=,;?:&|!*~%^': # todo ++ -- ,注:~ 为非预期操作,但因类型转换语法允许 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_operators
85
+ return True # 正则
86
+ elif ci == '+': # todo ++ --
87
+ i -= 1
88
+ if i > 0 and c[i] == '+': return False # 除号 # ++: / 前 为变量 为除尘
89
+ return True # 正则
90
+ elif ci == '-': # 要么 变量--,要反报错
91
+ i -= 1
92
+ if i > 0 and c[i] == '-': return False # 除号 # ++: / 前 为变量 为除尘
93
+ return True # raise BaseException('/ 前单 -') # 实测因类型转换,两边转数值类型,最多是 NaN,而不至于报错
94
+ else:
95
+ return False # 除号,可能是变量,什么的
96
+ return True # 正则
97
+
98
+
99
+ class JSParser:
100
+ def 普通引号(self, 号='"'): # 号:开始结束定界符,下同
101
+ while self.indexPointer < self.jsCodeLength: # 正常会提前 return 见下
102
+ self.indexPointer += 1
103
+ si = self.jsSourceCode[self.indexPointer]
104
+ self.jsWithoutComment += si # 这应该是以下所有情况包括 else 都要的
105
+ if si == 号:
106
+ return
107
+ elif si == '\\': # 转义,提前吃掉, \n'"` 等
108
+ # 即 r += '\\'
109
+ self.indexPointer += 1
110
+ if self.jsSourceCode[
111
+ self.indexPointer + 1] == 号: # 超标报错正好,对应其语法错误,TODO 以后完善,实测 JS, 如果在引号中回车前没\反是其语法错误;就当源文件语法吧
112
+ pass
113
+ self.jsWithoutComment += self.jsSourceCode[self.indexPointer] # 转义后的字符好像都是要吃掉的,不用 if,待深入思考
114
+ elif si == '\n':
115
+ raise BaseException('原稿语法有误》缺右字符串定界:\a' + 号)
116
+
117
+ def 反引号(self, 号='`'):
118
+ while self.indexPointer < self.jsCodeLength: # 正常会提前 return 见下
119
+ self.indexPointer += 1
120
+ si = self.jsSourceCode[self.indexPointer]
121
+ self.jsWithoutComment += si # 这应该是以下所有情况包括 else 都要的
122
+ if si == 号:
123
+ return
124
+ elif si == '\\':
125
+ # 即 r += '\\'
126
+ self.indexPointer += 1
127
+ if self.jsSourceCode[
128
+ self.indexPointer + 1] == 号: # 超标报错正好,对应其语法错误,TODO 以后完善,实测 JS, 如果在引号中回车前没\反是其语法错误;就当源文件语法吧
129
+ pass
130
+ self.jsWithoutComment += self.jsSourceCode[self.indexPointer] # 转义后的字符好像都是要吃掉的,不用 if,待深入思考
131
+ elif si == '$' and self.jsSourceCode[
132
+ self.indexPointer + 1] == '{': # 重要区别 。经实验 ${ 后必有 } 否则报错:g = `2${2+3 7`
133
+ self.indexPointer += 1
134
+ self.jsWithoutComment += '{'
135
+ self.反引号嵌套表达式允许多行注释()
136
+ pass
137
+
138
+ def 反引号嵌套表达式允许多行注释(self): # 适合钻牛角尖, TODO 用 堆栈 结构练手? 这好像可以 递归 main 了,差个 } return
139
+ while self.indexPointer < self.jsCodeLength: # 正常会提前 return 见下
140
+ self.indexPointer += 1
141
+ si = self.jsSourceCode[self.indexPointer]
142
+ if si == '}':
143
+ self.jsWithoutComment += si
144
+ return
145
+ elif si in '"\'': # 吃撑了的嵌套骚操作 si == '"' or si == "'"
146
+ self.jsWithoutComment += si
147
+ self.普通引号(号=si)
148
+ elif si == "`": # `,还能嵌套 String.raw` 算了,不玩了
149
+ self.jsWithoutComment += si
150
+ self.反引号()
151
+ elif si == '/': # 以下这一块逻辑 同 main ?
152
+ self.反斜线然后呢()
153
+ # if s[idx+1] == '*': # 注意这里,嵌套了注释!超标正好报错,有效的 JS 代码 这里不会超标
154
+ # idx += 1
155
+ # 多行注释()
156
+ # elif s[idx+1] == '/':
157
+ # idx += 1
158
+ # 单行注释()
159
+ # else:
160
+ # r += s[idx]
161
+ else:
162
+ self.jsWithoutComment += si
163
+
164
+ def 反斜线然后呢(self): # 共用于【main】与 【反引号嵌套表达式允许多行注释】中的表达式
165
+ 偷看 = self.jsSourceCode[self.indexPointer + 1]
166
+ if 偷看 == '/':
167
+ self.单行注释()
168
+ self.jsWithoutComment += self.注释占位
169
+ elif 偷看 == '*':
170
+ self.jsWithoutComment += self.注释占位
171
+ self.indexPointer += 1
172
+ self.多行注释()
173
+ elif 回溯区分正则除号(
174
+ self.jsWithoutComment): # bool(re.search('[\n\r(\[\{=+,;&?|]([ \t]*|\\[\n\r])*[ \t]*$', self.jsWithoutComment)): # def 区分正则或除号():
175
+ self.jsWithoutComment += '/'
176
+ self.正则()
177
+ else:
178
+ self.jsWithoutComment += '/'
179
+ # 除号不必特殊处理吧()
180
+ # TODO,重要【严重】有没有可能是除号:q = 1 / 2 + /2/ // 除号、正则 、注释并存
181
+ # 如果是除号,那之前应该有数值 \d),不好判断,考虑到 JS 有些隐性的类型转换,如:'6' / 3
182
+ # 如果是正则,往前回溯应该必有 = 或 + ,也可能在函数作为参数 (甚至可能还有变态 通过 \ 加回车,在正则前折行) re 如下
183
+
184
+ def 源反引号(self, 号='`'):
185
+ return self.反引号(号=号) # TODO 先借用,也就差个 \ ,其它有什么区别待考虑
186
+ pass
187
+
188
+ def 单行注释(self):
189
+ # while (s[idx] != '\n' or s[idx] != '\r') and idx < 长 :
190
+ while (self.jsSourceCode[self.indexPointer] not in '\n\r') and self.indexPointer < self.jsCodeLength:
191
+ self.indexPointer += 1
192
+ self.注释location.append(len(self.jsWithoutComment))
193
+ self.jsWithoutComment += '\n' # 补回车于删注释点后,不能反了
194
+
195
+ def 多行注释(self):
196
+ self.indexPointer += 1 # 要吗?加过没
197
+ while not (self.jsSourceCode[self.indexPointer] == '*' and self.jsSourceCode[
198
+ self.indexPointer + 1] == '/'): self.indexPointer += 1
199
+ self.indexPointer += 1
200
+ # r += '\n'
201
+ self.注释location.append(len(self.jsWithoutComment))
202
+
203
+ def 正则(self): # 正常的 JS 代码中 正则中无回车符
204
+ while True: # not (jsSourceCode[self.indexPointer + 1] == '/')
205
+ self.indexPointer += 1 # TODO 放哪里
206
+ si = self.jsSourceCode[self.indexPointer]
207
+ self.jsWithoutComment += si
208
+ if si == '/':
209
+ return
210
+ elif si in '\n\r':
211
+ raise BaseException('正则还能折行?你是哪个老师教的')
212
+ elif self.jsSourceCode[self.indexPointer] == '\\':
213
+ self.indexPointer += 1 # TODO 放哪里
214
+ self.jsWithoutComment += self.jsSourceCode[self.indexPointer]
215
+
216
+ def __init__(self, jsSourceCode):
217
+ # 就传源码吧,文件打开让用户自己去做,不然还得判断是文件名还是字符串,还得判断文件是否存在,还得依赖 os
218
+ self.jsSourceCode = jsSourceCode.replace('\r', '\n') + '\n ' # 防超标,为啥 .strip() 会异常
219
+ self.jsCodeLength = len(jsSourceCode)
220
+ self.indexPointer = -1 # 游标指针
221
+ self.注释占位 = '' # 生僻占位?以便后期删其前 \s
222
+ self.注释location = [] # 记录当时 len(self.jsWithoutComment),改天写吧
223
+ self.jsWithoutComment = False # init 先执行,所以不能执行调此类的其它函数,全写到一个这个函数里也麻烦,缩进了两级,如果要用方法可能重复执行,故用缓存法,
224
+
225
+ def clearComment(self): # 难道这个函数要在放类外吗(以便 init 能调用)
226
+ if self.jsWithoutComment: return self.jsWithoutComment # 已经求值过就用缓存,避免如下代码重复执行。这里一个变量两用:初始 bool 类型 False ,之后存清注释的str结果,类型改变,如果要让 GPT 改为 C++ 可能要注意一下,除了这里,别的地文都没有动态类型
227
+ # 仅第一次计算,
228
+ self.jsWithoutComment = ''
229
+ while self.indexPointer < self.jsCodeLength: # 要注意在哪里加 1 ,统一在处理开头处加吧,让当前处理的指标与相应字符初始一致,特殊情况再特殊加
230
+ self.indexPointer += 1
231
+ si = self.jsSourceCode[self.indexPointer]
232
+ if si == '/': # 正则、单/多行注释、除号,idea:注释前可能有多余的 \s,删注释时,可以留个 unicode 记号,到时用正则删
233
+ self.反斜线然后呢()
234
+ continue # 其后的情况都要 r += si, TODO 合并
235
+ elif si in '"\'': # ' " →1 si == '"' or si == "'"
236
+ self.jsWithoutComment += si
237
+ self.普通引号(号=si)
238
+ elif si == "`": # `
239
+ self.jsWithoutComment += si
240
+ self.反引号()
241
+ elif si == "S" and self.jsCodeLength - self.indexPointer > 11 and self.jsSourceCode[
242
+ self.indexPointer:self.indexPointer + 11] == 'String.raw`': # String.raw` 元字符串
243
+ self.jsWithoutComment += si
244
+ self.indexPointer += 10;
245
+ self.jsWithoutComment += 'tring.raw`' # 分两步,以便上面那个能和其它情景合并
246
+ self.源反引号()
247
+ elif si == "\n": # 压缩连续回车(空行)寻找一回车位置,这可能出现 BUG 吗?慎重,危险,不能出现于字符串,注释等中
248
+ tmp = self.indexPointer
249
+ while tmp < self.jsCodeLength:
250
+ tmp += 1
251
+ if self.jsSourceCode[tmp] == '\n':
252
+ self.indexPointer = tmp
253
+ elif self.jsSourceCode[tmp] in '\t ':
254
+ break
255
+ else:
256
+ break
257
+ self.jsWithoutComment += si
258
+ else:
259
+ self.jsWithoutComment += si
260
+ # 格式化字符串好像没什么特殊的
261
+ pass
262
+ self.jsWithoutComment = 删注释周围留空(self.jsWithoutComment, self.注释location).strip()
263
+ # print(self.注释location)
264
+ return self.jsWithoutComment
265
+
266
+
267
+ def remove_js_comments(jsSourceCode): # 对外接口,将本来用得两行代码封装为一行
268
+ js = JSParser(jsSourceCode)
269
+ return js.clearComment()
270
+
271
+
272
+ def __2_类js的as处理功能():
273
+ """
274
+
275
+ 250306周四15:23,airscript_head应该是旧版的jsa工具代码。
276
+ 而get_airscript_head2应该是获得新版jsa工具代码的函数接口。
277
+
278
+ :return:
279
+ """
280
+ pass
281
+
282
+
283
+ airscript_head = r"""
284
+ // 0 基础组件代码(可以放在功能代码之前,也能放在最后面)
285
+
286
+ // 根据提供的 pattern 在 range 中寻找 cell
287
+ // 如果没有提供 range,默认在 ActiveSheet.UsedRange 中寻找
288
+ function findCell(pattern, range = ActiveSheet.UsedRange) {
289
+ const cell = range.Find(pattern, range, xlValues, xlWhole)
290
+ return cell
291
+ }
292
+
293
+ function levenshteinDistance(a, b) {
294
+ const matrix = [];
295
+
296
+ let i;
297
+ for (i = 0; i <= b.length; i++) {
298
+ matrix[i] = [i];
299
+ }
300
+
301
+ let j;
302
+ for (j = 0; j <= a.length; j++) {
303
+ matrix[0][j] = j;
304
+ }
305
+
306
+ for (i = 1; i <= b.length; i++) {
307
+ for (j = 1; j <= a.length; j++) {
308
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
309
+ matrix[i][j] = matrix[i - 1][j - 1];
310
+ } else {
311
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1));
312
+ }
313
+ }
314
+ }
315
+
316
+ return matrix[b.length][a.length];
317
+ }
318
+
319
+ // 根据提供的 pattern 在 range 中寻找 column
320
+ // 如果没有提供 range,默认在 ActiveSheet.UsedRange 中寻找
321
+ function findColumn(pattern, range = ActiveSheet.UsedRange) {
322
+ let cell = findCell(pattern, range); // 首先尝试精确匹配
323
+ if (!cell) { // 如果精确匹配失败,尝试模糊匹配
324
+ let minDistance = Infinity;
325
+ let minDistanceColumn;
326
+ for (let i = 1; i <= range.Columns.Count; i++) {
327
+ let columnName = range.Cells(1, i).Value;
328
+ let distance = levenshteinDistance(pattern, columnName);
329
+ if (distance < minDistance) {
330
+ minDistance = distance;
331
+ minDistanceColumn = i;
332
+ }
333
+ }
334
+ return minDistanceColumn;
335
+ }
336
+ if (cell) { return cell.Column }
337
+ }
338
+
339
+ // 根据提供的 pattern 在 range 中寻找 row
340
+ // 如果没有提供 range,默认在 ActiveSheet.UsedRange 中寻找
341
+ function findRow(pattern, range = ActiveSheet.UsedRange) {
342
+ const cell = findCell(pattern, range)
343
+ if (cell) { return cell.Row }
344
+ }
345
+
346
+ // 判断一个 cells 集合是否为空
347
+ function isEmpty(cells) {
348
+ for (let i = 1; i <= cells.Count; i++) {
349
+ if (cells.Item(i).Text) {
350
+ return false;
351
+ }
352
+ }
353
+ return true;
354
+ }
355
+
356
+ // 获取实际使用的区域
357
+ function getUsedRange(maxRows = 500, maxColumns = 100, startFromA1 = true) {
358
+ /* 允许通过"表格上下文"信息,调整这里数据行的上限500行,或者列上限100列
359
+ 注意,如果分析预设的表格数据在这个限定参数内可以不改
360
+ 只有表格未知,或者明确数据量超过设置时,需要重新调整这里的参数
361
+ 调整的时候千万不要故意凑的刚刚好,可以设置一定的冗余区间
362
+ 比如数据说有4101条,那么这里阈值设置为5000也是可以的,比较保险。
363
+ */
364
+
365
+ // 默认获得的区间,有可能是有冗余的空行,所以还要进一步优化
366
+ let usedRange = ActiveSheet.UsedRange;
367
+
368
+ let lastRow = Math.min(usedRange.Rows.Count, maxRows);
369
+ let lastColumn = Math.min(usedRange.Columns.Count, maxColumns);
370
+
371
+ let firstRow = 1;
372
+ let firstColumn = 1;
373
+
374
+ // 找到最后一个非空行
375
+ for (; lastRow >= firstRow; lastRow--) {
376
+ if (!isEmpty(usedRange.Rows(lastRow).Cells)) {
377
+ break;
378
+ }
379
+ }
380
+
381
+ // 找到最后一个非空列
382
+ for (; lastColumn >= firstColumn; lastColumn--) {
383
+ if (!isEmpty(usedRange.Columns(lastColumn).Cells)) {
384
+ break;
385
+ }
386
+ }
387
+
388
+ // 如果表格不是从"A1"开始,找到第一个非空行和非空列
389
+ if (!startFromA1) {
390
+ for (; firstRow <= lastRow; firstRow++) {
391
+ if (!isEmpty(usedRange.Rows(firstRow).Cells)) {
392
+ break;
393
+ }
394
+ }
395
+
396
+ for (; firstColumn <= lastColumn; firstColumn++) {
397
+ if (!isEmpty(usedRange.Columns(firstColumn).Cells)) {
398
+ break;
399
+ }
400
+ }
401
+ }
402
+
403
+ // 创建一个新的 Range 对象,它只包含非空的行和列
404
+ let newUsedRange = ActiveSheet.Range(
405
+ usedRange.Cells(firstRow, firstColumn),
406
+ usedRange.Cells(lastRow, lastColumn)
407
+ );
408
+
409
+ return newUsedRange; // 返回新的实际数据区域
410
+ }
411
+
412
+ // 将 Excel 日期转换为 JavaScript 日期
413
+ function xlDateToJSDate(xlDate) {
414
+ return new Date((xlDate - 25569) * 24 * 3600 * 1000);
415
+ }
416
+
417
+ // 判断日期是否在本周
418
+ function isCurrentWeek(date) {
419
+ const today = new Date();
420
+ today.setHours(0, 0, 0, 0); // 把时间设为午夜以准确地比较日期
421
+ const firstDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay()));
422
+ const lastDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay() + 6));
423
+ return date >= firstDayOfWeek && date <= lastDayOfWeek;
424
+ }
425
+
426
+ // 判断日期是否在当前月份
427
+ function isCurrentMonth(date) {
428
+ const currentDate = new Date();
429
+ currentDate.setHours(0, 0, 0, 0); // 把时间设为午夜stdcode以准确地比较日期
430
+ return date.getMonth() === currentDate.getMonth() && date.getFullYear() === currentDate.getFullYear();
431
+ }
432
+
433
+ // 判断日期是否在下周
434
+ function isNextWeek(date) {
435
+ const today = new Date();
436
+ today.setHours(0, 0, 0, 0); // 把时间设为午夜以准确地比较日期
437
+ const nextWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
438
+ return date > today && date <= nextWeek;
439
+ }
440
+
441
+ // 判断日期是否在下个月
442
+ function isNextMonth(date) {
443
+ const today = new Date();
444
+ today.setHours(0, 0, 0, 0); // 把时间设为午夜以准确地比较日期
445
+ const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
446
+ const endDateOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0);
447
+ return date >= nextMonth && date <= endDateOfNextMonth;
448
+ }
449
+ """.strip()
450
+
451
+
452
+ @xlcache()
453
+ def get_airscript_head2(definitions=False):
454
+ """ 原始airscript.js并不是能直接全部运行的代码,里面有些占位变量要替换掉
455
+
456
+ :param bool definitions:
457
+ False: 正常填充变量后,返回全部代码
458
+ True: 填充后,拆分成一个个函数定义的字典返回
459
+ :return:
460
+ """
461
+ s = (XlPath(__file__).parent / 'airscript.js').read_text().strip()
462
+ vars = {
463
+ 'JSA_POST_HOST_URL': os.getenv('JSA_POST_HOST_URL'),
464
+ 'JSA_POST_TOKEN': os.getenv('JSA_POST_TOKEN'),
465
+ 'JSA_POST_DEFAULT_HOST': os.getenv('JSA_POST_DEFAULT_HOST'),
466
+ }
467
+ content = Template(s).render(vars)
468
+ if not definitions:
469
+ return content
470
+ return extract_definitions_with_comments(content + '\n')
471
+
472
+
473
+ class AirScriptCodeFixer:
474
+ @classmethod
475
+ def fix_colors(cls, code_text):
476
+ # 1 一些错误的颜色设置方法
477
+ if re.search(r'(?<!\.)\b(Color.\w+)\b', code_text):
478
+ return 0, code_text
479
+
480
+ # 2 不能像vba那样,直接对颜色设置一个数值
481
+ match = re.search(r'\.Color\s*=\s*(\d+)', code_text)
482
+ if match:
483
+ color_number = int(match.group(1))
484
+ red = color_number % 256
485
+ green = (color_number // 256) % 256
486
+ blue = (color_number // 256 // 256) % 256
487
+ rgb_format = f'RGB({red}, {green}, {blue})'
488
+ code_text = code_text[:match.start(1)] + rgb_format + code_text[match.end(1):]
489
+
490
+ # 3 一些错误的颜色设置方法,进行修正
491
+ configs = {
492
+ '红色': 'RGB(255, 0, 0)',
493
+ '黄色': 'RGB(255, 255, 0)',
494
+ '绿色': 'RGB(0, 255, 0)',
495
+ '蓝色': 'RGB(0, 0, 255)',
496
+ '灰色': 'RGB(128, 128, 128)',
497
+ 'red': 'RGB(255, 0, 0)',
498
+ 'yellow': 'RGB(255, 255, 0)',
499
+ 'green': 'RGB(0, 255, 0)',
500
+ 'blue': 'RGB(0, 0, 255)',
501
+ 'black': 'RGB(0, 0, 0)',
502
+ 'gray': 'RGB(128, 128, 128)',
503
+ 'grey': 'RGB(128, 128, 128)',
504
+ 'purple': 'RGB(128, 0, 128)',
505
+ 'pink': 'RGB(255, 192, 203)',
506
+ 'orange': 'RGB(255, 128, 0)',
507
+ }
508
+
509
+ def replace_color_fmt(m):
510
+ t1, t2 = m.groups()
511
+ t2 = t2.strip('"\'').lower()
512
+ if t2 in configs:
513
+ return f'{t1}{configs[t2]}'
514
+ elif m2 := re.search(r'[a-fA-F0-9]{6}', t2):
515
+ res = f'{t1}RGB({int(m2.group(0)[:2], 16)}, ' \
516
+ f'{int(m2.group(0)[2:4], 16)}, ' \
517
+ f'{int(m2.group(0)[4:], 16)})'
518
+ return res
519
+ return t1 + m.group(2)
520
+
521
+ text = re.sub(r'''(\bColor\s*=\s*)(['"].+?['"])''', replace_color_fmt, code_text)
522
+
523
+ # 4 经过优化仍无法修正的颜色问题
524
+ if re.search(r'''\bColor\s*=\s*['"]''', text):
525
+ # global count_target
526
+ # ms = re.findall(r'''\bColor\s*=\s*(['"].+)''', text)
527
+ # for m in ms:
528
+ # count_target[m] += 1
529
+ return 0, text
530
+
531
+ return 1, text
532
+
533
+ @classmethod
534
+ def fix_miscellaneous(cls, code_text):
535
+ """ 修复其他各种杂项问题 """
536
+ text = code_text
537
+
538
+ # Cannot convert a Symbol value to a string, 一般是对Excel对象使用'+='运算报错
539
+ text = re.sub(r'(\s+)((?:.+)Value2?)\s+(?:\+=)\s+(.+)', r'\1\2 = \2 + \3', text) # 531条
540
+
541
+ # 各种错误的接口调用形式
542
+ text = text.replace('.Range.Find(', '.Find(') # 8条
543
+
544
+ # sort接口问题
545
+ text = re.sub(r'(\.Sort\(.*?,\s+)(-1|0|false)\)', r'\g<1>2)', text) # 328条
546
+
547
+ # 做数据有效性的时候,有时候会有重复的引号嵌套
548
+ text = re.sub(r'''(Formula\d:\s*')"(.+?)"''', r'\1\2', text)
549
+
550
+ # 230907周四19:56,枚举值不用放在字符串中
551
+ text = re.sub(r'''(['"`])(xlCellTypeVisible)\1''', r'\2', text)
552
+
553
+ # 231106周一18:42,range的使用规范性
554
+ text = re.sub(r'Range\(("|\')([A-Z]+|\d+)("|\')\)', r'Range(\1\2:\2\1)', text)
555
+
556
+ return 1, text
557
+
558
+ @classmethod
559
+ def delete_error_record(cls, code_text):
560
+ return 1, code_text
561
+
562
+ @classmethod
563
+ def check_assistant_content(cls, code_text):
564
+ text = code_text
565
+
566
+ global count_target
567
+ pieces = re.findall(r'[a-zA-Z_\d\.]+\.Columns', text)
568
+ count_target += Counter([x.strip() for x in pieces])
569
+
570
+ # Columns前一般用ActiveSheet就行了
571
+
572
+ return 1, text
573
+
574
+ @classmethod
575
+ def simplify_advtools(cls, code_text):
576
+ """ 移除高级工具函数代码,用其他更简洁的方式取代 """
577
+ text = code_text
578
+ text = text.replace('getUsedRange()', 'ActiveSheet.UsedRange')
579
+ text = re.sub(r'''findCell\(((['"]).+?\2)(, [a-zA-Z]+)?\)''',
580
+ r'ActiveSheet.UsedRange.Find(\1)', text)
581
+ text = re.sub(r'''findColumn\(((['"]).+?\2)(, [a-zA-Z]+)?\)''',
582
+ r'ActiveSheet.UsedRange.Find(\1).Column', text)
583
+ text = re.sub(r'''findRow\(((['"]).+?\2(, [a-zA-Z]+)?)\)''',
584
+ r'ActiveSheet.UsedRange.Find(\1).Row', text)
585
+
586
+ return 1, text
587
+
588
+ @classmethod
589
+ def simplify_code(cls, code_text, indent=4):
590
+ """ 代码简化,去掉一些冗余写法
591
+
592
+ 包括代码美化,默认缩进是4,但在训练阶段,建议默认缩进是2
593
+ """
594
+ # 1 代码精简
595
+ code_text = re.sub(r'Application\.(WorksheetFunction|ActiveWorkbook|ActiveSheet|Sheets|Range|Workbook)', r'\1',
596
+ code_text)
597
+ code_text = re.sub(r'Workbook\.(Sheets)', r'\1', code_text)
598
+ code_text = re.sub(r'ActiveSheet\.(Range|Rows|Columns|Cells)', r'\1', code_text)
599
+ code_text = re.sub(r'(\w+)\.(Row|Column)\s*\+\s*\1\.\2s\.Count\s*-\s*1', r'\1.\2End', code_text)
600
+ code_text = re.sub(r'\bvar\b', 'let', code_text)
601
+ code_text = code_text.replace('Sheets.Item(', 'Sheets(')
602
+ code_text = re.sub(r'Application.Enum.\w+.(\w+)', r'\1', code_text)
603
+
604
+ # 2 代码美化
605
+ opts = jsbeautifier.default_options()
606
+ opts.indent_size = indent
607
+ code_text = jsbeautifier.beautify(code_text, opts)
608
+
609
+ return 1, code_text.strip()
610
+
611
+ @classmethod
612
+ def simplify_code2(cls, code_text, indent=4):
613
+ """ 有些规则可能在标注数据中想留着,但训练的时候想删除,则可以调用这个进一步级别的简化 """
614
+ _, code_text = cls.simplify_code(code_text, indent)
615
+ return code_text
616
+
617
+ @classmethod
618
+ def fix_stdcode(cls, code_text):
619
+ """ 更智能的,缺什么组件才补什么组件 """
620
+ # 1 检查依赖补充
621
+ text = code_text
622
+ _, text = cls.simplify_advtools(text)
623
+
624
+ defined_vars = set(re.findall(r'(?:<=^|\b)(?:var|let|const|function)\s+(\w+)(?:\s+|\()', text))
625
+ used_vars = set(re.findall(r'(?<!\.)\b(\w+)\b', text))
626
+
627
+ # 2 提取js中的函数
628
+ def extract_functions(code_string):
629
+ pattern = r"(function\s+(\w+).+?^\})"
630
+ matches = re.findall(pattern, code_string, re.MULTILINE | re.DOTALL)
631
+ return {name: func for func, name in matches}
632
+
633
+ js_funcs = extract_functions(airscript_head)
634
+
635
+ # 3 补充缺失的定义
636
+ pre_additional_code = []
637
+ for name, code in {'xlDateToJSDate': '',
638
+ 'isCurrentWeek': '',
639
+ 'isCurrentMonth': '',
640
+ 'isNextWeek': '',
641
+ 'isNextMonth': '',
642
+ 'usedRange': 'const usedRange = ActiveSheet.UsedRange;',
643
+ 'headerRows': 'const headerRows = usedRange.Rows("1:1");',
644
+ 'firstDataRow': 'const firstDataRow = headerRows.RowEnd + 1;',
645
+ 'lastDataRow': 'const lastRow = usedRange.RowEnd;',
646
+ }.items():
647
+ if name in used_vars and name not in defined_vars:
648
+ if name in js_funcs:
649
+ code = js_funcs[name]
650
+ if code:
651
+ pre_additional_code.append(code)
652
+ used_vars.remove(name)
653
+ else: # 有未定义就使用的变量,这条数据不要了
654
+ return 0, text
655
+ else:
656
+ # 还得再检查一波是不是有叫'xxxColumn'的变量未定义被使用
657
+ logo = True
658
+ for name in used_vars:
659
+ if name.endswith('Column') and name not in defined_vars:
660
+ logo = False
661
+ break
662
+ if logo and pre_additional_code:
663
+ text = '\n'.join(pre_additional_code) + '\n' + text
664
+ return 1, text
665
+
666
+ @classmethod
667
+ def pre_proc(cls, code_text):
668
+ code_text = re.sub(r'^\\n', '', code_text, flags=re.MULTILINE)
669
+ return 1, code_text
670
+
671
+ @classmethod
672
+ def fix_loc_head(cls, code_text):
673
+ """ 修复定位头 """
674
+ m1 = re.search(r'//\s*1([\.\s]+)定位', code_text)
675
+ m2 = re.search(r'//\s*2([\.\s]+)业务功能', code_text)
676
+ if not m1 and m2:
677
+ code_text = '// 1' + m2.group(1) + '定位\n' + code_text
678
+ return 1, code_text
679
+
680
+ @classmethod
681
+ def remove_stdcode(cls, code_text):
682
+ """ 删除开头固定的组件头代码 """
683
+ code_text = re.sub(r'(.*?)(//\s*1[\.\s]+定位)', r'\2', code_text, flags=re.DOTALL)
684
+ code_text = re.sub(r'// 0 基础组件代码(可以放在功能代码之前,也能放在最后面).+?$', '', code_text,
685
+ flags=re.DOTALL)
686
+ return 1, code_text
687
+
688
+ @classmethod
689
+ def fix_texts(cls, code_text):
690
+ """ 修复文本中出现的关键词,描述 """
691
+ s = code_text
692
+ s = s.replace('<表格结构信息描述>', '表格摘要')
693
+ s = s.replace('<孩子:表格摘要>', '表格摘要')
694
+ return 1, s
695
+
696
+ @classmethod
697
+ def fix_base(cls, code_text):
698
+ text = code_text
699
+ for func in [
700
+ cls.simplify_code,
701
+ cls.fix_colors,
702
+ cls.fix_miscellaneous,
703
+ cls.advanced_remove_comments_regex,
704
+ ]:
705
+ status, text = func(text)
706
+ if not status:
707
+ return status, text
708
+ return status, text
709
+
710
+ @classmethod
711
+ def fix_base2(cls, code_text):
712
+ text = code_text
713
+ for func in [
714
+ cls.simplify_code,
715
+ cls.fix_colors,
716
+ cls.fix_miscellaneous,
717
+ ]:
718
+ status, text = func(text)
719
+ if not status:
720
+ return status, text
721
+ return status, text
722
+
723
+ @classmethod
724
+ def fix_all(cls, code_text):
725
+ old_text = code_text
726
+ text = code_text
727
+ for func in [
728
+ cls.simplify_code,
729
+ cls.fix_colors,
730
+ cls.fix_miscellaneous,
731
+ cls.fix_stdcode,
732
+ # cls.advanced_remove_comments_regex,
733
+ ]:
734
+ status, text = func(text)
735
+ if not status:
736
+ return status, text
737
+ # if text != old_text:
738
+ # bcompare(old_text, text)
739
+ # dprint()
740
+ return status, text
741
+
742
+ @classmethod
743
+ def format_hanging_indent(cls, text):
744
+ r""" 优化悬挂缩进的文本排版
745
+
746
+ :param str text: 输入文本
747
+ :return str: 优化后的文本
748
+
749
+ >>> AirScriptCodeFixer.format_hanging_indent('const usedRange = getUsedRange();\\n const headerRows = usedRange.Rows(\'1:1\');')
750
+ 'const usedRange = getUsedRange();\\nconst headerRows = usedRange.Rows(\'1:1\');'
751
+ """
752
+ lines = text.strip().split('\n') # 去掉前后空行并分割成行
753
+ first_line = lines.pop(0) # 取出第1行
754
+ remaining_text = '\n'.join(lines) # 剩余行合并为一个字符串
755
+ dedented_text = textwrap.dedent(remaining_text) # 对剩余行进行反缩进处理
756
+
757
+ return 1, first_line + '\n' + dedented_text # 将处理后的剩余行和第1行拼接回去
758
+
759
+ @classmethod
760
+ def remove_comments_regex(cls, js_code):
761
+ """ 这个代码功能并不严谨,只是一个临时快速方案 """
762
+ js_code = re.sub(r'^\s*/\*.*?\*/\n?', '', js_code, flags=re.DOTALL | re.MULTILINE)
763
+ js_code = re.sub(r'^\s*//.*\n?', '', js_code, flags=re.MULTILINE)
764
+
765
+ # Removing multi-line comments
766
+ js_code = re.sub(r'\s*/\*.*?\*/', '', js_code, flags=re.DOTALL)
767
+ # Removing single-line comments
768
+ js_code = re.sub(r'\s*//.*', '', js_code)
769
+ return js_code
770
+
771
+ @classmethod
772
+ def advanced_remove_comments_regex(cls, js_code):
773
+ # Regex to match strings, either single or double quoted
774
+ string_pattern = r'(?:"[^"\\]*(?:\\.[^"\\]*)*"|\'[^\'\\]*(?:\\.[^\'\\]*)*\')'
775
+
776
+ # Combined regex pattern to match strings or single/multi-line comments
777
+ pattern = r'|'.join([
778
+ string_pattern, # match strings first to avoid removing content inside them
779
+ r'\/\/[^\n]*', # single line comments
780
+ r'\/\*.*?\*\/' # multi-line comments
781
+ ])
782
+
783
+ def replacer(match):
784
+ # If the matched text is a string, return it unchanged
785
+ if match.group(0).startswith(('"', "'")):
786
+ return match.group(0)
787
+ # Otherwise, it's a comment, so return an empty string
788
+ return ''
789
+
790
+ # Use re.sub with the replacer function
791
+ return 1, re.sub(pattern, replacer, js_code, flags=re.DOTALL)
792
+
793
+ @classmethod
794
+ def remove_js_comment(cls, js_code):
795
+ try:
796
+ js_code2 = remove_js_comments(js_code)
797
+ except BaseException as e:
798
+ js_code2 = cls.remove_comments_regex(js_code)
799
+ return 1, js_code2
800
+
801
+
802
+ def __3_js代码结构解析():
803
+ pass
804
+
805
+
806
+ def extract_definitions_with_comments(js_code):
807
+ """ 找出、切分每段函数的定义(包含函数开头的注释)
808
+
809
+ 这里是用正则实现的版本,强制要求函数结束的时候用的是单行}结尾
810
+ 如果实现中间内容也会出现这种单行}结尾,可以想写特殊手段规避开
811
+ """
812
+ pattern = r"""
813
+ # 第1组:前缀注释
814
+ ( (?:(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)|(?://[^\n]*))\s* )*
815
+ # 第2组:声明部分
816
+ ( \b(?:var|let|const|function|class)\b\s+ )
817
+ # 第3组:变量名或函数名
818
+ (\w+).*?
819
+ # 第4组:不以{结尾的行,或者已{结尾的行且后续有配对的}
820
+ ( (?:[^\{]\n) | (?:\{[\s\S]+?(?<=\n\}\n)) )
821
+ """
822
+ matches = re.finditer(pattern, js_code, flags=re.VERBOSE)
823
+
824
+ definitions = {}
825
+ for match in matches:
826
+ identifier = match.group(3).strip() # 根据正则表达式的修改,更新捕获组的索引
827
+ full_definition = match.group(0).strip()
828
+ definitions[identifier] = full_definition
829
+ return definitions
830
+
831
+
832
+ def find_identifiers_in_code(code):
833
+ """ 正则实现的找标识符的版本
834
+
835
+ 用基于esprima的语法树实现的方式,遇到不是那么标准的代码的时候,太多问题和局限了
836
+ 还会多此一举过滤掉注释部分等
837
+ """
838
+ return set(re.findall(r'\b(\w+)\b', code))
839
+
840
+
841
+ def find_direct_dependencies(definitions):
842
+ """
843
+ 查找每个定义中的直接依赖关系。
844
+ 使用 esprima 提取代码中的标识符,并与定义列表求交集。
845
+
846
+ :param definitions: 要输入一组数据是因为只检查这一组内的命名空间的东西
847
+ """
848
+ keys = set(definitions.keys())
849
+ dependencies = {key: [] for key in definitions}
850
+
851
+ for key, code in definitions.items():
852
+ identifiers = find_identifiers_in_code(code)
853
+ direct_deps = identifiers.intersection(keys)
854
+ dependencies[key] = list(direct_deps - {key}) # 排除自身
855
+
856
+ return dependencies
857
+
858
+
859
+ def assemble_dependencies_from_jstools(cur_code, jstools=None, place_tail=False, old_jsa=False):
860
+ """
861
+ 根据输入的 cur_code ,从预设的jstools工具代码库中自动提取所有相关依赖定义
862
+
863
+ :param str cur_node: 当前代码
864
+ :param str jstools: 依赖的工具代码
865
+ :param bool place_tail: 把工具代码放在末尾
866
+ 放在末尾的目的,是类似jsa那样的场景能在开头直接看到关键的业务代码逻辑
867
+
868
+ 一般大部分工具函数都是可以放在末尾的
869
+ 但是要注意也有个别特殊的实现,是以定义变量的模式来使用的,则不能放倒末尾
870
+
871
+ """
872
+ # 1 获得工具代码
873
+ # wps场景支持全局return处理,但这个在编译器里会报错,可以先暴力删掉,不影响我这里的相关处理逻辑
874
+ identifiers_in_input = find_identifiers_in_code(cur_code)
875
+
876
+ if jstools is None:
877
+ definitions = get_airscript_head2(True)
878
+ else:
879
+ definitions = extract_definitions_with_comments(jstools)
880
+ if old_jsa: # 如果使用的是旧版的jsa1.0,需要做个转换处理
881
+ # definitions字典里会有类似 as1_func 这样的key,还会有对应的 func 这样的key
882
+ # 将原本func的删掉,然后把as1_func替换成func的定义
883
+ # 注意字典替换后,value里的代码函数名等也要改掉
884
+ for key in list(definitions.keys()):
885
+ if key.startswith('as1_'):
886
+ definitions[key[4:]] = re.sub(r'(function\s+)as1_', r'\1', definitions[key])
887
+
888
+ # 2 找到所有使用到的符号
889
+ # 初始化结果列表,并按照 definitions 的顺序存储
890
+ visited = set()
891
+ dependencies = find_direct_dependencies(definitions)
892
+
893
+ def resolve_dependencies(identifier):
894
+ """递归解决依赖,确保按照 definitions 的顺序添加"""
895
+ if identifier in visited:
896
+ return
897
+ visited.add(identifier)
898
+ for dep in dependencies[identifier]:
899
+ resolve_dependencies(dep)
900
+
901
+ # 从输入代码的标识符开始,递归查找依赖
902
+ for identifier in set(definitions.keys()).intersection(identifiers_in_input):
903
+ resolve_dependencies(identifier)
904
+
905
+ # 3 拼接代码
906
+ required_code = [definitions[identifier] for identifier in definitions if identifier in visited]
907
+ if place_tail:
908
+ # required_code.insert(0, '\n\n// 以下是工具代码')
909
+ required_code.insert(0, '\n\nfunction __x_工具代码() {\n}')
910
+ required_code.insert(0, cur_code)
911
+ else:
912
+ required_code.append(cur_code)
913
+
914
+ return "\n\n".join(required_code)
915
+
916
+
917
+ if __name__ == '__main__':
918
+ # 1 检查正则匹配从代码提取的结构化字典
919
+ # d = get_airscript_head2(True)
920
+
921
+ # 2 检查使用runIsolatedPyScript函数时提取依赖项的具体效果
922
+ print(assemble_dependencies_from_jstools('runIsolatedPyScript'))