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.
- itp_interface/__init__.py +0 -0
- itp_interface/agent/__init__.py +0 -0
- itp_interface/agent/simple_proof_agent.py +100 -0
- itp_interface/coq_ser_api/__init__.py +165 -0
- itp_interface/coq_ser_api/contexts.py +283 -0
- itp_interface/coq_ser_api/coq_agent.py +459 -0
- itp_interface/coq_ser_api/coq_backend.py +135 -0
- itp_interface/coq_ser_api/coq_util.py +839 -0
- itp_interface/coq_ser_api/example.py +67 -0
- itp_interface/coq_ser_api/lsp_backend.py +375 -0
- itp_interface/coq_ser_api/py.typed +0 -0
- itp_interface/coq_ser_api/serapi_backend.py +841 -0
- itp_interface/coq_ser_api/util.py +145 -0
- itp_interface/coq_ser_api_old/__init__.py +2583 -0
- itp_interface/coq_ser_api_old/contexts.py +172 -0
- itp_interface/coq_ser_api_old/util.py +146 -0
- itp_interface/lean_server/__init__.py +0 -0
- itp_interface/lean_server/commands.py +484 -0
- itp_interface/lean_server/lean3_search_tool.py +358 -0
- itp_interface/lean_server/lean4_repl_interface.py +151 -0
- itp_interface/lean_server/lean4_utils.py +255 -0
- itp_interface/lean_server/lean_cmd_server.py +111 -0
- itp_interface/lean_server/lean_context.py +60 -0
- itp_interface/lean_server/lean_sync_server.py +174 -0
- itp_interface/lean_server/lean_utils.py +199 -0
- itp_interface/lean_server/py.typed +1 -0
- itp_interface/main/__init__.py +0 -0
- itp_interface/main/config/afp_data_gen.yaml +14 -0
- itp_interface/main/config/benchmark/CompCert.yaml +366 -0
- itp_interface/main/config/benchmark/GeoCoq.yaml +930 -0
- itp_interface/main/config/benchmark/UniMath.yaml +2690 -0
- itp_interface/main/config/benchmark/afp_isabelle.yaml +29200 -0
- itp_interface/main/config/benchmark/agent_proverbot_hard.yaml +247 -0
- itp_interface/main/config/benchmark/category-theory.yaml +470 -0
- itp_interface/main/config/benchmark/compcert_118_subset.yaml +148 -0
- itp_interface/main/config/benchmark/compcert_benchmark.yaml +36 -0
- itp_interface/main/config/benchmark/compcert_benchmark_hard.yaml +498 -0
- itp_interface/main/config/benchmark/compcert_benchmark_hard_1.yaml +55 -0
- itp_interface/main/config/benchmark/compcert_benchmark_hard_2.yaml +24 -0
- itp_interface/main/config/benchmark/compcert_benchmark_hard_3.yaml +95 -0
- itp_interface/main/config/benchmark/compcert_benchmark_hard_7_per_cent.yaml +78 -0
- itp_interface/main/config/benchmark/compcert_benchmark_test.yaml +38 -0
- itp_interface/main/config/benchmark/compcert_benchmark_train.yaml +340 -0
- itp_interface/main/config/benchmark/leandojo_novel_premises_test.yaml +2908 -0
- itp_interface/main/config/benchmark/leandojo_novel_premises_train.yaml +98645 -0
- itp_interface/main/config/benchmark/leandojo_novel_premises_val.yaml +2912 -0
- itp_interface/main/config/benchmark/leandojo_random.yaml +2889 -0
- itp_interface/main/config/benchmark/leandojo_random_test.yaml +2421 -0
- itp_interface/main/config/benchmark/leandojo_random_train.yaml +62729 -0
- itp_interface/main/config/benchmark/leandojo_random_val.yaml +2504 -0
- itp_interface/main/config/benchmark/math-comp.yaml +200 -0
- itp_interface/main/config/benchmark/miniF2F_test.yaml +12 -0
- itp_interface/main/config/benchmark/miniF2F_test_aime.yaml +27 -0
- itp_interface/main/config/benchmark/miniF2F_test_algebra.yaml +30 -0
- itp_interface/main/config/benchmark/miniF2F_test_amc12.yaml +57 -0
- itp_interface/main/config/benchmark/miniF2F_test_few_shot_hard.yaml +231 -0
- itp_interface/main/config/benchmark/miniF2F_test_imo.yaml +32 -0
- itp_interface/main/config/benchmark/miniF2F_test_induction.yaml +20 -0
- itp_interface/main/config/benchmark/miniF2F_test_mathd_algebra.yaml +82 -0
- itp_interface/main/config/benchmark/miniF2F_test_mathd_algebra_hard.yaml +72 -0
- itp_interface/main/config/benchmark/miniF2F_test_mathd_numbertheory.yaml +72 -0
- itp_interface/main/config/benchmark/miniF2F_test_numbertheory.yaml +20 -0
- itp_interface/main/config/benchmark/minicompcert_benchmark_1.yaml +14 -0
- itp_interface/main/config/benchmark/proverbot_hard.yaml +104 -0
- itp_interface/main/config/benchmark/re_prover.yaml +66 -0
- itp_interface/main/config/benchmark/re_prover_hard.yaml +41 -0
- itp_interface/main/config/benchmark/re_prover_very_hard.yaml +22 -0
- itp_interface/main/config/benchmark/reprover_with_retrieval.yaml +73 -0
- itp_interface/main/config/benchmark/reprover_with_retrieval_hard.yaml +30 -0
- itp_interface/main/config/benchmark/reprover_with_retrieval_neg.yaml +195 -0
- itp_interface/main/config/benchmark/simple_benchmark_1.yaml +24 -0
- itp_interface/main/config/benchmark/simple_benchmark_8.yaml +50 -0
- itp_interface/main/config/benchmark/simple_benchmark_9.yaml +65 -0
- itp_interface/main/config/benchmark/simple_benchmark_isabelle.yaml +18 -0
- itp_interface/main/config/benchmark/simple_benchmark_lean.yaml +12 -0
- itp_interface/main/config/benchmark/simple_benchmark_lean_training_data.yaml +12 -0
- itp_interface/main/config/benchmark/simple_rl_benchmark_lean.yaml +14 -0
- itp_interface/main/config/benchmark/stack_machine.yaml +13 -0
- itp_interface/main/config/benchmark/stack_machine_hard.yaml +15 -0
- itp_interface/main/config/category_theory_data_gen.yaml +14 -0
- itp_interface/main/config/category_theory_data_gen_random.yaml +16 -0
- itp_interface/main/config/compcert_data_gen_test.yaml +10 -0
- itp_interface/main/config/compcert_data_gen_train.yaml +7 -0
- itp_interface/main/config/env_settings/bm25_retrieval.yaml +2 -0
- itp_interface/main/config/env_settings/bm25_retrieval_no_dfns.yaml +2 -0
- itp_interface/main/config/env_settings/bm25_retrieval_only_local_no_dfns.yaml +2 -0
- itp_interface/main/config/env_settings/bm25_retrieval_with_print.yaml +2 -0
- itp_interface/main/config/env_settings/bm25_retrieval_with_print_only_local.yaml +2 -0
- itp_interface/main/config/env_settings/bm25_retrieval_with_print_only_local_no_dfns.yaml +2 -0
- itp_interface/main/config/env_settings/no_retrieval.yaml +2 -0
- itp_interface/main/config/experiments.yaml +12 -0
- itp_interface/main/config/geo_coq_data_gen.yaml +14 -0
- itp_interface/main/config/geo_coq_data_gen_random.yaml +16 -0
- itp_interface/main/config/leandojo_random_data_gen.yaml +16 -0
- itp_interface/main/config/math_comp_data_gen.yaml +14 -0
- itp_interface/main/config/math_comp_data_gen_random.yaml +16 -0
- itp_interface/main/config/mathlib_data_gen.yaml +14 -0
- itp_interface/main/config/repo/coq_repos.yaml +191 -0
- itp_interface/main/config/run_settings/default_coq_data_generation_transforms.yaml +24 -0
- itp_interface/main/config/run_settings/default_isabelle_data_generation_transforms.yaml +24 -0
- itp_interface/main/config/run_settings/default_lean4_data_generation_transforms.yaml +24 -0
- itp_interface/main/config/run_settings/default_lean_data_generation_transforms.yaml +24 -0
- itp_interface/main/config/simple_coq_data_gen.yaml +12 -0
- itp_interface/main/config/simple_coq_data_gen_random.yaml +17 -0
- itp_interface/main/config/simple_lean_data_gen.yaml +12 -0
- itp_interface/main/config/simple_rl_lean_data_gen.yaml +12 -0
- itp_interface/main/config/uni_math_data_gen.yaml +14 -0
- itp_interface/main/config.py +192 -0
- itp_interface/main/extract_benchmark_dataset.py +106 -0
- itp_interface/main/filter_dataset.py +107 -0
- itp_interface/main/install.py +92 -0
- itp_interface/main/merge_dataset.py +96 -0
- itp_interface/main/run_tool.py +444 -0
- itp_interface/pisa/.git +1 -0
- itp_interface/pisa/.gitignore +125 -0
- itp_interface/pisa/.idea/.gitignore +8 -0
- itp_interface/pisa/.idea/ClojureProjectResolveSettings.xml +6 -0
- itp_interface/pisa/.idea/codeStyles/Project.xml +7 -0
- itp_interface/pisa/.idea/codeStyles/codeStyleConfig.xml +5 -0
- itp_interface/pisa/.idea/inspectionProfiles/Project_Default.xml +16 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_android_annotations_4_1_1_4_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_api_grpc_proto_google_common_protos_1_17_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_code_findbugs_jsr305_3_0_2_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_code_gson_gson_2_8_6_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_errorprone_error_prone_annotations_2_3_4_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_guava_failureaccess_1_0_1_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_guava_guava_30_0_jre_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava_jar.xml +9 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_j2objc_j2objc_annotations_1_3_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_protobuf_protobuf_java_3_12_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_google_protobuf_protobuf_java_util_3_12_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_lihaoyi_fastparse_2_13_2_3_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_lihaoyi_geny_2_13_0_6_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_lihaoyi_sourcecode_2_13_0_2_1_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_lenses_2_13_0_10_9_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_scalapb_runtime_2_13_0_10_9_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_scalapb_runtime_grpc_2_13_0_10_9_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_thesamet_scalapb_zio_grpc_zio_grpc_core_2_13_0_4_2_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__com_thoughtworks_paranamer_paranamer_2_8_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__commons_io_commons_io_2_8_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__de_unruh_java_patterns_0_1_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__de_unruh_scala_isabelle_2_13_master_SNAPSHOT_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__dev_zio_izumi_reflect_2_13_1_0_0_M9_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__dev_zio_izumi_reflect_thirdparty_boopickle_shaded_2_13_1_0_0_M9_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__dev_zio_zio_2_13_1_0_3_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__dev_zio_zio_stacktracer_2_13_1_0_3_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__dev_zio_zio_streams_2_13_1_0_3_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_api_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_context_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_core_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_netty_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_protobuf_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_protobuf_lite_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_services_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_grpc_grpc_stub_1_34_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_buffer_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_http2_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_http_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_codec_socks_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_common_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_handler_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_handler_proxy_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_resolver_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_netty_netty_transport_4_1_51_Final_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__io_perfmark_perfmark_api_0_19_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__net_java_dev_jna_jna_5_3_1_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__net_liftweb_lift_json_2_13_3_4_3_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_apache_commons_commons_lang3_3_11_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_apache_commons_commons_text_1_9_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_checkerframework_checker_qual_3_5_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_codehaus_mojo_animal_sniffer_annotations_1_18_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_jetbrains_annotations_20_1_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_jline_jline_3_16_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_log4s_log4s_2_13_1_9_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_modules_scala_collection_compat_2_13_2_1_6_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_modules_scala_xml_2_13_1_3_0_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scala_compiler_2_13_4_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scala_library_2_13_4_jar.xml +23 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scala_reflect_2_13_4_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scala_lang_scalap_2_13_4_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_scalaz_scalaz_core_2_13_7_3_2_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_slf4j_slf4j_api_1_7_30_jar.xml +13 -0
- itp_interface/pisa/.idea/libraries/sbt__org_slf4j_slf4j_simple_1_7_30_jar.xml +13 -0
- itp_interface/pisa/.idea/misc.xml +7 -0
- itp_interface/pisa/.idea/modules/PISA-build.iml +127 -0
- itp_interface/pisa/.idea/modules/PISA.iml +94 -0
- itp_interface/pisa/.idea/modules.xml +9 -0
- itp_interface/pisa/.idea/other.xml +6 -0
- itp_interface/pisa/.idea/sbt.xml +20 -0
- itp_interface/pisa/.idea/scala_compiler.xml +6 -0
- itp_interface/pisa/.idea/uiDesigner.xml +124 -0
- itp_interface/pisa/.idea/vcs.xml +6 -0
- itp_interface/pisa/.scalafmt.conf +2 -0
- itp_interface/pisa/LICENSE +29 -0
- itp_interface/pisa/README.md +262 -0
- itp_interface/pisa/build.sbt +49 -0
- itp_interface/pisa/build.sh +26 -0
- itp_interface/pisa/command_generation/close_gaps.py +44 -0
- itp_interface/pisa/command_generation/conjecture_normal_order.py +62 -0
- itp_interface/pisa/command_generation/conjecturer_command_generator.py +36 -0
- itp_interface/pisa/command_generation/create_dirs.py +11 -0
- itp_interface/pisa/command_generation/find_std.py +67 -0
- itp_interface/pisa/command_generation/generate_build_commands_afp.py +15 -0
- itp_interface/pisa/command_generation/generate_build_commands_std.py +15 -0
- itp_interface/pisa/command_generation/generate_commands_afp.py +103 -0
- itp_interface/pisa/command_generation/generate_commands_mini.py +73 -0
- itp_interface/pisa/command_generation/generate_commands_std.py +69 -0
- itp_interface/pisa/command_generation/generate_hammer_extraction_text.py +5 -0
- itp_interface/pisa/command_generation/hammer_command_generator.py +40 -0
- itp_interface/pisa/command_generation/hp_search_command_generator.py +63 -0
- itp_interface/pisa/command_generation/oracle_command_generator.py +56 -0
- itp_interface/pisa/command_generation/search_command_generator.py +69 -0
- itp_interface/pisa/command_generation/summarise_problem_names.py +45 -0
- itp_interface/pisa/command_generation/tpu_hp_search.py +75 -0
- itp_interface/pisa/docker/Dockerfile +34 -0
- itp_interface/pisa/docker/docker_tutorial.md +64 -0
- itp_interface/pisa/eval_setup/copy_isabelle.py +42 -0
- itp_interface/pisa/eval_setup/copy_pisa_jars.py +18 -0
- itp_interface/pisa/mesh_transformer_utils/tokenization.py +86 -0
- itp_interface/pisa/project/build.properties +1 -0
- itp_interface/pisa/project/plugins.sbt +5 -0
- itp_interface/pisa/requirements.txt +4 -0
- itp_interface/pisa/scripts/extract_last_k_steps.py +28 -0
- itp_interface/pisa/scripts/extract_proof_corpus.py +26 -0
- itp_interface/pisa/scripts/gather_hammer_results.py +27 -0
- itp_interface/pisa/scripts/length_in_char_stats.py +20 -0
- itp_interface/pisa/scripts/mix.py +127 -0
- itp_interface/pisa/scripts/results_stat.py +52 -0
- itp_interface/pisa/scripts/test_array_job.sh +34 -0
- itp_interface/pisa/setup.sh +25 -0
- itp_interface/pisa/src/main/protobuf/server.proto +60 -0
- itp_interface/pisa/src/main/python/.idea/.gitignore +8 -0
- itp_interface/pisa/src/main/python/.idea/inspectionProfiles/Project_Default.xml +18 -0
- itp_interface/pisa/src/main/python/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- itp_interface/pisa/src/main/python/.idea/misc.xml +4 -0
- itp_interface/pisa/src/main/python/.idea/modules.xml +8 -0
- itp_interface/pisa/src/main/python/.idea/python.iml +12 -0
- itp_interface/pisa/src/main/python/.idea/vcs.xml +6 -0
- itp_interface/pisa/src/main/python/conjecturing_parsing/conjecturer_postprocessing.py +59 -0
- itp_interface/pisa/src/main/python/data_extraction/extract_data.py +184 -0
- itp_interface/pisa/src/main/python/data_extraction/find_premises.py +221 -0
- itp_interface/pisa/src/main/python/data_extraction/process_data.py +129 -0
- itp_interface/pisa/src/main/python/legacy/PisaFlexibleClient.py +167 -0
- itp_interface/pisa/src/main/python/legacy/autof_test.py +74 -0
- itp_interface/pisa/src/main/python/legacy/cmd_client.py +23 -0
- itp_interface/pisa/src/main/python/legacy/convert_scala_dump_to_test_name_jsons.py +14 -0
- itp_interface/pisa/src/main/python/legacy/create_data_txt.py +72 -0
- itp_interface/pisa/src/main/python/legacy/create_finetune_tfrecords.py +311 -0
- itp_interface/pisa/src/main/python/legacy/demo.py +49 -0
- itp_interface/pisa/src/main/python/legacy/evaluate.py +108 -0
- itp_interface/pisa/src/main/python/legacy/extract_first_step.py +25 -0
- itp_interface/pisa/src/main/python/legacy/get_global_facts.py +35 -0
- itp_interface/pisa/src/main/python/legacy/mix_data.py +19 -0
- itp_interface/pisa/src/main/python/legacy/one_stage_extraction.py +111 -0
- itp_interface/pisa/src/main/python/legacy/prepare_episodic_transitions.py +137 -0
- itp_interface/pisa/src/main/python/legacy/prepare_translation_pairs.py +277 -0
- itp_interface/pisa/src/main/python/pisa_client.py +322 -0
- itp_interface/pisa/src/main/python/server_pb2.py +394 -0
- itp_interface/pisa/src/main/python/server_pb2_grpc.py +230 -0
- itp_interface/pisa/src/main/python/test_client.py +17 -0
- itp_interface/pisa/src/main/python/test_client2.py +79 -0
- itp_interface/pisa/src/main/python/utils/filters.py +59 -0
- itp_interface/pisa/src/main/python/utils/pisa_server_control.py +29 -0
- itp_interface/pisa/src/main/scala/pisa/agent/CheckSyntax.scala +257 -0
- itp_interface/pisa/src/main/scala/pisa/agent/DepThms.scala +29 -0
- itp_interface/pisa/src/main/scala/pisa/agent/PisaStat.scala +46 -0
- itp_interface/pisa/src/main/scala/pisa/agent/RefactorTest.scala +40 -0
- itp_interface/pisa/src/main/scala/pisa/agent/RepHammer.scala +95 -0
- itp_interface/pisa/src/main/scala/pisa/server/HammFacts.scala +63 -0
- itp_interface/pisa/src/main/scala/pisa/server/PisaOS.scala +881 -0
- itp_interface/pisa/src/main/scala/pisa/server/PisaOneStage.scala +540 -0
- itp_interface/pisa/src/main/scala/pisa/server/PisaOneStageServers.scala +1048 -0
- itp_interface/pisa/src/main/scala/pisa/utils/TheoryManager.scala +95 -0
- itp_interface/pisa/src/test/python/analyse_debug.py +33 -0
- itp_interface/pisa/src/test/python/extract_test_seq2seq.py +53 -0
- itp_interface/pisa/src/test/python/extract_test_theorem_ground_truth_indices.py +31 -0
- itp_interface/pisa/src/test/python/proof_originality.py +24 -0
- itp_interface/pisa/src/test/python/test_command_generator.py +25 -0
- itp_interface/pisa/src/test/python/test_model_sequence_accuracy.py +70 -0
- itp_interface/pisa/src/test/scala/pisa/Easy.scala +26 -0
- itp_interface/pisa/src/test/scala/pisa/TestCurl.scala +82 -0
- itp_interface/pisa/src/test/scala/pisa/TestIsa.scala +27 -0
- itp_interface/pisa/test.sh +19 -0
- itp_interface/pisa/universal_test_theorems.tar.gz +0 -0
- itp_interface/repo/build.py +78 -0
- itp_interface/repo/clone.py +79 -0
- itp_interface/repo/dataset_discovery.py +99 -0
- itp_interface/retrieval/__init__.py +0 -0
- itp_interface/retrieval/abstraction.py +35 -0
- itp_interface/retrieval/coq_bm25_reranker.py +153 -0
- itp_interface/retrieval/isabelle_bm25_reranker.py +86 -0
- itp_interface/retrieval/lean3_bm25_reranker.py +86 -0
- itp_interface/rl/__init__.py +0 -0
- itp_interface/rl/abstraction.py +168 -0
- itp_interface/rl/proof_action.py +172 -0
- itp_interface/rl/proof_state.py +149 -0
- itp_interface/rl/proof_tree.py +109 -0
- itp_interface/rl/simpl_proof_env_pool.py +16 -0
- itp_interface/rl/simple_proof_env.py +713 -0
- itp_interface/rl/simple_proof_env_pool.py +591 -0
- itp_interface/scripts/setup.sh +228 -0
- itp_interface/tools/__init__.py +0 -0
- itp_interface/tools/basic_utils.py +172 -0
- itp_interface/tools/bin_packing.py +61 -0
- itp_interface/tools/cache.py +93 -0
- itp_interface/tools/coq_build_spec.py +31 -0
- itp_interface/tools/coq_build_tool.py +319 -0
- itp_interface/tools/coq_context_helper.py +354 -0
- itp_interface/tools/coq_executor.py +508 -0
- itp_interface/tools/coq_local_data_generation_transform.py +158 -0
- itp_interface/tools/coq_parse_utils.py +154 -0
- itp_interface/tools/coq_raw_proofs.py +193 -0
- itp_interface/tools/coq_theorem_proof_pair_generation_transform.py +146 -0
- itp_interface/tools/coq_training_data_generator.py +76 -0
- itp_interface/tools/dynamic_coq_proof_exec.py +220 -0
- itp_interface/tools/dynamic_isabelle_proof_exec.py +229 -0
- itp_interface/tools/dynamic_lean4_proof_exec.py +236 -0
- itp_interface/tools/dynamic_lean_proof_exec.py +228 -0
- itp_interface/tools/isabelle_context_helper.py +66 -0
- itp_interface/tools/isabelle_executor.py +862 -0
- itp_interface/tools/isabelle_local_data_generation_transform.py +149 -0
- itp_interface/tools/isabelle_parse_utils.py +131 -0
- itp_interface/tools/isabelle_server.py +106 -0
- itp_interface/tools/lean4_context_helper.py +72 -0
- itp_interface/tools/lean4_local_data_generation_transform.py +122 -0
- itp_interface/tools/lean4_sync_executor.py +1193 -0
- itp_interface/tools/lean_cmd_executor.py +804 -0
- itp_interface/tools/lean_context_helper.py +327 -0
- itp_interface/tools/lean_dojo_data_generation_transform.py +206 -0
- itp_interface/tools/lean_executor.py +687 -0
- itp_interface/tools/lean_local_data_generation_transform.py +136 -0
- itp_interface/tools/lean_parse_utils.py +32 -0
- itp_interface/tools/log_utils.py +20 -0
- itp_interface/tools/proof_exec_callback.py +76 -0
- itp_interface/tools/ray_utils.py +265 -0
- itp_interface/tools/repl/.git +1 -0
- itp_interface/tools/repl/.github/workflows/ci.yml +24 -0
- itp_interface/tools/repl/.gitignore +7 -0
- itp_interface/tools/repl/.vscode/copyright.code-snippets +13 -0
- itp_interface/tools/repl/.vscode/extensions.json +13 -0
- itp_interface/tools/repl/.vscode/module-docstring.code-snippets +35 -0
- itp_interface/tools/repl/.vscode/settings.json +11 -0
- itp_interface/tools/repl/README.md +174 -0
- itp_interface/tools/repl/REPL/Frontend.lean +47 -0
- itp_interface/tools/repl/REPL/JSON.lean +186 -0
- itp_interface/tools/repl/REPL/Lean/ContextInfo.lean +9 -0
- itp_interface/tools/repl/REPL/Lean/Environment.lean +31 -0
- itp_interface/tools/repl/REPL/Lean/InfoTree/ToJson.lean +114 -0
- itp_interface/tools/repl/REPL/Lean/InfoTree.lean +272 -0
- itp_interface/tools/repl/REPL/Main.lean +323 -0
- itp_interface/tools/repl/REPL/Snapshots.lean +306 -0
- itp_interface/tools/repl/REPL/Util/Path.lean +36 -0
- itp_interface/tools/repl/REPL/Util/Pickle.lean +44 -0
- itp_interface/tools/repl/REPL.lean +4 -0
- itp_interface/tools/repl/lake-manifest.json +5 -0
- itp_interface/tools/repl/lakefile.lean +15 -0
- itp_interface/tools/repl/lean-toolchain +1 -0
- itp_interface/tools/repl/test/Mathlib/.gitignore +5 -0
- itp_interface/tools/repl/test/Mathlib/H20231110.sh +2 -0
- itp_interface/tools/repl/test/Mathlib/ReplMathlibTests.lean +1 -0
- itp_interface/tools/repl/test/Mathlib/lake-manifest.json +68 -0
- itp_interface/tools/repl/test/Mathlib/lakefile.lean +11 -0
- itp_interface/tools/repl/test/Mathlib/lean-toolchain +1 -0
- itp_interface/tools/repl/test/Mathlib/test/20240209.expected.out +20 -0
- itp_interface/tools/repl/test/Mathlib/test/20240209.in +3 -0
- itp_interface/tools/repl/test/Mathlib/test/20240209.lean +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231020.expected.out +8 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231020.in +8 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231020.lean +22 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231110.expected.out +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231110.in +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231115.expected.out +19 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231115.in +5 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231115_2.expected.out +18 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231115_2.in +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231115_3.expected.out +10 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231115_3.in +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231214.in +9 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231214.lean +30 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231215.expected.out +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231215.in +4 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231215_2.expected.out +14 -0
- itp_interface/tools/repl/test/Mathlib/test/H20231215_2.in +3 -0
- itp_interface/tools/repl/test/Mathlib/test/exact.expected.out +37 -0
- itp_interface/tools/repl/test/Mathlib/test/exact.in +10 -0
- itp_interface/tools/repl/test/Mathlib/test/import_Mathlib.lean +1 -0
- itp_interface/tools/repl/test/Mathlib/test/induction.expected.out +29 -0
- itp_interface/tools/repl/test/Mathlib/test/induction.in +10 -0
- itp_interface/tools/repl/test/Mathlib/test/induction.lean +6 -0
- itp_interface/tools/repl/test/Mathlib/test/on_goal.expected.out +22 -0
- itp_interface/tools/repl/test/Mathlib/test/on_goal.in +5 -0
- itp_interface/tools/repl/test/Mathlib/test/pickle.expected.out +16 -0
- itp_interface/tools/repl/test/Mathlib/test/pickle.in +6 -0
- itp_interface/tools/repl/test/Mathlib/test/pickle_2.expected.out +4 -0
- itp_interface/tools/repl/test/Mathlib/test/pickle_2.in +4 -0
- itp_interface/tools/repl/test/Mathlib/test.sh +41 -0
- itp_interface/tools/repl/test/all_tactics.expected.out +13 -0
- itp_interface/tools/repl/test/all_tactics.in +1 -0
- itp_interface/tools/repl/test/by_cases.expected.out +25 -0
- itp_interface/tools/repl/test/by_cases.in +8 -0
- itp_interface/tools/repl/test/by_cases.lean +4 -0
- itp_interface/tools/repl/test/calc.expected.out +32 -0
- itp_interface/tools/repl/test/calc.in +1 -0
- itp_interface/tools/repl/test/def_eval.expected.out +9 -0
- itp_interface/tools/repl/test/def_eval.in +3 -0
- itp_interface/tools/repl/test/enableInitializersExecution.expected.out +2 -0
- itp_interface/tools/repl/test/enableInitializersExecution.in +1 -0
- itp_interface/tools/repl/test/file.expected.out +8 -0
- itp_interface/tools/repl/test/file.in +1 -0
- itp_interface/tools/repl/test/file.lean +5 -0
- itp_interface/tools/repl/test/have_by_sorry.expected.out +28 -0
- itp_interface/tools/repl/test/have_by_sorry.in +6 -0
- itp_interface/tools/repl/test/import_lean.in +1 -0
- itp_interface/tools/repl/test/incomplete.expected.out +18 -0
- itp_interface/tools/repl/test/incomplete.in +3 -0
- itp_interface/tools/repl/test/incomplete.lean +0 -0
- itp_interface/tools/repl/test/infotree.expected.out +20 -0
- itp_interface/tools/repl/test/infotree.in +2 -0
- itp_interface/tools/repl/test/invalid_tactic.expected.out +20 -0
- itp_interface/tools/repl/test/invalid_tactic.in +3 -0
- itp_interface/tools/repl/test/name_generator.expected.out +53 -0
- itp_interface/tools/repl/test/name_generator.in +18 -0
- itp_interface/tools/repl/test/no_goal_sorry.expected.out +11 -0
- itp_interface/tools/repl/test/no_goal_sorry.in +1 -0
- itp_interface/tools/repl/test/no_goal_sorry_2.expected.out +12 -0
- itp_interface/tools/repl/test/no_goal_sorry_2.in +1 -0
- itp_interface/tools/repl/test/options.expected.out +17 -0
- itp_interface/tools/repl/test/options.in +6 -0
- itp_interface/tools/repl/test/pickle_environment.expected.out +8 -0
- itp_interface/tools/repl/test/pickle_environment.in +7 -0
- itp_interface/tools/repl/test/pickle_environment_with_imports.expected.out +10 -0
- itp_interface/tools/repl/test/pickle_environment_with_imports.in +9 -0
- itp_interface/tools/repl/test/pickle_open.expected.out +8 -0
- itp_interface/tools/repl/test/pickle_open.in +7 -0
- itp_interface/tools/repl/test/pickle_open_2.expected.out +4 -0
- itp_interface/tools/repl/test/pickle_open_2.in +3 -0
- itp_interface/tools/repl/test/pickle_open_scoped.expected.out +18 -0
- itp_interface/tools/repl/test/pickle_open_scoped.in +8 -0
- itp_interface/tools/repl/test/pickle_open_scoped_2.expected.out +14 -0
- itp_interface/tools/repl/test/pickle_open_scoped_2.in +3 -0
- itp_interface/tools/repl/test/pickle_proof_state_1.expected.out +26 -0
- itp_interface/tools/repl/test/pickle_proof_state_1.in +15 -0
- itp_interface/tools/repl/test/pickle_proof_state_2.expected.out +4 -0
- itp_interface/tools/repl/test/pickle_proof_state_2.in +3 -0
- itp_interface/tools/repl/test/pickle_proof_state_env.expected.out +26 -0
- itp_interface/tools/repl/test/pickle_proof_state_env.in +15 -0
- itp_interface/tools/repl/test/pickle_scoped_notation.in +16 -0
- itp_interface/tools/repl/test/pickle_scoped_notation_2.in +3 -0
- itp_interface/tools/repl/test/proof_step.expected.out +18 -0
- itp_interface/tools/repl/test/proof_step.in +7 -0
- itp_interface/tools/repl/test/readme.expected.out +16 -0
- itp_interface/tools/repl/test/readme.in +5 -0
- itp_interface/tools/repl/test/sorry_hypotheses.expected.out +16 -0
- itp_interface/tools/repl/test/sorry_hypotheses.in +4 -0
- itp_interface/tools/repl/test/synthesize_placeholder.expected.out +7 -0
- itp_interface/tools/repl/test/synthesize_placeholder.in +1 -0
- itp_interface/tools/repl/test/tactic_mode_sorry.expected.out +14 -0
- itp_interface/tools/repl/test/tactic_mode_sorry.in +3 -0
- itp_interface/tools/repl/test/tactic_sorry.expected.out +12 -0
- itp_interface/tools/repl/test/tactic_sorry.in +1 -0
- itp_interface/tools/repl/test/term_sorry.expected.out +12 -0
- itp_interface/tools/repl/test/term_sorry.in +1 -0
- itp_interface/tools/repl/test/trace_simp.expected.out +41 -0
- itp_interface/tools/repl/test/trace_simp.in +15 -0
- itp_interface/tools/repl/test/unfinished_tactic_block.expected.out +11 -0
- itp_interface/tools/repl/test/unfinished_tactic_block.in +1 -0
- itp_interface/tools/repl/test/unknown_environment.expected.out +2 -0
- itp_interface/tools/repl/test/unknown_environment.in +1 -0
- itp_interface/tools/repl/test/unknown_proof_state.expected.out +14 -0
- itp_interface/tools/repl/test/unknown_proof_state.in +3 -0
- itp_interface/tools/repl/test/unknown_tactic.expected.out +14 -0
- itp_interface/tools/repl/test/unknown_tactic.in +3 -0
- itp_interface/tools/repl/test/variables.expected.out +26 -0
- itp_interface/tools/repl/test/variables.in +5 -0
- itp_interface/tools/repl/test.sh +43 -0
- itp_interface/tools/run_data_generation_transforms.py +350 -0
- itp_interface/tools/theorem_details.py +25 -0
- itp_interface/tools/training_data.py +358 -0
- itp_interface/tools/training_data_format.py +599 -0
- itp_interface-1.0.0.dist-info/METADATA +78 -0
- itp_interface-1.0.0.dist-info/RECORD +485 -0
- itp_interface-1.0.0.dist-info/WHEEL +4 -0
- itp_interface-1.0.0.dist-info/entry_points.txt +3 -0
- 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()
|