(************************************************************************* * Copyright (C) * 2019 The University of Exeter * 2018-2019 The University of Paris-Saclay * 2018 The University of Sheffield * * License: * This program can be redistributed and/or modified under the terms * of the 2-clause BSD-style license. * * SPDX-License-Identifier: BSD-2-Clause *************************************************************************) chapter \The Document Ontology Common Library for the Isabelle Ontology Framework\ text\ Building a fundamental infrastructure for common document elements such as Structuring Text-Elements (the top classes), Figures, (Tables yet todo) The COL provides a number of ontological "macros" like "section*" which automatically set a number of class-attributes in particular ways without user-interference. \ theory Isa_COL imports Isa_DOF keywords "title*" "subtitle*" "chapter*" "section*" "subsection*" "subsubsection*" "paragraph*" "subparagraph*" :: document_body and "figure*" "side_by_side_figure*" :: document_body and "assert*" :: thy_decl and "define_shortcut*" :: thy_decl begin section\Basic Text and Text-Structuring Elements\ text\ The attribute @{term "level"} in the subsequent enables doc-notation support section* etc. we follow LaTeX terminology on levels \<^enum> part = Some -1 \<^enum> chapter = Some 0 \<^enum> section = Some 1 \<^enum> subsection = Some 2 \<^enum> subsubsection = Some 3 \<^enum> ... for scholarly paper: invariant level > 0. \ doc_class text_element = level :: "int option" <= "None" referentiable :: bool <= "False" variants :: "String.literal set" <= "{STR ''outline'', STR ''document''}" doc_class "chapter" = text_element + level :: "int option" <= "Some 0" doc_class "section" = text_element + level :: "int option" <= "Some 1" doc_class "subsection" = text_element + level :: "int option" <= "Some 2" doc_class "subsubsection" = text_element + level :: "int option" <= "Some 3" subsection\Ontological Macros\ ML\ structure Onto_Macros = struct local open ODL_Command_Parser in (* *********************************************************************** *) (* Ontological Macro Command Support *) (* *********************************************************************** *) (* {markdown = true} sets the parsing process such that in the text-core markdown elements are accepted. *) fun enriched_text_element_cmd level = let fun transform doc_attrs = case level of NONE => doc_attrs | SOME(NONE) => (("level",@{here}),"None")::doc_attrs | SOME(SOME x) => (("level",@{here}),"Some("^ Int.toString x ^"::int)")::doc_attrs in gen_enriched_document_cmd {inline=true} I transform end; (* val enriched_document_command_macro = let fun transform_cid X = (writeln (@{make_string} X); X) in gen_enriched_document_command {inline=true} transform_cid I end; *) local fun transform_cid thy NONE X = X |transform_cid thy (SOME ncid) NONE = (SOME(ncid,@{here})) |transform_cid thy (SOME cid) (SOME (sub_cid,pos)) = let val cid_long = DOF_core.read_cid_global thy cid val sub_cid_long = DOF_core.read_cid_global thy sub_cid in if DOF_core.is_subclass_global thy sub_cid_long cid_long then (SOME (sub_cid,pos)) else (* (SOME (sub_cid,pos)) *) (* BUG : check reveals problem of Definition* misuse. *) error("class "^sub_cid_long^ " must be sub-class of "^cid_long) end in fun enriched_formal_statement_command ncid (S: (string * string) list) = let fun transform_attr doc_attrs = (map (fn(cat,tag) => ((cat,@{here}),tag)) S) @ (("formal_results",@{here}),"([]::thm list)")::doc_attrs in fn md => fn margs => fn thy => gen_enriched_document_cmd {inline=true} (transform_cid thy ncid) transform_attr md margs thy end; fun enriched_document_cmd_exp ncid (S: (string * string) list) = (* expands ncid into supertype-check. *) let fun transform_attr attrs = (map (fn(cat,tag) => ((cat,@{here}),tag)) S) @ attrs in fn md => fn margs => fn thy => gen_enriched_document_cmd {inline=true} (transform_cid thy ncid) transform_attr md margs thy end; end (* local *) fun assertion_cmd'((((((oid,pos),cid_pos),doc_attrs),name_opt:string option),modes : string list), prop) = let fun conv_2_holstring thy = (bstring_to_holstring (Proof_Context.init_global thy)) fun conv_attrs thy = (("properties",pos),"[@{termrepr ''"^conv_2_holstring thy prop ^" ''}]") ::doc_attrs fun conv_attrs' thy = map (fn ((lhs,pos),rhs) => (((lhs,pos),"+="),rhs)) (conv_attrs thy) fun mks thy = case DOF_core.get_object_global_opt oid thy of SOME NONE => (error("update of declared but not created doc_item:" ^ oid)) | SOME _ => (update_instance_command (((oid,pos),cid_pos),conv_attrs' thy) thy) | NONE => (create_and_check_docitem {is_monitor = false} {is_inline = false} oid pos cid_pos (conv_attrs thy) thy) val check = (assert_cmd name_opt modes prop) o Proof_Context.init_global in (* Toplevel.keep (check o Toplevel.context_of) *) Toplevel.theory (fn thy => (check thy; mks thy)) end val _ = Outer_Syntax.command @{command_keyword "assert*"} "evaluate and print term" (attributes -- opt_evaluator -- opt_modes -- Parse.term >> assertion_cmd'); val _ = Outer_Syntax.command ("title*", @{here}) "section heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd NONE {markdown = false} ))) ; val _ = Outer_Syntax.command ("subtitle*", @{here}) "section heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd NONE {markdown = false} ))); val _ = Outer_Syntax.command ("chapter*", @{here}) "section heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd (SOME(SOME 0)) {markdown = false} ))); val _ = Outer_Syntax.command ("section*", @{here}) "section heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd (SOME(SOME 1)) {markdown = false} ))); val _ = Outer_Syntax.command ("subsection*", @{here}) "subsection heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd (SOME(SOME 2)) {markdown = false} ))); val _ = Outer_Syntax.command ("subsubsection*", @{here}) "subsubsection heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd (SOME(SOME 3)) {markdown = false} ))); val _ = Outer_Syntax.command ("paragraph*", @{here}) "paragraph heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd (SOME(SOME 4)) {markdown = false} ))); val _ = Outer_Syntax.command ("subparagraph*", @{here}) "subparagraph heading" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (enriched_text_element_cmd (SOME(SOME 5)) {markdown = false} ))); end end \ section\ Library of Standard Text Ontology \ datatype placement = pl_h | (*here*) pl_t | (*top*) pl_b | (*bottom*) pl_ht | (*here -> top*) pl_hb (*here -> bottom*) ML\(Symtab.defined (#docclass_tab(DOF_core.get_data_global @{theory}))) "side_by_side_figure"\ print_doc_classes doc_class figure = relative_width :: "int" (* percent of textwidth *) src :: "string" placement :: placement spawn_columns :: bool <= True doc_class side_by_side_figure = figure + anchor :: "string" caption :: "string" relative_width2 :: "int" (* percent of textwidth *) src2 :: "string" anchor2 :: "string" caption2 :: "string" print_doc_classes doc_class figure_group = (* trace :: "doc_class rexp list" <= "[]" automatically generated since monitor clause *) caption :: "string" rejects figure_group (* this forbids recursive figure-groups not supported by the current LaTeX style-file. *) accepts "\figure\\<^sup>+" print_doc_classes subsection\Ontological Macros\ ML\ local open ODL_Command_Parser in (* *********************************************************************** *) (* Ontological Macro Command Support *) (* *********************************************************************** *) val _ = Outer_Syntax.command ("figure*", @{here}) "figure" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (Onto_Macros.enriched_text_element_cmd NONE {markdown = false} ))); val _ = Outer_Syntax.command ("side_by_side_figure*", @{here}) "multiple figures" (attributes -- Parse.opt_target -- Parse.document_source --| semi >> (Toplevel.theory o (Onto_Macros.enriched_text_element_cmd NONE {markdown = false} ))); end \ section\Shortcuts, Macros, Environments\ text\The features described in this section are actually \<^emph>\not\ real ISADOF features, rather a slightly more abstract layer over somewhat buried standard features of the Isabelle document generator ... (Thanks to Makarius) Conceptually, they are \<^emph>\sub-text-elements\. \ text\This module provides mechanisms to define front-end checked: \<^enum> \<^emph>\shortcuts\, i.e. machine-checked abbreviations without arguments that were mapped to user-defined LaTeX code (Example: \<^verbatim>\\ie\) \<^enum> \<^emph>\macros\ with one argument that were mapped to user-defined code. Example: \<^verbatim>\\myurl{bla}\. The argument can be potentially checked and reports can be sent to PIDE; if no such checking is desired, this can be expressed by setting the \<^theory_text>\reportNtest\-parameter to \<^theory_text>\K(K())\. \<^enum> \<^emph>\macros\ with two arguments, potentially independently checked. See above. Example: \<^verbatim>\\myurl[ding]{dong}\, \<^enum> \<^emph>\boxes\ which are more complex sub-text-elements in the line of the \<^verbatim>\verbatim\ or \<^verbatim>\theory_text\ environments. Note that we deliberately refrained from a code-template definition mechanism for simplicity, so the patterns were just described by strings. No additional ado with quoting/unquoting mechanisms ... \ ML\ structure DOF_lib = struct fun define_shortcut name latexshcut = Thy_Output.antiquotation_raw name (Scan.succeed ()) (fn _ => fn () => Latex.string latexshcut) (* This is a generalization of the Isabelle2020 function "control_antiquotation" from document_antiquotations.ML. (Thanks Makarius!) *) fun define_macro name s1 s2 reportNtest = Thy_Output.antiquotation_raw_embedded name (Scan.lift Args.cartouche_input) (fn ctxt => fn src => let val () = reportNtest ctxt src in src |> Latex.enclose_block s1 s2 o Thy_Output.output_document ctxt {markdown = false} end); local (* hide away really strange local construction *) fun enclose_body2 front body1 middle body2 post = (if front = "" then [] else [Latex.string front]) @ body1 @ (if middle = "" then [] else [Latex.string middle]) @ body2 @ (if post = "" then [] else [Latex.string post]); in fun define_macro2 name front middle post reportNtest1 reportNtest2 = Thy_Output.antiquotation_raw_embedded name (Scan.lift ( Args.cartouche_input -- Args.cartouche_input)) (fn ctxt => fn (src1,src2) => let val () = reportNtest1 ctxt src1 val () = reportNtest2 ctxt src2 val T1 = Thy_Output.output_document ctxt {markdown = false} src1 val T2 = Thy_Output.output_document ctxt {markdown = false} src2 in Latex.block(enclose_body2 front T1 middle T2 post) end); end fun report_text ctxt text = let val pos = Input.pos_of text in Context_Position.reports ctxt [(pos, Markup.language_text (Input.is_delimited text)), (pos, Markup.raw_text)] end; fun report_theory_text ctxt text = let val keywords = Thy_Header.get_keywords' ctxt; val _ = report_text ctxt text; val _ = Input.source_explode text |> Token.tokenize keywords {strict = true} |> maps (Token.reports keywords) |> Context_Position.reports_text ctxt; in () end fun prepare_text ctxt = Input.source_content #> #1 #> Document_Antiquotation.prepare_lines ctxt; (* This also produces indent-expansion and changes space to "\_" and the introduction of "\newline", I believe. Otherwise its in Thy_Output.output_source, the compiler from string to LaTeX.text. *) fun string_2_text_antiquotation ctxt text = prepare_text ctxt text |> Thy_Output.output_source ctxt |> Thy_Output.isabelle ctxt fun string_2_theory_text_antiquotation ctxt text = let val keywords = Thy_Header.get_keywords' ctxt; in prepare_text ctxt text |> Token.explode0 keywords |> maps (Thy_Output.output_token ctxt) |> Thy_Output.isabelle ctxt end fun gen_text_antiquotation name reportNcheck compile = Thy_Output.antiquotation_raw_embedded name (Scan.lift Args.text_input) (fn ctxt => fn text:Input.source => let val _ = reportNcheck ctxt text; in compile ctxt text end); fun std_text_antiquotation name (* redefined in these more abstract terms *) = gen_text_antiquotation name report_text string_2_text_antiquotation (* should be the same as (2020): fun text_antiquotation name = Thy_Output.antiquotation_raw_embedded name (Scan.lift Args.text_input) (fn ctxt => fn text => let val _ = report_text ctxt text; in prepare_text ctxt text |> Thy_Output.output_source ctxt |> Thy_Output.isabelle ctxt end); *) fun std_theory_text_antiquotation name (* redefined in these more abstract terms *) = gen_text_antiquotation name report_theory_text string_2_theory_text_antiquotation (* should be the same as (2020): fun theory_text_antiquotation name = Thy_Output.antiquotation_raw_embedded name (Scan.lift Args.text_input) (fn ctxt => fn text => let val keywords = Thy_Header.get_keywords' ctxt; val _ = report_text ctxt text; val _ = Input.source_explode text |> Token.tokenize keywords {strict = true} |> maps (Token.reports keywords) |> Context_Position.reports_text ctxt; in prepare_text ctxt text |> Token.explode0 keywords |> maps (Thy_Output.output_token ctxt) |> Thy_Output.isabelle ctxt |> enclose_env ctxt "isarbox" end); *) fun environment_delim name = ("%\n\\begin{" ^ Latex.output_name name ^ "}\n", "\n\\end{" ^ Latex.output_name name ^ "}"); fun environment_block name = environment_delim name |-> Latex.enclose_body #> Latex.block; fun enclose_env verbatim ctxt block_env body = if Config.get ctxt Document_Antiquotation.thy_output_display then if verbatim then environment_block block_env [body] else Latex.environment_block block_env [body] else Latex.block ([Latex.string ("\\inline"^block_env ^"{")] @ [body] @ [ Latex.string ("}")]); end \ ML\ local val parse_define_shortcut = Parse.binding -- ((\<^keyword>\\\ || \<^keyword>\==\) |-- (Parse.alt_string || Parse.cartouche)) val define_shortcuts = fold(uncurry DOF_lib.define_shortcut) in val _ = Outer_Syntax.command \<^command_keyword>\define_shortcut*\ "define LaTeX shortcut" (Scan.repeat1 parse_define_shortcut >> (Toplevel.theory o define_shortcuts)); end \ section\Tables\ (* TODO ! ! ! *) (* dito the future monitor: table - block *) section\Tests\ ML\@{term "side_by_side_figure"}; @{typ "doc_class rexp"}; DOF_core.SPY;\ end