src/HOL/Tools/BNF/bnf_gfp_rec_sugar.ML
changeset 59594 43f0c669302d
parent 59582 0fbed69ff081
child 59595 2d90b85b9264
equal deleted inserted replaced
59593:304ee0a475d1 59594:43f0c669302d
    82 val selN = "sel";
    82 val selN = "sel";
    83 
    83 
    84 val nitpicksimp_attrs = @{attributes [nitpick_simp]};
    84 val nitpicksimp_attrs = @{attributes [nitpick_simp]};
    85 val simp_attrs = @{attributes [simp]};
    85 val simp_attrs = @{attributes [simp]};
    86 
    86 
    87 exception PRIMCOREC of string * term list;
    87 fun error_at ctxt ts str =
    88 
    88   error (str ^ (if null ts then ""
    89 fun primcorec_error str = raise PRIMCOREC (str, []);
    89     else " at\n  " ^ space_implode "\n  " (map (quote o Syntax.string_of_term ctxt) ts)));
    90 fun primcorec_error_eqn str eqn = raise PRIMCOREC (str, [eqn]);
    90 
    91 fun primcorec_error_eqns str eqns = raise PRIMCOREC (str, eqns);
    91 fun not_codatatype ctxt T =
       
    92   error ("Not a codatatype: " ^ Syntax.string_of_typ ctxt T);
       
    93 fun ill_formed_corec_call ctxt t =
       
    94   error ("Ill-formed corecursive call " ^ quote (Syntax.string_of_term ctxt t));
       
    95 fun invalid_map ctxt t =
       
    96   error_at ctxt [t] "Invalid map function";
       
    97 fun unexpected_corec_call ctxt eqns t =
       
    98   error_at ctxt eqns ("Unexpected corecursive call " ^ quote (Syntax.string_of_term ctxt t));
    92 
    99 
    93 datatype corec_option =
   100 datatype corec_option =
    94   Plugins_Option of Proof.context -> Plugin_Name.filter |
   101   Plugins_Option of Proof.context -> Plugin_Name.filter |
    95   Sequential_Option |
   102   Sequential_Option |
    96   Exhaustive_Option |
   103   Exhaustive_Option |
   130    fp_nesting_map_ident0s: thm list,
   137    fp_nesting_map_ident0s: thm list,
   131    fp_nesting_map_comps: thm list,
   138    fp_nesting_map_comps: thm list,
   132    ctr_specs: corec_ctr_spec list};
   139    ctr_specs: corec_ctr_spec list};
   133 
   140 
   134 exception NO_MAP of term;
   141 exception NO_MAP of term;
   135 
       
   136 fun not_codatatype ctxt T =
       
   137   error ("Not a codatatype: " ^ Syntax.string_of_typ ctxt T);
       
   138 fun ill_formed_corec_call ctxt t =
       
   139   error ("Ill-formed corecursive call: " ^ quote (Syntax.string_of_term ctxt t));
       
   140 fun invalid_map ctxt t =
       
   141   error ("Invalid map function in " ^ quote (Syntax.string_of_term ctxt t));
       
   142 fun unexpected_corec_call ctxt t =
       
   143   error ("Unexpected corecursive call: " ^ quote (Syntax.string_of_term ctxt t));
       
   144 
   142 
   145 fun sort_list_duplicates xs = map snd (sort (int_ord o apply2 fst) xs);
   143 fun sort_list_duplicates xs = map snd (sort (int_ord o apply2 fst) xs);
   146 
   144 
   147 val mk_conjs = try (foldr1 HOLogic.mk_conj) #> the_default @{const True};
   145 val mk_conjs = try (foldr1 HOLogic.mk_conj) #> the_default @{const True};
   148 val mk_disjs = try (foldr1 HOLogic.mk_disj) #> the_default @{const False};
   146 val mk_disjs = try (foldr1 HOLogic.mk_disj) #> the_default @{const False};
   225 
   223 
   226 fun massage_let_if_case ctxt has_call massage_leaf =
   224 fun massage_let_if_case ctxt has_call massage_leaf =
   227   let
   225   let
   228     val thy = Proof_Context.theory_of ctxt;
   226     val thy = Proof_Context.theory_of ctxt;
   229 
   227 
   230     fun check_no_call t = if has_call t then unexpected_corec_call ctxt t else ();
   228     fun check_no_call t = if has_call t then unexpected_corec_call ctxt [] t else ();
   231 
   229 
   232     fun massage_abs bound_Ts 0 t = massage_rec bound_Ts t
   230     fun massage_abs bound_Ts 0 t = massage_rec bound_Ts t
   233       | massage_abs bound_Ts m (Abs (s, T, t)) = Abs (s, T, massage_abs (T :: bound_Ts) (m - 1) t)
   231       | massage_abs bound_Ts m (Abs (s, T, t)) = Abs (s, T, massage_abs (T :: bound_Ts) (m - 1) t)
   234       | massage_abs bound_Ts m t =
   232       | massage_abs bound_Ts m t =
   235         let val T = domain_type (fastype_of1 (bound_Ts, t)) in
   233         let val T = domain_type (fastype_of1 (bound_Ts, t)) in
   282 
   280 
   283 fun curried_type (Type (@{type_name fun}, [Type (@{type_name prod}, Ts), T])) = Ts ---> T;
   281 fun curried_type (Type (@{type_name fun}, [Type (@{type_name prod}, Ts), T])) = Ts ---> T;
   284 
   282 
   285 fun massage_nested_corec_call ctxt has_call raw_massage_call bound_Ts U t =
   283 fun massage_nested_corec_call ctxt has_call raw_massage_call bound_Ts U t =
   286   let
   284   let
   287     fun check_no_call t = if has_call t then unexpected_corec_call ctxt t else ();
   285     fun check_no_call t = if has_call t then unexpected_corec_call ctxt [] t else ();
   288 
   286 
   289     val build_map_Inl = build_map ctxt [] (uncurry Inl_const o dest_sumT o snd);
   287     val build_map_Inl = build_map ctxt [] (uncurry Inl_const o dest_sumT o snd);
   290 
   288 
   291     fun massage_mutual_call bound_Ts U T t =
   289     fun massage_mutual_call bound_Ts U T t =
   292       if has_call t then
   290       if has_call t then
   579 
   577 
   580 datatype coeqn_data =
   578 datatype coeqn_data =
   581   Disc of coeqn_data_disc |
   579   Disc of coeqn_data_disc |
   582   Sel of coeqn_data_sel;
   580   Sel of coeqn_data_sel;
   583 
   581 
   584 fun check_extra_variables lthy vars names eqn =
   582 fun is_free_in frees (Free (v, _)) = member (op =) frees v
       
   583   | is_free_in _ _ = false;
       
   584 
       
   585 fun check_extra_variables ctxt vars names eqn =
   585   let val b = fold_aterms (fn x as Free (v, _) =>
   586   let val b = fold_aterms (fn x as Free (v, _) =>
   586     if (not (member (op =) vars x) andalso
   587     if (not (member (op =) vars x) andalso
   587       not (member (op =) names v) andalso
   588       not (member (op =) names v) andalso
   588       v <> Name.uu_ andalso
   589       v <> Name.uu_ andalso
   589       not (Variable.is_fixed lthy v)) then cons x else I | _ => I) eqn []
   590       not (Variable.is_fixed ctxt v)) then cons x else I | _ => I) eqn []
   590   in
   591   in
   591     null b orelse
   592     null b orelse error_at ctxt [eqn]
   592     primcorec_error_eqn ("extra variable(s) in equation: " ^
   593       ("Extra variable " ^ quote (Syntax.string_of_term ctxt (hd b)))
   593       commas (map (Syntax.string_of_term lthy) b)) eqn
   594   end;
   594   end;
   595 
   595 
   596 fun dissect_coeqn_disc ctxt fun_names sequentials
   596 fun dissect_coeqn_disc lthy fun_names sequentials (basic_ctr_specss : basic_corec_ctr_spec list list)
   597     (basic_ctr_specss : basic_corec_ctr_spec list list) eqn_pos ctr_rhs_opt code_rhs_opt prems0
   597     eqn_pos ctr_rhs_opt code_rhs_opt prems' concl matchedsss =
   598     concl matchedsss =
   598   let
   599   let
   599     fun find_subterm p =
   600     fun find_subterm p =
   600       let (* FIXME \<exists>? *)
   601       let (* FIXME \<exists>? *)
   601         fun find (t as u $ v) = if p t then SOME t else merge_options (find u, find v)
   602         fun find (t as u $ v) = if p t then SOME t else merge_options (find u, find v)
   602           | find t = if p t then SOME t else NONE;
   603           | find t = if p t then SOME t else NONE;
   603       in find end;
   604       in find end;
   604 
   605 
   605     val applied_fun = concl
   606     val applied_fun = concl
   606       |> find_subterm (member (op = o apsnd SOME) fun_names o try (fst o dest_Free o head_of))
   607       |> find_subterm (member (op = o apsnd SOME) fun_names o try (fst o dest_Free o head_of))
   607       |> the
   608       |> the
   608       handle Option.Option => primcorec_error_eqn "malformed discriminator formula" concl;
   609       handle Option.Option => error_at ctxt [concl] "Ill-formed discriminator formula";
   609     val ((fun_name, fun_T), fun_args) = strip_comb applied_fun |>> dest_Free;
   610     val ((fun_name, fun_T), fun_args) = strip_comb applied_fun |>> dest_Free;
   610 
   611 
   611     val fixeds = filter (Variable.is_fixed lthy o fst o dest_Free) fun_args;
   612     val fixeds = filter (Variable.is_fixed ctxt o fst o dest_Free) fun_args;
   612     val _ = null fixeds orelse
   613     val _ = null fixeds orelse error_at ctxt fixeds "Function argument(s) are fixed in context";
   613       primcorec_error_eqns "function argument(s) are fixed in context" fixeds;
   614 
   614 
   615     val bads = filter (Term.exists_subterm (is_free_in fun_names)) prems0;
   615     val bad =
   616     val _ = null bads orelse error_at ctxt bads "Corecursive call(s) in condition(s)";
   616       filter (exists_subterm (fn Free (v, _) => member (op =) fun_names v | _ => false)) prems';
       
   617     val _ = null bad orelse primcorec_error_eqns "corecursive call(s) in condition(s)" bad;
       
   618 
   617 
   619     val _ = forall is_Free fun_args orelse
   618     val _ = forall is_Free fun_args orelse
   620       primcorec_error_eqn ("non-variable function argument \"" ^
   619       error_at ctxt [applied_fun] ("Non-variable function argument \"" ^
   621         Syntax.string_of_term lthy (find_first (not o is_Free) fun_args |> the) ^
   620         Syntax.string_of_term ctxt (find_first (not o is_Free) fun_args |> the) ^
   622           "\" (pattern matching is not supported by primcorec(ursive))") applied_fun
   621           "\" (pattern matching is not supported by primcorec(ursive))")
   623 
   622 
   624     val _ = let val d = duplicates (op =) fun_args in null d orelse
   623     val dups = duplicates (op =) fun_args;
   625       primcorec_error_eqn ("duplicate variable \"" ^ Syntax.string_of_term lthy (hd d) ^ "\"")
   624     val _ =
   626         applied_fun end;
   625       null dups orelse
       
   626       error_at ctxt [applied_fun]
       
   627         ("Duplicate variable " ^ quote (Syntax.string_of_term ctxt (hd dups)));
   627 
   628 
   628     val (sequential, basic_ctr_specs) =
   629     val (sequential, basic_ctr_specs) =
   629       the (AList.lookup (op =) (fun_names ~~ (sequentials ~~ basic_ctr_specss)) fun_name);
   630       the (AList.lookup (op =) (fun_names ~~ (sequentials ~~ basic_ctr_specss)) fun_name);
   630 
   631 
   631     val discs = map #disc basic_ctr_specs;
   632     val discs = map #disc basic_ctr_specs;
   632     val ctrs = map #ctr basic_ctr_specs;
   633     val ctrs = map #ctr basic_ctr_specs;
   633     val not_disc = head_of concl = @{term Not};
   634     val not_disc = head_of concl = @{term Not};
   634     val _ = not_disc andalso length ctrs <> 2 andalso
   635     val _ = not_disc andalso length ctrs <> 2 andalso
   635       primcorec_error_eqn "negated discriminator for a type with \<noteq> 2 constructors" concl;
   636       error_at ctxt [concl] "Negated discriminator for a type with \<noteq> 2 constructors";
   636     val disc' = find_subterm (member (op =) discs o head_of) concl;
   637     val disc' = find_subterm (member (op =) discs o head_of) concl;
   637     val eq_ctr0 = concl |> perhaps (try HOLogic.dest_not) |> try (HOLogic.dest_eq #> snd)
   638     val eq_ctr0 = concl |> perhaps (try HOLogic.dest_not) |> try (HOLogic.dest_eq #> snd)
   638         |> (fn SOME t => let val n = find_index (curry (op =) t) ctrs in
   639         |> (fn SOME t => let val n = find_index (curry (op =) t) ctrs in
   639           if n >= 0 then SOME n else NONE end | _ => NONE);
   640           if n >= 0 then SOME n else NONE end | _ => NONE);
   640 
   641 
   641     val _ = if is_some disc' andalso perhaps (try HOLogic.dest_not) concl <> the disc'
   642     val _ = is_none disc' orelse perhaps (try HOLogic.dest_not) concl = the disc' orelse
   642       then primcorec_error_eqn "malformed discriminator formula" concl else ();
   643       error_at ctxt [concl] "Ill-formed discriminator formula";
   643 
       
   644     val _ = is_some disc' orelse is_some eq_ctr0 orelse
   644     val _ = is_some disc' orelse is_some eq_ctr0 orelse
   645       primcorec_error_eqn "no discriminator in equation" concl;
   645       error_at ctxt [concl] "No discriminator in equation";
       
   646 
   646     val ctr_no' =
   647     val ctr_no' =
   647       if is_none disc' then the eq_ctr0 else find_index (curry (op =) (head_of (the disc'))) discs;
   648       if is_none disc' then the eq_ctr0 else find_index (curry (op =) (head_of (the disc'))) discs;
   648     val ctr_no = if not_disc then 1 - ctr_no' else ctr_no';
   649     val ctr_no = if not_disc then 1 - ctr_no' else ctr_no';
   649     val {ctr, disc, ...} = nth basic_ctr_specs ctr_no;
   650     val {ctr, disc, ...} = nth basic_ctr_specs ctr_no;
   650 
   651 
   651     val catch_all = try (fst o dest_Free o the_single) prems' = SOME Name.uu_;
   652     val catch_all = (try (fst o dest_Free o the_single) prems0 = SOME Name.uu_);
   652     val matchedss = AList.lookup (op =) matchedsss fun_name |> the_default [];
   653     val matchedss = AList.lookup (op =) matchedsss fun_name |> the_default [];
   653     val prems = map (abstract (List.rev fun_args)) prems';
   654     val prems = map (abstract (List.rev fun_args)) prems0;
   654     val actual_prems =
   655     val actual_prems =
   655       (if catch_all orelse sequential then maps s_not_conj matchedss else []) @
   656       (if catch_all orelse sequential then maps s_not_conj matchedss else []) @
   656       (if catch_all then [] else prems);
   657       (if catch_all then [] else prems);
   657 
   658 
   658     val matchedsss' = AList.delete (op =) fun_name matchedsss
   659     val matchedsss' = AList.delete (op =) fun_name matchedsss
   661     val user_eqn =
   662     val user_eqn =
   662       (actual_prems, concl)
   663       (actual_prems, concl)
   663       |>> map HOLogic.mk_Trueprop ||> HOLogic.mk_Trueprop o abstract (List.rev fun_args)
   664       |>> map HOLogic.mk_Trueprop ||> HOLogic.mk_Trueprop o abstract (List.rev fun_args)
   664       |> curry Logic.list_all (map dest_Free fun_args) o Logic.list_implies;
   665       |> curry Logic.list_all (map dest_Free fun_args) o Logic.list_implies;
   665 
   666 
   666     val _ = check_extra_variables lthy fun_args fun_names user_eqn;
   667     val _ = check_extra_variables ctxt fun_args fun_names user_eqn;
   667   in
   668   in
   668     (Disc {fun_name = fun_name, fun_T = fun_T, fun_args = fun_args, ctr = ctr, ctr_no = ctr_no,
   669     (Disc {fun_name = fun_name, fun_T = fun_T, fun_args = fun_args, ctr = ctr, ctr_no = ctr_no,
   669        disc = disc, prems = actual_prems, auto_gen = catch_all, ctr_rhs_opt = ctr_rhs_opt,
   670        disc = disc, prems = actual_prems, auto_gen = catch_all, ctr_rhs_opt = ctr_rhs_opt,
   670        code_rhs_opt = code_rhs_opt, eqn_pos = eqn_pos, user_eqn = user_eqn},
   671        code_rhs_opt = code_rhs_opt, eqn_pos = eqn_pos, user_eqn = user_eqn},
   671      matchedsss')
   672      matchedsss')
   672   end;
   673   end;
   673 
   674 
   674 fun dissect_coeqn_sel lthy fun_names (basic_ctr_specss : basic_corec_ctr_spec list list) eqn_pos
   675 fun dissect_coeqn_sel ctxt fun_names (basic_ctr_specss : basic_corec_ctr_spec list list) eqn_pos
   675     ctr_rhs_opt code_rhs_opt eqn0 of_spec_opt eqn =
   676     ctr_rhs_opt code_rhs_opt eqn0 of_spec_opt eqn =
   676   let
   677   let
   677     val (lhs, rhs) = HOLogic.dest_eq eqn
   678     val (lhs, rhs) = HOLogic.dest_eq eqn
   678       handle TERM _ =>
   679       handle TERM _ => error_at ctxt [eqn] "Ill-formed equation (expected \"lhs = rhs\")";
   679              primcorec_error_eqn "malformed function equation (expected \"lhs = rhs\")" eqn;
       
   680     val sel = head_of lhs;
   680     val sel = head_of lhs;
   681     val ((fun_name, fun_T), fun_args) = dest_comb lhs |> snd |> strip_comb |> apfst dest_Free
   681     val ((fun_name, fun_T), fun_args) = dest_comb lhs |> snd |> strip_comb |> apfst dest_Free
   682       handle TERM _ =>
   682       handle TERM _ => error_at ctxt [eqn] "Ill-formed selector argument in left-hand side";
   683              primcorec_error_eqn "malformed selector argument in left-hand side" eqn;
   683 
   684 
   684     val fixeds = filter (Variable.is_fixed ctxt o fst o dest_Free) fun_args;
   685     val _ = let val fixeds = filter (Variable.is_fixed lthy o fst o dest_Free) fun_args in
   685     val _ =
   686         null fixeds orelse primcorec_error_eqns "function argument(s) are fixed in context" fixeds
   686       null fixeds orelse error ("Function argument " ^
   687       end;
   687         quote (Syntax.string_of_term ctxt (hd fixeds)) ^ " is fixed in context");
   688 
   688 
   689     val basic_ctr_specs = the (AList.lookup (op =) (fun_names ~~ basic_ctr_specss) fun_name)
   689     val basic_ctr_specs = the (AList.lookup (op =) (fun_names ~~ basic_ctr_specss) fun_name)
   690       handle Option.Option =>
   690       handle Option.Option => error_at ctxt [eqn] "Ill-formed selector argument in left-hand side";
   691              primcorec_error_eqn "malformed selector argument in left-hand side" eqn;
       
   692     val {ctr, ...} =
   691     val {ctr, ...} =
   693       (case of_spec_opt of
   692       (case of_spec_opt of
   694         SOME of_spec => the (find_first (curry (op =) of_spec o #ctr) basic_ctr_specs)
   693         SOME of_spec => the (find_first (curry (op =) of_spec o #ctr) basic_ctr_specs)
   695       | NONE => filter (exists (curry (op =) sel) o #sels) basic_ctr_specs |> the_single
   694       | NONE => filter (exists (curry (op =) sel) o #sels) basic_ctr_specs |> the_single
   696           handle List.Empty => primcorec_error_eqn "ambiguous selector - use \"of\"" eqn);
   695           handle List.Empty => error_at ctxt [eqn] "Ambiguous selector (without \"of\")");
   697     val user_eqn = drop_all eqn0;
   696     val user_eqn = drop_all eqn0;
   698 
   697 
   699     val _ = check_extra_variables lthy fun_args fun_names user_eqn;
   698     val _ = check_extra_variables ctxt fun_args fun_names user_eqn;
   700   in
   699   in
   701     Sel {fun_name = fun_name, fun_T = fun_T, fun_args = fun_args, ctr = ctr, sel = sel,
   700     Sel {fun_name = fun_name, fun_T = fun_T, fun_args = fun_args, ctr = ctr, sel = sel,
   702       rhs_term = rhs, ctr_rhs_opt = ctr_rhs_opt, code_rhs_opt = code_rhs_opt, eqn_pos = eqn_pos,
   701       rhs_term = rhs, ctr_rhs_opt = ctr_rhs_opt, code_rhs_opt = code_rhs_opt, eqn_pos = eqn_pos,
   703       user_eqn = user_eqn}
   702       user_eqn = user_eqn}
   704   end;
   703   end;
   705 
   704 
   706 fun dissect_coeqn_ctr lthy fun_names sequentials (basic_ctr_specss : basic_corec_ctr_spec list list)
   705 fun dissect_coeqn_ctr ctxt fun_names sequentials (basic_ctr_specss : basic_corec_ctr_spec list list)
   707     eqn_pos eqn0 code_rhs_opt prems concl matchedsss =
   706     eqn_pos eqn0 code_rhs_opt prems concl matchedsss =
   708   let
   707   let
   709     val (lhs, rhs) = HOLogic.dest_eq concl;
   708     val (lhs, rhs) = HOLogic.dest_eq concl;
   710     val (fun_name, fun_args) = strip_comb lhs |>> fst o dest_Free;
   709     val (fun_name, fun_args) = strip_comb lhs |>> fst o dest_Free;
   711 
   710 
   712     val _ = check_extra_variables lthy fun_args fun_names (drop_all eqn0);
   711     val _ = check_extra_variables ctxt fun_args fun_names (drop_all eqn0);
   713 
   712 
   714     val basic_ctr_specs = the (AList.lookup (op =) (fun_names ~~ basic_ctr_specss) fun_name);
   713     val basic_ctr_specs = the (AList.lookup (op =) (fun_names ~~ basic_ctr_specss) fun_name);
   715     val (ctr, ctr_args) = strip_comb (unfold_lets_splits rhs);
   714     val (ctr, ctr_args) = strip_comb (unfold_lets_splits rhs);
   716     val {disc, sels, ...} = the (find_first (curry (op =) ctr o #ctr) basic_ctr_specs)
   715     val {disc, sels, ...} = the (find_first (curry (op =) ctr o #ctr) basic_ctr_specs)
   717       handle Option.Option => primcorec_error_eqn "not a constructor" ctr;
   716       handle Option.Option => error_at ctxt [ctr] "Not a constructor";
   718 
   717 
   719     val disc_concl = betapply (disc, lhs);
   718     val disc_concl = betapply (disc, lhs);
   720     val (eqn_data_disc_opt, matchedsss') =
   719     val (eqn_data_disc_opt, matchedsss') =
   721       if null (tl basic_ctr_specs) andalso not (null sels) then
   720       if null (tl basic_ctr_specs) andalso not (null sels) then
   722         (NONE, matchedsss)
   721         (NONE, matchedsss)
   723       else
   722       else
   724         apfst SOME (dissect_coeqn_disc lthy fun_names sequentials basic_ctr_specss eqn_pos
   723         apfst SOME (dissect_coeqn_disc ctxt fun_names sequentials basic_ctr_specss eqn_pos
   725           (SOME (abstract (List.rev fun_args) rhs)) code_rhs_opt prems disc_concl matchedsss);
   724           (SOME (abstract (List.rev fun_args) rhs)) code_rhs_opt prems disc_concl matchedsss);
   726 
   725 
   727     val sel_concls = sels ~~ ctr_args
   726     val sel_concls = sels ~~ ctr_args
   728       |> map (fn (sel, ctr_arg) => HOLogic.mk_eq (betapply (sel, lhs), ctr_arg))
   727       |> map (fn (sel, ctr_arg) => HOLogic.mk_eq (betapply (sel, lhs), ctr_arg))
   729         handle ListPair.UnequalLengths =>
   728         handle ListPair.UnequalLengths =>
   730           primcorec_error_eqn "partially applied constructor in right-hand side" rhs;
   729           error_at ctxt [rhs] "Partially applied constructor in right-hand side";
   731 
   730 
   732     val eqns_data_sel =
   731     val eqns_data_sel =
   733       map (dissect_coeqn_sel lthy fun_names basic_ctr_specss eqn_pos
   732       map (dissect_coeqn_sel ctxt fun_names basic_ctr_specss eqn_pos
   734         (SOME (abstract (List.rev fun_args) rhs)) code_rhs_opt eqn0 (SOME ctr)) sel_concls;
   733         (SOME (abstract (List.rev fun_args) rhs)) code_rhs_opt eqn0 (SOME ctr)) sel_concls;
   735   in
   734   in
   736     (the_list eqn_data_disc_opt @ eqns_data_sel, matchedsss')
   735     (the_list eqn_data_disc_opt @ eqns_data_sel, matchedsss')
   737   end;
   736   end;
   738 
   737 
   739 fun dissect_coeqn_code lthy has_call fun_names basic_ctr_specss eqn_pos eqn0 concl matchedsss =
   738 fun dissect_coeqn_code ctxt has_call fun_names basic_ctr_specss eqn_pos eqn0 concl matchedsss =
   740   let
   739   let
   741     val (lhs, (rhs', rhs)) = HOLogic.dest_eq concl ||> `(expand_corec_code_rhs lthy has_call []);
   740     val (lhs, (rhs', rhs)) = HOLogic.dest_eq concl ||> `(expand_corec_code_rhs ctxt has_call []);
   742     val (fun_name, fun_args) = strip_comb lhs |>> fst o dest_Free;
   741     val (fun_name, fun_args) = strip_comb lhs |>> fst o dest_Free;
   743 
   742 
   744     val _ = check_extra_variables lthy fun_args fun_names concl;
   743     val _ = check_extra_variables ctxt fun_args fun_names concl;
   745 
   744 
   746     val basic_ctr_specs = the (AList.lookup (op =) (fun_names ~~ basic_ctr_specss) fun_name);
   745     val basic_ctr_specs = the (AList.lookup (op =) (fun_names ~~ basic_ctr_specss) fun_name);
   747 
   746 
   748     val cond_ctrs = fold_rev_corec_code_rhs lthy (fn cs => fn ctr => fn _ =>
   747     val cond_ctrs = fold_rev_corec_code_rhs ctxt (fn cs => fn ctr => fn _ =>
   749         if member (op = o apsnd #ctr) basic_ctr_specs ctr then cons (ctr, cs)
   748         if member (op = o apsnd #ctr) basic_ctr_specs ctr then cons (ctr, cs)
   750         else primcorec_error_eqn "not a constructor" ctr) [] rhs' []
   749         else error ("Not a constructor: " ^ quote (Syntax.string_of_term ctxt ctr))) [] rhs' []
   751       |> AList.group (op =);
   750       |> AList.group (op =);
   752 
   751 
   753     val ctr_premss = (case cond_ctrs of [_] => [[]] | _ => map (s_dnf o snd) cond_ctrs);
   752     val ctr_premss = (case cond_ctrs of [_] => [[]] | _ => map (s_dnf o snd) cond_ctrs);
   754     val ctr_concls = cond_ctrs |> map (fn (ctr, _) =>
   753     val ctr_concls = cond_ctrs |> map (fn (ctr, _) =>
   755       binder_types (fastype_of ctr)
   754       binder_types (fastype_of ctr)
   756       |> map_index (fn (n, T) => massage_corec_code_rhs lthy (fn _ => fn ctr' => fn args =>
   755       |> map_index (fn (n, T) => massage_corec_code_rhs ctxt (fn _ => fn ctr' => fn args =>
   757         if ctr' = ctr then nth args n else Const (@{const_name undefined}, T)) [] rhs')
   756         if ctr' = ctr then nth args n else Const (@{const_name undefined}, T)) [] rhs')
   758       |> curry Term.list_comb ctr
   757       |> curry Term.list_comb ctr
   759       |> curry HOLogic.mk_eq lhs);
   758       |> curry HOLogic.mk_eq lhs);
   760 
   759 
       
   760     val bads = maps (filter (Term.exists_subterm (is_free_in fun_names))) ctr_premss;
       
   761     val _ = null bads orelse unexpected_corec_call ctxt [eqn0] rhs;
       
   762 
   761     val sequentials = replicate (length fun_names) false;
   763     val sequentials = replicate (length fun_names) false;
   762   in
   764   in
   763     @{fold_map 2} (dissect_coeqn_ctr lthy fun_names sequentials basic_ctr_specss eqn_pos eqn0
   765     @{fold_map 2} (dissect_coeqn_ctr ctxt fun_names sequentials basic_ctr_specss eqn_pos eqn0
   764         (SOME (abstract (List.rev fun_args) rhs)))
   766         (SOME (abstract (List.rev fun_args) rhs)))
   765       ctr_premss ctr_concls matchedsss
   767       ctr_premss ctr_concls matchedsss
   766   end;
   768   end;
   767 
   769 
   768 fun dissect_coeqn lthy has_call fun_names sequentials
   770 fun dissect_coeqn ctxt has_call fun_names sequentials
   769     (basic_ctr_specss : basic_corec_ctr_spec list list) (eqn_pos, eqn0) of_spec_opt matchedsss =
   771     (basic_ctr_specss : basic_corec_ctr_spec list list) (eqn_pos, eqn0) of_spec_opt matchedsss =
   770   let
   772   let
   771     val eqn = drop_all eqn0
   773     val eqn = drop_all eqn0
   772       handle TERM _ => primcorec_error_eqn "malformed function equation" eqn0;
   774       handle TERM _ => error_at ctxt [eqn0] "Ill-formed function equation";
   773     val (prems, concl) = Logic.strip_horn eqn
   775     val (prems, concl) = Logic.strip_horn eqn
   774       |> map_prod (map HOLogic.dest_Trueprop) HOLogic.dest_Trueprop
   776       |> map_prod (map HOLogic.dest_Trueprop) HOLogic.dest_Trueprop
   775         handle TERM _ => primcorec_error_eqn "malformed function equation" eqn;
   777         handle TERM _ => error_at ctxt [eqn] "Ill-formed function equation";
   776 
   778 
   777     val head = concl
   779     val head = concl
   778       |> perhaps (try HOLogic.dest_not) |> perhaps (try (fst o HOLogic.dest_eq))
   780       |> perhaps (try HOLogic.dest_not) |> perhaps (try (fst o HOLogic.dest_eq))
   779       |> head_of;
   781       |> head_of;
   780 
   782 
   786   in
   788   in
   787     if member (op =) discs head orelse
   789     if member (op =) discs head orelse
   788        (is_some rhs_opt andalso
   790        (is_some rhs_opt andalso
   789         member (op =) (map SOME fun_names) (try (fst o dest_Free) head) andalso
   791         member (op =) (map SOME fun_names) (try (fst o dest_Free) head) andalso
   790         member (op =) (filter (null o binder_types o fastype_of) ctrs) (the rhs_opt)) then
   792         member (op =) (filter (null o binder_types o fastype_of) ctrs) (the rhs_opt)) then
   791       dissect_coeqn_disc lthy fun_names sequentials basic_ctr_specss eqn_pos NONE NONE prems concl
   793       dissect_coeqn_disc ctxt fun_names sequentials basic_ctr_specss eqn_pos NONE NONE prems concl
   792         matchedsss
   794         matchedsss
   793       |>> single
   795       |>> single
   794     else if member (op =) sels head then
   796     else if member (op =) sels head then
   795       (null prems orelse primcorec_error_eqn "premise(s) in selector formula" eqn;
   797       (null prems orelse error_at ctxt [eqn] "Unexpected condition in selector formula";
   796        ([dissect_coeqn_sel lthy fun_names basic_ctr_specss eqn_pos NONE NONE eqn0 of_spec_opt
   798        ([dissect_coeqn_sel ctxt fun_names basic_ctr_specss eqn_pos NONE NONE eqn0 of_spec_opt
   797            concl], matchedsss))
   799            concl], matchedsss))
   798     else if is_some rhs_opt andalso
   800     else if is_some rhs_opt andalso is_Free head andalso is_free_in fun_names head then
   799         is_Free head andalso member (op =) fun_names (fst (dest_Free head)) then
       
   800       if member (op =) ctrs (head_of (unfold_lets_splits (the rhs_opt))) then
   801       if member (op =) ctrs (head_of (unfold_lets_splits (the rhs_opt))) then
   801         dissect_coeqn_ctr lthy fun_names sequentials basic_ctr_specss eqn_pos eqn0
   802         dissect_coeqn_ctr ctxt fun_names sequentials basic_ctr_specss eqn_pos eqn0
   802           (if null prems then
   803           (if null prems then
   803              SOME (snd (HOLogic.dest_eq (HOLogic.dest_Trueprop (Logic.strip_assums_concl eqn0))))
   804              SOME (snd (HOLogic.dest_eq (HOLogic.dest_Trueprop (Logic.strip_assums_concl eqn0))))
   804            else
   805            else
   805              NONE)
   806              NONE)
   806           prems concl matchedsss
   807           prems concl matchedsss
   807       else if null prems then
   808       else if null prems then
   808         dissect_coeqn_code lthy has_call fun_names basic_ctr_specss eqn_pos eqn0 concl matchedsss
   809         dissect_coeqn_code ctxt has_call fun_names basic_ctr_specss eqn_pos eqn0 concl matchedsss
   809         |>> flat
   810         |>> flat
   810       else
   811       else
   811         primcorec_error_eqn "cannot mix constructor and code views (see manual for details)" eqn
   812         error_at ctxt [eqn] "Cannot mix constructor and code views"
   812     else
   813     else
   813       primcorec_error_eqn "malformed function equation" eqn
   814       error_at ctxt [eqn] "Ill-formed function equation"
   814   end;
   815   end;
   815 
   816 
   816 fun build_corec_arg_disc (ctr_specs : corec_ctr_spec list)
   817 fun build_corec_arg_disc (ctr_specs : corec_ctr_spec list)
   817     ({fun_args, ctr_no, prems, ...} : coeqn_data_disc) =
   818     ({fun_args, ctr_no, prems, ...} : coeqn_data_disc) =
   818   if is_none (#pred (nth ctr_specs ctr_no)) then
   819   if is_none (#pred (nth ctr_specs ctr_no)) then
   827   find_first (curry (op =) sel o #sel) sel_eqns
   828   find_first (curry (op =) sel o #sel) sel_eqns
   828   |> try (fn SOME {fun_args, rhs_term, ...} => abs_tuple_balanced fun_args rhs_term)
   829   |> try (fn SOME {fun_args, rhs_term, ...} => abs_tuple_balanced fun_args rhs_term)
   829   |> the_default undef_const
   830   |> the_default undef_const
   830   |> K;
   831   |> K;
   831 
   832 
   832 fun build_corec_args_mutual_call lthy has_call (sel_eqns : coeqn_data_sel list) sel =
   833 fun build_corec_args_mutual_call ctxt has_call (sel_eqns : coeqn_data_sel list) sel =
   833   (case find_first (curry (op =) sel o #sel) sel_eqns of
   834   (case find_first (curry (op =) sel o #sel) sel_eqns of
   834     NONE => (I, I, I)
   835     NONE => (I, I, I)
   835   | SOME {fun_args, rhs_term, ... } =>
   836   | SOME {fun_args, rhs_term, ... } =>
   836     let
   837     let
   837       val bound_Ts = List.rev (map fastype_of fun_args);
   838       val bound_Ts = List.rev (map fastype_of fun_args);
   838       fun rewrite_stop _ t = if has_call t then @{term False} else @{term True};
   839       fun rewrite_stop _ t = if has_call t then @{term False} else @{term True};
   839       fun rewrite_end _ t = if has_call t then undef_const else t;
   840       fun rewrite_end _ t = if has_call t then undef_const else t;
   840       fun rewrite_cont bound_Ts t =
   841       fun rewrite_cont bound_Ts t =
   841         if has_call t then mk_tuple1_balanced bound_Ts (snd (strip_comb t)) else undef_const;
   842         if has_call t then mk_tuple1_balanced bound_Ts (snd (strip_comb t)) else undef_const;
   842       fun massage f _ = massage_let_if_case lthy has_call f bound_Ts rhs_term
   843       fun massage f _ = massage_let_if_case ctxt has_call f bound_Ts rhs_term
   843         |> abs_tuple_balanced fun_args;
   844         |> abs_tuple_balanced fun_args;
   844     in
   845     in
   845       (massage rewrite_stop, massage rewrite_end, massage rewrite_cont)
   846       (massage rewrite_stop, massage rewrite_end, massage rewrite_cont)
   846     end);
   847     end);
   847 
   848 
   848 fun build_corec_arg_nested_call lthy has_call (sel_eqns : coeqn_data_sel list) sel =
   849 fun build_corec_arg_nested_call ctxt has_call (sel_eqns : coeqn_data_sel list) sel =
   849   (case find_first (curry (op =) sel o #sel) sel_eqns of
   850   (case find_first (curry (op =) sel o #sel) sel_eqns of
   850     NONE => I
   851     NONE => I
   851   | SOME {fun_args, rhs_term, ...} =>
   852   | SOME {fun_args, rhs_term, ...} =>
   852     let
   853     let
   853       fun massage bound_Ts U T =
   854       fun massage bound_Ts U T =
   872 
   873 
   873       val bound_Ts = List.rev (map fastype_of fun_args);
   874       val bound_Ts = List.rev (map fastype_of fun_args);
   874 
   875 
   875       fun build t =
   876       fun build t =
   876         rhs_term
   877         rhs_term
   877         |> massage_nested_corec_call lthy has_call massage bound_Ts (range_type (fastype_of t))
   878         |> massage_nested_corec_call ctxt has_call massage bound_Ts (range_type (fastype_of t))
   878         |> abs_tuple_balanced fun_args;
   879         |> abs_tuple_balanced fun_args;
   879     in
   880     in
   880       build
   881       build
   881     end);
   882     end);
   882 
   883 
   883 fun build_corec_args_sel lthy has_call (all_sel_eqns : coeqn_data_sel list)
   884 fun build_corec_args_sel ctxt has_call (all_sel_eqns : coeqn_data_sel list)
   884     (ctr_spec : corec_ctr_spec) =
   885     (ctr_spec : corec_ctr_spec) =
   885   (case filter (curry (op =) (#ctr ctr_spec) o #ctr) all_sel_eqns of
   886   (case filter (curry (op =) (#ctr ctr_spec) o #ctr) all_sel_eqns of
   886     [] => I
   887     [] => I
   887   | sel_eqns =>
   888   | sel_eqns =>
   888     let
   889     let
   892       val nested_calls' = map_filter (try (apsnd (fn Nested_Corec n => n))) sel_call_list;
   893       val nested_calls' = map_filter (try (apsnd (fn Nested_Corec n => n))) sel_call_list;
   893     in
   894     in
   894       I
   895       I
   895       #> fold (fn (sel, n) => nth_map n (build_corec_arg_no_call sel_eqns sel)) no_calls'
   896       #> fold (fn (sel, n) => nth_map n (build_corec_arg_no_call sel_eqns sel)) no_calls'
   896       #> fold (fn (sel, (q, g, h)) =>
   897       #> fold (fn (sel, (q, g, h)) =>
   897         let val (fq, fg, fh) = build_corec_args_mutual_call lthy has_call sel_eqns sel in
   898         let val (fq, fg, fh) = build_corec_args_mutual_call ctxt has_call sel_eqns sel in
   898           nth_map q fq o nth_map g fg o nth_map h fh end) mutual_calls'
   899           nth_map q fq o nth_map g fg o nth_map h fh end) mutual_calls'
   899       #> fold (fn (sel, n) => nth_map n
   900       #> fold (fn (sel, n) => nth_map n
   900         (build_corec_arg_nested_call lthy has_call sel_eqns sel)) nested_calls'
   901         (build_corec_arg_nested_call ctxt has_call sel_eqns sel)) nested_calls'
   901     end);
   902     end);
   902 
   903 
   903 fun build_codefs lthy bs mxs has_call arg_Tss (corec_specs : corec_spec list)
   904 fun build_codefs ctxt bs mxs has_call arg_Tss (corec_specs : corec_spec list)
   904     (disc_eqnss : coeqn_data_disc list list) (sel_eqnss : coeqn_data_sel list list) =
   905     (disc_eqnss : coeqn_data_disc list list) (sel_eqnss : coeqn_data_sel list list) =
   905   let
   906   let
   906     val corecs = map #corec corec_specs;
   907     val corecs = map #corec corec_specs;
   907     val ctr_specss = map #ctr_specs corec_specs;
   908     val ctr_specss = map #ctr_specs corec_specs;
   908     val corec_args = hd corecs
   909     val corec_args = hd corecs
   909       |> fst o split_last o binder_types o fastype_of
   910       |> fst o split_last o binder_types o fastype_of
   910       |> map (fn T =>
   911       |> map (fn T =>
   911           if range_type T = HOLogic.boolT then Abs (Name.uu_, domain_type T, @{term False})
   912           if range_type T = HOLogic.boolT then Abs (Name.uu_, domain_type T, @{term False})
   912           else Const (@{const_name undefined}, T))
   913           else Const (@{const_name undefined}, T))
   913       |> fold2 (fold o build_corec_arg_disc) ctr_specss disc_eqnss
   914       |> fold2 (fold o build_corec_arg_disc) ctr_specss disc_eqnss
   914       |> fold2 (fold o build_corec_args_sel lthy has_call) sel_eqnss ctr_specss;
   915       |> fold2 (fold o build_corec_args_sel ctxt has_call) sel_eqnss ctr_specss;
   915 
   916 
   916     fun currys [] t = t
   917     fun currys [] t = t
   917       | currys Ts t = t $ mk_tuple1_balanced (List.rev Ts) (map Bound (length Ts - 1 downto 0))
   918       | currys Ts t = t $ mk_tuple1_balanced (List.rev Ts) (map Bound (length Ts - 1 downto 0))
   918           |> fold_rev (Term.abs o pair Name.uu) Ts;
   919           |> fold_rev (Term.abs o pair Name.uu) Ts;
   919 
   920 
   929             ||> curry Logic.list_all (map dest_Free fun_args))));
   930             ||> curry Logic.list_all (map dest_Free fun_args))));
   930   in
   931   in
   931     map (Term.list_comb o rpair corec_args) corecs
   932     map (Term.list_comb o rpair corec_args) corecs
   932     |> map2 (fn Ts => fn t => if length Ts = 0 then t $ HOLogic.unit else t) arg_Tss
   933     |> map2 (fn Ts => fn t => if length Ts = 0 then t $ HOLogic.unit else t) arg_Tss
   933     |> map2 currys arg_Tss
   934     |> map2 currys arg_Tss
   934     |> Syntax.check_terms lthy
   935     |> Syntax.check_terms ctxt
   935     |> @{map 3} (fn b => fn mx => fn t => ((b, mx), ((Binding.conceal (Thm.def_binding b), []), t)))
   936     |> @{map 3} (fn b => fn mx => fn t => ((b, mx), ((Binding.conceal (Thm.def_binding b), []), t)))
   936       bs mxs
   937       bs mxs
   937     |> rpair excludess'
   938     |> rpair excludess'
   938   end;
   939   end;
   939 
   940 
   990     val (bs, mxs) = map_split (apfst fst) fixes;
   991     val (bs, mxs) = map_split (apfst fst) fixes;
   991     val (arg_Ts, res_Ts) = map (strip_type o snd o fst #>> mk_tupleT_balanced) fixes |> split_list;
   992     val (arg_Ts, res_Ts) = map (strip_type o snd o fst #>> mk_tupleT_balanced) fixes |> split_list;
   992 
   993 
   993     val _ = (case filter_out (fn (_, T) => Sign.of_sort thy (T, @{sort type})) (bs ~~ arg_Ts) of
   994     val _ = (case filter_out (fn (_, T) => Sign.of_sort thy (T, @{sort type})) (bs ~~ arg_Ts) of
   994         [] => ()
   995         [] => ()
   995       | (b, _) :: _ => primcorec_error ("type of " ^ Binding.print b ^ " contains top sort"));
   996       | (b, _) :: _ => error ("Type of " ^ Binding.print b ^ " contains top sort"));
   996 
   997 
   997     val actual_nn = length bs;
   998     val actual_nn = length bs;
   998 
   999 
   999     val plugins = get_first (fn Plugins_Option f => SOME (f lthy) | _ => NONE) (rev opts)
  1000     val plugins = get_first (fn Plugins_Option f => SOME (f lthy) | _ => NONE) (rev opts)
  1000       |> the_default Plugin_Name.default_filter;
  1001       |> the_default Plugin_Name.default_filter;
  1003     val transfers = replicate actual_nn (exists (can (fn Transfer_Option => ())) opts);
  1004     val transfers = replicate actual_nn (exists (can (fn Transfer_Option => ())) opts);
  1004 
  1005 
  1005     val fun_names = map Binding.name_of bs;
  1006     val fun_names = map Binding.name_of bs;
  1006     val basic_ctr_specss = map (basic_corec_specs_of lthy) res_Ts;
  1007     val basic_ctr_specss = map (basic_corec_specs_of lthy) res_Ts;
  1007     val frees = map (fst #>> Binding.name_of #> Free) fixes;
  1008     val frees = map (fst #>> Binding.name_of #> Free) fixes;
  1008     val has_call = exists_subterm (member (op =) frees);
  1009     val has_call = Term.exists_subterm (member (op =) frees);
  1009     val eqns_data =
  1010     val eqns_data =
  1010       @{fold_map 2} (dissect_coeqn lthy has_call fun_names sequentials basic_ctr_specss)
  1011       @{fold_map 2} (dissect_coeqn lthy has_call fun_names sequentials basic_ctr_specss)
  1011         (tag_list 0 (map snd specs)) of_specs_opt []
  1012         (tag_list 0 (map snd specs)) of_specs_opt []
  1012       |> flat o fst;
  1013       |> flat o fst;
  1013 
  1014 
  1015       let
  1016       let
  1016         val missing = fun_names
  1017         val missing = fun_names
  1017           |> filter (map (fn Disc x => #fun_name x | Sel x => #fun_name x) eqns_data
  1018           |> filter (map (fn Disc x => #fun_name x | Sel x => #fun_name x) eqns_data
  1018             |> not oo member (op =));
  1019             |> not oo member (op =));
  1019       in
  1020       in
  1020         null missing
  1021         null missing orelse error ("Missing equations for " ^ commas missing)
  1021           orelse primcorec_error_eqns ("missing equations for function(s): " ^ commas missing) []
       
  1022       end;
  1022       end;
  1023 
  1023 
  1024     val callssss =
  1024     val callssss =
  1025       map_filter (try (fn Sel x => x)) eqns_data
  1025       map_filter (try (fn Sel x => x)) eqns_data
  1026       |> partition_eq (op = o apply2 #fun_name)
  1026       |> partition_eq (op = o apply2 #fun_name)
  1042       |> map (sort (op < o apply2 #ctr_no |> make_ord) o flat o snd);
  1042       |> map (sort (op < o apply2 #ctr_no |> make_ord) o flat o snd);
  1043 
  1043 
  1044     val _ = disc_eqnss' |> map (fn x =>
  1044     val _ = disc_eqnss' |> map (fn x =>
  1045       let val d = duplicates (op = o apply2 #ctr_no) x in
  1045       let val d = duplicates (op = o apply2 #ctr_no) x in
  1046         null d orelse
  1046         null d orelse
  1047          (if forall (is_some o #ctr_rhs_opt) x then
  1047         (if forall (is_some o #ctr_rhs_opt) x then
  1048             primcorec_error_eqns "multiple equations for constructor(s)"
  1048            error_at lthy
  1049               (maps (fn t => filter (curry (op =) (#ctr_no t) o #ctr_no) x) d
  1049              (maps (fn t => filter (curry (op =) (#ctr_no t) o #ctr_no) x) d
  1050                 |> map (the o #ctr_rhs_opt))
  1050               |> map (the o #ctr_rhs_opt))
  1051           else
  1051              "Multiple equations for same constructor"
  1052             primcorec_error_eqns "excess discriminator formula in definition"
  1052          else
  1053               (maps (fn t => filter (curry (op =) (#ctr_no t) o #ctr_no) x) d |> map #user_eqn))
  1053            error_at lthy
       
  1054              (maps (fn t => filter (curry (op =) (#ctr_no t) o #ctr_no) x) d |> map #user_eqn)
       
  1055              "Excess discriminator formula in definition")
  1054       end);
  1056       end);
  1055 
  1057 
  1056     val sel_eqnss = map_filter (try (fn Sel x => x)) eqns_data
  1058     val sel_eqnss = map_filter (try (fn Sel x => x)) eqns_data
  1057       |> partition_eq (op = o apply2 #fun_name)
  1059       |> partition_eq (op = o apply2 #fun_name)
  1058       |> fst o finds (fn (x, ({fun_name, ...} :: _)) => x = fun_name) fun_names
  1060       |> fst o finds (fn (x, ({fun_name, ...} :: _)) => x = fun_name) fun_names
  1471     val (raw_specs, of_specs_opt) =
  1473     val (raw_specs, of_specs_opt) =
  1472       split_list raw_specs_of ||> map (Option.map (Syntax.read_term lthy));
  1474       split_list raw_specs_of ||> map (Option.map (Syntax.read_term lthy));
  1473     val (fixes, specs) = fst (Specification.read_spec raw_fixes raw_specs lthy);
  1475     val (fixes, specs) = fst (Specification.read_spec raw_fixes raw_specs lthy);
  1474   in
  1476   in
  1475     add_primcorec_ursive auto opts fixes specs of_specs_opt lthy
  1477     add_primcorec_ursive auto opts fixes specs of_specs_opt lthy
  1476     handle ERROR str => primcorec_error str
  1478   end;
  1477   end
       
  1478   handle PRIMCOREC (str, eqns) =>
       
  1479          if null eqns then
       
  1480            error ("primcorec error:\n  " ^ str)
       
  1481          else
       
  1482            error ("primcorec error:\n  " ^ str ^ "\nin\n  " ^
       
  1483              space_implode "\n  " (map (quote o Syntax.string_of_term lthy) eqns));
       
  1484 
  1479 
  1485 val add_primcorecursive_cmd = (fn (goalss, after_qed, lthy) =>
  1480 val add_primcorecursive_cmd = (fn (goalss, after_qed, lthy) =>
  1486   lthy
  1481   lthy
  1487   |> Proof.theorem NONE after_qed goalss
  1482   |> Proof.theorem NONE after_qed goalss
  1488   |> Proof.refine (Method.primitive_text (K I))
  1483   |> Proof.refine (Method.primitive_text (K I))