itp-interface 1.0.0__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 (485) hide show
  1. itp_interface/__init__.py +0 -0
  2. itp_interface/agent/__init__.py +0 -0
  3. itp_interface/agent/simple_proof_agent.py +100 -0
  4. itp_interface/coq_ser_api/__init__.py +165 -0
  5. itp_interface/coq_ser_api/contexts.py +283 -0
  6. itp_interface/coq_ser_api/coq_agent.py +459 -0
  7. itp_interface/coq_ser_api/coq_backend.py +135 -0
  8. itp_interface/coq_ser_api/coq_util.py +839 -0
  9. itp_interface/coq_ser_api/example.py +67 -0
  10. itp_interface/coq_ser_api/lsp_backend.py +375 -0
  11. itp_interface/coq_ser_api/py.typed +0 -0
  12. itp_interface/coq_ser_api/serapi_backend.py +841 -0
  13. itp_interface/coq_ser_api/util.py +145 -0
  14. itp_interface/coq_ser_api_old/__init__.py +2583 -0
  15. itp_interface/coq_ser_api_old/contexts.py +172 -0
  16. itp_interface/coq_ser_api_old/util.py +146 -0
  17. itp_interface/lean_server/__init__.py +0 -0
  18. itp_interface/lean_server/commands.py +484 -0
  19. itp_interface/lean_server/lean3_search_tool.py +358 -0
  20. itp_interface/lean_server/lean4_repl_interface.py +151 -0
  21. itp_interface/lean_server/lean4_utils.py +255 -0
  22. itp_interface/lean_server/lean_cmd_server.py +111 -0
  23. itp_interface/lean_server/lean_context.py +60 -0
  24. itp_interface/lean_server/lean_sync_server.py +174 -0
  25. itp_interface/lean_server/lean_utils.py +199 -0
  26. itp_interface/lean_server/py.typed +1 -0
  27. itp_interface/main/__init__.py +0 -0
  28. itp_interface/main/config/afp_data_gen.yaml +14 -0
  29. itp_interface/main/config/benchmark/CompCert.yaml +366 -0
  30. itp_interface/main/config/benchmark/GeoCoq.yaml +930 -0
  31. itp_interface/main/config/benchmark/UniMath.yaml +2690 -0
  32. itp_interface/main/config/benchmark/afp_isabelle.yaml +29200 -0
  33. itp_interface/main/config/benchmark/agent_proverbot_hard.yaml +247 -0
  34. itp_interface/main/config/benchmark/category-theory.yaml +470 -0
  35. itp_interface/main/config/benchmark/compcert_118_subset.yaml +148 -0
  36. itp_interface/main/config/benchmark/compcert_benchmark.yaml +36 -0
  37. itp_interface/main/config/benchmark/compcert_benchmark_hard.yaml +498 -0
  38. itp_interface/main/config/benchmark/compcert_benchmark_hard_1.yaml +55 -0
  39. itp_interface/main/config/benchmark/compcert_benchmark_hard_2.yaml +24 -0
  40. itp_interface/main/config/benchmark/compcert_benchmark_hard_3.yaml +95 -0
  41. itp_interface/main/config/benchmark/compcert_benchmark_hard_7_per_cent.yaml +78 -0
  42. itp_interface/main/config/benchmark/compcert_benchmark_test.yaml +38 -0
  43. itp_interface/main/config/benchmark/compcert_benchmark_train.yaml +340 -0
  44. itp_interface/main/config/benchmark/leandojo_novel_premises_test.yaml +2908 -0
  45. itp_interface/main/config/benchmark/leandojo_novel_premises_train.yaml +98645 -0
  46. itp_interface/main/config/benchmark/leandojo_novel_premises_val.yaml +2912 -0
  47. itp_interface/main/config/benchmark/leandojo_random.yaml +2889 -0
  48. itp_interface/main/config/benchmark/leandojo_random_test.yaml +2421 -0
  49. itp_interface/main/config/benchmark/leandojo_random_train.yaml +62729 -0
  50. itp_interface/main/config/benchmark/leandojo_random_val.yaml +2504 -0
  51. itp_interface/main/config/benchmark/math-comp.yaml +200 -0
  52. itp_interface/main/config/benchmark/miniF2F_test.yaml +12 -0
  53. itp_interface/main/config/benchmark/miniF2F_test_aime.yaml +27 -0
  54. itp_interface/main/config/benchmark/miniF2F_test_algebra.yaml +30 -0
  55. itp_interface/main/config/benchmark/miniF2F_test_amc12.yaml +57 -0
  56. itp_interface/main/config/benchmark/miniF2F_test_few_shot_hard.yaml +231 -0
  57. itp_interface/main/config/benchmark/miniF2F_test_imo.yaml +32 -0
  58. itp_interface/main/config/benchmark/miniF2F_test_induction.yaml +20 -0
  59. itp_interface/main/config/benchmark/miniF2F_test_mathd_algebra.yaml +82 -0
  60. itp_interface/main/config/benchmark/miniF2F_test_mathd_algebra_hard.yaml +72 -0
  61. itp_interface/main/config/benchmark/miniF2F_test_mathd_numbertheory.yaml +72 -0
  62. itp_interface/main/config/benchmark/miniF2F_test_numbertheory.yaml +20 -0
  63. itp_interface/main/config/benchmark/minicompcert_benchmark_1.yaml +14 -0
  64. itp_interface/main/config/benchmark/proverbot_hard.yaml +104 -0
  65. itp_interface/main/config/benchmark/re_prover.yaml +66 -0
  66. itp_interface/main/config/benchmark/re_prover_hard.yaml +41 -0
  67. itp_interface/main/config/benchmark/re_prover_very_hard.yaml +22 -0
  68. itp_interface/main/config/benchmark/reprover_with_retrieval.yaml +73 -0
  69. itp_interface/main/config/benchmark/reprover_with_retrieval_hard.yaml +30 -0
  70. itp_interface/main/config/benchmark/reprover_with_retrieval_neg.yaml +195 -0
  71. itp_interface/main/config/benchmark/simple_benchmark_1.yaml +24 -0
  72. itp_interface/main/config/benchmark/simple_benchmark_8.yaml +50 -0
  73. itp_interface/main/config/benchmark/simple_benchmark_9.yaml +65 -0
  74. itp_interface/main/config/benchmark/simple_benchmark_isabelle.yaml +18 -0
  75. itp_interface/main/config/benchmark/simple_benchmark_lean.yaml +12 -0
  76. itp_interface/main/config/benchmark/simple_benchmark_lean_training_data.yaml +12 -0
  77. itp_interface/main/config/benchmark/simple_rl_benchmark_lean.yaml +14 -0
  78. itp_interface/main/config/benchmark/stack_machine.yaml +13 -0
  79. itp_interface/main/config/benchmark/stack_machine_hard.yaml +15 -0
  80. itp_interface/main/config/category_theory_data_gen.yaml +14 -0
  81. itp_interface/main/config/category_theory_data_gen_random.yaml +16 -0
  82. itp_interface/main/config/compcert_data_gen_test.yaml +10 -0
  83. itp_interface/main/config/compcert_data_gen_train.yaml +7 -0
  84. itp_interface/main/config/env_settings/bm25_retrieval.yaml +2 -0
  85. itp_interface/main/config/env_settings/bm25_retrieval_no_dfns.yaml +2 -0
  86. itp_interface/main/config/env_settings/bm25_retrieval_only_local_no_dfns.yaml +2 -0
  87. itp_interface/main/config/env_settings/bm25_retrieval_with_print.yaml +2 -0
  88. itp_interface/main/config/env_settings/bm25_retrieval_with_print_only_local.yaml +2 -0
  89. itp_interface/main/config/env_settings/bm25_retrieval_with_print_only_local_no_dfns.yaml +2 -0
  90. itp_interface/main/config/env_settings/no_retrieval.yaml +2 -0
  91. itp_interface/main/config/experiments.yaml +12 -0
  92. itp_interface/main/config/geo_coq_data_gen.yaml +14 -0
  93. itp_interface/main/config/geo_coq_data_gen_random.yaml +16 -0
  94. itp_interface/main/config/leandojo_random_data_gen.yaml +16 -0
  95. itp_interface/main/config/math_comp_data_gen.yaml +14 -0
  96. itp_interface/main/config/math_comp_data_gen_random.yaml +16 -0
  97. itp_interface/main/config/mathlib_data_gen.yaml +14 -0
  98. itp_interface/main/config/repo/coq_repos.yaml +191 -0
  99. itp_interface/main/config/run_settings/default_coq_data_generation_transforms.yaml +24 -0
  100. itp_interface/main/config/run_settings/default_isabelle_data_generation_transforms.yaml +24 -0
  101. itp_interface/main/config/run_settings/default_lean4_data_generation_transforms.yaml +24 -0
  102. itp_interface/main/config/run_settings/default_lean_data_generation_transforms.yaml +24 -0
  103. itp_interface/main/config/simple_coq_data_gen.yaml +12 -0
  104. itp_interface/main/config/simple_coq_data_gen_random.yaml +17 -0
  105. itp_interface/main/config/simple_lean_data_gen.yaml +12 -0
  106. itp_interface/main/config/simple_rl_lean_data_gen.yaml +12 -0
  107. itp_interface/main/config/uni_math_data_gen.yaml +14 -0
  108. itp_interface/main/config.py +192 -0
  109. itp_interface/main/extract_benchmark_dataset.py +106 -0
  110. itp_interface/main/filter_dataset.py +107 -0
  111. itp_interface/main/install.py +92 -0
  112. itp_interface/main/merge_dataset.py +96 -0
  113. itp_interface/main/run_tool.py +444 -0
  114. itp_interface/pisa/.git +1 -0
  115. itp_interface/pisa/.gitignore +125 -0
  116. itp_interface/pisa/.idea/.gitignore +8 -0
  117. itp_interface/pisa/.idea/ClojureProjectResolveSettings.xml +6 -0
  118. itp_interface/pisa/.idea/codeStyles/Project.xml +7 -0
  119. itp_interface/pisa/.idea/codeStyles/codeStyleConfig.xml +5 -0
  120. itp_interface/pisa/.idea/inspectionProfiles/Project_Default.xml +16 -0
  121. itp_interface/pisa/.idea/libraries/sbt__com_google_android_annotations_4_1_1_4_jar.xml +13 -0
  122. itp_interface/pisa/.idea/libraries/sbt__com_google_api_grpc_proto_google_common_protos_1_17_0_jar.xml +13 -0
  123. itp_interface/pisa/.idea/libraries/sbt__com_google_code_findbugs_jsr305_3_0_2_jar.xml +13 -0
  124. itp_interface/pisa/.idea/libraries/sbt__com_google_code_gson_gson_2_8_6_jar.xml +13 -0
  125. itp_interface/pisa/.idea/libraries/sbt__com_google_errorprone_error_prone_annotations_2_3_4_jar.xml +13 -0
  126. itp_interface/pisa/.idea/libraries/sbt__com_google_guava_failureaccess_1_0_1_jar.xml +13 -0
  127. itp_interface/pisa/.idea/libraries/sbt__com_google_guava_guava_30_0_jre_jar.xml +13 -0
  128. itp_interface/pisa/.idea/libraries/sbt__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava_jar.xml +9 -0
  129. itp_interface/pisa/.idea/libraries/sbt__com_google_j2objc_j2objc_annotations_1_3_jar.xml +13 -0
  130. itp_interface/pisa/.idea/libraries/sbt__com_google_protobuf_protobuf_java_3_12_0_jar.xml +13 -0
  131. itp_interface/pisa/.idea/libraries/sbt__com_google_protobuf_protobuf_java_util_3_12_0_jar.xml +13 -0
  132. itp_interface/pisa/.idea/libraries/sbt__com_lihaoyi_fastparse_2_13_2_3_0_jar.xml +13 -0
  133. itp_interface/pisa/.idea/libraries/sbt__com_lihaoyi_geny_2_13_0_6_0_jar.xml +13 -0
  134. itp_interface/pisa/.idea/libraries/sbt__com_lihaoyi_sourcecode_2_13_0_2_1_jar.xml +13 -0
  135. itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_lenses_2_13_0_10_9_jar.xml +13 -0
  136. itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_scalapb_runtime_2_13_0_10_9_jar.xml +13 -0
  137. itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_scalapb_runtime_grpc_2_13_0_10_9_jar.xml +13 -0
  138. itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_zio_grpc_zio_grpc_core_2_13_0_4_2_jar.xml +13 -0
  139. itp_interface/pisa/.idea/libraries/sbt__com_thoughtworks_paranamer_paranamer_2_8_jar.xml +13 -0
  140. itp_interface/pisa/.idea/libraries/sbt__commons_io_commons_io_2_8_0_jar.xml +13 -0
  141. itp_interface/pisa/.idea/libraries/sbt__de_unruh_java_patterns_0_1_0_jar.xml +13 -0
  142. itp_interface/pisa/.idea/libraries/sbt__de_unruh_scala_isabelle_2_13_master_SNAPSHOT_jar.xml +13 -0
  143. itp_interface/pisa/.idea/libraries/sbt__dev_zio_izumi_reflect_2_13_1_0_0_M9_jar.xml +13 -0
  144. itp_interface/pisa/.idea/libraries/sbt__dev_zio_izumi_reflect_thirdparty_boopickle_shaded_2_13_1_0_0_M9_jar.xml +13 -0
  145. itp_interface/pisa/.idea/libraries/sbt__dev_zio_zio_2_13_1_0_3_jar.xml +13 -0
  146. itp_interface/pisa/.idea/libraries/sbt__dev_zio_zio_stacktracer_2_13_1_0_3_jar.xml +13 -0
  147. itp_interface/pisa/.idea/libraries/sbt__dev_zio_zio_streams_2_13_1_0_3_jar.xml +13 -0
  148. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_api_1_34_0_jar.xml +13 -0
  149. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_context_1_34_0_jar.xml +13 -0
  150. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_core_1_34_0_jar.xml +13 -0
  151. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_netty_1_34_0_jar.xml +13 -0
  152. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_protobuf_1_34_0_jar.xml +13 -0
  153. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_protobuf_lite_1_34_0_jar.xml +13 -0
  154. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_services_1_34_0_jar.xml +13 -0
  155. itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_stub_1_34_0_jar.xml +13 -0
  156. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_buffer_4_1_51_Final_jar.xml +13 -0
  157. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_4_1_51_Final_jar.xml +13 -0
  158. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_http2_4_1_51_Final_jar.xml +13 -0
  159. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_http_4_1_51_Final_jar.xml +13 -0
  160. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_socks_4_1_51_Final_jar.xml +13 -0
  161. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_common_4_1_51_Final_jar.xml +13 -0
  162. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_handler_4_1_51_Final_jar.xml +13 -0
  163. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_handler_proxy_4_1_51_Final_jar.xml +13 -0
  164. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_resolver_4_1_51_Final_jar.xml +13 -0
  165. itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_transport_4_1_51_Final_jar.xml +13 -0
  166. itp_interface/pisa/.idea/libraries/sbt__io_perfmark_perfmark_api_0_19_0_jar.xml +13 -0
  167. itp_interface/pisa/.idea/libraries/sbt__net_java_dev_jna_jna_5_3_1_jar.xml +13 -0
  168. itp_interface/pisa/.idea/libraries/sbt__net_liftweb_lift_json_2_13_3_4_3_jar.xml +13 -0
  169. itp_interface/pisa/.idea/libraries/sbt__org_apache_commons_commons_lang3_3_11_jar.xml +13 -0
  170. itp_interface/pisa/.idea/libraries/sbt__org_apache_commons_commons_text_1_9_jar.xml +13 -0
  171. itp_interface/pisa/.idea/libraries/sbt__org_checkerframework_checker_qual_3_5_0_jar.xml +13 -0
  172. itp_interface/pisa/.idea/libraries/sbt__org_codehaus_mojo_animal_sniffer_annotations_1_18_jar.xml +13 -0
  173. itp_interface/pisa/.idea/libraries/sbt__org_jetbrains_annotations_20_1_0_jar.xml +13 -0
  174. itp_interface/pisa/.idea/libraries/sbt__org_jline_jline_3_16_0_jar.xml +13 -0
  175. itp_interface/pisa/.idea/libraries/sbt__org_log4s_log4s_2_13_1_9_0_jar.xml +13 -0
  176. itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_modules_scala_collection_compat_2_13_2_1_6_jar.xml +13 -0
  177. itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_modules_scala_xml_2_13_1_3_0_jar.xml +13 -0
  178. itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scala_compiler_2_13_4_jar.xml +13 -0
  179. itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scala_library_2_13_4_jar.xml +23 -0
  180. itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scala_reflect_2_13_4_jar.xml +13 -0
  181. itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scalap_2_13_4_jar.xml +13 -0
  182. itp_interface/pisa/.idea/libraries/sbt__org_scalaz_scalaz_core_2_13_7_3_2_jar.xml +13 -0
  183. itp_interface/pisa/.idea/libraries/sbt__org_slf4j_slf4j_api_1_7_30_jar.xml +13 -0
  184. itp_interface/pisa/.idea/libraries/sbt__org_slf4j_slf4j_simple_1_7_30_jar.xml +13 -0
  185. itp_interface/pisa/.idea/misc.xml +7 -0
  186. itp_interface/pisa/.idea/modules/PISA-build.iml +127 -0
  187. itp_interface/pisa/.idea/modules/PISA.iml +94 -0
  188. itp_interface/pisa/.idea/modules.xml +9 -0
  189. itp_interface/pisa/.idea/other.xml +6 -0
  190. itp_interface/pisa/.idea/sbt.xml +20 -0
  191. itp_interface/pisa/.idea/scala_compiler.xml +6 -0
  192. itp_interface/pisa/.idea/uiDesigner.xml +124 -0
  193. itp_interface/pisa/.idea/vcs.xml +6 -0
  194. itp_interface/pisa/.scalafmt.conf +2 -0
  195. itp_interface/pisa/LICENSE +29 -0
  196. itp_interface/pisa/README.md +262 -0
  197. itp_interface/pisa/build.sbt +49 -0
  198. itp_interface/pisa/build.sh +26 -0
  199. itp_interface/pisa/command_generation/close_gaps.py +44 -0
  200. itp_interface/pisa/command_generation/conjecture_normal_order.py +62 -0
  201. itp_interface/pisa/command_generation/conjecturer_command_generator.py +36 -0
  202. itp_interface/pisa/command_generation/create_dirs.py +11 -0
  203. itp_interface/pisa/command_generation/find_std.py +67 -0
  204. itp_interface/pisa/command_generation/generate_build_commands_afp.py +15 -0
  205. itp_interface/pisa/command_generation/generate_build_commands_std.py +15 -0
  206. itp_interface/pisa/command_generation/generate_commands_afp.py +103 -0
  207. itp_interface/pisa/command_generation/generate_commands_mini.py +73 -0
  208. itp_interface/pisa/command_generation/generate_commands_std.py +69 -0
  209. itp_interface/pisa/command_generation/generate_hammer_extraction_text.py +5 -0
  210. itp_interface/pisa/command_generation/hammer_command_generator.py +40 -0
  211. itp_interface/pisa/command_generation/hp_search_command_generator.py +63 -0
  212. itp_interface/pisa/command_generation/oracle_command_generator.py +56 -0
  213. itp_interface/pisa/command_generation/search_command_generator.py +69 -0
  214. itp_interface/pisa/command_generation/summarise_problem_names.py +45 -0
  215. itp_interface/pisa/command_generation/tpu_hp_search.py +75 -0
  216. itp_interface/pisa/docker/Dockerfile +34 -0
  217. itp_interface/pisa/docker/docker_tutorial.md +64 -0
  218. itp_interface/pisa/eval_setup/copy_isabelle.py +42 -0
  219. itp_interface/pisa/eval_setup/copy_pisa_jars.py +18 -0
  220. itp_interface/pisa/mesh_transformer_utils/tokenization.py +86 -0
  221. itp_interface/pisa/project/build.properties +1 -0
  222. itp_interface/pisa/project/plugins.sbt +5 -0
  223. itp_interface/pisa/requirements.txt +4 -0
  224. itp_interface/pisa/scripts/extract_last_k_steps.py +28 -0
  225. itp_interface/pisa/scripts/extract_proof_corpus.py +26 -0
  226. itp_interface/pisa/scripts/gather_hammer_results.py +27 -0
  227. itp_interface/pisa/scripts/length_in_char_stats.py +20 -0
  228. itp_interface/pisa/scripts/mix.py +127 -0
  229. itp_interface/pisa/scripts/results_stat.py +52 -0
  230. itp_interface/pisa/scripts/test_array_job.sh +34 -0
  231. itp_interface/pisa/setup.sh +25 -0
  232. itp_interface/pisa/src/main/protobuf/server.proto +60 -0
  233. itp_interface/pisa/src/main/python/.idea/.gitignore +8 -0
  234. itp_interface/pisa/src/main/python/.idea/inspectionProfiles/Project_Default.xml +18 -0
  235. itp_interface/pisa/src/main/python/.idea/inspectionProfiles/profiles_settings.xml +6 -0
  236. itp_interface/pisa/src/main/python/.idea/misc.xml +4 -0
  237. itp_interface/pisa/src/main/python/.idea/modules.xml +8 -0
  238. itp_interface/pisa/src/main/python/.idea/python.iml +12 -0
  239. itp_interface/pisa/src/main/python/.idea/vcs.xml +6 -0
  240. itp_interface/pisa/src/main/python/conjecturing_parsing/conjecturer_postprocessing.py +59 -0
  241. itp_interface/pisa/src/main/python/data_extraction/extract_data.py +184 -0
  242. itp_interface/pisa/src/main/python/data_extraction/find_premises.py +221 -0
  243. itp_interface/pisa/src/main/python/data_extraction/process_data.py +129 -0
  244. itp_interface/pisa/src/main/python/legacy/PisaFlexibleClient.py +167 -0
  245. itp_interface/pisa/src/main/python/legacy/autof_test.py +74 -0
  246. itp_interface/pisa/src/main/python/legacy/cmd_client.py +23 -0
  247. itp_interface/pisa/src/main/python/legacy/convert_scala_dump_to_test_name_jsons.py +14 -0
  248. itp_interface/pisa/src/main/python/legacy/create_data_txt.py +72 -0
  249. itp_interface/pisa/src/main/python/legacy/create_finetune_tfrecords.py +311 -0
  250. itp_interface/pisa/src/main/python/legacy/demo.py +49 -0
  251. itp_interface/pisa/src/main/python/legacy/evaluate.py +108 -0
  252. itp_interface/pisa/src/main/python/legacy/extract_first_step.py +25 -0
  253. itp_interface/pisa/src/main/python/legacy/get_global_facts.py +35 -0
  254. itp_interface/pisa/src/main/python/legacy/mix_data.py +19 -0
  255. itp_interface/pisa/src/main/python/legacy/one_stage_extraction.py +111 -0
  256. itp_interface/pisa/src/main/python/legacy/prepare_episodic_transitions.py +137 -0
  257. itp_interface/pisa/src/main/python/legacy/prepare_translation_pairs.py +277 -0
  258. itp_interface/pisa/src/main/python/pisa_client.py +322 -0
  259. itp_interface/pisa/src/main/python/server_pb2.py +394 -0
  260. itp_interface/pisa/src/main/python/server_pb2_grpc.py +230 -0
  261. itp_interface/pisa/src/main/python/test_client.py +17 -0
  262. itp_interface/pisa/src/main/python/test_client2.py +79 -0
  263. itp_interface/pisa/src/main/python/utils/filters.py +59 -0
  264. itp_interface/pisa/src/main/python/utils/pisa_server_control.py +29 -0
  265. itp_interface/pisa/src/main/scala/pisa/agent/CheckSyntax.scala +257 -0
  266. itp_interface/pisa/src/main/scala/pisa/agent/DepThms.scala +29 -0
  267. itp_interface/pisa/src/main/scala/pisa/agent/PisaStat.scala +46 -0
  268. itp_interface/pisa/src/main/scala/pisa/agent/RefactorTest.scala +40 -0
  269. itp_interface/pisa/src/main/scala/pisa/agent/RepHammer.scala +95 -0
  270. itp_interface/pisa/src/main/scala/pisa/server/HammFacts.scala +63 -0
  271. itp_interface/pisa/src/main/scala/pisa/server/PisaOS.scala +881 -0
  272. itp_interface/pisa/src/main/scala/pisa/server/PisaOneStage.scala +540 -0
  273. itp_interface/pisa/src/main/scala/pisa/server/PisaOneStageServers.scala +1048 -0
  274. itp_interface/pisa/src/main/scala/pisa/utils/TheoryManager.scala +95 -0
  275. itp_interface/pisa/src/test/python/analyse_debug.py +33 -0
  276. itp_interface/pisa/src/test/python/extract_test_seq2seq.py +53 -0
  277. itp_interface/pisa/src/test/python/extract_test_theorem_ground_truth_indices.py +31 -0
  278. itp_interface/pisa/src/test/python/proof_originality.py +24 -0
  279. itp_interface/pisa/src/test/python/test_command_generator.py +25 -0
  280. itp_interface/pisa/src/test/python/test_model_sequence_accuracy.py +70 -0
  281. itp_interface/pisa/src/test/scala/pisa/Easy.scala +26 -0
  282. itp_interface/pisa/src/test/scala/pisa/TestCurl.scala +82 -0
  283. itp_interface/pisa/src/test/scala/pisa/TestIsa.scala +27 -0
  284. itp_interface/pisa/test.sh +19 -0
  285. itp_interface/pisa/universal_test_theorems.tar.gz +0 -0
  286. itp_interface/repo/build.py +78 -0
  287. itp_interface/repo/clone.py +79 -0
  288. itp_interface/repo/dataset_discovery.py +99 -0
  289. itp_interface/retrieval/__init__.py +0 -0
  290. itp_interface/retrieval/abstraction.py +35 -0
  291. itp_interface/retrieval/coq_bm25_reranker.py +153 -0
  292. itp_interface/retrieval/isabelle_bm25_reranker.py +86 -0
  293. itp_interface/retrieval/lean3_bm25_reranker.py +86 -0
  294. itp_interface/rl/__init__.py +0 -0
  295. itp_interface/rl/abstraction.py +168 -0
  296. itp_interface/rl/proof_action.py +172 -0
  297. itp_interface/rl/proof_state.py +149 -0
  298. itp_interface/rl/proof_tree.py +109 -0
  299. itp_interface/rl/simpl_proof_env_pool.py +16 -0
  300. itp_interface/rl/simple_proof_env.py +713 -0
  301. itp_interface/rl/simple_proof_env_pool.py +591 -0
  302. itp_interface/scripts/setup.sh +228 -0
  303. itp_interface/tools/__init__.py +0 -0
  304. itp_interface/tools/basic_utils.py +172 -0
  305. itp_interface/tools/bin_packing.py +61 -0
  306. itp_interface/tools/cache.py +93 -0
  307. itp_interface/tools/coq_build_spec.py +31 -0
  308. itp_interface/tools/coq_build_tool.py +319 -0
  309. itp_interface/tools/coq_context_helper.py +354 -0
  310. itp_interface/tools/coq_executor.py +508 -0
  311. itp_interface/tools/coq_local_data_generation_transform.py +158 -0
  312. itp_interface/tools/coq_parse_utils.py +154 -0
  313. itp_interface/tools/coq_raw_proofs.py +193 -0
  314. itp_interface/tools/coq_theorem_proof_pair_generation_transform.py +146 -0
  315. itp_interface/tools/coq_training_data_generator.py +76 -0
  316. itp_interface/tools/dynamic_coq_proof_exec.py +220 -0
  317. itp_interface/tools/dynamic_isabelle_proof_exec.py +229 -0
  318. itp_interface/tools/dynamic_lean4_proof_exec.py +236 -0
  319. itp_interface/tools/dynamic_lean_proof_exec.py +228 -0
  320. itp_interface/tools/isabelle_context_helper.py +66 -0
  321. itp_interface/tools/isabelle_executor.py +862 -0
  322. itp_interface/tools/isabelle_local_data_generation_transform.py +149 -0
  323. itp_interface/tools/isabelle_parse_utils.py +131 -0
  324. itp_interface/tools/isabelle_server.py +106 -0
  325. itp_interface/tools/lean4_context_helper.py +72 -0
  326. itp_interface/tools/lean4_local_data_generation_transform.py +122 -0
  327. itp_interface/tools/lean4_sync_executor.py +1193 -0
  328. itp_interface/tools/lean_cmd_executor.py +804 -0
  329. itp_interface/tools/lean_context_helper.py +327 -0
  330. itp_interface/tools/lean_dojo_data_generation_transform.py +206 -0
  331. itp_interface/tools/lean_executor.py +687 -0
  332. itp_interface/tools/lean_local_data_generation_transform.py +136 -0
  333. itp_interface/tools/lean_parse_utils.py +32 -0
  334. itp_interface/tools/log_utils.py +20 -0
  335. itp_interface/tools/proof_exec_callback.py +76 -0
  336. itp_interface/tools/ray_utils.py +265 -0
  337. itp_interface/tools/repl/.git +1 -0
  338. itp_interface/tools/repl/.github/workflows/ci.yml +24 -0
  339. itp_interface/tools/repl/.gitignore +7 -0
  340. itp_interface/tools/repl/.vscode/copyright.code-snippets +13 -0
  341. itp_interface/tools/repl/.vscode/extensions.json +13 -0
  342. itp_interface/tools/repl/.vscode/module-docstring.code-snippets +35 -0
  343. itp_interface/tools/repl/.vscode/settings.json +11 -0
  344. itp_interface/tools/repl/README.md +174 -0
  345. itp_interface/tools/repl/REPL/Frontend.lean +47 -0
  346. itp_interface/tools/repl/REPL/JSON.lean +186 -0
  347. itp_interface/tools/repl/REPL/Lean/ContextInfo.lean +9 -0
  348. itp_interface/tools/repl/REPL/Lean/Environment.lean +31 -0
  349. itp_interface/tools/repl/REPL/Lean/InfoTree/ToJson.lean +114 -0
  350. itp_interface/tools/repl/REPL/Lean/InfoTree.lean +272 -0
  351. itp_interface/tools/repl/REPL/Main.lean +323 -0
  352. itp_interface/tools/repl/REPL/Snapshots.lean +306 -0
  353. itp_interface/tools/repl/REPL/Util/Path.lean +36 -0
  354. itp_interface/tools/repl/REPL/Util/Pickle.lean +44 -0
  355. itp_interface/tools/repl/REPL.lean +4 -0
  356. itp_interface/tools/repl/lake-manifest.json +5 -0
  357. itp_interface/tools/repl/lakefile.lean +15 -0
  358. itp_interface/tools/repl/lean-toolchain +1 -0
  359. itp_interface/tools/repl/test/Mathlib/.gitignore +5 -0
  360. itp_interface/tools/repl/test/Mathlib/H20231110.sh +2 -0
  361. itp_interface/tools/repl/test/Mathlib/ReplMathlibTests.lean +1 -0
  362. itp_interface/tools/repl/test/Mathlib/lake-manifest.json +68 -0
  363. itp_interface/tools/repl/test/Mathlib/lakefile.lean +11 -0
  364. itp_interface/tools/repl/test/Mathlib/lean-toolchain +1 -0
  365. itp_interface/tools/repl/test/Mathlib/test/20240209.expected.out +20 -0
  366. itp_interface/tools/repl/test/Mathlib/test/20240209.in +3 -0
  367. itp_interface/tools/repl/test/Mathlib/test/20240209.lean +4 -0
  368. itp_interface/tools/repl/test/Mathlib/test/H20231020.expected.out +8 -0
  369. itp_interface/tools/repl/test/Mathlib/test/H20231020.in +8 -0
  370. itp_interface/tools/repl/test/Mathlib/test/H20231020.lean +22 -0
  371. itp_interface/tools/repl/test/Mathlib/test/H20231110.expected.out +4 -0
  372. itp_interface/tools/repl/test/Mathlib/test/H20231110.in +4 -0
  373. itp_interface/tools/repl/test/Mathlib/test/H20231115.expected.out +19 -0
  374. itp_interface/tools/repl/test/Mathlib/test/H20231115.in +5 -0
  375. itp_interface/tools/repl/test/Mathlib/test/H20231115_2.expected.out +18 -0
  376. itp_interface/tools/repl/test/Mathlib/test/H20231115_2.in +4 -0
  377. itp_interface/tools/repl/test/Mathlib/test/H20231115_3.expected.out +10 -0
  378. itp_interface/tools/repl/test/Mathlib/test/H20231115_3.in +4 -0
  379. itp_interface/tools/repl/test/Mathlib/test/H20231214.in +9 -0
  380. itp_interface/tools/repl/test/Mathlib/test/H20231214.lean +30 -0
  381. itp_interface/tools/repl/test/Mathlib/test/H20231215.expected.out +4 -0
  382. itp_interface/tools/repl/test/Mathlib/test/H20231215.in +4 -0
  383. itp_interface/tools/repl/test/Mathlib/test/H20231215_2.expected.out +14 -0
  384. itp_interface/tools/repl/test/Mathlib/test/H20231215_2.in +3 -0
  385. itp_interface/tools/repl/test/Mathlib/test/exact.expected.out +37 -0
  386. itp_interface/tools/repl/test/Mathlib/test/exact.in +10 -0
  387. itp_interface/tools/repl/test/Mathlib/test/import_Mathlib.lean +1 -0
  388. itp_interface/tools/repl/test/Mathlib/test/induction.expected.out +29 -0
  389. itp_interface/tools/repl/test/Mathlib/test/induction.in +10 -0
  390. itp_interface/tools/repl/test/Mathlib/test/induction.lean +6 -0
  391. itp_interface/tools/repl/test/Mathlib/test/on_goal.expected.out +22 -0
  392. itp_interface/tools/repl/test/Mathlib/test/on_goal.in +5 -0
  393. itp_interface/tools/repl/test/Mathlib/test/pickle.expected.out +16 -0
  394. itp_interface/tools/repl/test/Mathlib/test/pickle.in +6 -0
  395. itp_interface/tools/repl/test/Mathlib/test/pickle_2.expected.out +4 -0
  396. itp_interface/tools/repl/test/Mathlib/test/pickle_2.in +4 -0
  397. itp_interface/tools/repl/test/Mathlib/test.sh +41 -0
  398. itp_interface/tools/repl/test/all_tactics.expected.out +13 -0
  399. itp_interface/tools/repl/test/all_tactics.in +1 -0
  400. itp_interface/tools/repl/test/by_cases.expected.out +25 -0
  401. itp_interface/tools/repl/test/by_cases.in +8 -0
  402. itp_interface/tools/repl/test/by_cases.lean +4 -0
  403. itp_interface/tools/repl/test/calc.expected.out +32 -0
  404. itp_interface/tools/repl/test/calc.in +1 -0
  405. itp_interface/tools/repl/test/def_eval.expected.out +9 -0
  406. itp_interface/tools/repl/test/def_eval.in +3 -0
  407. itp_interface/tools/repl/test/enableInitializersExecution.expected.out +2 -0
  408. itp_interface/tools/repl/test/enableInitializersExecution.in +1 -0
  409. itp_interface/tools/repl/test/file.expected.out +8 -0
  410. itp_interface/tools/repl/test/file.in +1 -0
  411. itp_interface/tools/repl/test/file.lean +5 -0
  412. itp_interface/tools/repl/test/have_by_sorry.expected.out +28 -0
  413. itp_interface/tools/repl/test/have_by_sorry.in +6 -0
  414. itp_interface/tools/repl/test/import_lean.in +1 -0
  415. itp_interface/tools/repl/test/incomplete.expected.out +18 -0
  416. itp_interface/tools/repl/test/incomplete.in +3 -0
  417. itp_interface/tools/repl/test/incomplete.lean +0 -0
  418. itp_interface/tools/repl/test/infotree.expected.out +20 -0
  419. itp_interface/tools/repl/test/infotree.in +2 -0
  420. itp_interface/tools/repl/test/invalid_tactic.expected.out +20 -0
  421. itp_interface/tools/repl/test/invalid_tactic.in +3 -0
  422. itp_interface/tools/repl/test/name_generator.expected.out +53 -0
  423. itp_interface/tools/repl/test/name_generator.in +18 -0
  424. itp_interface/tools/repl/test/no_goal_sorry.expected.out +11 -0
  425. itp_interface/tools/repl/test/no_goal_sorry.in +1 -0
  426. itp_interface/tools/repl/test/no_goal_sorry_2.expected.out +12 -0
  427. itp_interface/tools/repl/test/no_goal_sorry_2.in +1 -0
  428. itp_interface/tools/repl/test/options.expected.out +17 -0
  429. itp_interface/tools/repl/test/options.in +6 -0
  430. itp_interface/tools/repl/test/pickle_environment.expected.out +8 -0
  431. itp_interface/tools/repl/test/pickle_environment.in +7 -0
  432. itp_interface/tools/repl/test/pickle_environment_with_imports.expected.out +10 -0
  433. itp_interface/tools/repl/test/pickle_environment_with_imports.in +9 -0
  434. itp_interface/tools/repl/test/pickle_open.expected.out +8 -0
  435. itp_interface/tools/repl/test/pickle_open.in +7 -0
  436. itp_interface/tools/repl/test/pickle_open_2.expected.out +4 -0
  437. itp_interface/tools/repl/test/pickle_open_2.in +3 -0
  438. itp_interface/tools/repl/test/pickle_open_scoped.expected.out +18 -0
  439. itp_interface/tools/repl/test/pickle_open_scoped.in +8 -0
  440. itp_interface/tools/repl/test/pickle_open_scoped_2.expected.out +14 -0
  441. itp_interface/tools/repl/test/pickle_open_scoped_2.in +3 -0
  442. itp_interface/tools/repl/test/pickle_proof_state_1.expected.out +26 -0
  443. itp_interface/tools/repl/test/pickle_proof_state_1.in +15 -0
  444. itp_interface/tools/repl/test/pickle_proof_state_2.expected.out +4 -0
  445. itp_interface/tools/repl/test/pickle_proof_state_2.in +3 -0
  446. itp_interface/tools/repl/test/pickle_proof_state_env.expected.out +26 -0
  447. itp_interface/tools/repl/test/pickle_proof_state_env.in +15 -0
  448. itp_interface/tools/repl/test/pickle_scoped_notation.in +16 -0
  449. itp_interface/tools/repl/test/pickle_scoped_notation_2.in +3 -0
  450. itp_interface/tools/repl/test/proof_step.expected.out +18 -0
  451. itp_interface/tools/repl/test/proof_step.in +7 -0
  452. itp_interface/tools/repl/test/readme.expected.out +16 -0
  453. itp_interface/tools/repl/test/readme.in +5 -0
  454. itp_interface/tools/repl/test/sorry_hypotheses.expected.out +16 -0
  455. itp_interface/tools/repl/test/sorry_hypotheses.in +4 -0
  456. itp_interface/tools/repl/test/synthesize_placeholder.expected.out +7 -0
  457. itp_interface/tools/repl/test/synthesize_placeholder.in +1 -0
  458. itp_interface/tools/repl/test/tactic_mode_sorry.expected.out +14 -0
  459. itp_interface/tools/repl/test/tactic_mode_sorry.in +3 -0
  460. itp_interface/tools/repl/test/tactic_sorry.expected.out +12 -0
  461. itp_interface/tools/repl/test/tactic_sorry.in +1 -0
  462. itp_interface/tools/repl/test/term_sorry.expected.out +12 -0
  463. itp_interface/tools/repl/test/term_sorry.in +1 -0
  464. itp_interface/tools/repl/test/trace_simp.expected.out +41 -0
  465. itp_interface/tools/repl/test/trace_simp.in +15 -0
  466. itp_interface/tools/repl/test/unfinished_tactic_block.expected.out +11 -0
  467. itp_interface/tools/repl/test/unfinished_tactic_block.in +1 -0
  468. itp_interface/tools/repl/test/unknown_environment.expected.out +2 -0
  469. itp_interface/tools/repl/test/unknown_environment.in +1 -0
  470. itp_interface/tools/repl/test/unknown_proof_state.expected.out +14 -0
  471. itp_interface/tools/repl/test/unknown_proof_state.in +3 -0
  472. itp_interface/tools/repl/test/unknown_tactic.expected.out +14 -0
  473. itp_interface/tools/repl/test/unknown_tactic.in +3 -0
  474. itp_interface/tools/repl/test/variables.expected.out +26 -0
  475. itp_interface/tools/repl/test/variables.in +5 -0
  476. itp_interface/tools/repl/test.sh +43 -0
  477. itp_interface/tools/run_data_generation_transforms.py +350 -0
  478. itp_interface/tools/theorem_details.py +25 -0
  479. itp_interface/tools/training_data.py +358 -0
  480. itp_interface/tools/training_data_format.py +599 -0
  481. itp_interface-1.0.0.dist-info/METADATA +78 -0
  482. itp_interface-1.0.0.dist-info/RECORD +485 -0
  483. itp_interface-1.0.0.dist-info/WHEEL +4 -0
  484. itp_interface-1.0.0.dist-info/entry_points.txt +3 -0
  485. itp_interface-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,2583 @@
1
+ #!/usr/bin/env python3
2
+ ##########################################################################
3
+ #
4
+ # This file is part of Proverbot9001.
5
+ #
6
+ # Proverbot9001 is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Proverbot9001 is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Proverbot9001. If not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ # Copyright 2019 Alex Sanchez-Stern and Yousef Alhessi
20
+ #
21
+ ##########################################################################
22
+
23
+ import subprocess
24
+ import threading
25
+ import re
26
+ import queue
27
+ import os
28
+ from pathlib import Path
29
+ import argparse
30
+ import sys
31
+ import signal
32
+ import functools
33
+ from dataclasses import dataclass
34
+ import contextlib
35
+
36
+ from typing import (List, Any, Optional, cast, Tuple, Union, Iterable,
37
+ Iterator, Pattern, Match, Dict, TYPE_CHECKING)
38
+ from tqdm import tqdm
39
+ # These dependencies is in pip, the python package manager
40
+ from pampy import match, _, TAIL
41
+
42
+ if TYPE_CHECKING:
43
+ from sexpdata import Sexp
44
+ from sexpdata import Symbol, loads, dumps, ExpectClosingBracket
45
+ from .util import (split_by_char_outside_matching, eprint, mybarfmt,
46
+ hash_file, sighandler_context, unwrap, progn,
47
+ parseSexpOneLevel)
48
+ from .contexts import ScrapedTactic, TacticContext, Obligation, ProofContext, SexpObligation
49
+
50
+
51
+ def set_parseSexpOneLevel_fn(newfn) -> None:
52
+ global parseSexpOneLevel
53
+ parseSexpOneLevel = newfn
54
+
55
+
56
+ # Some Exceptions to throw when various responses come back from coq
57
+ @dataclass
58
+ class SerapiException(Exception):
59
+ msg: Union['Sexp', str]
60
+
61
+
62
+ @dataclass
63
+ class AckError(SerapiException):
64
+ pass
65
+
66
+
67
+ @dataclass
68
+ class CompletedError(SerapiException):
69
+ pass
70
+
71
+
72
+ @dataclass
73
+ class CoqExn(SerapiException):
74
+ pass
75
+
76
+
77
+ @dataclass
78
+ class BadResponse(SerapiException):
79
+ pass
80
+
81
+
82
+ @dataclass
83
+ class NotInProof(SerapiException):
84
+ pass
85
+
86
+
87
+ @dataclass
88
+ class ParseError(SerapiException):
89
+ pass
90
+
91
+
92
+ @dataclass
93
+ class LexError(SerapiException):
94
+ pass
95
+
96
+
97
+ @dataclass
98
+ class TimeoutError(SerapiException):
99
+ pass
100
+
101
+
102
+ @dataclass
103
+ class OverflowError(SerapiException):
104
+ pass
105
+
106
+
107
+ @dataclass
108
+ class UnrecognizedError(SerapiException):
109
+ pass
110
+
111
+
112
+ @dataclass
113
+ class NoSuchGoalError(SerapiException):
114
+ pass
115
+
116
+
117
+ @dataclass
118
+ class CoqAnomaly(SerapiException):
119
+ pass
120
+
121
+
122
+ def raise_(ex):
123
+ raise ex
124
+
125
+
126
+ @dataclass
127
+ class TacticTree:
128
+ children: List[Union['TacticTree', str]]
129
+ isClosed: bool
130
+
131
+ def __repr__(self) -> str:
132
+ result = "["
133
+ for child in self.children:
134
+ result += repr(child)
135
+ result += ","
136
+ result += "]"
137
+ return result
138
+
139
+
140
+ class TacticHistory:
141
+ __tree: TacticTree
142
+ __cur_subgoal_depth: int
143
+ __subgoal_tree: List[List[Obligation]]
144
+
145
+ def __init__(self) -> None:
146
+ self.__tree = TacticTree([], False)
147
+ self.__cur_subgoal_depth = 0
148
+ self.__subgoal_tree = []
149
+
150
+ def openSubgoal(self, background_subgoals: List[Obligation]) -> None:
151
+ curTree = self.__tree
152
+ for i in range(self.__cur_subgoal_depth):
153
+ assert isinstance(curTree.children[-1], TacticTree)
154
+ curTree = curTree.children[-1]
155
+ curTree.children.append(TacticTree([], False))
156
+ self.__cur_subgoal_depth += 1
157
+
158
+ self.__subgoal_tree.append(background_subgoals)
159
+ pass
160
+
161
+ def closeSubgoal(self) -> None:
162
+ curTree = self.__tree
163
+ for i in range(self.__cur_subgoal_depth):
164
+ assert isinstance(curTree.children[-1], TacticTree)
165
+ curTree = curTree.children[-1]
166
+ curTree.isClosed = True
167
+ assert self.__cur_subgoal_depth > 0
168
+ self.__cur_subgoal_depth -= 1
169
+ self.__subgoal_tree.pop()
170
+ pass
171
+
172
+ def curDepth(self) -> int:
173
+ return self.__cur_subgoal_depth
174
+
175
+ def addTactic(self, tactic: str) -> None:
176
+ curTree = self.__tree
177
+ for i in range(self.__cur_subgoal_depth):
178
+ assert isinstance(curTree.children[-1], TacticTree)
179
+ curTree = curTree.children[-1]
180
+ curTree.children.append(tactic)
181
+ pass
182
+
183
+ def removeLast(self, all_subgoals: List[Obligation]) -> None:
184
+ assert len(self.__tree.children) > 0, \
185
+ "Tried to remove from an empty tactic history!"
186
+ curTree = self.__tree
187
+ for i in range(self.__cur_subgoal_depth):
188
+ assert isinstance(curTree.children[-1], TacticTree)
189
+ curTree = curTree.children[-1]
190
+ if len(curTree.children) == 0:
191
+ parent = self.__tree
192
+ for i in range(self.__cur_subgoal_depth-1):
193
+ assert isinstance(parent.children[-1], TacticTree)
194
+ parent = parent.children[-1]
195
+ parent.children.pop()
196
+ self.__cur_subgoal_depth -= 1
197
+ self.__subgoal_tree.pop()
198
+ else:
199
+ lastChild = curTree.children[-1]
200
+ if isinstance(lastChild, str):
201
+ curTree.children.pop()
202
+ else:
203
+ assert isinstance(lastChild, TacticTree)
204
+ self.__cur_subgoal_depth += 1
205
+ lastChild.isClosed = False
206
+ self.__subgoal_tree.append(all_subgoals)
207
+ pass
208
+
209
+ def getCurrentHistory(self) -> List[str]:
210
+ def generate() -> Iterable[str]:
211
+ curTree = self.__tree
212
+ for i in range(self.__cur_subgoal_depth+1):
213
+ yield from (child for child in curTree.children
214
+ if isinstance(child, str))
215
+ if i < self.__cur_subgoal_depth:
216
+ assert isinstance(curTree.children[-1], TacticTree)
217
+ curTree = curTree.children[-1]
218
+ pass
219
+ return list(generate())
220
+
221
+ def getFullHistory(self) -> List[str]:
222
+ def generate(tree: TacticTree) -> Iterable[str]:
223
+ for child in tree.children:
224
+ if isinstance(child, TacticTree):
225
+ yield "{"
226
+ yield from generate(child)
227
+ if child.isClosed:
228
+ yield "}"
229
+ else:
230
+ yield child
231
+ return list(generate(self.__tree))
232
+
233
+ def getAllBackgroundObligations(self) -> List[Obligation]:
234
+ return [item for lst in self.__subgoal_tree for item in reversed(lst)]
235
+
236
+ def getNextCancelled(self) -> str:
237
+ curTree = self.__tree
238
+ assert len(curTree.children) > 0, \
239
+ "Tried to cancel from an empty history"
240
+ for i in range(self.__cur_subgoal_depth):
241
+ assert isinstance(curTree.children[-1], TacticTree)
242
+ curTree = curTree.children[-1]
243
+
244
+ if len(curTree.children) == 0:
245
+ return "{"
246
+ elif isinstance(curTree.children[-1], TacticTree):
247
+ return "}"
248
+ else:
249
+ assert isinstance(curTree.children[-1], str), curTree.children[-1]
250
+ return curTree.children[-1]
251
+
252
+ def __str__(self) -> str:
253
+ return f"depth {self.__cur_subgoal_depth}, {repr(self.__tree)}"
254
+
255
+
256
+ # This is the class which represents a running Coq process with Serapi
257
+ # frontend. It runs its own thread to do the actual passing of
258
+ # characters back and forth from the process, so all communication is
259
+ # asynchronous unless otherwise noted.
260
+ class SerapiInstance(threading.Thread):
261
+ # This takes three parameters: a string to use to run serapi, a
262
+ # list of coq includes which the files we're running on will
263
+ # expect, and a base directory
264
+ def __init__(self, coq_command: List[str], module_name: Optional[str],
265
+ prelude: str,
266
+ timeout: int = 30, use_hammer: bool = False,
267
+ log_outgoing_messages: Optional[str] = None,
268
+ use_human_readable_str : bool = False) -> None:
269
+ try:
270
+ with open(prelude + "/_CoqProject", 'r') as includesfile:
271
+ includes = includesfile.read()
272
+ except FileNotFoundError:
273
+ try:
274
+ with open(prelude + "/Make", 'r') as includesfile:
275
+ includes = includesfile.read()
276
+ except FileNotFoundError:
277
+ eprint(f"Didn't find _CoqProject or Make for {prelude}")
278
+ includes = ""
279
+ self.use_human_readable_str = use_human_readable_str
280
+ self._includes = includes
281
+ self._prelude = prelude
282
+ self._module_name = module_name
283
+ # Set up some threading stuff. I'm not totally sure what
284
+ # daemon=True does, but I think I wanted it at one time or
285
+ # other.
286
+ self.__sema = threading.Semaphore(value=0)
287
+ threading.Thread.__init__(self, daemon=True)
288
+
289
+ setup_opam_env()
290
+ self.version_string = subprocess.run(["sertop", "--version"], stdout=subprocess.PIPE,
291
+ text=True).stdout
292
+ assert self.coq_minor_version() >= 10, f"Versions of Coq before 8.10 are not supported! Currently installed coq is {self.version_string}"
293
+ assert self.coq_minor_version() <= 15, f"Versions of Coq after 8.15 are not supported! Currently installed coq is {self.version_string}"
294
+ if self.coq_minor_version() <= 12:
295
+ self.all_goals_regex = all_goals_regex_10
296
+ else:
297
+ self.all_goals_regex = all_goals_regex_13
298
+ # Open a process to coq, with streams for communicating with
299
+ # it.
300
+ self._proc = subprocess.Popen(coq_command,
301
+ cwd=prelude,
302
+ stdin=subprocess.PIPE,
303
+ stdout=subprocess.PIPE,
304
+ stderr=subprocess.PIPE)
305
+ self._fout = self._proc.stdout
306
+ self._fin = self._proc.stdin
307
+ self.timeout = timeout
308
+ self.log_outgoing_messages = log_outgoing_messages
309
+
310
+ # Initialize some state that we'll use to keep track of the
311
+ # coq state. This way we don't have to do expensive queries to
312
+ # the other process to answer simple questions.
313
+ self.proof_context: Optional[ProofContext] = None
314
+ self.cur_state = 0
315
+ self.tactic_history = TacticHistory()
316
+ self._local_lemmas: List[Tuple[str, bool]] = []
317
+
318
+ # Set up the message queue, which we'll populate with the
319
+ # messages from serapi.
320
+ self.message_queue = queue.Queue() # type: queue.Queue[str]
321
+ # Verbosity is zero until set otherwise
322
+ self.verbose = 0
323
+ # Set the "extra quiet" flag (don't print on failures) to false
324
+ self.quiet = False
325
+ # The messages printed to the *response* buffer by the command
326
+ self.feedbacks: List[Any] = []
327
+ # Start the message queue thread
328
+ self.start()
329
+ # Go through the messages and throw away the initial feedback.
330
+ self._discard_feedback()
331
+ # Stacks for keeping track of the current lemma and module
332
+ self.sm_stack: List[Tuple[str, bool]] = []
333
+
334
+ # Open the top level module
335
+ if module_name and module_name not in ["Parameter", "Prop", "Type"]:
336
+ self.run_stmt(f"Module {module_name}.")
337
+ # Execute the commands corresponding to include flags we were
338
+ # passed
339
+ self._exec_includes(includes, prelude)
340
+ # Unset Printing Notations (to get more learnable goals?)
341
+ self._unset_printing_notations()
342
+
343
+ self._local_lemmas_cache: Optional[List[str]] = None
344
+ self._module_changed = True
345
+
346
+ # Set up CoqHammer
347
+ self.use_hammer = use_hammer
348
+ if self.use_hammer:
349
+ try:
350
+ self.init_hammer()
351
+ except TimeoutError:
352
+ eprint("Failed to initialize hammer!")
353
+ raise
354
+
355
+ # Run a command. This is the main api function for this
356
+ # class. Sends a single command to the running serapi
357
+ # instance. Returns nothing: if you want a response, call one of
358
+ # the other methods to get it.
359
+ def run_stmt(self, stmt: str, timeout: Optional[int] = None,
360
+ force_update_nonfg_goals: bool = False):
361
+ if timeout:
362
+ old_timeout = self.timeout
363
+ self.timeout = timeout
364
+ self._flush_queue()
365
+ eprint("Running statement: " + stmt.lstrip('\n'),
366
+ guard=self.verbose >= 2) # lstrip makes output shorter
367
+ # We need to escape some stuff so that it doesn't get stripped
368
+ # too early.
369
+ stmt = stmt.replace("\\", "\\\\")
370
+ stmt = stmt.replace("\"", "\\\"")
371
+ # Kill the comments early so we can recognize comments earlier
372
+ stmt = kill_comments(stmt)
373
+ # We'll wrap the actual running in a try block so that we can
374
+ # report which command the error came from at this
375
+ # level. Other higher level code might re-catch it.
376
+ context_before = self.proof_context
377
+ # history_len_before = len(self.tactic_history.getFullHistory())
378
+ try:
379
+ # Preprocess_command sometimes turns one command into two,
380
+ # to get around some limitations of the serapi interface.
381
+ for stm in preprocess_command(stmt):
382
+ self._add_potential_module_stack_cmd(stm)
383
+ # Get initial context
384
+ # Send the command
385
+ assert self.message_queue.empty(), self.messages
386
+ self._send_acked("(Add () \"{}\")\n".format(stm))
387
+ # If our statement was just a comment or other thing which gets
388
+ # turned into an empty string, serapi isn't going to give us a
389
+ # new state to update to, so just continue.
390
+ if stm.strip() == "":
391
+ self._get_completed()
392
+ continue
393
+ # Get the response, which indicates what state we put
394
+ # serapi in.
395
+ self._update_state()
396
+
397
+ # Scann till we get a completed message
398
+ while True:
399
+ try:
400
+ # TODO: This is a hack to get around a bug in
401
+ self._get_completed()
402
+ except CompletedError as e:
403
+ if isinstance(e.args, tuple) and len(e.args) > 0 \
404
+ and len(e.args[0]) >= 3 and isinstance(e.args[0][2], list) \
405
+ and isinstance(e.args[0][2][0], Symbol) and e.args[0][2][0].value() == "Added":
406
+ # This is a partially truncated message, so we'll
407
+ # just ignore it and try again
408
+ continue
409
+ else:
410
+ # This is some other error, so we'll re-raise it
411
+ raise
412
+ break
413
+ assert self.message_queue.empty()
414
+
415
+ # Track goal opening/closing
416
+ is_goal_open = re.match(r"\s*(?:\d+\s*:)?\s*[{]\s*", stm)
417
+ is_goal_close = re.match(r"\s*[}]\s*", stm)
418
+ is_unshelve = re.match(r"\s*Unshelve\s*\.\s*", stm)
419
+ is_bullet = re.match(r"\s*[-+*]+", stm)
420
+
421
+ # Execute the statement.
422
+ self._send_acked("(Exec {})\n".format(self.cur_state))
423
+ # Finally, get the result of the command
424
+ self.feedbacks = self._get_feedbacks()
425
+ # Get a new proof context, if it exists
426
+ if is_goal_open:
427
+ self._get_enter_goal_context()
428
+ elif is_goal_close or is_unshelve or is_bullet:
429
+ self._get_proof_context(update_nonfg_goals=True)
430
+ else:
431
+ self._get_proof_context(update_nonfg_goals=force_update_nonfg_goals)
432
+
433
+ if not context_before:
434
+ self._add_potential_local_lemmas(stm)
435
+ if not self.proof_context:
436
+ self._remove_potential_local_lemmas(stm)
437
+ self.tactic_history = TacticHistory()
438
+
439
+ # Manage the tactic history
440
+ if possibly_starting_proof(stm) and self.proof_context:
441
+ self.tactic_history.addTactic(stm)
442
+ elif is_goal_open:
443
+ assert context_before
444
+ self.tactic_history.openSubgoal(
445
+ context_before.fg_goals[1:])
446
+ elif is_goal_close:
447
+ self.tactic_history.closeSubgoal()
448
+ elif self.proof_context:
449
+ # If we saw a new proof context, we're still in a
450
+ # proof so append the command to our prev_tactics
451
+ # list.
452
+ self.tactic_history.addTactic(stm)
453
+
454
+ # If we hit a problem let the user know what file it was in,
455
+ # and then throw it again for other handlers. NOTE: We may
456
+ # want to make this printing togglable (at this level), since
457
+ # sometimes errors are expected.
458
+ except (CoqExn, BadResponse, AckError,
459
+ CompletedError, TimeoutError) as e:
460
+ self._handle_exception(e, stmt)
461
+ finally:
462
+ if self.proof_context and self.verbose >= 3:
463
+ eprint(
464
+ f"History is now {self.tactic_history.getFullHistory()}")
465
+ summarizeContext(self.proof_context)
466
+ if timeout:
467
+ self.timeout = old_timeout
468
+
469
+ # Cancel the last command which was sucessfully parsed by
470
+ # serapi. Even if the command failed after parsing, this will
471
+ # still cancel it. You need to call this after a command that
472
+ # fails after parsing, but not if it fails before.
473
+ def cancel_last(self, force_update_nonfg_goals: bool = False) -> None:
474
+ context_before = self.proof_context
475
+ if self.proof_context:
476
+ if len(self.tactic_history.getFullHistory()) > 0:
477
+ cancelled = self.tactic_history.getNextCancelled()
478
+ eprint(f"Cancelling {cancelled} "
479
+ f"from state {self.cur_state}",
480
+ guard=self.verbose >= 2)
481
+ self._cancel_potential_local_lemmas(cancelled)
482
+ else:
483
+ cancelled = ""
484
+ eprint("Cancelling something (not in history)",
485
+ guard=self.verbose >= 2)
486
+ else:
487
+ cancelled = ""
488
+ eprint(f"Cancelling vernac "
489
+ f"from state {self.cur_state}",
490
+ guard=self.verbose >= 2)
491
+ is_goal_open = re.match(r"\s*(?:\d+\s*:)?\s*[{]\s*", cancelled)
492
+ is_goal_close = re.match(r"\s*[}]\s*", cancelled)
493
+ is_unshelve = re.match(r"\s*Unshelve\s*\.\s*", cancelled)
494
+ is_bullet = re.match(r"\s*[-+*]+", cancelled)
495
+ self.__cancel(update_nonfg_goals=
496
+ is_goal_open or is_goal_close or
497
+ is_unshelve or is_bullet or
498
+ force_update_nonfg_goals)
499
+
500
+ # Fix up the previous tactics
501
+ if context_before and len(self.tactic_history.getFullHistory()) > 0:
502
+ self.tactic_history.removeLast(context_before.fg_goals)
503
+ if not self.proof_context:
504
+ assert len(self.tactic_history.getFullHistory()) == 0, \
505
+ ("History is desynced!", self.tactic_history.getFullHistory())
506
+ self.tactic_history = TacticHistory()
507
+ assert self.message_queue.empty(), self.messages
508
+ if self.proof_context and self.verbose >= 3:
509
+ eprint(f"History is now {self.tactic_history.getFullHistory()}")
510
+ summarizeContext(self.proof_context)
511
+
512
+ def cancel_failed(self) -> None:
513
+ self.__cancel()
514
+
515
+ def run_into_next_proof(self, commands: List[str]) \
516
+ -> Optional[Tuple[List[str], List[str]]]:
517
+ assert not self.proof_context, "We're already in a proof"
518
+ commands_iter = iter(commands)
519
+ commands_run = []
520
+ for command in commands_iter:
521
+ self.run_stmt(command, timeout=60)
522
+ commands_run.append(command)
523
+ if self.proof_context:
524
+ return list(commands_iter), commands_run
525
+ return [], commands_run
526
+
527
+ def finish_proof(self, commands: List[str]) \
528
+ -> Optional[Tuple[List[str], List[str]]]:
529
+ assert self.proof_context, "We're already out of a proof"
530
+ commands_iter = iter(commands)
531
+ commands_run = []
532
+ for command in commands_iter:
533
+ self.run_stmt(command, timeout=60)
534
+ commands_run.append(command)
535
+ if not self.proof_context:
536
+ return list(commands_iter), commands_run
537
+ return None
538
+
539
+ def add_lib(self, origpath: str, logicalpath: str) -> None:
540
+ addStm = ("(Add () \"Add LoadPath \\\"{}\\\" as {}.\")\n"
541
+ .format(origpath, logicalpath))
542
+ self._send_acked(addStm)
543
+ self._update_state()
544
+ self._get_completed()
545
+ self._send_acked("(Exec {})\n".format(self.cur_state))
546
+ self._discard_feedback()
547
+ self._discard_feedback()
548
+ self._get_completed()
549
+
550
+ def add_ocaml_lib(self, path: str) -> None:
551
+ addStm = ("(Add () \"Add ML Path \\\"{}\\\".\")\n"
552
+ .format(path))
553
+ self._send_acked(addStm)
554
+ self._update_state()
555
+ self._get_completed()
556
+ self._send_acked("(Exec {})\n".format(self.cur_state))
557
+ self._discard_feedback()
558
+ self._discard_feedback()
559
+ self._get_completed()
560
+
561
+ def add_lib_rec(self, origpath: str, logicalpath: str) -> None:
562
+ addStm = ("(Add () \"Add Rec LoadPath \\\"{}\\\" as {}.\")\n"
563
+ .format(origpath, logicalpath))
564
+ self._send_acked(addStm)
565
+ self._update_state()
566
+ self._get_completed()
567
+ self._send_acked("(Exec {})\n".format(self.cur_state))
568
+ self._discard_feedback()
569
+ self._discard_feedback()
570
+ self._get_completed()
571
+
572
+ def search_about(self, symbol: str) -> List[str]:
573
+ try:
574
+ # Escape the symbols correctly
575
+ symb = symbol.replace("\\", "\\\\") # Replace \ with \\
576
+ symb = symb.replace("\"", "\\\"") # Replace " with \"
577
+ symb = f"\"{symb}\""
578
+ symb = f"Search {symb}."
579
+ symb = symb.replace("\\", "\\\\") # Replace \ with \\
580
+ symb = symb.replace("\"", "\\\"") # Replace " with \"
581
+ symb = f"\"{symb}\""
582
+ # Escape the backslashes
583
+ # Search \"\\\"b\\\".
584
+ # Search "\"b\\\"".
585
+ # self._send_acked(f"(Query () (Vernac \"Search \\\"{symbol}\\\".\"))")
586
+ self._send_acked(f"(Query () (Vernac {symb}))")
587
+ lemma_msgs: List[str] = []
588
+ nextmsg = self._get_message()
589
+ while match(normalizeMessage(nextmsg),
590
+ ["Feedback", [["doc_id", int], ["span_id", int],
591
+ ["route", int],
592
+ ["contents", ["ProcessingIn", str]]]],
593
+ lambda *args: True,
594
+ ["Feedback", [["doc_id", int], ["span_id", int],
595
+ ["route", int],
596
+ ["contents", "Processed"]]],
597
+ lambda *args: True,
598
+ _,
599
+ lambda *args: False):
600
+ nextmsg = self._get_message()
601
+ while match(normalizeMessage(nextmsg),
602
+ ["Feedback", [["doc_id", int], ["span_id", int],
603
+ ["route", int],
604
+ ["contents", ["Message", TAIL]]]],# "Notice",
605
+ # [], TAIL]]]],
606
+ lambda *args: True,
607
+ _, lambda *args: False):
608
+ oldmsg = nextmsg
609
+ try:
610
+ nextmsg = self._get_message()
611
+ lemma_msgs.append(oldmsg)
612
+ except RecursionError:
613
+ pass
614
+ self._get_completed()
615
+ str_lemmas = [lemma_msg[1][3][1][4][1] for lemma_msg in lemma_msgs]
616
+ return str_lemmas
617
+ except CoqAnomaly as e:
618
+ if e.msg == "Timing out":
619
+ return []
620
+ raise
621
+
622
+ def kill(self) -> None:
623
+ assert self._proc.stdout
624
+ self._proc.terminate()
625
+ self._proc.kill()
626
+ self.__sema.release()
627
+
628
+ def reset(self) -> None:
629
+ self.proof_context = None
630
+ self.tactic_history = TacticHistory()
631
+ self._local_lemmas = []
632
+ self.feedbacks = []
633
+ self.sm_stack = []
634
+ self.run_stmt("Reset Initial.")
635
+ # Open the top level module
636
+ if self._module_name and self._module_name not in ["Parameter", "Prop", "Type"]:
637
+ self.run_stmt(f"Module {self._module_name}.")
638
+ # Execute the commands corresponding to include flags we were
639
+ # passed
640
+ self._exec_includes(self._includes, self._prelude)
641
+ self._local_lemmas_cache = None
642
+
643
+ @property
644
+ def goals(self) -> str:
645
+ if self.proof_context and self.proof_context.fg_goals:
646
+ return self.proof_context.fg_goals[0].goal
647
+ else:
648
+ return ""
649
+
650
+ @property
651
+ def hypotheses(self) -> List[str]:
652
+ if self.proof_context and self.proof_context.fg_goals:
653
+ return self.proof_context.fg_goals[0].hypotheses
654
+ else:
655
+ return []
656
+
657
+ @property
658
+ def prev_tactics(self):
659
+ return self.tactic_history.getCurrentHistory()
660
+
661
+ @property
662
+ def module_stack(self) -> List[str]:
663
+ return [entry for entry, is_section in self.sm_stack
664
+ if not is_section]
665
+
666
+ @property
667
+ def section_stack(self) -> List[str]:
668
+ return [entry for entry, is_section in self.sm_stack
669
+ if is_section]
670
+
671
+ @property
672
+ def local_lemmas(self) -> List[str]:
673
+ def generate() -> Iterable[str]:
674
+ for (lemma, is_section) in self._local_lemmas:
675
+ if lemma.startswith(self.module_prefix):
676
+ yield lemma[len(self.module_prefix):].replace('\n', '')
677
+ else:
678
+ yield lemma.replace('\n', '')
679
+ if self._module_changed:
680
+ self._local_lemmas_cache = list(generate())
681
+ self._module_changed = False
682
+ return unwrap(self._local_lemmas_cache)
683
+
684
+ @property
685
+ def sm_prefix(self) -> str:
686
+ return "".join([sm + "." for sm, is_sec in self.sm_stack])
687
+
688
+ @property
689
+ def module_prefix(self) -> str:
690
+ return "".join([module + "." for module in self.module_stack])
691
+
692
+ @property
693
+ def cur_lemma(self) -> str:
694
+ return self.local_lemmas[-1]
695
+
696
+ @property
697
+ def cur_lemma_name(self) -> str:
698
+ match = re.match(r"\s*([\w'\.]+)\s+:.*", self.cur_lemma)
699
+ assert match, f"Can't match {self.cur_lemma}"
700
+ return match.group(1)
701
+
702
+ def tactic_context(self, relevant_lemmas) -> TacticContext:
703
+ return TacticContext(relevant_lemmas,
704
+ self.prev_tactics,
705
+ self.hypotheses,
706
+ self.goals)
707
+
708
+ @property
709
+ def messages(self):
710
+ return [dumps(msg) for msg in list(self.message_queue.queue)]
711
+
712
+ def check_symbols(self, name: str) -> str:
713
+ try:
714
+ self._send_acked(f"(Query () (Vernac \"Check {name}.\"))")
715
+ try:
716
+ nextmsg = self._get_message()
717
+ except TimeoutError:
718
+ eprint("Timed out waiting for initial message")
719
+ normalized_message = normalizeMessage(nextmsg)
720
+ while match(normalized_message,
721
+ ["Feedback", [["doc_id", int], ["span_id", int],
722
+ ["route", int],
723
+ ["contents", "Processed"]]],
724
+ lambda *args: True,
725
+ _,
726
+ lambda *args: False):
727
+ try:
728
+ nextmsg = self._get_message()
729
+ normalized_message = normalizeMessage(nextmsg)
730
+ except TimeoutError:
731
+ eprint("Timed out waiting for message")
732
+ result = ""
733
+ if len(normalized_message) == 3 and normalized_message[2][0] == "CoqExn":
734
+ self.scan_till_complete()
735
+ return result
736
+ elif len(nextmsg) >= 2 and len(nextmsg[1]) >= 4 and len(nextmsg[1][3]) >= 2 and len(nextmsg[1][3][1]) >= 5 and len(nextmsg[1][3][1][4]) >= 2:
737
+ result = nextmsg[1][3][1][4][1]
738
+ try:
739
+ nextmsg = self._get_message()
740
+ except TimeoutError:
741
+ eprint("Timed out waiting for message")
742
+ match(normalizeMessage(nextmsg),
743
+ ["Answer", int, ["ObjList", []]],
744
+ lambda *args: None,
745
+ _, lambda *args: raise_(UnrecognizedError(nextmsg)))
746
+ try:
747
+ self._get_completed()
748
+ except TimeoutError:
749
+ eprint("Timed out waiting for completed message")
750
+ # try:
751
+ # result = re.sub(r"\s+", " ", self._ppToTermStr(pp_term))
752
+ # except TimeoutError:
753
+ # eprint("Timed out when converting ppterm")
754
+ return result
755
+ else:
756
+ raise Exception("Unrecognized message: " + str(nextmsg))
757
+ except TimeoutError:
758
+ eprint("Timed out when getting full line!")
759
+ return ""
760
+
761
+ def scan_till_complete(self) -> None:
762
+ completed = self._get_message()
763
+ while not match(normalizeMessage(completed),
764
+ ["Answer", int, "Completed"],
765
+ lambda *args: True,
766
+ _,
767
+ lambda *args: False):
768
+ completed = self._get_message()
769
+
770
+ def print_symbols(self, name: str) -> str:
771
+ # This doesn't throw an exception if the symbol doesn't exist
772
+ str_term = ""
773
+ assert self.message_queue.empty(), "Message queue not empty, something is already running!!"
774
+ try:
775
+ self._send_acked(f"(Query () (Vernac \"Print {name}.\"))")
776
+ try:
777
+ nextmsg = self._get_message()
778
+ except TimeoutError:
779
+ eprint("Timed out waiting for initial message")
780
+ normalized_message = normalizeMessage(nextmsg)
781
+ while match(normalized_message,
782
+ ["Feedback", [["doc_id", int], ["span_id", int],
783
+ ["route", int],
784
+ ["contents", "Processed"]]],
785
+ lambda *args: True,
786
+ _,
787
+ lambda *args: False):
788
+ try:
789
+ nextmsg = self._get_message()
790
+ normalized_message = normalizeMessage(nextmsg)
791
+ except TimeoutError:
792
+ eprint("Timed out waiting for message")
793
+ if len(normalized_message) == 3 and normalized_message[2][0] == "CoqExn":
794
+ str_term = ""
795
+ self.scan_till_complete()
796
+ elif len(nextmsg) >= 2 and len(nextmsg[1]) >= 4 and len(nextmsg[1][3]) >= 2 and len(nextmsg[1][3][1]) >= 4 and len(nextmsg[1][3][1][4]) >= 2:
797
+ try:
798
+ str_term = nextmsg[1][3][1][4][1]
799
+ except:
800
+ str_term = ""
801
+ pass
802
+ self.scan_till_complete()
803
+ else:
804
+ str_term = ""
805
+ self.scan_till_complete()
806
+ raise Exception("Unrecognized message: " + str(nextmsg))
807
+ assert isinstance(str_term, str)
808
+ return str_term
809
+ except TimeoutError:
810
+ eprint("Timed out when getting full line!")
811
+ return str_term
812
+ # except CoqAnomaly as e:
813
+ # if e.msg == "Timing Out":
814
+ # return str_term
815
+ # else:
816
+ # raise e
817
+ # except Exception:
818
+ # return str_term
819
+ # finally:
820
+ # self._discard_and_complete() # Remove all the junk and complete the message reading
821
+
822
+ # Hammer prints a lot of stuff when it gets imported. Discard all of it.
823
+ def init_hammer(self):
824
+ self.hammer_timeout = 10
825
+ atp_limit = 29 * self.hammer_timeout // 60
826
+ reconstr_limit = 28 * self.hammer_timeout // 60
827
+ crush_limit = 3 * self.hammer_timeout // 60
828
+ eprint("Initializing hammer", guard=self.verbose >= 2)
829
+ self.run_stmt("From Hammer Require Import Hammer.")
830
+ self.run_stmt(f"Set Hammer ATPLimit {atp_limit}.")
831
+ self.run_stmt(f"Set Hammer ReconstrLimit {reconstr_limit}.")
832
+ self.run_stmt(f"Set Hammer CrushLimit {crush_limit}.")
833
+
834
+ def get_hammer_premise_names(self, k: int) -> List[str]:
835
+ if not self.goals:
836
+ return []
837
+ try:
838
+ oldquiet = self.quiet
839
+ self.quiet = True
840
+ self.run_stmt(f"predict {k}.", timeout=120)
841
+ self.quiet = oldquiet
842
+ premise_names = self.feedbacks[3][1][3][1][3][1].split(", ")
843
+ self.cancel_last()
844
+ return premise_names
845
+ except CoqExn:
846
+ return []
847
+
848
+ def get_hammer_premises(self, k: int = 10) -> List[str]:
849
+ old_timeout = self.timeout
850
+ self.timeout = 600
851
+ names = self.get_hammer_premise_names(k)
852
+
853
+ def get_full_line(name: str) -> str:
854
+ try:
855
+ self._send_acked(f"(Query () (Vernac \"Check {name}.\"))")
856
+ try:
857
+ nextmsg = self._get_message()
858
+ except TimeoutError:
859
+ eprint("Timed out waiting for initial message")
860
+ while match(normalizeMessage(nextmsg),
861
+ ["Feedback", [["doc_id", int], ["span_id", int],
862
+ ["route", int],
863
+ ["contents", "Processed"]]],
864
+ lambda *args: True,
865
+ _,
866
+ lambda *args: False):
867
+ try:
868
+ nextmsg = self._get_message()
869
+ except TimeoutError:
870
+ eprint("Timed out waiting for message")
871
+ pp_term = nextmsg[1][3][1][3]
872
+ try:
873
+ nextmsg = self._get_message()
874
+ except TimeoutError:
875
+ eprint("Timed out waiting for message")
876
+ match(normalizeMessage(nextmsg),
877
+ ["Answer", int, ["ObjList", []]],
878
+ lambda *args: None,
879
+ _, lambda *args: raise_(UnrecognizedError(nextmsg)))
880
+ try:
881
+ self._get_completed()
882
+ except TimeoutError:
883
+ eprint("Timed out waiting for completed message")
884
+ try:
885
+ result = re.sub(r"\s+", " ", self._ppToTermStr(pp_term))
886
+ except TimeoutError:
887
+ eprint("Timed out when converting ppterm")
888
+ return result
889
+ except TimeoutError:
890
+ eprint("Timed out when getting full line!")
891
+ return ""
892
+ full_lines = [line for line in
893
+ [get_full_line(name) for name in names]
894
+ if line]
895
+ self.timeout = old_timeout
896
+ return full_lines
897
+
898
+ def check_term(self, term: str) -> str:
899
+ self._send_acked(f"(Query () (Vernac \"Check {term}.\"))")
900
+ self._get_processed()
901
+ result = self._get_feedback_str()
902
+ self._get_empty_objslist()
903
+ self._get_completed()
904
+ return result
905
+
906
+ def locate_ident(self, ident: str) -> str:
907
+ self._send_acked(f"(Query () (Vernac \"Locate {ident}.\"))")
908
+ self._get_processed()
909
+ result = self._get_feedback_str()
910
+ self._get_empty_objslist()
911
+ self._get_completed()
912
+ return result
913
+
914
+ def interrupt(self) -> None:
915
+ self._proc.send_signal(signal.SIGINT)
916
+ self._flush_queue()
917
+
918
+ def count_fg_goals(self) -> int:
919
+ if not self.proof_context:
920
+ return 0
921
+ return len(self.proof_context.fg_goals)
922
+
923
+ def get_lemmas_about_head(self) -> List[str]:
924
+ if self.goals.strip() == "":
925
+ return []
926
+ goal_head = self.goals.split()[0]
927
+ if (goal_head == "forall"):
928
+ return []
929
+ answer = self.search_about(goal_head)
930
+ assert self.message_queue.empty(), self.messages
931
+ return answer
932
+
933
+ def coq_minor_version(self) -> int:
934
+ version_match = re.fullmatch("\d+\.(\d+).*", self.version_string,
935
+ flags=re.DOTALL)
936
+ assert version_match, f"Version {self.version_string} doesn't match regex"
937
+ return int(version_match.group(1))
938
+
939
+ def run(self) -> None:
940
+ assert self._fout
941
+ while not self.__sema.acquire(False):
942
+ try:
943
+ line = self._fout.readline().decode('utf-8')
944
+ except ValueError:
945
+ continue
946
+ if line.strip() == '':
947
+ break
948
+ self.message_queue.put(line)
949
+ eprint(f"RECEIVED: {line}", guard=self.verbose >= 4)
950
+
951
+ def get_all_sexp_goals(self) -> List[SexpObligation]:
952
+ assert self.proof_context, "Can only call get_all_sexp_goals when you're in a proof!"
953
+ text_response = self._ask_text("(Query () Goals)")
954
+ context_match = re.fullmatch(
955
+ r"\(Answer\s+\d+\s*\(ObjList\s*(.*)\)\)\n",
956
+ text_response)
957
+ if not context_match:
958
+ if "Stack overflow" in text_response:
959
+ raise CoqAnomaly(f"\"{text_response}\"")
960
+ else:
961
+ raise BadResponse(f"\"{text_response}\"")
962
+ context_str = context_match.group(1)
963
+ assert context_str != "()"
964
+ goals_match = self.all_goals_regex.match(context_str)
965
+ if not goals_match:
966
+ raise BadResponse(context_str)
967
+ fg_goals_str, bg_goals_str, \
968
+ shelved_goals_str, given_up_goals_str = \
969
+ goals_match.groups()
970
+ fg_goal_strs = cast(List[str], parseSexpOneLevel(fg_goals_str))
971
+ bg_goal_strs = [uuulevel for ulevel in cast(List[str],
972
+ parseSexpOneLevel(bg_goals_str))
973
+ for uulevel in cast(List[str], parseSexpOneLevel(ulevel))
974
+ for uuulevel in cast(List[str], parseSexpOneLevel(uulevel))]
975
+ if len(fg_goal_strs) > 0 or len(bg_goal_strs) > 0:
976
+ goals: List[SexpObligation] = []
977
+ for goal_str in fg_goal_strs + bg_goal_strs:
978
+ loaded = loads(goal_str)
979
+ goals.append(SexpObligation([['CoqConstr', ty[2]] for ty in loaded[2][1]],
980
+ ['CoqConstr', loaded[1][1]]))
981
+ return goals
982
+ else:
983
+ return []
984
+
985
+ def _cancel_potential_local_lemmas(self, cmd: str) -> None:
986
+ lemmas = self._lemmas_defined_by_stmt(cmd)
987
+ is_section = "Let" in cmd
988
+ for lemma in lemmas:
989
+ self._local_lemmas.remove((lemma, is_section))
990
+
991
+ def _remove_potential_local_lemmas(self, cmd: str) -> None:
992
+ reset_match = re.match(r"Reset\s+(.*)\.", cmd)
993
+ if reset_match:
994
+ reseted_lemma_name = self.module_prefix + reset_match.group(1)
995
+ for (lemma, is_section) in list(self._local_lemmas):
996
+ if lemma == ":":
997
+ continue
998
+ lemma_match = re.match(r"\s*([\w'\.]+)\s*:", lemma)
999
+ assert lemma_match, f"{lemma} doesnt match!"
1000
+ lemma_name = lemma_match.group(1)
1001
+ if lemma_name == reseted_lemma_name:
1002
+ self._local_lemmas.remove((lemma, is_section))
1003
+ abort_match = re.match(r"\s*Abort", cmd)
1004
+ if abort_match:
1005
+ self._local_lemmas.pop()
1006
+
1007
+ def _add_potential_local_lemmas(self, cmd: str) -> None:
1008
+ lemmas = self._lemmas_defined_by_stmt(cmd)
1009
+ is_section = "Let" in cmd
1010
+ for lemma in lemmas:
1011
+ self._local_lemmas.append((lemma, is_section))
1012
+ if lemma.startswith(self.module_prefix):
1013
+ cached = lemma[len(self.module_prefix):].replace('\n', '')
1014
+ else:
1015
+ cached = lemma.replace("\n", "")
1016
+ if self._local_lemmas_cache is not None:
1017
+ self._local_lemmas_cache.append(cached)
1018
+
1019
+ def _lemmas_defined_by_stmt(self, cmd: str) -> List[str]:
1020
+ cmd = kill_comments(cmd)
1021
+ normal_lemma_match = re.match(
1022
+ r"\s*(?:(?:Local|Global)\s+)?(?:" +
1023
+ "|".join(normal_lemma_starting_patterns) +
1024
+ r")\s+([\w']*)(.*)",
1025
+ cmd,
1026
+ flags=re.DOTALL)
1027
+
1028
+ if normal_lemma_match:
1029
+ lemma_name = normal_lemma_match.group(1)
1030
+ binders, body = unwrap(split_by_char_outside_matching(
1031
+ r"\(", r"\)", ":", normal_lemma_match.group(2)))
1032
+ if binders.strip():
1033
+ lemma_statement = (self.module_prefix + lemma_name +
1034
+ " : forall " + binders + ", " + body[1:])
1035
+ else:
1036
+ lemma_statement = self.module_prefix + lemma_name + " " + body
1037
+ return [lemma_statement]
1038
+
1039
+ goal_match = re.match(r"\s*(?:Goal)\s+(.*)", cmd, flags=re.DOTALL)
1040
+
1041
+ if goal_match:
1042
+ return [": " + goal_match.group(1)]
1043
+
1044
+ morphism_match = re.match(
1045
+ r"\s*Add\s+(?:Parametric\s+)?Morphism.*"
1046
+ r"with signature(.*)\s+as\s+(\w*)\.",
1047
+ cmd, flags=re.DOTALL)
1048
+ if morphism_match:
1049
+ return [morphism_match.group(2) + " : " + morphism_match.group(1)]
1050
+
1051
+ proposition_match = re.match(r".*Inductive\s*\w+\s*:.*Prop\s*:=(.*)",
1052
+ cmd, flags=re.DOTALL)
1053
+ if proposition_match:
1054
+ case_matches = re.finditer(r"\|\s*(\w+\s*:[^|]*)",
1055
+ proposition_match.group(1))
1056
+ constructor_lemmas = [self.module_prefix + case_match.group(1)
1057
+ for case_match in
1058
+ case_matches]
1059
+ return constructor_lemmas
1060
+ obligation_match = re.match(".*Obligation", cmd, flags=re.DOTALL)
1061
+ if obligation_match:
1062
+ return [":"]
1063
+
1064
+ return []
1065
+
1066
+
1067
+ # Send some text to serapi, and flush the stream to make sure they
1068
+ # get it. NOT FOR EXTERNAL USE
1069
+ def _send_flush(self, cmd: str):
1070
+ assert self._fin
1071
+ eprint("SENT: " + cmd, guard=self.verbose >= 4)
1072
+ if self.log_outgoing_messages:
1073
+ with open(self.log_outgoing_messages, 'w') as f:
1074
+ print(cmd, file=f)
1075
+ try:
1076
+ self._fin.write(cmd.encode('utf-8'))
1077
+ self._fin.flush()
1078
+ except BrokenPipeError:
1079
+ raise CoqAnomaly("Coq process unexpectedly quit. Possibly running "
1080
+ "out of memory due to too many threads?")
1081
+
1082
+ def _send_acked(self, cmd: str):
1083
+ self._send_flush(cmd)
1084
+ self._get_ack()
1085
+
1086
+ def _ask(self, cmd: str, complete: bool = True):
1087
+ return loads(self._ask_text(cmd, complete))
1088
+
1089
+ def _ask_text(self, cmd: str, complete: bool = True):
1090
+ assert self.message_queue.empty(), self.messages
1091
+ self._send_acked(cmd)
1092
+ msg = self._get_message_text(complete)
1093
+ return msg
1094
+
1095
+ def _handle_exception(self, e: SerapiException, stmt: str):
1096
+ eprint("Problem running statement: {}\n".format(stmt),
1097
+ guard=(not self.quiet or self.verbose >= 2))
1098
+ match(e,
1099
+ TimeoutError,
1100
+ lambda *args: progn(self.cancel_failed(), # type: ignore
1101
+ raise_(TimeoutError(
1102
+ "Statment \"{}\" timed out."
1103
+ .format(stmt)))),
1104
+ _, lambda e: None)
1105
+ coqexn_msg = match(normalizeMessage(e.msg),
1106
+ ['Answer', int, ['CoqExn', TAIL]],
1107
+ lambda sentence_num, rest:
1108
+ "\n".join(searchStrsInMsg(rest)),
1109
+ str, lambda s: s,
1110
+ [str], lambda s: s,
1111
+ _, None)
1112
+ if coqexn_msg:
1113
+ eprint(coqexn_msg, guard=(not self.quiet or self.verbose >= 2))
1114
+ if ("Stream\\.Error" in coqexn_msg
1115
+ or "Syntax error" in coqexn_msg
1116
+ or "Syntax Error" in coqexn_msg):
1117
+ self._get_completed()
1118
+ raise ParseError(f"Couldn't parse command {stmt}")
1119
+ elif "CLexer.Error" in coqexn_msg:
1120
+ self._get_completed()
1121
+ raise ParseError(f"Couldn't parse command {stmt}")
1122
+ elif "NoSuchGoals" in coqexn_msg:
1123
+ self._get_completed()
1124
+ self.cancel_failed()
1125
+ raise NoSuchGoalError("")
1126
+ elif "Invalid_argument" in coqexn_msg:
1127
+ if "index out of bounds" in coqexn_msg and "Anomaly" in coqexn_msg:
1128
+ self._get_completed()
1129
+ self.cancel_failed()
1130
+ raise ParseError(f"Invalid argument in {stmt}")
1131
+ elif "Not_found" in coqexn_msg:
1132
+ self._get_completed()
1133
+ self.cancel_failed()
1134
+ raise e
1135
+ elif "Overflowed" in coqexn_msg or "Stack overflow" in coqexn_msg:
1136
+ self._get_completed()
1137
+ raise CoqAnomaly("Overflowed")
1138
+ elif "Anomaly" in coqexn_msg:
1139
+ self._get_completed()
1140
+ raise CoqAnomaly(coqexn_msg)
1141
+ elif "Unable to unify" in coqexn_msg:
1142
+ self._get_completed()
1143
+ self.cancel_failed()
1144
+ raise CoqExn(coqexn_msg)
1145
+ elif re.match(r".*The identifier (.*) is reserved\..*",
1146
+ coqexn_msg):
1147
+ self._get_completed()
1148
+ raise CoqExn(coqexn_msg)
1149
+ else:
1150
+ self._get_completed()
1151
+ self.cancel_failed()
1152
+ raise CoqExn(coqexn_msg)
1153
+ else:
1154
+ match(normalizeMessage(e.msg),
1155
+ ['Stream\\.Error', str],
1156
+ lambda *args: progn(self._get_completed(),
1157
+ raise_(ParseError(
1158
+ "Couldn't parse command {}"
1159
+ .format(stmt)))),
1160
+
1161
+ ['CErrors\\.UserError', _],
1162
+ lambda inner: progn(self._get_completed(),
1163
+ self.cancel_failed(), # type: ignore
1164
+ raise_(e)),
1165
+ ['ExplainErr\\.EvaluatedError', TAIL],
1166
+ lambda inner: progn(self._get_completed(),
1167
+ self.cancel_failed(), # type: ignore
1168
+ raise_(e)),
1169
+ _, lambda *args: progn(raise_(UnrecognizedError(args))))
1170
+
1171
+
1172
+ # Flush all messages in the message queue
1173
+ def _flush_queue(self) -> None:
1174
+ while not self.message_queue.empty():
1175
+ self._get_message()
1176
+
1177
+ def _ppStrToTermStr(self, pp_str: str) -> str:
1178
+ answer = self._ask(
1179
+ f"(Print ((pp ((pp_format PpStr)))) (CoqPp {pp_str}))")
1180
+ return match(normalizeMessage(answer),
1181
+ ["Answer", int, ["ObjList", [["CoqString", _]]]],
1182
+ lambda statenum, s: str(s),
1183
+ ["Answer", int, ["CoqExn", TAIL]],
1184
+ lambda statenum, msg:
1185
+ raise_(CoqExn(searchStrsInMsg(msg))))
1186
+
1187
+ def _ppToTermStr(self, pp) -> str:
1188
+ return self._ppStrToTermStr(dumps(pp))
1189
+
1190
+ @functools.lru_cache(maxsize=128)
1191
+ def _sexpStrToTermStr(self, sexp_str: str) -> str:
1192
+ try:
1193
+ answer = self._ask(
1194
+ f"(Print ((pp ((pp_format PpStr)))) (CoqConstr {sexp_str}))")
1195
+ return match(normalizeMessage(answer),
1196
+ ["Answer", int, ["ObjList", [["CoqString", _]]]],
1197
+ lambda statenum, s: str(s),
1198
+ ["Answer", int, ["CoqExn", TAIL]],
1199
+ lambda statenum, msg:
1200
+ raise_(CoqExn(searchStrsInMsg(msg))))
1201
+ except CoqExn as e:
1202
+ eprint("Coq exception when trying to convert to string:\n"
1203
+ f"{sexp_str}", guard=self.verbose >= 1)
1204
+ eprint(e, guard=self.verbose >= 2)
1205
+ raise
1206
+
1207
+ def _sexpToTermStr(self, sexp) -> str:
1208
+ return self._sexpStrToTermStr(dumps(sexp))
1209
+
1210
+ def _parseSexpHypStr(self, sexp_str: str) -> str:
1211
+ var_sexps_str, mid_str, term_sexp_str = \
1212
+ cast(List[str], parseSexpOneLevel(sexp_str))
1213
+
1214
+ def get_id(var_pair_str: str) -> str:
1215
+ id_possibly_quoted = unwrap(
1216
+ id_regex.match(var_pair_str)).group(1)
1217
+ if id_possibly_quoted[0] == "\"" and \
1218
+ id_possibly_quoted[-1] == "\"":
1219
+ return id_possibly_quoted[1:-1]
1220
+ return id_possibly_quoted
1221
+ ids_str = ",".join([get_id(var_pair_str) for
1222
+ var_pair_str in
1223
+ cast(List[str], parseSexpOneLevel(var_sexps_str))])
1224
+ term_str = self._sexpStrToTermStr(term_sexp_str)
1225
+ return f"{ids_str} : {term_str}"
1226
+
1227
+ def _parseSexpHyp(self, sexp) -> str:
1228
+ var_sexps, _, term_sexp = sexp
1229
+ ids_str = ",".join([dumps(var_sexp[1]) for var_sexp in var_sexps])
1230
+ term_str = self._sexpToTermStr(term_sexp)
1231
+ return f"{ids_str} : {term_str}"
1232
+
1233
+ def _parseSexpGoalStr(self, sexp_str: str) -> Obligation:
1234
+ goal_match = goal_regex.fullmatch(sexp_str)
1235
+ assert goal_match, sexp_str + "didn't match"
1236
+ goal_num_str, goal_term_str, hyps_list_str = \
1237
+ goal_match.group(1, 2, 3)
1238
+ goal_str = self._sexpStrToTermStr(goal_term_str).replace(r"\.", ".")
1239
+ hyps = [self._parseSexpHypStr(hyp_str) for hyp_str in
1240
+ cast(List[str], parseSexpOneLevel(hyps_list_str))]
1241
+ return Obligation(hyps, goal_str)
1242
+
1243
+ def _parseSexpGoal(self, sexp) -> Obligation:
1244
+ goal_num, goal_term, hyps_list = \
1245
+ match(normalizeMessage(sexp),
1246
+ [["name", int], ["ty", _], ["hyp", list]],
1247
+ lambda *args: args)
1248
+ goal_str = self._sexpToTermStr(goal_term)
1249
+ hyps = [self._parseSexpHyp(hyp_sexp) for hyp_sexp in hyps_list]
1250
+ return Obligation(hyps, goal_str)
1251
+
1252
+ def _parseBgGoal(self, sexp) -> Obligation:
1253
+ return match(normalizeMessage(sexp),
1254
+ [[], [_]],
1255
+ lambda inner_sexp: self._parseSexpGoal(inner_sexp))
1256
+
1257
+
1258
+ def __cancel(self, update_nonfg_goals: bool = False) -> None:
1259
+ self._flush_queue()
1260
+ assert self.message_queue.empty(), self.messages
1261
+ # Run the cancel
1262
+ self._send_acked("(Cancel ({}))".format(self.cur_state))
1263
+ # Get the response from cancelling
1264
+ self.cur_state = self._get_cancelled()
1265
+ # Get a new proof context, if it exists
1266
+ self._get_proof_context(update_nonfg_goals=update_nonfg_goals)
1267
+
1268
+ # Get the next message from the message queue, and make sure it's
1269
+ # an Ack
1270
+ def _get_ack(self) -> None:
1271
+ ack = self._get_message()
1272
+ match(normalizeMessage(ack),
1273
+ ["Answer", _, "Ack"], lambda state: None,
1274
+ ["Feedback", TAIL], lambda rest: self._get_ack(),
1275
+ _, lambda msg: raise_(AckError(dumps(ack))))
1276
+
1277
+ # Get the next message from the message queue, and make sure it's
1278
+ # a Completed.
1279
+ def _get_completed(self) -> None:
1280
+ completed = self._get_message()
1281
+ match(normalizeMessage(completed),
1282
+ ["Answer", int, "Completed"], lambda state: None,
1283
+ _, lambda msg: raise_(CompletedError(completed)))
1284
+
1285
+ def _get_processed(self) -> None:
1286
+ match(normalizeMessage(self._get_message()),
1287
+ ["Feedback", [["doc_id", int],
1288
+ ["span_id", int],
1289
+ ["route", int],
1290
+ ["contents", "Processed"]]],
1291
+ lambda *rest: True,
1292
+ ["Feedback", [["doc_id", int],
1293
+ ["span_id", int],
1294
+ ["route", int],
1295
+ ["contents", ["ProcessingIn", str]]]],
1296
+ lambda *rest: progn(self._get_message(),
1297
+ self._get_processed()), # type: ignore
1298
+ _,
1299
+ lambda msg: raise_(UnrecognizedError(msg)))
1300
+
1301
+ def _get_feedback_str(self) -> str:
1302
+ return match(normalizeMessage(self._get_message()),
1303
+ ["Feedback", [["doc_id", int],
1304
+ ["span_id", int],
1305
+ ["route", int],
1306
+ ["contents", _]]],
1307
+ lambda d, s, r, contents:
1308
+ searchStrsInMsg(contents)[0],
1309
+ _,
1310
+ lambda msg: raise_(UnrecognizedError(msg)))
1311
+
1312
+ def _get_empty_objslist(self) -> None:
1313
+ match(normalizeMessage(self._get_message()),
1314
+ ["Answer", int, ["ObjList", []]],
1315
+ lambda *args: True,
1316
+ _,
1317
+ lambda msg: raise_(UnrecognizedError(msg)))
1318
+
1319
+
1320
+ # Not adding any types here because it would require a lot of
1321
+ # casting. Will reassess when recursive types are added to mypy
1322
+ # https://github.com/python/mypy/issues/731
1323
+ def _ppSexpContent(self, content):
1324
+ if content[0] == "Feedback":
1325
+ return self._ppSexpContent(content[1][1][1][3][1][2])
1326
+ elif (content[0] == "PCData" and len(content) == 2
1327
+ and isinstance(content[1], str)):
1328
+ return content[1]
1329
+ elif (content[0] == "PCData" and len(content) == 2
1330
+ and content[1] == "."):
1331
+ return "."
1332
+ elif (content[0] == "Element" and len(content) == 2
1333
+ and isinstance(content[1], list) and
1334
+ (content[1][0] == "constr.keyword" or
1335
+ content[1][0] == "constr.type" or
1336
+ content[1][0] == "constr.variable" or
1337
+ content[1][0] == "constr.reference" or
1338
+ content[1][0] == "constr.path")):
1339
+ return dumps(content[1][2][0][1])
1340
+ elif isinstance(content[0], list):
1341
+ return "".join([self._ppSexpContent(item) for item in content])
1342
+ else:
1343
+ return dumps(content)
1344
+
1345
+ def _exec_includes(self, includes_string: str, prelude: str) -> None:
1346
+ for rmatch in re.finditer(r"-R\s*(\S*)\s*(\S*)\s*", includes_string):
1347
+ self.add_lib_rec("./" + rmatch.group(1), rmatch.group(2))
1348
+ for qmatch in re.finditer(r"-Q\s*(\S*)\s*(\S*)\s*", includes_string):
1349
+ self.add_lib("./" + qmatch.group(1), qmatch.group(2))
1350
+ for imatch in re.finditer(r"-I\s*(\S*)", includes_string):
1351
+ self.add_ocaml_lib("./" + imatch.group(1))
1352
+
1353
+ def _update_state(self) -> None:
1354
+ self.cur_state = self._get_next_state()
1355
+
1356
+ def _unset_printing_notations(self) -> None:
1357
+ if self.use_human_readable_str:
1358
+ self._send_acked("(Add () \"Unset Printing All.\")\n")
1359
+ else:
1360
+ self._send_acked("(Add () \"Unset Printing Notations.\")\n")
1361
+ self._update_state()
1362
+ self._get_completed()
1363
+
1364
+ def _get_next_state(self) -> int:
1365
+ msg = self._get_message()
1366
+ while match(normalizeMessage(msg),
1367
+ ["Feedback", TAIL], lambda tail: True,
1368
+ ["Answer", int, "Completed"], lambda sidx: True,
1369
+ _, lambda x: False):
1370
+ msg = self._get_message()
1371
+
1372
+ return match(normalizeMessage(msg),
1373
+ ["Answer", int, list],
1374
+ lambda state_num, contents:
1375
+ match(contents,
1376
+ ["CoqExn", TAIL],
1377
+ lambda rest:
1378
+ raise_(CoqExn("\n".join(searchStrsInMsg(rest)))),
1379
+ ["Added", int, TAIL],
1380
+ lambda state_num, tail: state_num),
1381
+ _, lambda x: raise_(BadResponse(msg)))
1382
+
1383
+ def _discard_bad_queue_messages(self) -> None:
1384
+ while self.message_queue.qsize() > 0:
1385
+ try:
1386
+ _ = self.message_queue.get(timeout=self.timeout)
1387
+ except:
1388
+ break
1389
+ assert self.message_queue.empty(), "Message queue not empty"
1390
+
1391
+ def _discard_and_complete(self) -> None:
1392
+ while True:
1393
+ try:
1394
+ # TODO: This is a hack to get around a bug in
1395
+ self._get_completed()
1396
+ except CompletedError:
1397
+ continue
1398
+ except Exception:
1399
+ break
1400
+ break
1401
+
1402
+ def _discard_feedback(self) -> None:
1403
+ try:
1404
+ feedback_message = self._get_message()
1405
+ while feedback_message[1][3][1] != Symbol("Processed"):
1406
+ feedback_message = self._get_message()
1407
+ except TimeoutError:
1408
+ pass
1409
+ except CoqAnomaly as e:
1410
+ if e.msg != "Timing Out":
1411
+ raise
1412
+
1413
+ def _discard_initial_feedback(self) -> None:
1414
+ feedback1 = self._get_message()
1415
+ feedback2 = self._get_message()
1416
+ match(normalizeMessage(feedback1), ["Feedback", TAIL],
1417
+ lambda *args: None,
1418
+ _, lambda *args: raise_(BadResponse(feedback1)))
1419
+ match(normalizeMessage(feedback2), ["Feedback", TAIL],
1420
+ lambda *args: None,
1421
+ _, lambda *args: raise_(BadResponse(feedback2)))
1422
+
1423
+ def _get_message(self, complete=False) -> Any:
1424
+ msg_text = self._get_message_text(complete=complete)
1425
+ assert msg_text != "None", msg_text
1426
+ if msg_text[0] != "(":
1427
+ eprint(f"Skipping non-sexp output {msg_text}",
1428
+ guard=self.verbose>=3)
1429
+ return self._get_message(complete=complete)
1430
+ try:
1431
+ return loads(msg_text, nil=None)
1432
+ except ExpectClosingBracket:
1433
+ eprint(
1434
+ f"Tried to load a message but it's ill formed! \"{msg_text}\"",
1435
+ guard=self.verbose)
1436
+ raise CoqAnomaly("")
1437
+ except AssertionError:
1438
+ eprint(f"Assertion error while parsing s-expr {msg_text}")
1439
+ raise CoqAnomaly("")
1440
+
1441
+ def _get_message_text(self, complete=False) -> Any:
1442
+ try:
1443
+ msg = self.message_queue.get(timeout=self.timeout)
1444
+ if complete:
1445
+ self._get_completed()
1446
+ assert msg is not None
1447
+ return msg
1448
+ except queue.Empty:
1449
+ eprint("Command timed out! Interrupting", guard=self.verbose)
1450
+ self._proc.send_signal(signal.SIGINT)
1451
+ num_breaks = 1
1452
+ try:
1453
+ interrupt_response = \
1454
+ loads(self.message_queue.get(timeout=self.timeout))
1455
+ except queue.Empty:
1456
+ self._proc.send_signal(signal.SIGINT)
1457
+ num_breaks += 1
1458
+ try:
1459
+ interrupt_response = \
1460
+ loads(self.message_queue.get(timeout=self.timeout))
1461
+ except queue.Empty:
1462
+ raise CoqAnomaly("Timing Out")
1463
+
1464
+ got_answer_after_interrupt = match(
1465
+ normalizeMessage(interrupt_response),
1466
+ ["Answer", int, ["CoqExn", TAIL]],
1467
+ lambda *args: False,
1468
+ ["Answer", TAIL],
1469
+ lambda *args: True,
1470
+ _, lambda *args: False)
1471
+ if got_answer_after_interrupt:
1472
+ self._get_completed()
1473
+ for i in range(num_breaks):
1474
+ try:
1475
+ after_interrupt_msg = loads(self.message_queue.get(
1476
+ timeout=self.timeout))
1477
+ except queue.Empty:
1478
+ raise CoqAnomaly("Timing out")
1479
+ assert isBreakMessage(after_interrupt_msg), \
1480
+ after_interrupt_msg
1481
+ assert self.message_queue.empty(), self.messages
1482
+ return dumps(interrupt_response)
1483
+ else:
1484
+ for i in range(num_breaks):
1485
+ try:
1486
+ after_interrupt_msg = loads(self.message_queue.get(
1487
+ timeout=self.timeout))
1488
+ except queue.Empty:
1489
+ raise CoqAnomaly("Timing out")
1490
+ self._get_completed()
1491
+ assert self.message_queue.empty(), self.messages
1492
+ raise TimeoutError("")
1493
+ assert False, (interrupt_response, self.messages)
1494
+
1495
+ def _get_feedbacks(self) -> List['Sexp']:
1496
+ unparsed_feedbacks: List[str] = []
1497
+ unparsed_next_message = self._get_message_text()
1498
+ while(unparsed_next_message.startswith("(Feedback")):
1499
+ unparsed_feedbacks.append(unparsed_next_message)
1500
+ unparsed_next_message = self._get_message_text()
1501
+ fin = unparsed_next_message
1502
+ if re.match("\(Answer\s+\d+\s*\(CoqExn", fin):
1503
+ raise CoqExn("\n".join(searchStrsInMsg(loads(unparsed_feedbacks[-1], nil=None))))
1504
+
1505
+ return [loads(feedback_text, nil=None) for feedback_text in unparsed_feedbacks]
1506
+
1507
+ def _get_cancelled(self) -> int:
1508
+ # exception_raised = None
1509
+ try:
1510
+ feedback = self._get_message()
1511
+
1512
+ new_statenum = \
1513
+ match(normalizeMessage(feedback),
1514
+ ["Answer", int, ["CoqExn", TAIL]],
1515
+ lambda docnum, rest:
1516
+ raise_(CoqAnomaly("Overflowed"))
1517
+ if "Stack overflow" in "\n".join(searchStrsInMsg(rest))
1518
+ else raise_(CoqExn(feedback)),
1519
+ ["Feedback", [['doc_id', int], ['span_id', int], TAIL]],
1520
+ lambda docnum, statenum, *rest: statenum,
1521
+ _, lambda *args: raise_(BadResponse(feedback)))
1522
+ cancelled_answer = self._get_message()
1523
+ match(normalizeMessage(cancelled_answer),
1524
+ ["Answer", int, ["Canceled", list]],
1525
+ lambda _, statenums: min(statenums),
1526
+ ["Answer", int, ["CoqExn", TAIL]],
1527
+ lambda statenum, rest:
1528
+ raise_(CoqAnomaly("\n".join(searchStrsInMsg(rest))))
1529
+ if "Anomaly" in "\n".join(searchStrsInMsg(rest)) else
1530
+ raise_(CoqExn("\n".join(searchStrsInMsg(rest)))),
1531
+ _, lambda *args: raise_(BadResponse(cancelled_answer)))
1532
+ self._get_completed()
1533
+ # except BadResponse as e:
1534
+ # exception_raised = e
1535
+ except Exception as e1:
1536
+ try:
1537
+ self._discard_and_complete()
1538
+ except Exception as e2:
1539
+ raise Exception([e1, e2])
1540
+ # finally:
1541
+ # # if exception_raised is None:
1542
+ # self._discard_and_complete()
1543
+ # self._get_completed()
1544
+ # if exception_raised is not None:
1545
+ # # Scann till we get a completed message
1546
+ # while True:
1547
+ # try:
1548
+ # # TODO: This is a hack to get around a bug in
1549
+ # self._get_completed()
1550
+ # except CompletedError as e:
1551
+ # pass
1552
+ # except Exception as e:
1553
+ # raise Exception([
1554
+ # exception_raised,
1555
+ # e
1556
+ # ])
1557
+ # break
1558
+
1559
+ return new_statenum
1560
+
1561
+ def _extract_proof_context(self, raw_proof_context: 'Sexp') -> str:
1562
+ assert isinstance(raw_proof_context, list), raw_proof_context
1563
+ assert len(raw_proof_context) > 0, raw_proof_context
1564
+ assert isinstance(raw_proof_context[0], list), raw_proof_context
1565
+ return cast(List[List[str]], raw_proof_context)[0][1]
1566
+
1567
+ def _get_enter_goal_context(self) -> None:
1568
+ assert self.proof_context
1569
+ self.proof_context = ProofContext([self.proof_context.fg_goals[0]],
1570
+ self.proof_context.bg_goals +
1571
+ self.proof_context.fg_goals[1:],
1572
+ self.proof_context.shelved_goals,
1573
+ self.proof_context.given_up_goals)
1574
+
1575
+ def _get_proof_context(self, update_nonfg_goals: bool = True) -> None:
1576
+ # Try to do this the right way, fall back to the
1577
+ # wrong way if we run into this bug:
1578
+ # https://github.com/ejgallego/coq-serapi/issues/150
1579
+ def parse_goals_as_sexp():
1580
+ text_response = self._ask_text("(Query () Goals)")
1581
+ if text_response == None:
1582
+ self.proof_context = None
1583
+ return
1584
+ context_match = re.fullmatch(
1585
+ r"\(Answer\s+\d+\s*\(ObjList\s*(.*)\)\)\n",
1586
+ text_response)
1587
+ if not context_match:
1588
+ if "Stack overflow" in text_response:
1589
+ raise CoqAnomaly(f"\"{text_response}\"")
1590
+ else:
1591
+ raise BadResponse(f"\"{text_response}\"")
1592
+ context_str = context_match.group(1)
1593
+ if context_str == "()":
1594
+ self.proof_context = None
1595
+ else:
1596
+ goals_match = self.all_goals_regex.match(context_str)
1597
+ if not goals_match:
1598
+ raise BadResponse(context_str)
1599
+ fg_goals_str, bg_goals_str, \
1600
+ shelved_goals_str, given_up_goals_str = \
1601
+ goals_match.groups()
1602
+ if update_nonfg_goals or self.proof_context is None:
1603
+ unparsed_levels = cast(List[str],
1604
+ parseSexpOneLevel(bg_goals_str))
1605
+ parsed2 = [uuulevel
1606
+ for ulevel in unparsed_levels
1607
+ for uulevel in cast(List[str],
1608
+ parseSexpOneLevel(ulevel))
1609
+ for uuulevel in
1610
+ cast(List[str], parseSexpOneLevel(uulevel))]
1611
+ bg_goals = [self._parseSexpGoalStr(bg_goal_str)
1612
+ for bg_goal_str in parsed2]
1613
+ self.proof_context = ProofContext(
1614
+ [self._parseSexpGoalStr(goal)
1615
+ for goal in cast(List[str],
1616
+ parseSexpOneLevel(fg_goals_str))],
1617
+ bg_goals,
1618
+ [self._parseSexpGoalStr(shelved_goal)
1619
+ for shelved_goal in
1620
+ cast(List[str],
1621
+ parseSexpOneLevel(shelved_goals_str))],
1622
+ [self._parseSexpGoalStr(given_up_goal)
1623
+ for given_up_goal in
1624
+ cast(List[str],
1625
+ parseSexpOneLevel(given_up_goals_str))])
1626
+ else:
1627
+ self.proof_context = ProofContext(
1628
+ [self._parseSexpGoalStr(goal)
1629
+ for goal in cast(List[str],
1630
+ parseSexpOneLevel(fg_goals_str))],
1631
+ unwrap(self.proof_context).bg_goals,
1632
+ [self._parseSexpGoalStr(shelved_goal)
1633
+ for shelved_goal in
1634
+ cast(List[str],
1635
+ parseSexpOneLevel(shelved_goals_str))],
1636
+ unwrap(self.proof_context).given_up_goals)
1637
+
1638
+ def parse_goals_as_text():
1639
+ self._send_acked("(Query ((pp ((pp_format PpStr)))) Goals)")
1640
+
1641
+ msg = self._get_message()
1642
+ proof_context_msg = match(
1643
+ normalizeMessage(msg),
1644
+ ["Answer", int, ["CoqExn", TAIL]],
1645
+ lambda statenum, rest:
1646
+ raise_(CoqAnomaly("Stack overflow")) if
1647
+ "Stack overflow." in searchStrsInMsg(rest) else
1648
+ raise_(CoqExn(searchStrsInMsg(rest))),
1649
+ ["Answer", int, list],
1650
+ lambda statenum, contents: contents,
1651
+ _, lambda *args:
1652
+ raise_(UnrecognizedError(dumps(msg))))
1653
+ self._get_completed()
1654
+ if len(proof_context_msg) == 0 or len(proof_context_msg[1]) == 0:
1655
+ self.proof_context = None
1656
+ else:
1657
+ newcontext = self._extract_proof_context(proof_context_msg[1])
1658
+ if newcontext == "none":
1659
+ self.proof_context = ProofContext([], [], [], [])
1660
+ else:
1661
+ self.proof_context = \
1662
+ ProofContext(
1663
+ [parsePPSubgoal(substr) for substr
1664
+ in re.split(r"\n\n|(?=\snone)", newcontext)
1665
+ if substr.strip()],
1666
+ [], [], [])
1667
+
1668
+ try:
1669
+ # if self.use_human_readable_str:
1670
+ # parse_goals_as_text()
1671
+ # else:
1672
+ parse_goals_as_sexp()
1673
+ # text_response = self._ask_text("(Query () Goals)")
1674
+ # context_match = re.fullmatch(
1675
+ # r"\(Answer\s+\d+\s*\(ObjList\s*(.*)\)\)\n",
1676
+ # text_response)
1677
+ # if not context_match:
1678
+ # if "Stack overflow" in text_response:
1679
+ # raise CoqAnomaly(f"\"{text_response}\"")
1680
+ # else:
1681
+ # raise BadResponse(f"\"{text_response}\"")
1682
+ # context_str = context_match.group(1)
1683
+ # if context_str == "()":
1684
+ # self.proof_context = None
1685
+ # else:
1686
+ # goals_match = self.all_goals_regex.match(context_str)
1687
+ # if not goals_match:
1688
+ # raise BadResponse(context_str)
1689
+ # fg_goals_str, bg_goals_str, \
1690
+ # shelved_goals_str, given_up_goals_str = \
1691
+ # goals_match.groups()
1692
+ # if update_nonfg_goals or self.proof_context is None:
1693
+ # unparsed_levels = cast(List[str],
1694
+ # parseSexpOneLevel(bg_goals_str))
1695
+ # parsed2 = [uuulevel
1696
+ # for ulevel in unparsed_levels
1697
+ # for uulevel in cast(List[str],
1698
+ # parseSexpOneLevel(ulevel))
1699
+ # for uuulevel in
1700
+ # cast(List[str], parseSexpOneLevel(uulevel))]
1701
+ # bg_goals = [self._parseSexpGoalStr(bg_goal_str)
1702
+ # for bg_goal_str in parsed2]
1703
+ # self.proof_context = ProofContext(
1704
+ # [self._parseSexpGoalStr(goal)
1705
+ # for goal in cast(List[str],
1706
+ # parseSexpOneLevel(fg_goals_str))],
1707
+ # bg_goals,
1708
+ # [self._parseSexpGoalStr(shelved_goal)
1709
+ # for shelved_goal in
1710
+ # cast(List[str],
1711
+ # parseSexpOneLevel(shelved_goals_str))],
1712
+ # [self._parseSexpGoalStr(given_up_goal)
1713
+ # for given_up_goal in
1714
+ # cast(List[str],
1715
+ # parseSexpOneLevel(given_up_goals_str))])
1716
+ # else:
1717
+ # self.proof_context = ProofContext(
1718
+ # [self._parseSexpGoalStr(goal)
1719
+ # for goal in cast(List[str],
1720
+ # parseSexpOneLevel(fg_goals_str))],
1721
+ # unwrap(self.proof_context).bg_goals,
1722
+ # [self._parseSexpGoalStr(shelved_goal)
1723
+ # for shelved_goal in
1724
+ # cast(List[str],
1725
+ # parseSexpOneLevel(shelved_goals_str))],
1726
+ # unwrap(self.proof_context).given_up_goals)
1727
+ except CoqExn:
1728
+ parse_goals_as_text()
1729
+ # self._send_acked("(Query ((pp ((pp_format PpStr)))) Goals)")
1730
+
1731
+ # msg = self._get_message()
1732
+ # proof_context_msg = match(
1733
+ # normalizeMessage(msg),
1734
+ # ["Answer", int, ["CoqExn", TAIL]],
1735
+ # lambda statenum, rest:
1736
+ # raise_(CoqAnomaly("Stack overflow")) if
1737
+ # "Stack overflow." in searchStrsInMsg(rest) else
1738
+ # raise_(CoqExn(searchStrsInMsg(rest))),
1739
+ # ["Answer", int, list],
1740
+ # lambda statenum, contents: contents,
1741
+ # _, lambda *args:
1742
+ # raise_(UnrecognizedError(dumps(msg))))
1743
+ # self._get_completed()
1744
+ # if len(proof_context_msg) == 0:
1745
+ # self.proof_context = None
1746
+ # else:
1747
+ # newcontext = self._extract_proof_context(proof_context_msg[1])
1748
+ # if newcontext == "none":
1749
+ # self.proof_context = ProofContext([], [], [], [])
1750
+ # else:
1751
+ # self.proof_context = \
1752
+ # ProofContext(
1753
+ # [parsePPSubgoal(substr) for substr
1754
+ # in re.split(r"\n\n|(?=\snone)", newcontext)
1755
+ # if substr.strip()],
1756
+ # [], [], [])
1757
+
1758
+
1759
+ def _add_potential_module_stack_cmd(self, cmd: str) -> None:
1760
+ new_stack = update_sm_stack(self.sm_stack, cmd)
1761
+ if len(self.sm_stack) > 0 and \
1762
+ self.sm_stack[-1][1] and \
1763
+ len(new_stack) < len(self.sm_stack):
1764
+ self._local_lemmas = \
1765
+ [(lemma, is_section) for (lemma, is_section)
1766
+ in self._local_lemmas if not is_section]
1767
+ if len(new_stack) != len(self.sm_stack):
1768
+ self._module_changed = True
1769
+ self.sm_stack = new_stack
1770
+ pass
1771
+
1772
+ goal_regex = re.compile(r"\(\(info\s*\(\(evar\s*\(Ser_Evar\s*(\d+)\)\)"
1773
+ r"\(name\s*\((?:\(Id\"?\s*[\w']+\"?\))*\)\)\)\)"
1774
+ r"\(ty\s*(.*)\)\s*\(hyp\s*(.*)\)\)")
1775
+
1776
+ all_goals_regex_10 = re.compile(r"\(\(CoqGoal\s*"
1777
+ r"\(\(goals\s*(.*)\)"
1778
+ r"\(stack\s*(.*)\)"
1779
+ r"\(shelf\s*(.*)\)"
1780
+ r"\(given_up\s*(.*)\)"
1781
+ r"\(bullet\s*.*\)\)\)\)")
1782
+
1783
+ all_goals_regex_13 = re.compile(r"\(\(CoqGoal\s*"
1784
+ r"\(\(goals\s*(.*)\)"
1785
+ r"\(stack\s*(.*)\)"
1786
+ r"\(bullet\s*.*\)"
1787
+ r"\(shelf\s*(.*)\)"
1788
+ r"\(given_up\s*(.*)\)\)\)\)")
1789
+
1790
+ id_regex = re.compile(r"\(Id\s*(.*)\)")
1791
+
1792
+
1793
+ def isBreakMessage(msg: 'Sexp') -> bool:
1794
+ return match(normalizeMessage(msg),
1795
+ "Sys\\.Break", lambda *args: True,
1796
+ _, lambda *args: False)
1797
+
1798
+
1799
+ def isBreakAnswer(msg: 'Sexp') -> bool:
1800
+ return "Sys\\.Break" in searchStrsInMsg(normalizeMessage(msg))
1801
+
1802
+
1803
+ @contextlib.contextmanager
1804
+ def SerapiContext(coq_commands: List[str], module_name: Optional[str],
1805
+ prelude: str, use_hammer: bool = False,
1806
+ log_outgoing_messages: Optional[str] = None) \
1807
+ -> Iterator[SerapiInstance]:
1808
+ try:
1809
+ coq = SerapiInstance(coq_commands, module_name, prelude,
1810
+ use_hammer=use_hammer,
1811
+ log_outgoing_messages=log_outgoing_messages)
1812
+ except CoqAnomaly:
1813
+ eprint("Anomaly during initialization! Something has gone horribly wrong.")
1814
+ raise
1815
+ try:
1816
+ yield coq
1817
+ finally:
1818
+ coq.kill()
1819
+
1820
+
1821
+ normal_lemma_starting_patterns = [
1822
+ r"(?:Program\s+)?(?:Polymorphic\s+)?Lemma",
1823
+ "Coercion",
1824
+ r"(?:Polymorphic\s+)?Theorem",
1825
+ "Remark",
1826
+ "Proposition",
1827
+ r"(?:Polymorphic\s+)?Definition",
1828
+ "Program\s+Definition",
1829
+ "Example",
1830
+ "Fixpoint",
1831
+ "Corollary",
1832
+ "Let",
1833
+ r"(?<!Declare\s)(?:Polymorphic\s+)?Instance",
1834
+ "Function",
1835
+ "Property",
1836
+ "Fact",
1837
+ "Equations(?:\??)"]
1838
+ special_lemma_starting_patterns = [
1839
+ "Derive",
1840
+ "Goal",
1841
+ "Add Morphism",
1842
+ "Next Obligation",
1843
+ r"Obligation\s+\d+",
1844
+ "Add Parametric Morphism"]
1845
+
1846
+ lemma_starting_patterns = \
1847
+ normal_lemma_starting_patterns + special_lemma_starting_patterns
1848
+
1849
+
1850
+ def possibly_starting_proof(command: str) -> bool:
1851
+ stripped_command = kill_comments(command).strip()
1852
+ pattern = r"(?:(?:Local|Global)\s+)?(" + "|".join(lemma_starting_patterns) + r")\s*"
1853
+ return bool(re.match(pattern,
1854
+ stripped_command))
1855
+
1856
+
1857
+ def ending_proof(command: str) -> bool:
1858
+ stripped_command = kill_comments(command).strip()
1859
+ return ("Qed." in stripped_command or
1860
+ "Defined." in stripped_command or
1861
+ "Admitted." in stripped_command or
1862
+ stripped_command == "Abort." or
1863
+ "Save" in stripped_command or
1864
+ (re.match(r"\s*Proof\s+\S+\s*", stripped_command) is not None and
1865
+ re.match(r"\s*Proof\s+with", stripped_command) is None and
1866
+ re.match(r"\s*Proof\s+using", stripped_command) is None))
1867
+
1868
+
1869
+ def initial_sm_stack(filename: str) -> List[Tuple[str, bool]]:
1870
+ return [(get_module_from_filename(filename), False)]
1871
+
1872
+
1873
+ def update_sm_stack(sm_stack: List[Tuple[str, bool]],
1874
+ cmd: str) -> List[Tuple[str, bool]]:
1875
+ new_stack = list(sm_stack)
1876
+ stripped_cmd = kill_comments(cmd).strip()
1877
+ module_start_match = re.match(
1878
+ r"Module\s+(?:(?:Import|Export)\s+)?(?:Type\s+)?([\w']*)", stripped_cmd)
1879
+ if stripped_cmd.count(":=") > stripped_cmd.count("with"):
1880
+ module_start_match = None
1881
+ section_start_match = re.match(r"Section\s+([\w']*)(?!.*:=)",
1882
+ stripped_cmd)
1883
+ end_match = re.match(r"End\s+([\w']*)\.", stripped_cmd)
1884
+ reset_match = re.match(r"Reset\s+([\w']*)\.", stripped_cmd)
1885
+ if module_start_match:
1886
+ new_stack.append((module_start_match.group(1), False))
1887
+ elif section_start_match:
1888
+ new_stack.append((section_start_match.group(1), True))
1889
+ elif end_match:
1890
+ if new_stack and new_stack[-1][0] == end_match.group(1):
1891
+ entry, is_sec = new_stack.pop()
1892
+ else:
1893
+ assert False, \
1894
+ f"Unrecognized End \"{cmd}\", " \
1895
+ f"top of module stack is {new_stack[-1]}"
1896
+ elif reset_match:
1897
+ if new_stack and any([item[0] == reset_match.group(1)
1898
+ for item in new_stack]):
1899
+ while new_stack[-1][0] != reset_match.group(1):
1900
+ new_stack.pop()
1901
+ new_stack.pop()
1902
+ return new_stack
1903
+
1904
+
1905
+ def module_prefix_from_stack(sm_stack: List[Tuple[str, bool]]) -> str:
1906
+ return "".join([sm[0] + "." for sm in sm_stack if not sm[1]])
1907
+
1908
+ def sm_prefix_from_stack(sm_stack: List[Tuple[str, bool]]) -> str:
1909
+ return "".join([sm[0] + "." for sm in sm_stack])
1910
+
1911
+
1912
+ def kill_comments(string: str) -> str:
1913
+ result = ""
1914
+ depth = 0
1915
+ in_quote = False
1916
+ for i in range(len(string)):
1917
+ if in_quote:
1918
+ if depth == 0:
1919
+ result += string[i]
1920
+ if string[i] == '"' and string[i-1] != '\\':
1921
+ in_quote = False
1922
+ else:
1923
+ if string[i:i+2] == '(*':
1924
+ depth += 1
1925
+ if depth == 0:
1926
+ result += string[i]
1927
+ if string[i-1:i+1] == '*)' and depth > 0:
1928
+ depth -= 1
1929
+ if string[i] == '"' and string[i-1] != '\\':
1930
+ in_quote = True
1931
+ return result
1932
+
1933
+
1934
+ def next_proof(cmds: Iterator[str]) -> Iterator[str]:
1935
+ next_cmd = next(cmds)
1936
+ assert possibly_starting_proof(next_cmd), next_cmd
1937
+ while not ending_proof(next_cmd):
1938
+ yield next_cmd
1939
+ try:
1940
+ next_cmd = next(cmds)
1941
+ except StopIteration:
1942
+ return
1943
+ yield next_cmd
1944
+
1945
+
1946
+ def preprocess_command(cmd: str) -> List[str]:
1947
+ coq_import_match = re.fullmatch(r"\s*Require\s+Import\s+Coq\.([\w\.'])", cmd)
1948
+ if coq_import_match:
1949
+ return ["Require Import {}".format(coq_import_match.group(1))]
1950
+
1951
+ return [cmd]
1952
+
1953
+
1954
+ def get_stem(tactic: str) -> str:
1955
+ return split_tactic(tactic)[0]
1956
+
1957
+
1958
+ def split_tactic(tactic: str) -> Tuple[str, str]:
1959
+ tactic = kill_comments(tactic).strip()
1960
+ if not tactic:
1961
+ return ("", "")
1962
+ outer_parens_match = re.fullmatch(r"\((.*)\)\.", tactic)
1963
+ if outer_parens_match:
1964
+ return split_tactic(outer_parens_match.group(1) + ".")
1965
+ if re.match(r"^\s*[-+*\{\}]+\s*$", tactic):
1966
+ stripped = tactic.strip()
1967
+ return stripped[:-1], stripped[-1]
1968
+ if split_by_char_outside_matching(r"\(", r"\)", ";", tactic):
1969
+ return tactic, ""
1970
+ for prefix in ["try", "now", "repeat", "decide"]:
1971
+ prefix_match = re.match(r"{}\s+(.*)".format(prefix), tactic)
1972
+ if prefix_match:
1973
+ rest_stem, rest_rest = split_tactic(prefix_match.group(1))
1974
+ return prefix + " " + rest_stem, rest_rest
1975
+ for special_stem in ["rewrite <-", "rewrite !",
1976
+ "intros until", "simpl in"]:
1977
+ special_match = re.match(r"{}(:?(:?\s+(.*))|(\.))".format(special_stem), tactic)
1978
+ if special_match:
1979
+ return special_stem, special_match.group(1)
1980
+ match = re.match(r"^\(?([\w']+)(\W+.*)?", tactic)
1981
+ if not match:
1982
+ return tactic, ""
1983
+ stem, rest = match.group(1, 2)
1984
+ if not rest:
1985
+ rest = ""
1986
+ return stem, rest
1987
+
1988
+
1989
+ def parse_hyps(hyps_str: str) -> List[str]:
1990
+ lets_killed = kill_nested(r"\Wlet\s", r"\sin\s", hyps_str)
1991
+ funs_killed = kill_nested(r"\Wfun\s", "=>", lets_killed)
1992
+ foralls_killed = kill_nested(r"\Wforall\s", ",", funs_killed)
1993
+ fixs_killed = kill_nested(r"\Wfix\s", ":=", foralls_killed)
1994
+ structs_killed = kill_nested(r"\W\{\|\s", r"\|\}", fixs_killed)
1995
+ hyps_replaced = re.sub(":=.*?:(?!=)", ":", structs_killed, flags=re.DOTALL)
1996
+ var_terms = re.findall(r"(\S+(?:, \S+)*) (?::=.*?)?:(?!=)\s.*?",
1997
+ hyps_replaced, flags=re.DOTALL)
1998
+ if len(var_terms) == 0:
1999
+ return []
2000
+ rest_hyps_str = hyps_str
2001
+ hyps_list = []
2002
+ # Assumes hypothesis are printed in reverse order, because for
2003
+ # whatever reason they seem to be.
2004
+ for next_term in reversed(var_terms[1:]):
2005
+ next_match = rest_hyps_str.rfind(" " + next_term + " :")
2006
+ hyp = rest_hyps_str[next_match:].strip()
2007
+ rest_hyps_str = rest_hyps_str[:next_match].strip()
2008
+ hyps_list.append(hyp)
2009
+ hyps_list.append(rest_hyps_str)
2010
+ for hyp in hyps_list:
2011
+ assert re.search(":(?!=)", hyp) is not None, \
2012
+ "hyp: {}, hyps_str: {}\nhyps_list: {}\nvar_terms: {}"\
2013
+ .format(hyp, hyps_str, hyps_list, var_terms)
2014
+ return hyps_list
2015
+
2016
+
2017
+ def kill_nested(start_string: str, end_string: str, hyps: str) \
2018
+ -> str:
2019
+ def searchpos(pattern: str, hyps: str, end: bool = False):
2020
+ match = re.search(pattern, hyps, flags=re.DOTALL)
2021
+ if match:
2022
+ if end:
2023
+ return match.end()
2024
+ else:
2025
+ return match.start()
2026
+ else:
2027
+ return float("Inf")
2028
+ next_forall_pos = searchpos(start_string, hyps)
2029
+ next_comma_pos = searchpos(end_string, hyps, end=True)
2030
+ forall_depth = 0
2031
+ last_forall_position = -1
2032
+ cur_position = 0
2033
+ while (next_forall_pos != float("Inf") or
2034
+ (next_comma_pos != float("Inf") and forall_depth > 0)):
2035
+ old_forall_depth = forall_depth
2036
+ if next_forall_pos < next_comma_pos:
2037
+ cur_position = next_forall_pos
2038
+ if forall_depth == 0:
2039
+ last_forall_position = next_forall_pos
2040
+ forall_depth += 1
2041
+ else:
2042
+ if forall_depth == 1:
2043
+ hyps = hyps[:last_forall_position] + hyps[next_comma_pos:]
2044
+ cur_position = last_forall_position
2045
+ last_forall_position = -1
2046
+ else:
2047
+ cur_position = next_comma_pos
2048
+ if forall_depth > 0:
2049
+ forall_depth -= 1
2050
+
2051
+ new_next_forall_pos = \
2052
+ searchpos(start_string, hyps[cur_position+1:]) + cur_position + 1
2053
+ new_next_comma_pos = \
2054
+ searchpos(end_string, hyps[cur_position+1:], end=True) + \
2055
+ cur_position + 1
2056
+ assert new_next_forall_pos != next_forall_pos or \
2057
+ new_next_comma_pos != next_comma_pos or \
2058
+ forall_depth != old_forall_depth, \
2059
+ "old start pos was {}, new start pos is {}, old end pos was {},"\
2060
+ "new end pos is {}, cur_position is {}"\
2061
+ .format(next_forall_pos, new_next_forall_pos, next_comma_pos,
2062
+ new_next_comma_pos, cur_position)
2063
+ next_forall_pos = new_next_forall_pos
2064
+ next_comma_pos = new_next_comma_pos
2065
+ return hyps
2066
+
2067
+
2068
+ def get_var_term_in_hyp(hyp: str) -> str:
2069
+ return hyp.partition(":")[0].strip()
2070
+
2071
+
2072
+ hypcolon_regex = re.compile(":(?!=)")
2073
+
2074
+
2075
+ def get_hyp_type(hyp: str) -> str:
2076
+ splits = hypcolon_regex.split(hyp, maxsplit=1)
2077
+ if len(splits) == 1:
2078
+ return ""
2079
+ else:
2080
+ return splits[1].strip()
2081
+
2082
+
2083
+ def get_vars_in_hyps(hyps: List[str]) -> List[str]:
2084
+ var_terms = [get_var_term_in_hyp(hyp) for hyp in hyps]
2085
+ var_names = [name.strip() for term in var_terms
2086
+ for name in term.split(",")]
2087
+ return var_names
2088
+
2089
+
2090
+ def get_indexed_vars_in_hyps(hyps: List[str]) -> List[Tuple[str, int]]:
2091
+ var_terms = [get_var_term_in_hyp(hyp) for hyp in hyps]
2092
+ var_names = [(name.strip(), hyp_idx)
2093
+ for hyp_idx, term in enumerate(var_terms)
2094
+ for name in term.split(",")]
2095
+ return var_names
2096
+
2097
+
2098
+ def get_indexed_vars_dict(hyps: List[str]) -> Dict[str, int]:
2099
+ result = {}
2100
+ for hyp_var, hyp_idx in get_indexed_vars_in_hyps(hyps):
2101
+ if hyp_var not in result:
2102
+ result[hyp_var] = hyp_idx
2103
+ return result
2104
+
2105
+
2106
+ def get_first_var_in_hyp(hyp: str) -> str:
2107
+ return get_var_term_in_hyp(hyp).split(",")[0].strip()
2108
+
2109
+
2110
+ def normalizeMessage(sexp, depth: int = 5):
2111
+ if depth <= 0:
2112
+ return sexp
2113
+ if isinstance(sexp, list):
2114
+ return [normalizeMessage(item, depth=depth-1) for item in sexp]
2115
+ if isinstance(sexp, Symbol):
2116
+ return dumps(sexp)
2117
+ else:
2118
+ return sexp
2119
+
2120
+
2121
+ def tacticTakesHypArgs(stem: str) -> bool:
2122
+ now_match = re.match(r"\s*now\s+(.*)", stem)
2123
+ if now_match:
2124
+ return tacticTakesHypArgs(now_match.group(1))
2125
+ try_match = re.match(r"\s*try\s+(.*)", stem)
2126
+ if try_match:
2127
+ return tacticTakesHypArgs(try_match.group(1))
2128
+ repeat_match = re.match(r"\s*repeat\s+(.*)", stem)
2129
+ if repeat_match:
2130
+ return tacticTakesHypArgs(repeat_match.group(1))
2131
+ return (
2132
+ stem == "apply"
2133
+ or stem == "eapply"
2134
+ or stem == "eexploit"
2135
+ or stem == "exploit"
2136
+ or stem == "erewrite"
2137
+ or stem == "rewrite"
2138
+ or stem == "erewrite !"
2139
+ or stem == "rewrite !"
2140
+ or stem == "erewrite <-"
2141
+ or stem == "rewrite <-"
2142
+ or stem == "destruct"
2143
+ or stem == "elim"
2144
+ or stem == "eelim"
2145
+ or stem == "inversion"
2146
+ or stem == "monadInv"
2147
+ or stem == "pattern"
2148
+ or stem == "revert"
2149
+ or stem == "exact"
2150
+ or stem == "eexact"
2151
+ or stem == "simpl in"
2152
+ or stem == "fold"
2153
+ or stem == "generalize"
2154
+ or stem == "exists"
2155
+ or stem == "case"
2156
+ or stem == "inv"
2157
+ or stem == "subst"
2158
+ or stem == "specialize"
2159
+ )
2160
+
2161
+
2162
+ def tacticTakesBinderArgs(stem: str) -> bool:
2163
+ return stem == "induction"
2164
+
2165
+
2166
+ def tacticTakesIdentifierArg(stem: str) -> bool:
2167
+ return stem == "unfold"
2168
+
2169
+
2170
+ def lemma_name_from_statement(stmt: str) -> str:
2171
+ if ("Goal" in stmt or "Obligation" in stmt or "Morphism" in stmt):
2172
+ return ""
2173
+ stripped_stmt = kill_comments(stmt).strip()
2174
+ derive_match = re.fullmatch(
2175
+ r"\s*Derive\s+([\w'_]+)\s+SuchThat\s+(.*)\s+As\s+([\w']+)\.\s*",
2176
+ stripped_stmt, flags=re.DOTALL)
2177
+ if derive_match:
2178
+ return derive_match.group(3)
2179
+ lemma_match = re.match(
2180
+ r"\s*(?:(?:Local|Global)\s+)?(?:" + "|".join(normal_lemma_starting_patterns) +
2181
+ r")\s+([\w'\.]*)(.*)",
2182
+ stripped_stmt,
2183
+ flags=re.DOTALL)
2184
+ assert lemma_match, (stripped_stmt, stmt)
2185
+ lemma_name = lemma_match.group(1)
2186
+ assert ":" not in lemma_name, stripped_stmt
2187
+ return lemma_name
2188
+
2189
+
2190
+ symbols_regexp = (r',|(?::>)|(?::(?!=))|(?::=)|\)|\(|;|@\{|~|\+{1,2}|\*{1,2}'
2191
+ r'|&&|\|\||(?<!\\)/(?!\\)|/\\|\\/|(?<![<*+-/|&])=(?!>)|%|'
2192
+ r'(?<!<)-(?!>)|<-|->|<=|>=|<>|\^|\[|\]|(?<!\|)\}|\{(?!\|)')
2193
+
2194
+
2195
+ def get_words(string: str) -> List[str]:
2196
+ return [word for word in re.sub(
2197
+ r'(\.+|' + symbols_regexp + ')',
2198
+ r' \1 ',
2199
+ string).split()
2200
+ if word.strip() != '']
2201
+
2202
+
2203
+ def get_binder_var(goal: str, binder_idx: int) -> Optional[str]:
2204
+ paren_depth = 0
2205
+ binders_passed = 0
2206
+ skip = False
2207
+ forall_match = re.match(r"forall\s+", goal.strip())
2208
+ if not forall_match:
2209
+ return None
2210
+ rest_goal = goal[forall_match.end():]
2211
+ for w in get_words(rest_goal):
2212
+ if w == "(":
2213
+ paren_depth += 1
2214
+ elif w == ")":
2215
+ paren_depth -= 1
2216
+ if paren_depth == 1 or paren_depth == 0:
2217
+ skip = False
2218
+ elif (paren_depth == 1 or paren_depth == 0) and not skip:
2219
+ if w == ":":
2220
+ skip = True
2221
+ else:
2222
+ binders_passed += 1
2223
+ if binders_passed == binder_idx:
2224
+ return w
2225
+ return None
2226
+
2227
+
2228
+ def normalizeNumericArgs(datum: ScrapedTactic) -> ScrapedTactic:
2229
+ numerical_induction_match = re.match(
2230
+ r"\s*(induction|destruct)\s+(\d+)\s*\.",
2231
+ kill_comments(datum.tactic).strip())
2232
+ if numerical_induction_match:
2233
+ stem = numerical_induction_match.group(1)
2234
+ binder_idx = int(numerical_induction_match.group(2))
2235
+ binder_var = get_binder_var(datum.context.fg_goals[0].goal, binder_idx)
2236
+ if binder_var:
2237
+ newtac = stem + " " + binder_var + "."
2238
+ return ScrapedTactic(datum.prev_tactics,
2239
+ datum.relevant_lemmas,
2240
+ datum.context, newtac)
2241
+ else:
2242
+ return datum
2243
+ else:
2244
+ return datum
2245
+
2246
+
2247
+ def parsePPSubgoal(substr: str) -> Obligation:
2248
+ split = re.split("\n====+\n", substr)
2249
+ assert len(split) == 2, substr
2250
+ hypsstr, goal = split
2251
+ return Obligation(parse_hyps(hypsstr), goal)
2252
+
2253
+
2254
+ def summarizeContext(context: ProofContext) -> None:
2255
+ eprint("Foreground:")
2256
+ for i, subgoal in enumerate(context.fg_goals):
2257
+ hyps_str = ",".join(get_first_var_in_hyp(hyp)
2258
+ for hyp in subgoal.hypotheses)
2259
+ goal_str = re.sub("\n", "\\n", subgoal.goal)[:100]
2260
+ eprint(f"S{i}: {hyps_str} -> {goal_str}")
2261
+
2262
+
2263
+ def isValidCommand(command: str) -> bool:
2264
+ command = kill_comments(command)
2265
+ goal_selector_match = re.fullmatch(r"\s*\d+\s*:(.*)", command,
2266
+ flags=re.DOTALL)
2267
+ if goal_selector_match:
2268
+ return isValidCommand(goal_selector_match.group(1))
2269
+ return ((command.strip()[-1] == "."
2270
+ and not re.match(r"\s*{", command))
2271
+ or re.fullmatch(r"\s*[-+*{}]*\s*", command) is not None) \
2272
+ and (command.count('(') == command.count(')'))
2273
+
2274
+
2275
+ def load_commands_preserve(args: argparse.Namespace, file_idx: int,
2276
+ filename: str) -> List[str]:
2277
+ try:
2278
+ should_show = args.progress
2279
+ except AttributeError:
2280
+ should_show = False
2281
+ try:
2282
+ should_show = should_show or args.read_progress
2283
+ except AttributeError:
2284
+ pass
2285
+
2286
+ try:
2287
+ command_limit = args.command_limit
2288
+ except AttributeError:
2289
+ command_limit = None
2290
+ return load_commands(filename, max_commands=command_limit,
2291
+ progress_bar=should_show,
2292
+ progress_bar_offset=file_idx * 2)
2293
+
2294
+
2295
+ def load_commands(filename: str,
2296
+ max_commands: Optional[int] = None,
2297
+ progress_bar: bool = False,
2298
+ progress_bar_offset: Optional[int] = None) -> List[str]:
2299
+ with open(filename, 'r') as fin:
2300
+ contents = fin.read()
2301
+ return read_commands(contents,
2302
+ max_commands=max_commands,
2303
+ progress_bar=progress_bar,
2304
+ progress_bar_offset=progress_bar_offset)
2305
+
2306
+
2307
+ def read_commands(contents: str,
2308
+ max_commands: Optional[int] = None,
2309
+ progress_bar: bool = False,
2310
+ progress_bar_offset: Optional[int] = None) -> List[str]:
2311
+ result: List[str] = []
2312
+ cur_command = ""
2313
+ comment_depth = 0
2314
+ in_quote = False
2315
+ curPos = 0
2316
+
2317
+ def search_pat(pat: Pattern) -> Tuple[Optional[Match], int]:
2318
+ match = pat.search(contents, curPos)
2319
+ return match, match.end() if match else len(contents) + 1
2320
+
2321
+ with tqdm(total=len(contents)+1, file=sys.stdout,
2322
+ disable=(not progress_bar),
2323
+ position=progress_bar_offset,
2324
+ desc="Reading file", leave=False,
2325
+ dynamic_ncols=True, bar_format=mybarfmt) as pbar:
2326
+ while curPos < len(contents) and (max_commands is None or
2327
+ len(result) < max_commands):
2328
+ _, next_quote = search_pat(re.compile(r"(?<!\\)\""))
2329
+ _, next_open_comment = search_pat(re.compile(r"\(\*"))
2330
+ _, next_close_comment = search_pat(re.compile(r"\*\)"))
2331
+ _, next_bracket = search_pat(re.compile(r"[\{\}]"))
2332
+ next_bullet_match, next_bullet = search_pat(
2333
+ re.compile(r"[\+\-\*]+(?![\)\+\-\*])"))
2334
+ _, next_period = search_pat(
2335
+ re.compile(r"(?<!\.)\.($|\s)|\.\.\.($|\s)"))
2336
+ nextPos = min(next_quote,
2337
+ next_open_comment, next_close_comment,
2338
+ next_bracket,
2339
+ next_bullet, next_period)
2340
+ assert curPos < nextPos
2341
+ next_chunk = contents[curPos:nextPos]
2342
+ cur_command += next_chunk
2343
+ pbar.update(nextPos - curPos)
2344
+ if nextPos == next_quote:
2345
+ if comment_depth == 0:
2346
+ in_quote = not in_quote
2347
+ elif nextPos == next_open_comment:
2348
+ if not in_quote:
2349
+ comment_depth += 1
2350
+ elif nextPos == next_close_comment:
2351
+ if not in_quote and comment_depth > 0:
2352
+ comment_depth -= 1
2353
+ elif nextPos == next_bracket:
2354
+ if not in_quote and comment_depth == 0 and \
2355
+ re.match(r"\s*(?:\d+\s*:)?\s*$",
2356
+ kill_comments(cur_command[:-1])):
2357
+ result.append(cur_command)
2358
+ cur_command = ""
2359
+ elif nextPos == next_bullet:
2360
+ assert next_bullet_match
2361
+ match_length = next_bullet_match.end() - \
2362
+ next_bullet_match.start()
2363
+ if not in_quote and comment_depth == 0 and \
2364
+ re.match(r"\s*$",
2365
+ kill_comments(cur_command[:-match_length])):
2366
+ result.append(cur_command)
2367
+ cur_command = ""
2368
+ assert next_bullet_match.end() >= nextPos
2369
+ elif nextPos == next_period:
2370
+ if not in_quote and comment_depth == 0:
2371
+ result.append(cur_command)
2372
+ cur_command = ""
2373
+ curPos = nextPos
2374
+ return result
2375
+
2376
+
2377
+ parsePat = re.compile("[() ]", flags=(re.ASCII | re.IGNORECASE))
2378
+
2379
+
2380
+ def searchStrsInMsg(sexp, fuel: int = 30) -> List[str]:
2381
+ if isinstance(sexp, list) and len(sexp) > 0 and fuel > 0:
2382
+ if sexp[0] == "str" or sexp[0] == Symbol("str"):
2383
+ assert len(sexp) == 2 and isinstance(sexp[1], str)
2384
+ return [sexp[1]]
2385
+ else:
2386
+ return [substr
2387
+ for substrs in [searchStrsInMsg(sublst, fuel - 1)
2388
+ for sublst in sexp]
2389
+ for substr in substrs]
2390
+ return []
2391
+
2392
+
2393
+ def get_module_from_filename(filename: Union[Path, str]) -> str:
2394
+ return Path(filename).stem
2395
+
2396
+
2397
+ def symbol_matches(full_symbol: str, shorthand_symbol: str) -> bool:
2398
+ if full_symbol == shorthand_symbol:
2399
+ return True
2400
+ else:
2401
+ return full_symbol.split(".")[-1] == shorthand_symbol
2402
+ pass
2403
+
2404
+
2405
+ def subgoalSurjective(newsub: Obligation, oldsub: Obligation) -> bool:
2406
+ oldhyp_terms = [get_hyp_type(hyp) for hyp in oldsub.hypotheses]
2407
+ for newhyp_term in [get_hyp_type(hyp) for hyp in newsub.hypotheses]:
2408
+ if newhyp_term not in oldhyp_terms:
2409
+ return False
2410
+ return newsub.goal == oldsub.goal
2411
+
2412
+
2413
+ def contextSurjective(newcontext: ProofContext, oldcontext: ProofContext):
2414
+ for oldsub in oldcontext.all_goals:
2415
+ if not any([subgoalSurjective(newsub, oldsub)
2416
+ for newsub in newcontext.all_goals]):
2417
+ return False
2418
+ return len(newcontext.all_goals) >= len(oldcontext.all_goals)
2419
+
2420
+
2421
+ def lemmas_in_file(filename: str, cmds: List[str],
2422
+ include_proof_relevant: bool = False) \
2423
+ -> List[Tuple[str, str]]:
2424
+ lemmas = []
2425
+ proof_relevant = False
2426
+ in_proof = False
2427
+ for cmd_idx, cmd in reversed(list(enumerate(cmds))):
2428
+ if in_proof and possibly_starting_proof(cmd):
2429
+ in_proof = False
2430
+ proof_relevant = proof_relevant or \
2431
+ cmd.strip().startswith("Derive") or \
2432
+ cmd.strip().startswith("Equations")
2433
+ if not proof_relevant or include_proof_relevant:
2434
+ lemmas.append((cmd_idx, cmd))
2435
+ if ending_proof(cmd):
2436
+ in_proof = True
2437
+ proof_relevant = cmd.strip() == "Defined."
2438
+ sm_stack = initial_sm_stack(filename)
2439
+ full_lemmas = []
2440
+ obl_num = 0
2441
+ last_program_statement = ""
2442
+ for cmd_idx, cmd in enumerate(cmds):
2443
+ scmd = kill_comments(cmd).strip()
2444
+ sm_stack = update_sm_stack(sm_stack, cmd)
2445
+ if (cmd_idx, cmd) in lemmas:
2446
+ if re.match(r"\s*Next\s+Obligation\s*\.\s*",
2447
+ scmd):
2448
+ assert last_program_statement != ""
2449
+ unique_lemma_statement = f"{last_program_statement} Obligation {obl_num}."
2450
+ obl_num += 1
2451
+ else:
2452
+ unique_lemma_statement = cmd
2453
+ full_lemmas.append((sm_prefix_from_stack(
2454
+ sm_stack), unique_lemma_statement))
2455
+ if re.match(r"\s*Program\s+.*", scmd):
2456
+ last_program_statement = cmd
2457
+ obl_num = 0
2458
+ return full_lemmas
2459
+
2460
+
2461
+ def let_to_hyp(let_cmd: str) -> str:
2462
+ let_match = re.match(r"\s*Let(?:\s+Fixpoint)?\s+(.*)\.\s*$",
2463
+ let_cmd,
2464
+ flags=re.DOTALL)
2465
+ assert let_match, "Command passed in isn't a Let!"
2466
+ split = split_by_char_outside_matching(r"\(", r"\)", ":=",
2467
+ let_match.group(1))
2468
+ if split:
2469
+ name_and_type, body = split
2470
+ else:
2471
+ name_and_type = let_match.group(1)
2472
+
2473
+ name_and_prebinders, ty = \
2474
+ unwrap(split_by_char_outside_matching(r"\(", r"\)", ":",
2475
+ name_and_type))
2476
+ prebinders_match = re.match(
2477
+ r"\s*([\w']*)([^{}]*)",
2478
+ name_and_prebinders)
2479
+ assert prebinders_match, \
2480
+ f"{name_and_prebinders} doesn't match prebinders pattern"
2481
+ name = prebinders_match.group(1)
2482
+ prebinders = prebinders_match.group(2)
2483
+ if prebinders.strip() != "":
2484
+ prebinders = f"forall {prebinders},"
2485
+
2486
+ return f"{name} : {prebinders} {ty[1:]}."
2487
+
2488
+
2489
+ def admit_proof_cmds(lemma_statement: str, ending_statement: str) -> List[str]:
2490
+ lemma_statement = kill_comments(lemma_statement)
2491
+ let_match = re.fullmatch(r"\s*Let(?:\s+Fixpoint)?\s+(.*)\.\s*$",
2492
+ lemma_statement,
2493
+ flags=re.DOTALL)
2494
+ if let_match and ":=" not in lemma_statement:
2495
+ admitted_defn = f"Hypothesis {let_to_hyp(lemma_statement)}"
2496
+ return ["Abort.", admitted_defn]
2497
+ save_match = re.fullmatch(r"\s*Save\s+(.*)\.\s*$",
2498
+ kill_comments(ending_statement),
2499
+ flags=re.DOTALL)
2500
+ if save_match:
2501
+ goal_match = re.fullmatch(r"\s*Goal\s+(.*)\.\s*$",
2502
+ lemma_statement, flags=re.DOTALL)
2503
+ assert goal_match, f"Didn't start with 'Goal'! lemma_statement is {lemma_statement}"
2504
+
2505
+ admitted_defn = f"Axiom {save_match.group(1)} : {goal_match.group(1)}."
2506
+ return ["Abort.", admitted_defn]
2507
+ return ["Admitted."]
2508
+
2509
+
2510
+ def admit_proof(coq: SerapiInstance,
2511
+ lemma_statement: str, ending_statement: str) -> List[str]:
2512
+ admit_cmds = admit_proof_cmds(lemma_statement, ending_statement)
2513
+ for cmd in admit_cmds:
2514
+ coq.run_stmt(cmd)
2515
+ return admit_cmds
2516
+
2517
+ def set_switch(switch: str) -> None:
2518
+ env_string = subprocess.run(f"opam env --switch={switch} --set-switch",
2519
+ shell=True, stdout=subprocess.PIPE, text=True).stdout
2520
+
2521
+ _setup_opam_env_from_str(env_string)
2522
+
2523
+ def setup_opam_env() -> None:
2524
+ env_string = subprocess.run(f"opam env", shell=True, stdout=subprocess.PIPE,
2525
+ text=True).stdout
2526
+ _setup_opam_env_from_str(env_string)
2527
+
2528
+
2529
+ def _setup_opam_env_from_str(env_string: str) -> None:
2530
+ for env_line in env_string.splitlines():
2531
+ linematch = re.fullmatch(r"(\w*)='([^;]*)'; export (\w*);", env_line)
2532
+ assert linematch, env_line
2533
+ envvar = linematch.group(1)
2534
+ assert envvar == linematch.group(3)
2535
+ envval = linematch.group(2)
2536
+ os.environ[envvar] = envval
2537
+
2538
+ def main() -> None:
2539
+ parser = argparse.ArgumentParser(
2540
+ description="Module for interacting with a coq-serapi instance "
2541
+ "from Python (3).")
2542
+ parser.add_argument(
2543
+ "--prelude", default=".", type=str,
2544
+ help="The `home` directory in which to look for the _CoqProject file.")
2545
+ parser.add_argument(
2546
+ "--sertop", default="sertop",
2547
+ dest="sertopbin", type=str,
2548
+ help="The location of the serapi (sertop) binary to use.")
2549
+ parser.add_argument(
2550
+ "--srcfile", "-f", nargs='*', dest='srcfiles', default=[], type=str,
2551
+ help="Coq source file(s) to execute.")
2552
+ parser.add_argument(
2553
+ "--interactive", "-i",
2554
+ action='store_const', const=True, default=False,
2555
+ help="Drop into a pdb prompt after executing source file(s). "
2556
+ "A `coq` object will be in scope as an instance of SerapiInstance, "
2557
+ "and will kill the process when you leave.")
2558
+ parser.add_argument("--verbose", "-v",
2559
+ action='store_const', const=True, default=False)
2560
+ parser.add_argument("--progress",
2561
+ action='store_const', const=True, default=False)
2562
+ args = parser.parse_args()
2563
+ with SerapiContext([args.sertopbin, '--implicit'],
2564
+ "",
2565
+ args.prelude) as coq:
2566
+ def handle_interrupt(*args):
2567
+ nonlocal coq
2568
+ print("Running coq interrupt")
2569
+ coq.interrupt()
2570
+
2571
+ with sighandler_context(signal.SIGINT, handle_interrupt):
2572
+ for srcpath in args.srcfiles:
2573
+ commands = load_commands(srcpath)
2574
+ for cmd in commands:
2575
+ eprint(f"Running: \"{cmd}\"")
2576
+ coq.run_stmt(cmd)
2577
+ if args.interactive:
2578
+ breakpoint()
2579
+ x = 50
2580
+
2581
+
2582
+ if __name__ == "__main__":
2583
+ main()