1691 |
1691 |
1692 thm foo_def |
1692 thm foo_def |
1693 *) |
1693 *) |
1694 (*>*) |
1694 (*>*) |
1695 |
1695 |
1696 section {* Pretty-Printing *} |
1696 section {* Pretty-Printing\label{sec:pretty} *} |
1697 |
1697 |
1698 text {* |
1698 text {* |
1699 Isabelle has a sophisticated infrastructure for pretty-printing text |
1699 So far we printed out only plain strings without any formatting except for |
1700 involving for example long formulae. This infrastructure is loosely based |
1700 occasional explicit line breaks using @{text [quotes] "\\n"}. This is |
1701 on a paper by Oppen~\cite{Oppen80}. Most of its functions do not operate |
1701 sufficient for ``quick-and-dirty'' printouts. For something more |
1702 directly on @{ML_type string}s, but on instances of the type: |
1702 sophisticated, Isabelle includes an infrastructure for formatting text. |
|
1703 This infrastructure is quite useful for printing for example large formulae. |
|
1704 It is loosely based on a paper by Oppen~\cite{Oppen80}. Most of |
|
1705 its functions do not operate on @{ML_type string}s, but on instances of the |
|
1706 type: |
1703 |
1707 |
1704 @{ML_type [display, gray] "Pretty.T"} |
1708 @{ML_type [display, gray] "Pretty.T"} |
1705 |
1709 |
1706 The function @{ML "Pretty.str"} transforms a string into such a pretty |
1710 The function @{ML "Pretty.str"} transforms a (plain) string into such a pretty |
1707 type. For example |
1711 type. For example |
1708 |
1712 |
1709 @{ML_response_fake [display,gray] |
1713 @{ML_response_fake [display,gray] |
1710 "Pretty.str \"test\"" "String (\"test\", 4)"} |
1714 "Pretty.str \"test\"" "String (\"test\", 4)"} |
1711 |
1715 |
1712 where the result indicates that we transformed a string with length 4. |
1716 where the result indicates that we transformed a string with length 4. Once |
1713 Once you have a pretty type, you can, for example, control where line breaks |
1717 you have a pretty type, you can, for example, control where line breaks can |
1714 can occur and with how much indentation a text should be printed. |
1718 occur in case the text wraps over a line, or with how much indentation a |
1715 However, if you want to actually output the formatted text, you have to |
1719 text should be printed. However, if you want to actually output the |
1716 transform the pretty type back into a @{ML_type string}. This can be done |
1720 formatted text, you have to transform the pretty type back into a @{ML_type |
1717 with the function @{ML Pretty.string_of}. In what follows we will use |
1721 string}. This can be done with the function @{ML Pretty.string_of}. In what |
1718 the following wrapper function for printing a pretty type: |
1722 follows we will use the following wrapper function for printing a pretty |
|
1723 type: |
1719 *} |
1724 *} |
1720 |
1725 |
1721 ML{*fun pprint prt = writeln (Pretty.string_of prt)*} |
1726 ML{*fun pprint prt = writeln (Pretty.string_of prt)*} |
1722 |
1727 |
1723 text {* |
1728 text {* |
1724 The point of the pretty-printing infrastructure is to aid the layout |
1729 The point of the pretty-printing infrastructure is to give hints how to |
1725 of text. Let us first explain how you can insert places where a |
1730 layout text and let the system do the actual layout. Let us first explain |
1726 line break can occur. For this assume the following function that |
1731 how you can insert places where a line break can occur. For this assume the |
1727 replicates a string n times: |
1732 following function that replicates a string n times: |
1728 *} |
1733 *} |
1729 |
1734 |
1730 ML{*fun rep n str = implode (replicate n str) *} |
1735 ML{*fun rep n str = implode (replicate n str) *} |
1731 |
1736 |
1732 text {* |
1737 text {* |
1734 *} |
1739 *} |
1735 |
1740 |
1736 ML{*val test_str = rep 8 "fooooooooooooooobaaaaaaaaaaaar "*} |
1741 ML{*val test_str = rep 8 "fooooooooooooooobaaaaaaaaaaaar "*} |
1737 |
1742 |
1738 text {* |
1743 text {* |
1739 If we use for printing the usial ``quick-and-dirty'' method |
1744 Since @{ML test_str} is a string spanning over more than one line, we |
|
1745 obtain the ugly |
1740 |
1746 |
1741 @{ML_response_fake [display,gray] |
1747 @{ML_response_fake [display,gray] |
1742 "writeln test_str" |
1748 "writeln test_str" |
1743 "fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar foooooooooo |
1749 "fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar foooooooooo |
1744 ooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaa |
1750 ooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaa |
1745 aaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fo |
1751 aaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fo |
1746 oooooooooooooobaaaaaaaaaaaar"} |
1752 oooooooooooooobaaaaaaaaaaaar"} |
1747 |
1753 |
1748 we obtain an ugly layout. Similarly if we just use |
1754 if we print the string by the usual ``quick-and-dirty'' method. |
|
1755 We obtain the same if we just use |
1749 |
1756 |
1750 @{ML_response_fake [display,gray] |
1757 @{ML_response_fake [display,gray] |
1751 "pprint (Pretty.str test_str)" |
1758 "pprint (Pretty.str test_str)" |
1752 "fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar foooooooooo |
1759 "fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar foooooooooo |
1753 ooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaa |
1760 ooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaa |
1754 aaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fo |
1761 aaaaaaar fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar fo |
1755 oooooooooooooobaaaaaaaaaaaar"} |
1762 oooooooooooooobaaaaaaaaaaaar"} |
1756 |
1763 |
1757 *} |
1764 However by using pretty types you have the ability to indicate a possible |
1758 |
1765 line break for example at each space. You can achieve this with the function |
1759 text {* |
1766 @{ML Pretty.breaks}, which expects a list of pretty types and inserts a |
1760 We obtain better results if we indicate a possible line break at every |
1767 possible line break in between every two elements in this list. To print |
1761 spece. You can achieve this with the function @{ML Pretty.breaks}, which |
1768 this this list of pretty types as a single string, we concatenate them |
1762 expects a list of pretty types and inserts a possible place for a |
1769 with the function @{ML Pretty.blk} as follows |
1763 line break in between every two elements in this list. To print this |
1770 |
1764 this list of pretty types we concatenate them with the function @{ML |
|
1765 Pretty.blk} as follows |
|
1766 |
1771 |
1767 @{ML_response_fake [display,gray] |
1772 @{ML_response_fake [display,gray] |
1768 "let |
1773 "let |
1769 val ptrs = map Pretty.str (space_explode \" \" test_str) |
1774 val ptrs = map Pretty.str (space_explode \" \" test_str) |
1770 in |
1775 in |
1803 " fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar |
1808 " fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar |
1804 fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar |
1809 fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar |
1805 fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar |
1810 fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar |
1806 fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar"} |
1811 fooooooooooooooobaaaaaaaaaaaar fooooooooooooooobaaaaaaaaaaaar"} |
1807 |
1812 |
1808 *} |
1813 If you want to print out a list of items separated by commas and |
1809 |
1814 have the line breaks handled properly, you can use the function |
1810 |
1815 @{ML Pretty.commas}. For example |
1811 ML{*fun p_strs str = Pretty.breaks (map Pretty.str (space_explode " " str)) *} |
1816 |
1812 |
1817 @{ML_response_fake [display,gray] |
1813 ML{*pprint (Pretty.blk (0, Pretty.commas (map (Pretty.str o string_of_int) (1 upto 20))))*} |
1818 "let |
1814 |
1819 val ptrs = map (Pretty.str o string_of_int) (99998 upto 100020) |
1815 ML{*fun and_list [] = [] |
1820 in |
|
1821 pprint (Pretty.blk (0, Pretty.commas ptrs)) |
|
1822 end" |
|
1823 "99998, 99999, 100000, 100001, 100002, 100003, 100004, 100005, 100006, |
|
1824 100007, 100008, 100009, 100010, 100011, 100012, 100013, 100014, 100015, |
|
1825 100016, 100017, 100018, 100019, 100020"} |
|
1826 |
|
1827 where @{ML upto} generates a list of integers. You can print this |
|
1828 list out as an ``set'' enclosed inside @{text [quotes] "{"} and |
|
1829 @{text [quotes] "}"}, and separated by commas by using the function |
|
1830 @{ML Pretty.enum}. For example |
|
1831 *} |
|
1832 |
|
1833 text {* |
|
1834 |
|
1835 @{ML_response_fake [display,gray] |
|
1836 "let |
|
1837 val ptrs = map (Pretty.str o string_of_int) (99998 upto 100020) |
|
1838 in |
|
1839 pprint (Pretty.enum \",\" \"{\" \"}\" ptrs) |
|
1840 end" |
|
1841 "{99998, 99999, 100000, 100001, 100002, 100003, 100004, 100005, 100006, |
|
1842 100007, 100008, 100009, 100010, 100011, 100012, 100013, 100014, 100015, |
|
1843 100016, 100017, 100018, 100019, 100020}"} |
|
1844 |
|
1845 As can be seen, this function prints out the ``set'' so that starting |
|
1846 from the second each new line as an indentation of 2. |
|
1847 |
|
1848 If you print something out which goes beyond the capabilities of the |
|
1849 standard function, you can do the formating yourself. Assume you want |
|
1850 to print out a list of items where like in English the last two items |
|
1851 are separated by @{text [quotes] "and"}. For this you can write the |
|
1852 function |
|
1853 *} |
|
1854 |
|
1855 ML %linenosgray{*fun and_list [] = [] |
1816 | and_list [x] = [x] |
1856 | and_list [x] = [x] |
1817 | and_list xs = |
1857 | and_list xs = |
1818 let |
1858 let |
1819 val (front, last) = split_last xs |
1859 val (front, last) = split_last xs |
1820 in |
1860 in |
1821 (Pretty.commas front) @ [Pretty.brk 1, Pretty.str "and", Pretty.brk 1, last] |
1861 (Pretty.commas front) @ |
|
1862 [Pretty.brk 1, Pretty.str "and", Pretty.brk 1, last] |
1822 end *} |
1863 end *} |
1823 |
1864 |
1824 ML{* pprint (Pretty.block (and_list (map (Pretty.str o string_of_int) (1 upto 20)))) *} |
1865 text {* |
1825 |
1866 where in Line 7 we print the beginning of the list and in Line |
1826 ML{* Pretty.enum "l" "r" "sep" *} |
1867 8 the last item. We have to use @{ML "Pretty.brk 1"} in order |
1827 |
1868 to insert a space (of length 1) before the @{text [quotes] "and"} |
1828 text {* chunks = one ptr above the other*} |
1869 where we also want that a line break can occur. We do the |
1829 |
1870 same ater the @{text [quotes] "and"}. This gives you |
1830 ML{* pprint (Pretty.chunks ([(Pretty.str (rep 3 "foo "))] @ [(Pretty.str (rep 4 "bar "))])) *} |
1871 |
1831 |
1872 @{ML_response_fake [display,gray] |
1832 ML {* pprint (Pretty.str "foo"); pprint (Pretty.str "bar") *} |
1873 "let |
1833 |
1874 val ptrs = map (Pretty.str o string_of_int) (1 upto 22) |
1834 |
1875 in |
1835 text {* types, terms and text *} |
1876 pprint (Pretty.blk (0, and_list ptrs)) |
1836 |
1877 end" |
1837 ML {*fun tell_type ctxt t = |
1878 "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 |
1838 pprint (Pretty.blk (0, (p_strs "The term ") |
1879 and 22"} |
1839 @ [Pretty.quote (Syntax.pretty_term ctxt t)] |
1880 |
1840 @ p_strs " has type " |
1881 Next we like to pretty print a term and its type. For this we use the |
1841 @ [Pretty.quote (Syntax.pretty_typ ctxt (fastype_of t)), Pretty.str "."]))*} |
1882 function @{text "tell_type"}: |
1842 |
1883 *} |
1843 ML{*tell_type @{context} @{term "min (Suc 0)"} *} |
1884 |
1844 |
1885 ML %linenosgray{*fun tell_type ctxt t = |
1845 |
1886 let |
1846 ML {* |
1887 fun pstr s = Pretty.breaks (map Pretty.str (space_explode " " s)) |
1847 tell_type @{context} @{term "(op =) ((op =) ((op =) ((op =) ((op =) (op =)))))"} |
1888 val ptrm = Pretty.quote (Syntax.pretty_term ctxt t) |
1848 *} |
1889 val pty = Pretty.quote (Syntax.pretty_typ ctxt (fastype_of t)) |
1849 |
1890 in |
1850 text {* does not break inside the term or type *} |
1891 pprint (Pretty.blk (0, |
1851 |
1892 (pstr "The term " @ [ptrm] @ pstr " has type " |
1852 text {* |
1893 @ [pty, Pretty.str "."]))) |
|
1894 end*} |
|
1895 |
|
1896 text {* |
|
1897 In Line 3 we define a function that inserts possible linebreaks into |
|
1898 an (English) text according to spaces. In Lines 4 and 5 we pretty print |
|
1899 the term and its type using the functions @{ML Syntax.pretty_term} and |
|
1900 @{ML Syntax.pretty_typ}. We also use the function @{ML Pretty.quote} |
|
1901 in order to enclose the term and type with quotes. In Line 9 we add the |
|
1902 period right after the type without the possibility of a line break, |
|
1903 because we do not want that a line break occurs there. |
|
1904 |
|
1905 Now you can write |
|
1906 |
|
1907 @{ML_response_fake [display,gray] |
|
1908 "tell_type @{context} @{term \"min (Suc 0)\"}" |
|
1909 "The term \"min (Suc 0)\" has type \"nat \<Rightarrow> nat\"."} |
|
1910 |
|
1911 You can see the proper line breaking with the term |
|
1912 |
|
1913 @{ML_response_fake [display,gray] |
|
1914 "tell_type @{context} @{term \"op = (op = (op = (op = (op = op =))))\"}" |
|
1915 "The term \"op = (op = (op = (op = (op = op =))))\" has type |
|
1916 \"((((('a \<Rightarrow> 'a \<Rightarrow> bool) \<Rightarrow> bool) \<Rightarrow> bool) \<Rightarrow> bool) \<Rightarrow> bool) \<Rightarrow> bool\"."} |
|
1917 |
|
1918 *} |
|
1919 |
|
1920 text {* |
|
1921 Still to be done: |
|
1922 |
1853 @{ML Pretty.big_list}, |
1923 @{ML Pretty.big_list}, |
1854 |
1924 |
|
1925 @{ML Pretty.chunks} |
|
1926 |
1855 equations |
1927 equations |
1856 |
1928 |
1857 colours |
1929 colours |
|
1930 |
|
1931 What happens with big formulae? |
|
1932 |
|
1933 \begin{readmore} |
|
1934 The general infrastructure for pretty printing is implemented in |
|
1935 @{ML_file "Pure/General/pretty.ML"}. The file @{ML_file "Pure/Syntax/syntax.ML"} |
|
1936 contains pretty printing functions for terms, types, theorems and so on. |
|
1937 \end{readmore} |
1858 *} |
1938 *} |
1859 |
1939 |
1860 section {* Misc (TBD) *} |
1940 section {* Misc (TBD) *} |
1861 |
1941 |
1862 ML {*DatatypePackage.get_datatype @{theory} "List.list"*} |
1942 ML {*DatatypePackage.get_datatype @{theory} "List.list"*} |