File ‹Tools/SMT/alethe/alethe_proof.ML›
open Alethe_Node
signature ALETHE_PROOF =
sig
exception ALETHE_PROOF_PARSE of string
val parse_raw_proof_steps: string option -> SMTLIB.tree list -> SMTLIB_Proof.name_bindings ->
raw_alethe_node list * SMTLIB.tree list * SMTLIB_Proof.name_bindings
val parse: typ Symtab.table -> term Symtab.table -> string list ->
Proof.context -> alethe_step list * Proof.context
val parse_replay: typ Symtab.table -> term Symtab.table -> string list ->
Proof.context -> alethe_replay_node list * Proof.context
val parse_fun_def: SMTLIB.tree ->
SMTLIB_Proof.name_bindings ->
raw_alethe_node * SMTLIB_Proof.name_bindings
val step_prefix : string
val and_pos_rule: string
val contract_rule : string
val eq_congruent_pred_rule : string
val eq_congruent_rule : string
val equiv_pos2_rule: string
val forall_inst_rule : string
val hole: string
val ite_intro_rule : string
val la_generic_rule : string
val not_not_rule : string
val or_rule : string
val refl_rule : string
val cong_rule : string
val simp_arith_rule : string
val subproof_rule : string
val th_resolution_rule: string
val input_rule: string
val normalized_input_rule: string
val local_input_rule : string
val rewrite_rule : string
val skolem_def : string
val theory_resolution2_rule: string
val skolemization_steps : string list
val is_skolem_def : string -> bool
val is_skolemization : string -> bool
val keep_app_symbols: string -> bool
val keep_raw_lifting: string -> bool
val keep_proposition_structure: string -> bool
val number_of_steps: alethe_replay_node list -> int
val extract_coefficients: SMTLIB.tree -> int * int
end;
structure Alethe_Proof: ALETHE_PROOF =
struct
open SMTLIB_Proof
open Alethe_Node
open SMT_Config
exception ALETHE_PROOF_PARSE of string
fun throw_alethe_ex str = ALETHE_PROOF_PARSE ("Error parsing Alethe proof step: " ^ str)
fun debug_msg ctxt = alethe_debug_msg ctxt "alethe_proof"
val step_prefix = ".c"
val input_rule = "input"
val normalized_input_rule = "__normalized_input"
val rewrite_rule = "__rewrite"
val local_input_rule = "__local_input"
val skolem_def = "__skolem_definition"
val default_skolem_rule = "sko_forall"
val theory_resolution2_rule = "__theory_resolution2"
val and_pos_rule = "and_pos"
val contract_rule = "contraction"
val eq_congruent_pred_rule = "eq_congruent_pred"
val eq_congruent_rule = "eq_congruent"
val equiv_pos2_rule = "equiv_pos2"
val forall_inst_rule = "forall_inst"
val hole = "hole"
val ite_intro_rule = "ite_intro"
val la_generic_rule = "la_generic"
val not_not_rule = "not_not"
val or_rule = "or"
val refl_rule = "refl"
val cong_rule = "cong"
val simp_arith_rule = "simp_arith"
val subproof_rule = "subproof"
val th_resolution_rule = "th_resolution"
val shuffle_rule = "shuffle"
val is_SH_trivial = member (op =) [not_not_rule, contract_rule]
val is_skolem_def = String.isSuffix skolem_def
fun get_skolem_def_id id = id ^ skolem_def
val skolemization_steps = ["sko_forall", "sko_ex"]
val is_skolemization = member (op =) skolemization_steps
fun is_assm_repetition id rule =
rule = input_rule andalso can SMTLIB_Interface.role_and_index_of_assert_name id
val keep_app_symbols = member (op =) [eq_congruent_pred_rule, eq_congruent_rule, ite_intro_rule, and_pos_rule]
val keep_raw_lifting = member (op =) [eq_congruent_pred_rule, eq_congruent_rule, ite_intro_rule, and_pos_rule]
val keep_proposition_structure = member (op =) ["resolution", "contraction", "reordering"]
val anchor_rules = ["bind","sko_forall","sko_ex","let","onepoint","subproof"]
val is_anchor_rule = member (op =) anchor_rules
val context_assignment_rules = ["bind","sko_forall","sko_ex","let","onepoint"]
val allows_context_assignment = member (op =) context_assignment_rules
val index_rules = ["and","not_or","and_pos","or_neg"]
val step_args_rules = index_rules @ ["rare_rewrite","forall_inst", skolem_def,la_generic_rule,shuffle_rule]
val allows_step_args = member (op =) step_args_rules
fun number_of_steps [] = 0
| number_of_steps ((Alethe_Replay_Node {subproof = (_, _, _, subproof), ...}) :: pf) =
1 + number_of_steps subproof + number_of_steps pf
| number_of_steps (_ :: pf) = 1 + number_of_steps pf
fun node_of p cx =
([], cx)
||>> `(with_fresh_names (term_of p))
|>> snd
fun node_of_term p cx = (term_of p cx)
datatype step_kind = ASSUME | ANCHOR | NO_STEP | NORMAL_STEP | SKOLEM
fun parse_fun_def (SMTLIB.S [SMTLIB.Sym "define-fun", SMTLIB.Sym id, _, _,
SMTLIB.S (SMTLIB.Sym "!" :: t :: [SMTLIB.Key "named", SMTLIB.Sym name])]) cx =
let
val (l, cx) = (fst oo SMTLIB_Proof.extract_and_update_name_bindings) t cx
|> apsnd (SMTLIB_Proof.update_name_binding (name, SMTLIB.Sym id))
in
(mk_raw_definition_node (id ^ skolem_def) skolem_def id l, cx)
end
| parse_fun_def (SMTLIB.S [SMTLIB.Sym "define-fun", SMTLIB.Sym id, _, _, SMTLIB.S l]) cx =
let
val (l, cx) = (fst oo SMTLIB_Proof.extract_and_update_name_bindings) (SMTLIB.S l) cx
in
(mk_raw_definition_node (id ^ skolem_def) skolem_def id l, cx)
end
| parse_fun_def t _ = raise Fail ("unrecognized Alethe proof " ^ \<^make_string> t)
fun parse_raw_proof_steps (curr_anchor_id : string option) (smtlib_lines : SMTLIB.tree list) (cx : name_bindings):
(raw_alethe_node list * SMTLIB.tree list * name_bindings) =
let
fun step_kind "anchor" = ANCHOR
| step_kind "assume" = ASSUME
| step_kind "step" = NORMAL_STEP
| step_kind "define-fun" = SKOLEM
| step_kind kind = raise throw_alethe_ex ("step_kind unrec: " ^ @{make_string} kind)
fun split_step [] = (NO_STEP, SMTLIB.S [], [])
| split_step ((step as SMTLIB.S (SMTLIB.Sym kind :: _)) :: remaining_steps) = (step_kind kind, step, remaining_steps)
| split_step steps = raise throw_alethe_ex ("malformed step: " ^ String.concat (map SMTLIB.str_of steps))
fun parse_id (SMTLIB.S ((SMTLIB.Sym _) :: (SMTLIB.Sym id) :: l)) = (id, l)
| parse_id t = raise throw_alethe_ex ("unrecognized Alethe proof " ^ \<^make_string> t)
fun parse_id_cx (t, cx) = parse_id t ||> rpair cx
fun parse_prems (SMTLIB.Key "premises" :: SMTLIB.S prems :: l, cx) =
let
val prems = map (SMTLIB.extract_sym_value "Malformed premises.") prems
in (SOME prems, (l, cx)) end
| parse_prems (l, cx) = (NONE, (l, cx))
fun parse_rule (SMTLIB.Key "rule" :: SMTLIB.Sym rule_name :: l, cx) = (rule_name, (l, cx))
| parse_rule t = raise throw_alethe_ex ("unrecognized Alethe proof rule " ^ \<^make_string> t)
fun parse_anchor_step (SMTLIB.S (SMTLIB.Sym "anchor" :: SMTLIB.Key "step" :: SMTLIB.Sym id :: l), cx) = (id, (l, cx))
| parse_anchor_step t = raise Fail ("unrecognized Alethe proof " ^ \<^make_string> t)
fun parse_args (SMTLIB.Key "args" :: args :: l, cx) =
let val ((args, cx), _) = SMTLIB_Proof.extract_and_update_name_bindings args cx
val args = SMTLIB.extract_subtrees "malformed argument" args
in (args, (l, cx)) end
| parse_args (l, cx) = ([], (l, cx))
fun parse_and_clausify_conclusion (SMTLIB.S (SMTLIB.Sym "cl" :: []) :: l, cx) =
(SMTLIB.Sym "false", (l, cx))
| parse_and_clausify_conclusion (SMTLIB.S (SMTLIB.Sym "cl" :: concl) :: l, cx) =
let val (concl, cx) = fold_map (fst oo SMTLIB_Proof.extract_and_update_name_bindings) concl cx
in (SMTLIB.S (SMTLIB.Sym "or" :: concl), (l, cx)) end
| parse_and_clausify_conclusion t = raise Fail ("unrecognized Alethe proof " ^ \<^make_string> t)
fun parse_and_create_skeleton (SMTLIB.S (SMTLIB.Sym "cl" :: []) :: _, _) =
SMTLIB.Sym "true"
| parse_and_create_skeleton (SMTLIB.S (SMTLIB.Sym "cl" :: concl) :: _, _) =
SMTLIB.S (SMTLIB.Sym "or" :: map (fn _ => SMTLIB.Sym "true") concl)
| parse_and_create_skeleton t = raise Fail ("unrecognized Alethe proof " ^ \<^make_string> t)
fun rotate_pair (a, (b, c)) = ((a, b), c)
val parse_normal_step =
parse_id_cx
##> (fn t=> (parse_and_create_skeleton t, parse_and_clausify_conclusion t))
#> rotate_pair
#> rotate_pair
##> parse_rule
#> rotate_pair
##> parse_prems
#> rotate_pair
##> parse_args
#> rotate_pair
fun to_raw_node ANCHOR subproof (((((id, prop_skeleton), concl), rule), prems), context_assignment) =
mk_raw_node id rule [] context_assignment (the_default [] prems) concl subproof prop_skeleton
| to_raw_node _ subproof (((((id, prop_skeleton), concl), rule), prems), step_args) =
mk_raw_node id rule step_args [] (the_default [] prems) concl subproof prop_skeleton
fun closes_subproof NONE _ = false
| closes_subproof (SOME id) step = step |> parse_id |> fst |> (curry (op =) id)
in
case split_step smtlib_lines of
(NO_STEP, _, _) => ([],[], cx)
| (NORMAL_STEP, step, rest) =>
if closes_subproof curr_anchor_id step
then ([], smtlib_lines, cx)
else
let
val (step', (_, cx)) = (step, cx)
|> parse_normal_step
|>> (to_raw_node NORMAL_STEP [])
val (rp, rl, cx) = parse_raw_proof_steps curr_anchor_id rest cx
in (step' :: rp, rl, cx) end
| (ASSUME, step, rest) =>
let
val (id, t) = step |> parse_id ||> the_single
val ((t, cx), _) = SMTLIB_Proof.extract_and_update_name_bindings t cx
val s = mk_raw_node id input_rule [] [] [] t [] (SMTLIB.Sym "true")
val (rstep, rrest, cx) = parse_raw_proof_steps curr_anchor_id rest cx
in (s :: rstep, rrest, cx) end
| (ANCHOR, p, l) =>
let
val (anchor_id, (anchor_args, (_, cx))) = (p, cx) |> (parse_anchor_step ##> parse_args)
val (subproof, rest, cx) = parse_raw_proof_steps (SOME anchor_id) l cx
val (discharge_step, remaining_proof) =
(case rest of
(x::xs) => (x,xs) |
_ => raise throw_alethe_ex ("Could not parse subproof " ^ @{make_string} anchor_id))
val (curss, (_, cx)) = parse_normal_step (discharge_step, cx)
val s = to_raw_node ANCHOR subproof (fst curss, anchor_args)
val (rp, rl, cx) = parse_raw_proof_steps curr_anchor_id remaining_proof cx
in (s :: rp, rl, cx) end
| (SKOLEM, p, l) =>
let
val (s, cx) = parse_fun_def p cx
val (rp, rl, cx) = parse_raw_proof_steps curr_anchor_id l cx
in (s :: rp, rl, cx) end
end
fun preprocess compress step =
let
fun expand_assms cs =
map (fn t => case AList.lookup (op =) cs t of NONE => t | SOME a => a)
fun ignore_unexpected ctxt msg rule f xs =
if xs <> [] andalso not (f rule)
then
let val _ = (SMT_Config.verit_msg ctxt) (fn () => \<^print> ("Ignored unexpected " ^ msg ^ " Rule has kind: ", rule))
in [] end
else xs
val replace_assignment_sym = (fn SMTLIB.S [SMTLIB.Key "=", x, y] => SMTLIB.S [SMTLIB.Sym "=", x, y] | x => x)
fun preprocess (x as Raw_Definition_Node _) (cx : ('a list, 'b) context, remap_prems) = ([x], (cx, remap_prems))
| preprocess (Raw_Alethe_Node {id, rule, step_args, context_assignments, prems, concl, subproof, prop_skeleton, ...})
(cx : ('a list, 'b) context, remap_prems) =
let
val ctxt = #ctxt cx
val _ = debug_msg ctxt high (fn () => \<^print> ("preprocessing started for rule: ", id))
val sanitized_context_assignments =
context_assignments
|> ignore_unexpected ctxt "context annotation" rule allows_context_assignment
|> map replace_assignment_sym
val sanitized_step_args =
step_args
|> ignore_unexpected ctxt "args" rule allows_step_args
|> (if rule = forall_inst_rule
then map replace_assignment_sym
else (fn x => x))
val (subproof, (cx, _)) = fold_map preprocess subproof (cx, remap_prems) |> apfst flat
val skip_step = rule = or_rule andalso compress andalso not (SMT_Config.alethe_use_prop_skeleton ctxt)
fun compress_or_rule remap_prems = (id, hd prems) :: remap_prems
val repaired_prems =
if compress then
let val _ = debug_msg ctxt low (fn () => \<^print> ("If any premise of the current step has been skipped the premise is updated to that premise's premise"))
in expand_assms remap_prems prems end
else prems
in
if skip_step
then
let
val _ = debug_msg ctxt low (fn () => \<^print> ("Compress proof by skipping current 'or' step"))
val skipped_step = ([], (cx, compress_or_rule remap_prems))
in skipped_step end
else
let
val preprocessed_node = [mk_raw_node id rule sanitized_step_args sanitized_context_assignments
repaired_prems concl subproof prop_skeleton]
in (preprocessed_node,(cx, remap_prems)) end
end
in preprocess step end
fun filter_split _ [] = ([], [])
| filter_split f (a :: xs) =
(if f a then apfst (curry op :: a) else apsnd (curry op :: a)) (filter_split f xs)
fun split_context_assignment _ [] (fixed : (string * term) list) substituted substitute = (fixed,substituted,substitute) |
split_context_assignment cx (SMTLIB.S [SMTLIB.Sym x, t] :: xs) (fs : (string * term) list) ss ts =
split_context_assignment cx xs ((x, (Free (x, type_of cx t)))::fs) ss ts |
split_context_assignment cx (SMTLIB.S [SMTLIB.Sym "=", SMTLIB.S [SMTLIB.Sym x, t], y] :: xs) fs ss ts =
split_context_assignment cx xs fs ((x, (Free (x, type_of cx t)))::ss) ((y,type_of cx t)::ts) |
split_context_assignment _ _ _ _ _ = raise throw_alethe_ex ("context assignment malformed ")
fun split_context_ass cx context_assignments = split_context_assignment cx context_assignments [] [] []
fun has_local_rebound (SMTLIB.S [SMTLIB.Sym "=", SMTLIB.S [SMTLIB.Sym x, _], SMTLIB.Sym y] :: xs) lnames rnames =
member (op=) lnames y orelse member (op=) rnames x orelse has_local_rebound xs (x :: lnames) (y :: rnames)
| has_local_rebound (_ :: xs) lnames rnames = has_local_rebound xs lnames rnames
| has_local_rebound [] _ _ = false
fun skolems_introduced_by_rule [] = [] |
skolems_introduced_by_rule bds = fold
(fn (SMTLIB.S [SMTLIB.Sym "=", _, SMTLIB.Sym y]) => curry (op ::) y | _ => (fn x => x))
bds []
fun collect_skolem_defs (Raw_Alethe_Node {rule, subproof = subproof, context_assignments, ...}) =
(if is_skolemization rule then map get_skolem_def_id (skolems_introduced_by_rule context_assignments) else []) @
maps collect_skolem_defs subproof
| collect_skolem_defs _ = []
val desymbolize = Name.desymbolize (SOME false) o perhaps (try (unprefix "?"))
fun could_unify (Bound i, Bound j) = i = j
| could_unify (Var v, Var v') = v = v'
| could_unify (Free v, Free v') = v = v'
| could_unify (Const (v, ty), Const (v', ty')) = v = v' andalso ty = ty'
| could_unify (Abs (_, ty, bdy), Abs (_, ty', bdy')) = ty = ty' andalso could_unify (bdy, bdy')
| could_unify (u $ v, u' $ v') = could_unify (u, u') andalso could_unify (v, v')
| could_unify _ = false
fun is_alpha_renaming t =
t
|> HOLogic.dest_Trueprop
|> HOLogic.dest_eq
|> could_unify
handle TERM _ => false
fun extract_coefficients (SMTLIB.Num num) =
(num,1)
| extract_coefficients (SMTLIB.S [SMTLIB.Sym "-", SMTLIB.Num num1]) =
(~num1,1)
| extract_coefficients (SMTLIB.S [SMTLIB.Sym _, SMTLIB.Num num1, SMTLIB.Num num2]) =
(num1,num2)
| extract_coefficients (SMTLIB.S [SMTLIB.Sym "-", SMTLIB.Dec d]) =
let val (numerator, denominator) = SMTLIB.convert_dec_to_rat d
in (~numerator, denominator) end
| extract_coefficients (SMTLIB.Dec d) =
SMTLIB.convert_dec_to_rat d
| extract_coefficients (SMTLIB.S [SMTLIB.Sym "/",SMTLIB.Dec (1,0),SMTLIB.Dec (1,0)]) =
(1,1)
| extract_coefficients (SMTLIB.S [SMTLIB.Sym "/",SMTLIB.Dec (num1,0),SMTLIB.Dec (num2,0)]) =
(num1, num2)
| extract_coefficients (SMTLIB.S [SMTLIB.Sym "-", SMTLIB.S[SMTLIB.Sym "/",SMTLIB.Dec (num1,0),SMTLIB.Dec (num2,0)]]) =
(~num1, num2)
| extract_coefficients t =
raise throw_alethe_ex ("malformed argument of la_generic step: " ^ SMTLIB.str_of t)
local
fun id_of_last_step prems =
if null prems then []
else
(case List.last prems of
Alethe_Replay_Node {id, ...} => [id]
| Alethe_Definition_Node {id, ...} => [id])
fun extract_assumptions_from_subproof subproof =
let
fun extract_assumptions (Alethe_Replay_Node {rule, concl, ...}) assms =
if rule = local_input_rule then concl :: assms else assms |
extract_assumptions (Alethe_Definition_Node _) assms = assms
in
fold extract_assumptions subproof []
end
in
fun postprocess_proof compress ctxt step cx =
let
fun process_context_assignments context_assignments cx =
let
val _ = debug_msg ctxt middle (fn () => \<^print> ("... process context assignments", context_assignments))
val (fixed_vars,substituted_vars,substitute_terms) = split_context_ass cx context_assignments
val _ = debug_msg ctxt high (fn () => \<^print> ("... split up context assignments (fixed variables, substituted variables, substitute terms)", (fixed_vars,substituted_vars,substitute_terms)))
fun not_already_bound cx t = SMTLIB_Proof.lookup_binding cx (fst t) = None
val (shadowing_vars, rebound_lhs_vars) =
substituted_vars @ fixed_vars
|> filter_split (not_already_bound cx)
|> (fn (shadow, rebound) => (Library.distinct (op =) shadow, Library.duplicates (op =) shadow @ rebound))
val _ = debug_msg ctxt high (fn () => \<^print> ("... (new variables, already bound variables)", (shadowing_vars, rebound_lhs_vars)))
fun assign_unused_name t i =
let
val new_name = (if i=0 then t else (t^"_isabelle"^Int.toString i))
in
if SMTLIB_Proof.lookup_binding cx new_name = None then new_name else assign_unused_name t (i+1)
end
val start_idx = (if has_local_rebound context_assignments [] [] then 1 else 0)
val new_variable_names = fold (fn t => curry (op ::) (t, assign_unused_name t start_idx)) (map fst rebound_lhs_vars) []
val _ = debug_msg ctxt high (fn () => \<^print> ("... mapping between new variables introduced in the anchor and their original names", new_variable_names))
in ((shadowing_vars, map (apfst (fn t => assign_unused_name t start_idx)) rebound_lhs_vars, substitute_terms), new_variable_names) end
fun syntactic_var_subst old_name new_name (u $ v) =
(syntactic_var_subst old_name new_name u $ syntactic_var_subst old_name new_name v)
| syntactic_var_subst old_name new_name (Abs (v, T, u)) =
Abs (if String.isPrefix old_name v then new_name else v, T,
syntactic_var_subst old_name new_name u)
| syntactic_var_subst old_name new_name (Free (v, T)) =
if old_name = v orelse String.isPrefix (old_name ^ "_") v then Free (new_name, T) else Free (v, T)
| syntactic_var_subst _ _ t = t
fun syntactic_rew_in_lhs_subst old_name new_name (Const(\<^const_name>‹HOL.eq›, T) $ t1 $ t2) =
Const(\<^const_name>‹HOL.eq›, T) $ syntactic_var_subst old_name new_name t1 $ t2
| syntactic_rew_in_lhs_subst old_name new_name (Const(\<^const_name>‹Trueprop›, T) $ t1) =
Const(\<^const_name>‹Trueprop›, T) $ (syntactic_rew_in_lhs_subst old_name new_name t1)
| syntactic_rew_in_lhs_subst _ _ t = t
fun subproof_rewriter new_variable_names = fold (fn (t, t') => syntactic_rew_in_lhs_subst t t') new_variable_names
fun add_bound_variables_to_cx ts cx = fold update_binding ts cx
fun unsk_and_rewrite new_variable_names = SMTLIB_Isar.unskolemize_names ctxt o subproof_rewriter new_variable_names
fun transform_concl concl id (cx, new_variable_names) =
let
val ((termified_concl, extra_var), cx') = node_of concl cx
val _ = debug_msg ctxt high (fn () => \<^print> ("... transformed conclusion into Isabelle term", termified_concl))
val _ = if extra_var <> []
then raise ERROR ("found dangling variable (s) " ^ (@{make_string} extra_var) ^
" in concl " ^ (@{make_string} concl) ^ " of step" ^ id) else ()
val termified_concl = unsk_and_rewrite new_variable_names termified_concl
val _ = debug_msg ctxt middle (fn () => \<^print> ("... rewrite conclusion with new variable names introduced by prior anchors", termified_concl))
in (cx',termified_concl) end
fun transform_step_args rule step_args (cx, new_variable_names) =
if not (allows_step_args rule) then []
else let
val (termified_step_args,_) = fold_map node_of_term step_args cx
val normalized_step_args = map (unsk_and_rewrite new_variable_names o subproof_rewriter new_variable_names) termified_step_args
val _ = debug_msg ctxt middle (fn () => \<^print> ("Termified and normalized steps args:", normalized_step_args))
in normalized_step_args end
fun postprocess (Raw_Definition_Node {id, rule, skolem_name, skolem_term}) (cx, new_variable_names) =
let
val _ = debug_msg ctxt low (fn () => \<^print> ("postprocessing started for definition: ", (id,rule)))
val cx =
let
fun extract_skolem_type (SMTLIB.S [SMTLIB.Sym "choice", SMTLIB.S [SMTLIB.S [_,typ]], _]) = typ
| extract_skolem_type t = raise throw_alethe_ex ("unrecognized skolem_definition " ^ @{make_string} t)
fun declared_csts (name,typ) = (name, Term (Free (name, type_of cx typ)))
val globally_bound_vars = declared_csts (skolem_name, extract_skolem_type skolem_term)
val _ = debug_msg ctxt middle (fn () => \<^print> ("... extracted globally bound vars from skolem definition", fst globally_bound_vars))
in update_binding globally_bound_vars cx end
val (termified_concl,cx') = node_of_term skolem_term cx
val normalized_concl = unsk_and_rewrite new_variable_names termified_concl
val _ = debug_msg ctxt middle (fn () => \<^print> ("... transformed conclusion", normalized_concl))
val step = Alethe_Definition_Node {id=id,rule=rule,skolem_name=skolem_name,skolem_term=normalized_concl}
in
(step, (cx', new_variable_names))
end
| postprocess (Raw_Alethe_Node {id, rule, step_args, context_assignments, prems, concl, subproof,
prop_skeleton}) (cx, orig_new_variable_names) =
if is_anchor_rule rule
then
let
val unsk_and_rewrite' = unsk_and_rewrite orig_new_variable_names
val subproof_rewriter' = subproof_rewriter orig_new_variable_names
val _ = debug_msg ctxt low (fn () => \<^print> ("postprocessing started for anchor: ", id))
fun normalize_external (raw_name, Free(a,b)) =
if Config.get ctxt SMT_Config.check_external andalso Char.isUpper (String.sub (raw_name, 0))
then (raw_name,Free("isabelle_internal_" ^ a,b)) else (raw_name,Free(a,b)) |
normalize_external (_, _) = raise throw_alethe_ex "todo"
fun normalize_external2 (raw_name,b) =
if Config.get ctxt SMT_Config.check_external andalso Char.isUpper (String.sub (raw_name, 0))
then (raw_name, "isabelle_internal_" ^ b) else (raw_name, b)
val ((shadowing_vars, rebound_lhs_vars, substitute_terms),new_variable_names) =
process_context_assignments context_assignments cx
||> (curry (op @)) orig_new_variable_names
|> (fn ((i,j,k),n) => ((map normalize_external i,map normalize_external j,k), map normalize_external2 n))
val (cx',termified_concl) = transform_concl concl id (cx, new_variable_names)
val (_,termified_prop_skeleton) =
transform_concl prop_skeleton id (cx, new_variable_names)
val _ = debug_msg ctxt middle (fn () => \<^print> ("... transformed conclusion of step closing the anchor ", termified_concl))
val alpha_conversion = rule = "bind" andalso is_alpha_renaming termified_concl
val (new_subproof,subproof_cx) =
let
val subproof_cx =
add_bound_variables_to_cx (map (fn (a,b) => (a,Term b)) (shadowing_vars @ rebound_lhs_vars)) cx
val _ = debug_msg ctxt high (fn () => \<^print> ("... add new variables to context",shadowing_vars @ rebound_lhs_vars))
val can_remove_subproof =
compress andalso (is_skolemization rule orelse alpha_conversion)
val _ = debug_msg ctxt high (fn () => \<^print> ("... does the subproof only rename variables and therefore can be removed?",can_remove_subproof))
val new_subproof = (if can_remove_subproof then [] else subproof)
val (new_subproof : alethe_replay_node list, _) =
fold_map postprocess new_subproof (subproof_cx, new_variable_names)
val _ = debug_msg ctxt low (fn () => \<^print> ("Finished postprocessing subproof. Returning to rule with id",id))
in (new_subproof,subproof_cx) end
fun rm_types [] = [] |
rm_types (SMTLIB.S [SMTLIB.Sym v,_]::xs) = SMTLIB.Sym v::rm_types xs |
rm_types (SMTLIB.S [SMTLIB.Sym "=", SMTLIB.S [SMTLIB.Sym v,_],t]::xs) = SMTLIB.S [SMTLIB.Sym "=",SMTLIB.Sym v,t]::rm_types xs |
rm_types t = raise throw_alethe_ex ("malformed context assignment" ^ @{make_string} t)
val (termified_context_ass, _) =
fold_map node_of (rm_types context_assignments) subproof_cx
|>> map fst
val normalized_context_ass = map (unsk_and_rewrite' o subproof_rewriter' o subproof_rewriter new_variable_names) termified_context_ass
val _ = debug_msg ctxt middle (fn () => \<^print> ("... transformed context assignments of anchor ", normalized_context_ass))
fun mk_sko_def_prems () =
let
val skolems_of_subproof = (if compress then maps collect_skolem_defs subproof else [])
val skolem_defs = (map (get_skolem_def_id o SMTLIB.extract_sym_value "cannot parse skolem id" o fst) substitute_terms)
in (SOME (Sko_Def_Prems (skolems_of_subproof, skolem_defs))) end
val (tactic_args) = (if is_skolemization rule then mk_sko_def_prems () else (NONE))
val normalized_rule =
if compress andalso alpha_conversion then refl_rule
else rule
val assms_from_subproof = (if rule = subproof_rule then extract_assumptions_from_subproof new_subproof else [])
val normalized_premises =
prems @
id_of_last_step new_subproof
val _ = debug_msg ctxt middle (fn () => \<^print> ("... added more premises ", normalized_premises))
val normalized_step_args = transform_step_args rule step_args (cx, new_variable_names)
val _ = debug_msg ctxt middle (fn () => \<^print> ("... transformed step args of step closing the anchor. Should be empty:", normalized_step_args))
val step =
mk_replay_node id normalized_rule normalized_step_args normalized_context_ass
normalized_premises termified_concl
((map (fn (a,b) => (a, fastype_of b)) (shadowing_vars @ rebound_lhs_vars)), [], assms_from_subproof, new_subproof)
tactic_args termified_prop_skeleton
in
(step, (cx', new_variable_names))
end
else
let
val _ = debug_msg ctxt low (fn () => \<^print> ("postprocessing started for rule: ", id))
val (cx',termified_concl) = transform_concl concl id (cx, orig_new_variable_names)
val (_,termified_prop_skeleton) = transform_concl prop_skeleton id (cx, orig_new_variable_names)
val _ = debug_msg ctxt middle (fn () => \<^print> ("... transform conclusion ", termified_concl))
val normalized_step_args = (if rule = "la_generic" then []
else transform_step_args rule step_args (cx, orig_new_variable_names))
val tactic_args =
(case rule of
"shuffle" =>
let
fun getOp (SMTLIB.S [_, SMTLIB.S [SMTLIB.Sym "=", SMTLIB.S (SMTLIB.Sym commOp :: _), _]]) =
(if (commOp = "or") then @{term "HOL.disj"} else @{term "HOL.conj"}) |
getOp x = (@{print}("x",x);raise throw_alethe_ex ("malformed shuffle rule"))
in SOME (CommOp (getOp concl)) end
| "la_generic" => SOME (Farkas_Coefficients (map extract_coefficients step_args))
| "forall_inst" =>
let
fun get_foralls (SMTLIB.S [_,SMTLIB.S[_,SMTLIB.S [_,SMTLIB.S[_,SMTLIB.S xs,_]],_]]) step_args =
let
fun get_vars [] _ = []
| get_vars (SMTLIB.S[v,_]::ys) (z::zs) = SMTLIB.S[SMTLIB.Sym "=",v,z]:: get_vars ys zs
| get_vars _ _ = raise throw_alethe_ex ("malformed conclusion or argument of forall_inst step")
in get_vars xs step_args end
| get_foralls _ _ = raise throw_alethe_ex ("malformed conclusion or argument of forall_inst step")
fun normalize_external2 (raw_name,b) =
if Config.get ctxt SMT_Config.check_external andalso Char.isUpper (String.sub (raw_name, 0))
then (("isabelle_internal_" ^ raw_name),b) else ( raw_name,b)
fun insts_of_forall_inst (SMTLIB.S [_, SMTLIB.Sym x, a]) = (x, a) |
insts_of_forall_inst _ = raise throw_alethe_ex ("forall inst")
val raw_insts = map normalize_external2 (map insts_of_forall_inst (get_foralls concl step_args))
fun termify_term (x, t) cx = let val (t, cx) = term_of t cx in ((x, t), cx) end
val (termified_inst, _) = fold_map termify_term raw_insts cx
val insts = Symtab.empty
|> fold (fn (x, t) => fn insts => Symtab.update_new (desymbolize x, t) insts) termified_inst
|> Symtab.map (K (unsk_and_rewrite orig_new_variable_names))
handle (Symtab.DUP _) => raise throw_alethe_ex ("forall inst, tried to add same name twice")
in SOME (Insts insts) end
| r =>
if member (op =) index_rules r
then let
fun get_index [SMTLIB.Num k] = SOME (Index k)
| get_index _ = NONE
in
get_index step_args
end
else NONE)
val normalized_premises = prems @ (if is_assm_repetition id rule then [id] else [])
fun normalized_input_rule_name id = if can SMTLIB_Interface.role_and_index_of_assert_name id then normalized_input_rule else local_input_rule
val (normalized_rule, termified_prop_skeleton) =
if rule = input_rule
then (normalized_input_rule_name id, @{term ‹Trueprop True›})
else (rule, termified_prop_skeleton)
val empty_subproof = ([], [], [], [])
val empty_context_assignment = []
val step =
mk_replay_node
id
normalized_rule
normalized_step_args
empty_context_assignment
normalized_premises
termified_concl
empty_subproof
tactic_args
termified_prop_skeleton
in
(step, (cx', orig_new_variable_names))
end
in
postprocess step (cx, [])
|> (fn (step, (cx, _)) => (step, cx))
end
end
fun combine_proof_steps ((Alethe_Replay_Node step1 : alethe_replay_node) :: (Alethe_Replay_Node step2) :: steps) =
let
val {id = id1, rule = rule1, step_args = step_args1,
context_assignments = context_assignment1, prems = prems1,
concl = concl1,
subproof = (bound_sub1, assms_sub1, assms_extra1, subproof1),args = args1, prop_skeleton = prop_skeleton1} = step1
val {id = id2, rule = rule2, step_args = step_args2,
context_assignments = context_assignment2, prems = prems2,
concl = concl2,
subproof = (bound_sub2, assms_sub2, assms_extra2, subproof2),args = args2, ...} = step2
val goals1 =
(case concl1 of
_ $ (Const (\<^const_name>‹HOL.disj›, _) $ _ $
(Const (\<^const_name>‹HOL.disj›, _) $ (Const (\<^const_name>‹HOL.Not›, _) $a) $ b)) => [a,b]
| _ => [])
val goal2 = (case concl2 of _ $ a => a | _ => raise throw_alethe_ex "conclusion has wrong form")
in
if rule1 = equiv_pos2_rule andalso rule2 = th_resolution_rule andalso member (op =) prems2 id1
andalso member (op =) goals1 goal2
then
(mk_replay_node id2 theory_resolution2_rule step_args2 context_assignment2 (filter_out (curry (op =) id1) prems2)
concl2
(bound_sub2, assms_sub2, assms_extra2, combine_proof_steps subproof2) args2 @{term ‹Trueprop True›}) ::
combine_proof_steps steps
else
(mk_replay_node id1 rule1 step_args1 context_assignment1 prems1
concl1
(bound_sub1, assms_sub1, assms_extra1, combine_proof_steps subproof1) args1 prop_skeleton1) ::
combine_proof_steps (Alethe_Replay_Node step2 :: steps)
end
| combine_proof_steps ((step1 as Alethe_Definition_Node _ : alethe_replay_node) :: steps) =
step1 :: combine_proof_steps steps
| combine_proof_steps steps = steps
val linearize_proof =
let
fun map_node_concl f (Alethe_Step {id, rule, prems, concl, prop_skeleton}) =
mk_step id rule prems (f concl) prop_skeleton
fun linearize (Alethe_Replay_Node {id = id, rule = rule, step_args = _, context_assignments = _, prems = prems,
concl = concl,
subproof = (_, assms, inputs, subproof), args = _, prop_skeleton = prop_skeleton2}) =
let
fun mk_prop_of_term concl =
concl |> fastype_of concl = \<^typ>‹bool› ? curry (op $) \<^term>‹Trueprop›
fun remove_assumption_id assumption_id prems =
filter_out (curry (op =) assumption_id) prems
fun add_assumption assumption concl =
\<^Const>‹Pure.imp for ‹mk_prop_of_term assumption› ‹mk_prop_of_term concl››
fun inline_assumption assumption assumption_id
(Alethe_Step {id, rule, prems, concl, prop_skeleton}) =
mk_step id rule (remove_assumption_id assumption_id prems)
(add_assumption assumption concl) prop_skeleton
fun find_input_steps_and_inline [] = []
| find_input_steps_and_inline
(Alethe_Step {id = id', rule, prems, concl, prop_skeleton, ...} :: steps) =
if rule = input_rule then
find_input_steps_and_inline (map (inline_assumption concl id') steps)
else
mk_step id' rule prems concl prop_skeleton :: find_input_steps_and_inline steps
val subproof = subproof
|> maps linearize
|> map (map_node_concl (fold add_assumption (assms @ inputs)))
|> find_input_steps_and_inline
in
subproof @ [mk_step id rule prems concl prop_skeleton2]
end |
linearize (Alethe_Definition_Node _) = []
in linearize end
fun rule_of (Alethe_Replay_Node {rule,...}) = rule |
rule_of _ = raise throw_alethe_ex "the current Alethe_Replay_Node has no rule"
fun subproof_of (Alethe_Replay_Node {subproof = (_, _, _, subproof),...}) = subproof |
subproof_of _ = raise throw_alethe_ex "the current Alethe_Replay_Node has no subproof"
fun remove_skolem_definitions_proof steps =
let
fun replace_equivalent_by_imp (judgement $ ((Const(\<^const_name>‹HOL.eq›, typ) $ arg1) $ arg2)) =
judgement $ ((Const(\<^const_name>‹HOL.implies›, typ) $ arg1) $ arg2)
| replace_equivalent_by_imp a = a
fun remove_skolem_definitions (Alethe_Definition_Node {id = id, ...}) (prems_to_remove, skolems) =
([], (id :: prems_to_remove, id :: skolems))
| remove_skolem_definitions (Alethe_Replay_Node {id = id, rule = rule, step_args = step_args,
context_assignments = context_assignments,
prems = prems,
concl = concl,
subproof = (vars, assms', extra_assms', subproof), args = _,
prop_skeleton = prop_skeleton}) (prems_to_remove, skolems) =
let
val prems = prems
|> filter_out (member (op =) prems_to_remove)
val trivial_step = is_SH_trivial rule
fun has_skolem_substep st NONE = if is_skolemization (rule_of st) then SOME (rule_of st)
else fold has_skolem_substep (subproof_of st) NONE
| has_skolem_substep _ a = a
val promote_to_skolem = exists (fn t => member (op =) skolems t) prems
val promote_from_assms = fold has_skolem_substep subproof NONE <> NONE
val promote_step = promote_to_skolem orelse promote_from_assms
val skolem_step_to_skip = is_skolemization rule orelse
(promote_from_assms andalso length prems > 1)
val is_skolem = is_skolemization rule orelse promote_step
val prems = prems
|> filter_out (fn t => member (op =) skolems t)
|> is_skolem ? filter_out (String.isPrefix id)
val rule = (if promote_step then default_skolem_rule else rule)
val subproof = subproof
|> (is_skolem ? K [])
|> maps (fst o (fn st => remove_skolem_definitions st (prems_to_remove, skolems)))
val concl = concl
|> is_skolem ? replace_equivalent_by_imp
val step = (if skolem_step_to_skip orelse rule = skolem_def orelse trivial_step then []
else mk_replay_node id rule step_args context_assignments prems concl
(vars, assms', extra_assms', subproof) NONE prop_skeleton
|> single)
val defs = (if rule = skolem_def orelse trivial_step then id :: prems_to_remove
else prems_to_remove)
val skolems = (if skolem_step_to_skip then id :: skolems else skolems)
in
(step, (defs, skolems))
end
in
fold_maps remove_skolem_definitions steps ([], [])
|> fst
end
local
fun remove_pattern (SMTLIB.S (SMTLIB.Sym "!" :: t :: [SMTLIB.Key _, SMTLIB.S _])) = t
| remove_pattern (SMTLIB.S xs) = SMTLIB.S (map remove_pattern xs)
| remove_pattern p = p
fun remove_all_qm (SMTLIB.Sym v) = SMTLIB.Sym (perhaps (try (unprefix "?")) v)
| remove_all_qm (SMTLIB.S l) = SMTLIB.S (map remove_all_qm l)
| remove_all_qm (SMTLIB.Key v) = SMTLIB.Key v
| remove_all_qm v = v
fun import_proof_and_post_process typs funs lines ctxt =
let
val _ = debug_msg ctxt middle (fn () => \<^print> ("In import_proof_and_post_process"))
val smtlib_lines_without_qm : SMTLIB.tree list =
let
fun parse_line (s : string) : SMTLIB.tree =
remove_pattern (remove_all_qm (SMTLIB.parse [s]))
fun parse_lines [] = []
| parse_lines ("" :: ss) = parse_lines ss
| parse_lines (s :: ss) = parse_line s :: parse_lines ss
fun parse_wrapped_lines [] =
raise throw_alethe_ex "proof does not end with closing parenthesis"
| parse_wrapped_lines ("" :: ss) = parse_wrapped_lines ss
| parse_wrapped_lines (")" :: ss) =
if forall (equal "") ss then
[]
else
raise throw_alethe_ex "unexpected closing parenthesis in proof"
| parse_wrapped_lines (s :: ss) = parse_line s :: parse_wrapped_lines ss
in
(case drop_prefix (equal "") lines of
"(" :: lines => parse_wrapped_lines lines
| lines =>
parse_lines lines)
end
val _ = debug_msg ctxt high (fn () => \<^print> ("Removed questionmarks and patterns from proof"))
val (raw_steps, _, _) =
parse_raw_proof_steps NONE smtlib_lines_without_qm SMTLIB_Proof.empty_name_binding
val _ = debug_msg ctxt middle (fn () => \<^print> ("Successfully parsed into raw proof step"))
val compress = SMT_Config.compress_alethe_proofs ctxt
fun process step (cx, cx') =
let fun postprocess step (cx, cx') =
let val (step, cx) = postprocess_proof compress ctxt step cx
in (step, (cx, cx')) end
in uncurry (fold_map postprocess) (preprocess compress step (cx, cx')) end
val step =
(empty_context ctxt typs funs, [])
|> fold_map process raw_steps
|> (fn (steps, (cx, _)) => (flat steps, cx))
|> compress? apfst combine_proof_steps
in step end
in
fun parse typs funs lines ctxt =
let
val (u, env) = import_proof_and_post_process typs funs lines ctxt
val t = u
|> remove_skolem_definitions_proof
|> maps linearize_proof
fun node_to_step (Alethe_Step {id, rule, prems, concl, prop_skeleton, ...}) =
mk_step id rule prems concl prop_skeleton
in
(map node_to_step t, ctxt_of env)
end
fun parse_replay typs funs lines ctxt =
let
val (u, env) = import_proof_and_post_process typs funs lines ctxt
in
(u, ctxt_of env)
end
end
end;