test/Pure/Isar/Test_Parse_Isac.thy
author Walther Neuper <walther.neuper@jku.at>
Fri, 22 Jan 2021 11:27:47 +0100
changeset 60147 d3cb5af53d3d
parent 60132 2f94484d6637
permissions -rw-r--r--
step 5.2: shift tests from Test_Parse_Isac.thy to test/../parseC.sml
     1 subsection \<open>definitions for keywords\<close>
     2 
     3 theory Test_Parse_Isac
     4 (**)imports Isac.Calculation(**)
     5 keywords
     6   "OLDProblem" (* :: thy_decl  in Calculation.thy; this would break *_WITH_Problem*)
     7 begin
     8 
     9 chapter \<open>Tools and Goals\<close>
    10 section \<open>Tools\<close>
    11 ML \<open>
    12 \<close> ML \<open>
    13 open ParseC
    14 \<close> ML \<open>
    15 fun tokenize str =
    16   filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) Position.none str)
    17 (* here we overwrite Calculation.thy "Problem" :: thy_decl -------^^^^^^^^^^*)
    18 \<close> ML \<open>
    19 fun string_of_strs strs = fold (curry op ^) (rev strs) ""
    20 \<close> ML \<open>
    21 \<close>
    22 
    23 section \<open>Goals for parsing\<close>
    24 subsection \<open>The final goal for parsing from a cartouche\<close>
    25 text \<open>
    26   Running example for development of parsing:
    27   # completely collapsed is
    28     OLDProblem ("Biegelinie", ["vonBelastungZu", "Biegelinien"])
    29   # partially collapsed is
    30     OLDProblem ("Biegelinie", ["setzeRandbedingungen", "Biegelinien"])
    31   # strings are not escaped (like in a cartouche)
    32   as follows:
    33 
    34 OLDProblem ("Biegelinie", ["Biegelinien"])
    35   Specification:
    36     Model :                  
    37       Given: "Traegerlaenge L", "Streckenlast q_0"
    38       Where: "q_0 ist_integrierbar_auf {| 0, L |}", "0 < L"
    39       Find: "Biegelinie y"
    40       Relate: "Randbedingungen [ Q_0 = q_0 * L, M_b L = 0, y_0 = 0, d_d x y_0 = 0 ]"
    41     References:
    42       RTheory: "Biegelinie"
    43       RProblem: ["Biegelinien"]
    44       RMethod: ["Integrieren", "KonstanteBestimmen2"]
    45   Solution:
    46     OLDProblem ("Biegelinie", ["vonBelastungZu", "Biegelinien"])
    47     "[Q x = c + -1 * q_0 * x, M_b x = c_2 + c * x + -1 * q_0 / 2 * x ^ 2,  y' x =  c_3 + 1 / (-1 * EI) * (c_2 * x + c / 2 * x ^ 2 + -1 * q_0 / 6 * x ^ 3),  y x =  c_4 + c_3 * x +  1 / (-1 * EI) *  (c_2 / 2 * x ^ 2 + c / 6 * x ^ 3 + -1 * q_0 / 24 * x ^ 4)]"
    48     OLDProblem ("Biegelinie", ["setzeRandbedingungen", "Biegelinien"])
    49       Specification:
    50       Solution:
    51     "[L * q_0 = c, 0 = (2 * c_2 + 2 * L * c + -1 * L ^ 2 * q_0) / 2, 0 = c_4,  0 = c_3]"
    52 (*
    53     "solveSystem [L * q_0 = c, 0 = (2 * c_2 + 2 * L * c + -1 * L ^ 2 * q_0) / 2, 0 = c_4,  0 = c_3] [c, c_1, c_2, c_4]"
    54     "[c = -1 * L * q_0 / -1, c_2 = -1 * L ^ 2 * q_0 / 2, c_3 = 0, c_4 = 0]"
    55 *)
    56     "y x = c_4 + c_3 * x + 1 / (-1 * EI) * (c_2 / 2 * x ^ 2 + c / 6 * x ^ 3 + -1 * q_0 / 24 * x ^ 4)"
    57       Tactic Substitute "c_4 + c_3 * x +  1 / (-1 * EI) *  (c_2 / 2 * x ^ 2 + c / 6 * x ^ 3 + -1 * q_0 / 24 * x ^ 4)" "[c, c_1, c_2, c_4]"
    58     "y x = 0 + 0 * x + 1 / (-1 * EI) * (-1 * L ^ 2 * q_0 / 2 / 2 * x ^ 2 + -1 * L * q_0 / -1 / 6 * x ^ 3 +  -1 * q_0 / 24 * x ^ 4)"
    59       Tactic Rewrite_Set_Inst "[(bdv, x)]" "make_ratpoly_in"
    60     "y x = -6 * q_0 * L ^ 2 / (-24 * EI) * x ^ 2 + 4 * L * q_0 / (-24 * EI) * x ^ 3 + -1 * q_0 / (-24 * EI) * x ^ 4"
    61       Tactic Check_Postcond ["Biegelinien"]
    62 "y x = -6 * q_0 * L ^ 2 / (-24 * EI) * x ^ 2 + 4 * L * q_0 / (-24 * EI) * x ^ 3 + -1 * q_0 / (-24 * EI) * x ^ 4"
    63 \<close>
    64 subsection \<open>The goal bypassing cartouches, terms shortened\<close>
    65 text \<open>
    66   for further goals see test/../parseC.sml
    67 \<close> ML \<open>
    68 val problem_headline_str_WITH_Problem =
    69   "OLDProblem ( \"Biegelinie\" , [\"Biegelinien\"] )"
    70 \<close> ML \<open>
    71 val problem_headline_str =
    72   "( \"Biegelinie\" , [\"Biegelinien\"] )"
    73 \<close> ML \<open>
    74 val tactic_Substitute_str =
    75         "Tactic Substitute \"c_4 + c_3 * x +  1 / (-1 * EI)\" \"[c, c_1, c_2, c_4]\""
    76 val tactic_Rewrite_Set_Inst_str =
    77         "Tactic Rewrite_Set_Inst \"[(bdv, x)]\" \"make_ratpoly_in\""
    78 val tactic_Check_Postcond_str =
    79         "Tactic Check_Postcond [\"Biegelinien\", \"xxx\"]"
    80 val final_result_str = "\"y x = -6 * q_0 * L ^ 2 / (-24 * EI)\""
    81 \<close> ML \<open>
    82 val steps_nonrec_str = (
    83      "\"[c = -1 * L * q_0 / -1, c_2 = -1 * L ^ 2 * q_0 / 2, c_3 = 0, c_4 = 0]\"" (**) ^
    84      "\"y x = c_4 + c_3 * x + 1 / (-1 * EI)\"" (**) ^
    85         tactic_Substitute_str ^
    86      "\"y x = 0 + 0 * x + 1 / (-1 * EI)\"" (**) ^
    87         tactic_Rewrite_Set_Inst_str ^
    88      final_result_str (**) ^
    89         tactic_Check_Postcond_str ^
    90   final_result_str (**)
    91 )
    92 \<close> ML \<open>
    93 val steps_subproblem_str = (
    94      "OLDProblem (\"Biegelinie\", [\"vonBelastungZu\", \"Biegelinien\"])" ^
    95      "\"[Q x = c + -1 * q_0 * x, M_b x = c_2 + c * x + -1 * q_0 / 2 * x ^ 2\"" ^
    96      "OLDProblem (\"Biegelinie\", [\"setzeRandbedingungen\", \"Biegelinien\"])" ^
    97        "Specification:" ^
    98        "Solution:" ^
    99      "\"[L * q_0 = c, 0 = (2 * c_2 + 2 * L * c + -1 * L ^ 2 * q_0) / 2, 0 = c_4,  0 = c_3]\""  ^
   100 (*
   101     "solveSystem [L * q_0 = c, 0 = (2 * c_2 + 2 * L * c + -1 * L ^ 2 * q_0) / 2, 0 = c_4,  0 = c_3] [c, c_1, c_2, c_4]"
   102     "[c = -1 * L * q_0 / -1, c_2 = -1 * L ^ 2 * q_0 / 2, c_3 = 0, c_4 = 0]"
   103 *)
   104      "\"y x = c_4 + c_3 * x + 1 / (-1 * EI)\"" (**) ^
   105         tactic_Substitute_str ^
   106      "\"y x = 0 + 0 * x + 1 / (-1 * EI)\"" (**) ^
   107         tactic_Rewrite_Set_Inst_str ^
   108      final_result_str (**) ^
   109         tactic_Check_Postcond_str ^
   110   final_result_str (**)
   111 )
   112 \<close> ML \<open>
   113 \<close>
   114 
   115 chapter \<open>OLDProblem\<close>
   116 section \<open>headline\<close>
   117 ML \<open>
   118 \<close> ML \<open>
   119 val toks_WITH_Problem = tokenize problem_headline_str_WITH_Problem
   120 \<close> ML \<open>
   121 \<close> ML \<open>
   122 val problem_headline_WITH_Problem = Parse.$$$ "OLDProblem" --
   123   Parse.$$$ "(" --
   124     Parse.string -- Parse.$$$ "," --
   125       (Parse.$$$ "[" |-- Parse.list1 Parse.string --| Parse.$$$ "]") --
   126   Parse.$$$ ")"
   127 \<close> ML \<open>
   128 case problem_headline_WITH_Problem toks_WITH_Problem of
   129   (((((("OLDProblem", "("), "Biegelinie"), ","), ["Biegelinien"]), ")"), [])
   130   => () | _ => error "parse problem_headline CHANGED"
   131 \<close> ML \<open>
   132 val problem_headline = (*Parse.$$$ "OLDProblem" --*)
   133   Parse.$$$ "(" --
   134     Parse.string -- Parse.$$$ "," --
   135       (Parse.$$$ "[" |-- Parse.list1 Parse.string --| Parse.$$$ "]") --
   136   Parse.$$$ ")"
   137 \<close> ML \<open>
   138 val toks = tokenize problem_headline_str
   139 \<close> ML \<open>
   140 case problem_headline toks of
   141   ((((("(", "Biegelinie"), ","), ["Biegelinien"]), ")"), [])
   142   => () | _ => error "parse problem_headline CHANGED"
   143 \<close> ML \<open>
   144 \<close>
   145 
   146 section \<open>towards recursion\<close>
   147 text \<open>
   148   we make steps close to recursive version according to subsequent subsubsection.
   149 \<close> ML \<open>
   150 val toks_WITH_Problem = tokenize (
   151   problem_headline_str_WITH_Problem ^
   152     "Specification:" ^
   153     "Solution:" ^
   154       steps_nonrec_str
   155 )
   156 \<close> ML \<open>
   157 val steps_nonrec =
   158   Scan.repeat (
   159     ((Parse.term -- (Scan.optional tactic ("", ""))) >> exec_step_term) (** ) ||
   160     (problem >> exec_subproblem) ( **)
   161   )
   162 : Token.T list -> string list * Token.T list
   163 \<close> ML \<open>
   164 steps_nonrec :    string list  parser
   165 (*                repeat ^^^^  ^^^^^^*)
   166 \<close> ML \<open>
   167 \<close>
   168 declare [[ML_print_depth = 999]] (* ..for finding the type of steps_nonrec, exec_step_subproblem *)
   169 ML \<open>
   170 \<close> ML \<open> (* copy <Output> from val problem_nonrec = ..*)
   171 type problem_nonrec_WITH_Problem =
   172   (((((((((string * string) * string) * string) * string list) * string) *
   173          ((string * string) *
   174           (((((((((((((((string * ((string * string) * string)) * string) * string) * string) * string list) * string) * string) * string list) * string) *
   175                 string) * string) * string) * string) * string list) *
   176            ((string * string) * ((((((((string * string) * string) * string) * string) * string list) * string) * string) * string list)))))
   177         * string) * string) * string list)
   178 (*                  ? repeat ^^^^^^^^^^^^*)
   179 \<close> ML \<open>
   180 val empty_problem_nonrec_WITH_Problem = 
   181   ((((((((("", ""), ""), ""), []), ""),
   182          (("", ""),
   183           ((((((((((((((("", (("", ""), "")), ""), ""), ""), []), ""), ""), []), ""),
   184                 ""), ""), ""), ""), []),
   185            (("", ""), (((((((("", ""), ""), ""), ""), []), ""), ""), [])))))
   186        , ""), ""), [])
   187 \<close> ML \<open>
   188 val problem_nonrec_WITH_Problem =
   189   problem_headline_WITH_Problem --
   190     specification --
   191     Parse.$$$ "Solution" -- Parse.$$$ ":" -- steps_nonrec
   192   :
   193   problem_nonrec_WITH_Problem  parser
   194 (*               ^^^^^^^*)
   195 \<close> ML \<open>
   196 ParseC.parse problem_nonrec_WITH_Problem toks_WITH_Problem
   197 \<close> ML \<open>
   198 case ParseC.parse problem_nonrec_WITH_Problem toks_WITH_Problem of ((((((((((
   199   "OLDProblem", "("), "Biegelinie"), ","), ["Biegelinien"]), ")"),
   200         ((
   201     "Specification", ":"),
   202          ((((((((((((((("", (("", ""), "")), ""), ""), ""), []), ""), ""), []), ""), ""), ""), ""), ""), []), (("", ""), (((((((("", ""), ""), ""), ""), []), ""), ""), []))))),
   203     "Solution"), ":"),
   204       [_(*"EXEC IMMEDIATELY step_term: <markup> (, )"*),
   205        _(*"EXEC IMMEDIATELY step_term: <markup> (Tactic, EXEC IMMEDIATELY: Substitute <markup> <markup>)"*),
   206        _(*"EXEC IMMEDIATELY step_term: <markup> (Tactic, EXEC IMMEDIATELY: Rewrite_Set_Inst <markup> make_ratpoly_in)"*),
   207        _(*"EXEC IMMEDIATELY step_term: <markup> (Tactic, EXEC IMMEDIATELY: Check_Postcond xxxBiegelinien)"*),
   208        _(*"EXEC IMMEDIATELY step_term: <markup> (, )"*)]),
   209    [])
   210   => () | _ => error "parse problem_nonrec CHANGED"
   211 \<close> ML \<open>
   212 \<close>
   213 
   214 section \<open>problem_mini\<close>
   215 text \<open>
   216   we make a minimal problem in order to investigate types
   217   of the parser's components
   218 \<close> ML \<open>
   219 val problem_headline_mini = Parse.$$$ "OLDProblem" 
   220 \<close> ML \<open>
   221 val specification_mini = Parse.$$$ "Specification"
   222 \<close> ML \<open>
   223 fun exec_name str = "EXEC IMMEDIATELY exec_name: " ^ str;
   224 fun exec_int int = "EXEC IMMEDIATELY exec_int: " ^ string_of_int int;
   225 \<close> ML \<open>
   226 Parse.name >> exec_name: string parser;
   227 Parse.int >> exec_int  : string parser
   228 \<close> ML \<open>
   229 \<close> ML \<open> (* nonrecursive *)
   230 fun problem_mini toks =
   231   problem_headline_mini --
   232   specification_mini -- steps_mini toks
   233 and steps_mini _ =
   234   Parse.name >> exec_name || Parse.int >> exec_int
   235 \<close> ML \<open>
   236 (problem_mini "") (tokenize "OLDProblem Specification aaa")
   237 (* = ((("OLDProblem", "Specification"), "EXEC IMMEDIATELY exec_name: aaa"), [])*)
   238 \<close> ML \<open>
   239 problem_mini: 'a -> Token.T list -> ((string * string) * string) * Token.T list
   240 \<close> ML \<open>
   241 fun exec_problem_mini ((headl, spec), steps) =
   242   ["EXEC IMMEDIATELY exec_int: ((" ^ headl ^ ", " ^ spec ^ "0, " ^ string_of_strs steps];
   243 \<close> ML \<open>
   244 fun exec_name str = ["EXEC IMMEDIATELY exec_name: " ^ str];
   245 \<close> ML \<open>
   246 \<close> ML \<open> (* recursive *)
   247 fun problem_mini toks =
   248   (problem_headline_mini --
   249   specification_mini -- steps_mini) toks
   250 and steps_mini toks =
   251   (Parse.name >> exec_name || problem_mini >> exec_problem_mini) toks
   252 \<close> ML \<open>
   253 problem_mini: Token.T list -> ((string * string) * string list) * Token.T list ;
   254 steps_mini  : Token.T list -> string list * Token.T list ;
   255 \<close> ML \<open>
   256 problem_mini: ((string * string) * string list) parser;
   257 steps_mini  : string list parser;
   258 \<close> ML \<open>
   259 problem_mini (tokenize "OLDProblem Specification aaa")
   260 (* = ((("OLDProblem", "Specification"), ["EXEC IMMEDIATELY exec_name: aaa"]), [])*)
   261 \<close> text \<open> ERROR ?!?!?
   262 parse problem_mini (tokenize "OLDProblem Specification   OLDProblem Specification")
   263 exception FAIL (SOME fn) raised (line 152 of "General/scan.ML")
   264 \<close> ML \<open>
   265 \<close> ML \<open>
   266 \<close> ML \<open> (* recursive + repeat *)
   267 fun exec_name str = "EXEC IMMEDIATELY exec_name: " ^ str;
   268 \<close> ML \<open>
   269 fun exec_problem_mini ((headl, spec), steps) =
   270   "EXEC IMMEDIATELY exec_int: ((" ^ headl ^ ", " ^ spec ^ "0, " ^ string_of_strs steps;
   271 \<close> ML \<open>
   272 fun problem_mini toks =
   273   (writeln ("problem_mini " ^ string_of_toks toks); problem_headline_mini --
   274   specification_mini -- (writeln ("steps_mini   " ^ string_of_toks toks); steps_mini)) toks
   275 and steps_mini toks =
   276   (Scan.repeat
   277     ((writeln ("name         " ^ string_of_toks toks); Parse.name >> exec_name) (**)||
   278      (writeln ("sub-problem  " ^ string_of_toks toks); problem_mini >> exec_problem_mini)(**))) toks
   279 \<close> text \<open>
   280 problem_mini (tokenize "OLDProblem Specification")
   281 (*
   282 problem_mini OLDProblem, Specification 
   283 steps_mini   OLDProblem, Specification 
   284 name          
   285 sub-problem   
   286 exception MORE () raised (line 159 of "General/scan.ML")*)
   287 \<close> text \<open>
   288 problem_mini (tokenize "OLDProblem Specification aaa")
   289 (*
   290 problem_mini OLDProblem, Specification, aaa 
   291 steps_mini   OLDProblem, Specification, aaa 
   292 name         aaa 
   293 sub-problem  aaa 
   294 exception MORE () raised (line 159 of "General/scan.ML")
   295 
   296 NOTE: this error persists in case outcommented (** )||
   297      (writeln ("sub-problem  " ^ string_of_toks toks); problem_mini >> exec_problem_mini)( **)
   298 
   299 THUS THERE IS SOMETHING NOT UNDERSTOOD, see !2! arguments in Test_Parsers_Cookbook.thy
   300 see Test_Parsers_Cookbook (* recursive parser p.140 \<section>6.1 adapted to tokens: *)
   301 *)
   302 \<close> ML \<open>
   303 \<close>
   304 
   305 section \<open>trials on recursion stopped\<close>
   306 text \<open>
   307   Trials on recursion have been postponed after parsing from cartouche
   308   (or even ?after executing Specification?).
   309   
   310 \<close> ML \<open>
   311 val toks_WITH_Problem = tokenize (
   312   problem_headline_str_WITH_Problem ^
   313     "Specification:" ^
   314     "Solution:" ^
   315       steps_subproblem_str
   316 )
   317 \<close> ML \<open> (* finding the type of exec_subproblem: *)
   318 fun problem_WITH_Problem xxx =
   319   problem_headline_WITH_Problem --
   320     specification --
   321     Parse.$$$ "Solution" -- Parse.$$$ ":" -- (steps xxx)
   322 and steps xxx =
   323       Scan.repeat (
   324         (Parse.term -- (Scan.optional tactic ("", "")) >> exec_step_term) ||
   325         (problem_WITH_Problem xxx >> xxx)
   326       )
   327 \<close> ML \<open>
   328 problem_WITH_Problem
   329 \<close> ML \<open> (* copy from <Output> abstracted ..*)
   330 problem_WITH_Problem : (problem_nonrec_WITH_Problem -> string) -> Token.T list -> problem_nonrec_WITH_Problem * Token.T list;
   331 (*  xxx : ^^^^^^^^^^^^^^^^^^^^^^^^^^                   vvvvvv*)
   332 problem_WITH_Problem : (problem_nonrec_WITH_Problem -> string) -> problem_nonrec_WITH_Problem parser
   333 \<close> ML \<open> (* compose arguments from empty_problem_nonrec ..*)
   334 fun subproblem_msg_WITH_Problem
   335   (((((((((s1, s2), s3), s4), strs0), s5),
   336          ((s6, s7),
   337           (((((((((((((((s11, ((s12, s13), s14)), s15), s16), s17), strs18), s19), s20), strs21), s22),
   338                 s23), s24), s25), s26), strs27),
   339            ((s31, s32), ((((((((s30, s33), s34), s35), s36), strs37), s38), s39), strs40)))))
   340        , s51), s61), strs71) = "EXEC IMMEDIATELY step_subproblem:\n" ^
   341 "  (((((((((" ^ s1 ^ ", " ^ s2 ^ "), " ^ s3 ^ "), " ^ s4 ^ "), " ^ string_of_strs strs0 ^ "), " ^ s5 ^ "),\n" ^
   342 "         ((" ^ s6 ^ ", " ^ s7 ^ "),\n" ^
   343 "          (((((((((((((((" ^ s11 ^ ", ((" ^ s12 ^ ", " ^ s13 ^ "), " ^ s14 ^ ")), " ^ s15 ^ "), " ^ s16 ^ "), " ^ s17 ^ "), " ^ string_of_strs strs18 ^ "), " ^ s19 ^ "), " ^ s20 ^ "), " ^ string_of_strs strs21 ^ "), " ^ s22 ^ "),\n" ^
   344                  s23 ^ "), " ^ s24 ^ "), " ^ s25 ^ "), " ^ s26 ^ "), " ^ string_of_strs strs27 ^ "),\n" ^
   345 "           ((" ^ s31 ^ ", " ^ s32 ^ "), ((((((((" ^ s30 ^ ", " ^ s33 ^ "), " ^ s34 ^ "), " ^ s35 ^ "), " ^ s36 ^ "), " ^ string_of_strs strs37 ^ "), " ^ s38 ^ "), " ^ s39 ^ "), " ^ string_of_strs strs40 ^ ")))))\n" ^
   346 "       , " ^ s51 ^ "), " ^ s61 ^ "), " ^ string_of_strs strs71 ^ ")"
   347 \<close> ML \<open>
   348 fun exec_subproblem_WITH_Problem
   349   (((((((((s1, s2), s3), s4), strs0), s5),
   350          ((s6, s7),
   351           (((((((((((((((s11, ((s12, s13), s14)), s15), s16), s17), strs18), s19), s20), strs21), s22),
   352                 s23), s24), s25), s26), strs27),
   353            ((s31, s32), ((((((((s30, s33), s34), s35), s36), strs37), s38), s39), strs40)))))
   354        , s51), s61), strs71) = subproblem_msg_WITH_Problem
   355   (((((((((s1, s2), s3), s4), strs0), s5),
   356          ((s6, s7),
   357           (((((((((((((((s11, ((s12, s13), s14)), s15), s16), s17), strs18), s19), s20), strs21), s22),
   358                 s23), s24), s25), s26), strs27),
   359            ((s31, s32), ((((((((s30, s33), s34), s35), s36), strs37), s38), s39), strs40)))))
   360        , s51), s61), strs71)
   361 \<close> ML \<open>
   362 exec_subproblem_WITH_Problem : problem_nonrec_WITH_Problem -> string
   363 \<close> ML \<open>
   364 \<close> ML \<open> (* exec_subproblem has right? type in problem *)
   365 fun problem_WITH_Problem _ =
   366   problem_headline_WITH_Problem --
   367     specification --
   368     Parse.$$$ "Solution" -- Parse.$$$ ":" -- (steps 1)
   369 and steps _ =
   370       Scan.repeat (
   371         (
   372           ((Parse.term -- (Scan.optional tactic ("", ""))) >> exec_step_term) ||
   373           (problem_WITH_Problem 1 >> exec_subproblem_WITH_Problem)
   374     (*                  ^^^^^^^^^^^^^^^*)
   375         )
   376       )
   377 \<close> ML \<open>
   378 problem_WITH_Problem : int -> problem_nonrec_WITH_Problem parser
   379 \<close> ML \<open>
   380 steps   : int -> string list    parser
   381 \<close> ML \<open> (* BUT: omitting the parsers' arguments does NOT work:
   382 
   383  *)
   384 \<close> ML \<open> (* here is the first attempt towards finalisation:
   385   THIS REQUIRES 1-2 ARGUMENTS, see Test_Parsers_Cookbook subsection "recursive parsers" * )
   386 
   387 val problem =
   388   problem_headline --
   389     specification --
   390     Parse.$$$ "Solution" -- Parse.$$$ ":" -- steps
   391 and steps =
   392       Scan.repeat (
   393         (
   394           ((Parse.term -- (Scan.optional tactic ("", ""))) >> exec_step_term) ||
   395           (problem >> exec_subproblem)
   396         )
   397       )
   398 *)
   399 \<close> ML \<open>
   400 \<close>
   401 
   402 end