diff -r be23597e81db -r f875a25aa72d ProgTutorial/Parsing.thy --- a/ProgTutorial/Parsing.thy Fri May 17 07:29:51 2019 +0200 +++ b/ProgTutorial/Parsing.thy Fri May 17 10:38:01 2019 +0200 @@ -60,18 +60,18 @@ the rest of the input list. For example: @{ML_matchresult [display,gray] - "($$ \"h\") (Symbol.explode \"hello\")" "(\"h\", [\"e\", \"l\", \"l\", \"o\"])"} + \($$ "h") (Symbol.explode "hello")\ \("h", ["e", "l", "l", "o"])\} @{ML_matchresult [display,gray] - "($$ \"w\") (Symbol.explode \"world\")" "(\"w\", [\"o\", \"r\", \"l\", \"d\"])"} + \($$ "w") (Symbol.explode "world")\ \("w", ["o", "r", "l", "d"])\} - The function @{ML "$$"} will either succeed (as in the two examples above) + The function @{ML \$$\} will either succeed (as in the two examples above) or raise the exception \FAIL\ if no string can be consumed. For example trying to parse @{ML_matchresult_fake [display,gray] - "($$ \"x\") (Symbol.explode \"world\")" - "Exception FAIL raised"} + \($$ "x") (Symbol.explode "world")\ + \Exception FAIL raised\} will raise the exception \FAIL\. The function @{ML_ind "$$" in Scan} will also fail if you try to consume more than a single character. @@ -84,7 +84,7 @@ \item \MORE\ indicates that there is not enough input for the parser. For example in \($$ "h") []\. \item \ABORT\ indicates that a dead end is reached. - It is used for example in the function @{ML "!!"} (see below). + It is used for example in the function @{ML \!!\} (see below). \end{itemize} However, note that these exceptions are private to the parser and cannot be accessed @@ -98,15 +98,15 @@ Isabelle. To see the difference consider @{ML_matchresult_fake [display,gray] -"let - val input = \"\ bar\" +\let + val input = "\ bar" in (String.explode input, Symbol.explode input) -end" -"([\"\\\", \"<\", \"f\", \"o\", \"o\", \">\", \" \", \"b\", \"a\", \"r\"], - [\"\\", \" \", \"b\", \"a\", \"r\"])"} +end\ +\(["\\", "<", "f", "o", "o", ">", " ", "b", "a", "r"], + ["\", " ", "b", "a", "r"])\} - Slightly more general than the parser @{ML "$$"} is the function + Slightly more general than the parser @{ML \$$\} is the function @{ML_ind one in Scan}, in that it takes a predicate as argument and then parses exactly one item from the input list satisfying this predicate. For example the @@ -114,22 +114,22 @@ [quotes] "w"}: @{ML_matchresult [display,gray] -"let - val hw = Scan.one (fn x => x = \"h\" orelse x = \"w\") - val input1 = Symbol.explode \"hello\" - val input2 = Symbol.explode \"world\" +\let + val hw = Scan.one (fn x => x = "h" orelse x = "w") + val input1 = Symbol.explode "hello" + val input2 = Symbol.explode "world" in (hw input1, hw input2) -end" - "((\"h\", [\"e\", \"l\", \"l\", \"o\"]),(\"w\", [\"o\", \"r\", \"l\", \"d\"]))"} +end\ + \(("h", ["e", "l", "l", "o"]),("w", ["o", "r", "l", "d"]))\} Two parsers can be connected in sequence by using the function @{ML_ind "--" in Scan}. For example parsing \h\, \e\ and \l\ (in this order) you can achieve by: @{ML_matchresult [display,gray] - "($$ \"h\" -- $$ \"e\" -- $$ \"l\") (Symbol.explode \"hello\")" - "(((\"h\", \"e\"), \"l\"), [\"l\", \"o\"])"} + \($$ "h" -- $$ "e" -- $$ "l") (Symbol.explode "hello")\ + \((("h", "e"), "l"), ["l", "o"])\} Note how the result of consumed strings builds up on the left as nested pairs. @@ -137,24 +137,24 @@ then you can use the function @{ML_ind this_string in Scan}. @{ML_matchresult [display,gray] - "Scan.this_string \"hell\" (Symbol.explode \"hello\")" - "(\"hell\", [\"o\"])"} + \Scan.this_string "hell" (Symbol.explode "hello")\ + \("hell", ["o"])\} Parsers that explore alternatives can be constructed using the function - @{ML_ind "||" in Scan}. The parser @{ML "(p || q)" for p q} returns the + @{ML_ind "||" in Scan}. The parser @{ML \(p || q)\ for p q} returns the result of \p\, in case it succeeds; otherwise it returns the result of \q\. For example: @{ML_matchresult [display,gray] -"let - val hw = $$ \"h\" || $$ \"w\" - val input1 = Symbol.explode \"hello\" - val input2 = Symbol.explode \"world\" +\let + val hw = $$ "h" || $$ "w" + val input1 = Symbol.explode "hello" + val input2 = Symbol.explode "world" in (hw input1, hw input2) -end" - "((\"h\", [\"e\", \"l\", \"l\", \"o\"]), (\"w\", [\"o\", \"r\", \"l\", \"d\"]))"} +end\ + \(("h", ["e", "l", "l", "o"]), ("w", ["o", "r", "l", "d"]))\} The functions @{ML_ind "|--" in Scan} and @{ML_ind "--|" in Scan} work like the sequencing function for parsers, except that they discard the item being parsed by the @@ -163,95 +163,95 @@ For example: @{ML_matchresult [display,gray] -"let - val just_e = $$ \"h\" |-- $$ \"e\" - val just_h = $$ \"h\" --| $$ \"e\" - val input = Symbol.explode \"hello\" +\let + val just_e = $$ "h" |-- $$ "e" + val just_h = $$ "h" --| $$ "e" + val input = Symbol.explode "hello" in (just_e input, just_h input) -end" - "((\"e\", [\"l\", \"l\", \"o\"]), (\"h\", [\"l\", \"l\", \"o\"]))"} +end\ + \(("e", ["l", "l", "o"]), ("h", ["l", "l", "o"]))\} - The parser @{ML "Scan.optional p x" for p x} returns the result of the parser + The parser @{ML \Scan.optional p x\ for p x} returns the result of the parser \p\, in case it succeeds; otherwise it returns the default value \x\. For example: @{ML_matchresult [display,gray] -"let - val p = Scan.optional ($$ \"h\") \"x\" - val input1 = Symbol.explode \"hello\" - val input2 = Symbol.explode \"world\" +\let + val p = Scan.optional ($$ "h") "x" + val input1 = Symbol.explode "hello" + val input2 = Symbol.explode "world" in (p input1, p input2) -end" - "((\"h\", [\"e\", \"l\", \"l\", \"o\"]), (\"x\", [\"w\", \"o\", \"r\", \"l\", \"d\"]))"} +end\ + \(("h", ["e", "l", "l", "o"]), ("x", ["w", "o", "r", "l", "d"]))\} The function @{ML_ind option in Scan} works similarly, except no default value can be given. Instead, the result is wrapped as an \option\-type. For example: @{ML_matchresult [display,gray] -"let - val p = Scan.option ($$ \"h\") - val input1 = Symbol.explode \"hello\" - val input2 = Symbol.explode \"world\" +\let + val p = Scan.option ($$ "h") + val input1 = Symbol.explode "hello" + val input2 = Symbol.explode "world" in (p input1, p input2) -end" "((SOME \"h\", [\"e\", \"l\", \"l\", \"o\"]), (NONE, [\"w\", \"o\", \"r\", \"l\", \"d\"]))"} +end\ \((SOME "h", ["e", "l", "l", "o"]), (NONE, ["w", "o", "r", "l", "d"]))\} The function @{ML_ind ahead in Scan} parses some input, but leaves the original input unchanged. For example: @{ML_matchresult [display,gray] - "Scan.ahead (Scan.this_string \"foo\") (Symbol.explode \"foo\")" - "(\"foo\", [\"f\", \"o\", \"o\"])"} + \Scan.ahead (Scan.this_string "foo") (Symbol.explode "foo")\ + \("foo", ["f", "o", "o"])\} The function @{ML_ind "!!" in Scan} helps with producing appropriate error messages during parsing. For example if you want to parse \p\ immediately followed by \q\, or start a completely different parser \r\, you might write: - @{ML [display,gray] "(p -- q) || r" for p q r} + @{ML [display,gray] \(p -- q) || r\ for p q r} However, this parser is problematic for producing a useful error - message, if the parsing of @{ML "(p -- q)" for p q} fails. Because with the + message, if the parsing of @{ML \(p -- q)\ for p q} fails. Because with the parser above you lose the information that \p\ should be followed by \q\. To see this assume that \p\ is present in the input, but it is not - followed by \q\. That means @{ML "(p -- q)" for p q} will fail and + followed by \q\. That means @{ML \(p -- q)\ for p q} will fail and hence the alternative parser \r\ will be tried. However, in many circumstances this will be the wrong parser for the input ``\p\-followed-by-something'' and therefore will also fail. The error message is then caused by the failure of \r\, not by the absence of \q\ in the input. Such - situation can be avoided when using the function @{ML "!!"}. This function + situation can be avoided when using the function @{ML \!!\}. This function aborts the whole process of parsing in case of a failure and prints an error message. For example if you invoke the parser - @{ML [display,gray] "!! (fn _ => fn _ =>\"foo\") ($$ \"h\")"} + @{ML [display,gray] \!! (fn _ => fn _ =>"foo") ($$ "h")\} on @{text [quotes] "hello"}, the parsing succeeds @{ML_matchresult [display,gray] - "(!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"hello\")" - "(\"h\", [\"e\", \"l\", \"l\", \"o\"])"} + \(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "hello")\ + \("h", ["e", "l", "l", "o"])\} but if you invoke it on @{text [quotes] "world"} - @{ML_matchresult_fake [display,gray] "(!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"world\")" - "Exception ABORT raised"} + @{ML_matchresult_fake [display,gray] \(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\ + \Exception ABORT raised\} then the parsing aborts and the error message \foo\ is printed. In order to see the error message properly, you need to prefix the parser with the function @{ML_ind error in Scan}. For example: @{ML_matchresult_fake [display,gray] - "Scan.error (!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"world\")" - "Exception Error \"foo\" raised"} + \Scan.error (!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\ + \Exception Error "foo" raised\} This kind of ``prefixing'' to see the correct error message is usually done by wrappers such as @{ML_ind local_theory in Outer_Syntax} (see Section~\ref{sec:newcommand} which explains this function in more detail). - Let us now return to our example of parsing @{ML "(p -- q) || r" for p q + Let us now return to our example of parsing @{ML \(p -- q) || r\ for p q r}. If you want to generate the correct error message for failure of parsing \p\-followed-by-\q\, then you have to write: \ @@ -269,21 +269,21 @@ @{text [quotes] "h"}, @{text [quotes] "e"} and @{text [quotes] "w"}, and the input @{text [quotes] "holle"} - @{ML_matchresult_fake [display,gray] "Scan.error (p_followed_by_q \"h\" \"e\" \"w\") (Symbol.explode \"holle\")" - "Exception ERROR \"h is not followed by e\" raised"} + @{ML_matchresult_fake [display,gray] \Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "holle")\ + \Exception ERROR "h is not followed by e" raised\} produces the correct error message. Running it with - @{ML_matchresult [display,gray] "Scan.error (p_followed_by_q \"h\" \"e\" \"w\") (Symbol.explode \"wworld\")" - "((\"w\", \"w\"), [\"o\", \"r\", \"l\", \"d\"])"} + @{ML_matchresult [display,gray] \Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "wworld")\ + \(("w", "w"), ["o", "r", "l", "d"])\} yields the expected parsing. - The function @{ML "Scan.repeat p" for p} will apply a parser \p\ as + The function @{ML \Scan.repeat p\ for p} will apply a parser \p\ as long as it succeeds. For example: - @{ML_matchresult [display,gray] "Scan.repeat ($$ \"h\") (Symbol.explode \"hhhhello\")" - "([\"h\", \"h\", \"h\", \"h\"], [\"e\", \"l\", \"l\", \"o\"])"} + @{ML_matchresult [display,gray] \Scan.repeat ($$ "h") (Symbol.explode "hhhhello")\ + \(["h", "h", "h", "h"], ["e", "l", "l", "o"])\} Note that @{ML_ind repeat in Scan} stores the parsed items in a list. The function @{ML_ind repeat1 in Scan} is similar, but requires that the parser \p\ @@ -294,8 +294,8 @@ the wrapper @{ML_ind finite in Scan} and the ``stopper-token'' @{ML_ind stopper in Symbol}. With them you can write: - @{ML_matchresult [display,gray] "Scan.finite Symbol.stopper (Scan.repeat ($$ \"h\")) (Symbol.explode \"hhhh\")" - "([\"h\", \"h\", \"h\", \"h\"], [])"} + @{ML_matchresult [display,gray] \Scan.finite Symbol.stopper (Scan.repeat ($$ "h")) (Symbol.explode "hhhh")\ + \(["h", "h", "h", "h"], [])\} The function @{ML stopper in Symbol} is the ``end-of-input'' indicator for parsing strings; other stoppers need to be used when parsing, for example, tokens. However, this kind of @@ -305,13 +305,13 @@ string as in @{ML_matchresult [display,gray] -"let +\let val p = Scan.repeat (Scan.one Symbol.not_eof) - val input = Symbol.explode \"foo bar foo\" + val input = Symbol.explode "foo bar foo" in Scan.finite Symbol.stopper p input -end" -"([\"f\", \"o\", \"o\", \" \", \"b\", \"a\", \"r\", \" \", \"f\", \"o\", \"o\"], [])"} +end\ +\(["f", "o", "o", " ", "b", "a", "r", " ", "f", "o", "o"], [])\} where the function @{ML_ind not_eof in Symbol} ensures that we do not read beyond the end of the input string (i.e.~stopper symbol). @@ -319,13 +319,13 @@ The function @{ML_ind unless in Scan} takes two parsers: if the first one can parse the input, then the whole parser fails; if not, then the second is tried. Therefore - @{ML_matchresult_fake_both [display,gray] "Scan.unless ($$ \"h\") ($$ \"w\") (Symbol.explode \"hello\")" - "Exception FAIL raised"} + @{ML_matchresult_fake_both [display,gray] \Scan.unless ($$ "h") ($$ "w") (Symbol.explode "hello")\ + \Exception FAIL raised\} fails, while - @{ML_matchresult [display,gray] "Scan.unless ($$ \"h\") ($$ \"w\") (Symbol.explode \"world\")" - "(\"w\",[\"o\", \"r\", \"l\", \"d\"])"} + @{ML_matchresult [display,gray] \Scan.unless ($$ "h") ($$ "w") (Symbol.explode "world")\ + \("w",["o", "r", "l", "d"])\} succeeds. @@ -334,43 +334,43 @@ example below the marker symbol is a @{text [quotes] "*"}. @{ML_matchresult [display,gray] -"let - val p = Scan.repeat (Scan.unless ($$ \"*\") (Scan.one Symbol.not_eof)) - val input1 = Symbol.explode \"fooooo\" - val input2 = Symbol.explode \"foo*ooo\" +\let + val p = Scan.repeat (Scan.unless ($$ "*") (Scan.one Symbol.not_eof)) + val input1 = Symbol.explode "fooooo" + val input2 = Symbol.explode "foo*ooo" in (Scan.finite Symbol.stopper p input1, Scan.finite Symbol.stopper p input2) -end" -"(([\"f\", \"o\", \"o\", \"o\", \"o\", \"o\"], []), - ([\"f\", \"o\", \"o\"], [\"*\", \"o\", \"o\", \"o\"]))"} +end\ +\((["f", "o", "o", "o", "o", "o"], []), + (["f", "o", "o"], ["*", "o", "o", "o"]))\} After parsing is done, you almost always want to apply a function to the parsed items. One way to do this is the function @{ML_ind ">>" in Scan} where - @{ML "(p >> f)" for p f} runs + @{ML \(p >> f)\ for p f} runs first the parser \p\ and upon successful completion applies the function \f\ to the result. For example @{ML_matchresult [display,gray] -"let +\let fun double (x, y) = (x ^ x, y ^ y) - val parser = $$ \"h\" -- $$ \"e\" + val parser = $$ "h" -- $$ "e" in - (parser >> double) (Symbol.explode \"hello\") -end" -"((\"hh\", \"ee\"), [\"l\", \"l\", \"o\"])"} + (parser >> double) (Symbol.explode "hello") +end\ +\(("hh", "ee"), ["l", "l", "o"])\} doubles the two parsed input strings; or @{ML_matchresult [display,gray] -"let +\let val p = Scan.repeat (Scan.one Symbol.not_eof) - val input = Symbol.explode \"foo bar foo\" + val input = Symbol.explode "foo bar foo" in Scan.finite Symbol.stopper (p >> implode) input -end" -"(\"foo bar foo\",[])"} +end\ +\("foo bar foo",[])\} where the single-character strings in the parsed output are transformed back into one string. @@ -380,8 +380,8 @@ untouched. For example @{ML_matchresult [display,gray] -"Scan.lift ($$ \"h\" -- $$ \"e\") (1, Symbol.explode \"hello\")" -"((\"h\", \"e\"), (1, [\"l\", \"l\", \"o\"]))"} +\Scan.lift ($$ "h" -- $$ "e") (1, Symbol.explode "hello")\ +\(("h", "e"), (1, ["l", "l", "o"]))\} \footnote{\bf FIXME: In which situations is \lift\ useful? Give examples.} @@ -426,8 +426,8 @@ loop, even if it is called without any input. For example @{ML_matchresult_fake_both [display, gray] - "parse_tree \"A\"" - "*** Exception- TOPLEVEL_ERROR raised"} + \parse_tree "A"\ + \*** Exception- TOPLEVEL_ERROR raised\} raises an exception indicating that the stack limit is reached. Such looping parser are not useful, because of ML's strict evaluation of @@ -450,20 +450,19 @@ exactly the parser what we wanted. An example is as follows: @{ML_matchresult [display, gray] - "let - val input = Symbol.explode \"(A,((A))),A\" + \let + val input = Symbol.explode "(A,((A))),A" in - Scan.finite Symbol.stopper (parse_tree \"A\") input -end" - "(Br (Br (Lf \"A\", Lf \"A\"), Lf \"A\"), [])"} + Scan.finite Symbol.stopper (parse_tree "A") input +end\ + \(Br (Br (Lf "A", Lf "A"), Lf "A"), [])\} \begin{exercise}\label{ex:scancmts} Write a parser that parses an input string so that any comment enclosed within \(*\*)\ is replaced by the same comment but enclosed within \(**\**)\ in the output string. To enclose a string, you can use the - function @{ML "enclose s1 s2 s" for s1 s2 s} which produces the string @{ML - "s1 ^ s ^ s2" for s1 s2 s}. Hint: To simplify the task ignore the proper + function @{ML \enclose s1 s2 s\ for s1 s2 s} which produces the string @{ML \s1 ^ s ^ s2\ for s1 s2 s}. Hint: To simplify the task ignore the proper nesting of comments. \end{exercise} \ @@ -497,7 +496,7 @@ token parsers take into account the kind of tokens. The first example shows how to generate a token list out of a string using the function @{ML_ind explode in Token}. It is given the argument - @{ML "Position.none"} since, + @{ML \Position.none\} since, at the moment, we are not interested in generating precise error messages. The following code @@ -524,11 +523,11 @@ text \ then lexing @{text [quotes] "hello world"} will produce - @{ML_matchresult_fake [display,gray] "Token.explode - (Thy_Header.get_keywords' @{context}) Position.none \"hello world\"" -"[Token (_,(Keyword, \"hello\"),_), - Token (_,(Space, \" \"),_), - Token (_,(Ident, \"world\"),_)]"} + @{ML_matchresult_fake [display,gray] \Token.explode + (Thy_Header.get_keywords' @{context}) Position.none "hello world"\ +\[Token (_,(Keyword, "hello"),_), + Token (_,(Space, " "),_), + Token (_,(Ident, "world"),_)]\} Many parsing functions later on will require white space, comments and the like to have already been filtered out. So from now on we are going to use the @@ -536,12 +535,12 @@ For example: @{ML_matchresult_fake [display,gray] -"let - val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none \"hello world\" +\let + val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "hello world" in filter Token.is_proper input -end" -"[Token (_,(Ident, \"hello\"), _), Token (_,(Ident, \"world\"), _)]"} +end\ +\[Token (_,(Ident, "hello"), _), Token (_,(Ident, "world"), _)]\} For convenience we define the function: \ @@ -554,10 +553,10 @@ If you now parse @{ML_matchresult_fake [display,gray] -"filtered_input \"inductive | for\"" -"[Token (_,(Command, \"inductive\"),_), - Token (_,(Keyword, \"|\"),_), - Token (_,(Keyword, \"for\"),_)]"} +\filtered_input "inductive | for"\ +\[Token (_,(Command, "inductive"),_), + Token (_,(Keyword, "|"),_), + Token (_,(Keyword, "for"),_)]\} you obtain a list consisting of only one command and two keyword tokens. If you want to see which keywords and commands are currently known to Isabelle, @@ -569,47 +568,47 @@ The parser @{ML_ind "$$$" in Parse} parses a single keyword. For example: @{ML_matchresult [display,gray] -"let - val input1 = filtered_input \"where for\" - val input2 = filtered_input \"| in\" +\let + val input1 = filtered_input "where for" + val input2 = filtered_input "| in" in - (Parse.$$$ \"where\" input1, Parse.$$$ \"|\" input2) -end" -"((\"where\",_), (\"|\",_))"} + (Parse.$$$ "where" input1, Parse.$$$ "|" input2) +end\ +\(("where",_), ("|",_))\} Any non-keyword string can be parsed with the function @{ML_ind reserved in Parse}. For example: @{ML_matchresult [display,gray] -"let - val p = Parse.reserved \"bar\" - val input = filtered_input \"bar\" +\let + val p = Parse.reserved "bar" + val input = filtered_input "bar" in p input -end" - "(\"bar\",[])"} +end\ + \("bar",[])\} - Like before, you can sequentially connect parsers with @{ML "--"}. For example: + Like before, you can sequentially connect parsers with @{ML \--\}. For example: @{ML_matchresult [display,gray] -"let - val input = filtered_input \"| in\" +\let + val input = filtered_input "| in" in - (Parse.$$$ \"|\" -- Parse.$$$ \"in\") input -end" -"((\"|\", \"in\"), [])"} + (Parse.$$$ "|" -- Parse.$$$ "in") input +end\ +\(("|", "in"), [])\} - The parser @{ML "Parse.enum s p" for s p} parses a possibly empty + The parser @{ML \Parse.enum s p\ for s p} parses a possibly empty list of items recognised by the parser \p\, where the items being parsed are separated by the string \s\. For example: @{ML_matchresult [display,gray] -"let - val input = filtered_input \"in | in | in foo\" +\let + val input = filtered_input "in | in | in foo" in - (Parse.enum \"|\" (Parse.$$$ \"in\")) input -end" -"([\"in\", \"in\", \"in\"], [_])"} + (Parse.enum "|" (Parse.$$$ "in")) input +end\ +\(["in", "in", "in"], [_])\} The function @{ML_ind enum1 in Parse} works similarly, except that the parsed list must be non-empty. Note that we had to add a string @{text @@ -619,13 +618,13 @@ ``stopper-token'' @{ML Token.stopper}. We can write: @{ML_matchresult [display,gray] -"let - val input = filtered_input \"in | in | in\" - val p = Parse.enum \"|\" (Parse.$$$ \"in\") +\let + val input = filtered_input "in | in | in" + val p = Parse.enum "|" (Parse.$$$ "in") in Scan.finite Token.stopper p input -end" -"([\"in\", \"in\", \"in\"], [])"} +end\ +\(["in", "in", "in"], [])\} The following function will help to run examples. \ @@ -634,20 +633,19 @@ text \ The function @{ML_ind "!!!" in Parse} can be used to force termination - of the parser in case of a dead end, just like @{ML "Scan.!!"} (see previous - section). A difference, however, is that the error message of @{ML - "Parse.!!!"} is fixed to be @{text [quotes] "Outer syntax error"} + of the parser in case of a dead end, just like @{ML \Scan.!!\} (see previous + section). A difference, however, is that the error message of @{ML \Parse.!!!\} is fixed to be @{text [quotes] "Outer syntax error"} together with a relatively precise description of the failure. For example: @{ML_matchresult_fake [display,gray] -"let - val input = filtered_input \"in |\" - val parse_bar_then_in = Parse.$$$ \"|\" -- Parse.$$$ \"in\" +\let + val input = filtered_input "in |" + val parse_bar_then_in = Parse.$$$ "|" -- Parse.$$$ "in" in parse (Parse.!!! parse_bar_then_in) input -end" -"Exception ERROR \"Outer syntax error: keyword \"|\" expected, -but keyword in was found\" raised" +end\ +\Exception ERROR "Outer syntax error: keyword "|" expected, +but keyword in was found" raised\ } \begin{exercise} (FIXME) @@ -672,9 +670,9 @@ where we pretend the parsed string starts on line 7. An example is @{ML_matchresult_fake [display,gray] -"filtered_input' \"foo \\n bar\"" -"[Token ((\"foo\", ({line=7, end_line=7}, {line=7})), (Ident, \"foo\"), _), - Token ((\"bar\", ({line=8, end_line=8}, {line=8})), (Ident, \"bar\"), _)]"} +\filtered_input' "foo \\n bar"\ +\[Token (("foo", ({line=7, end_line=7}, {line=7})), (Ident, "foo"), _), + Token (("bar", ({line=8, end_line=8}, {line=8})), (Ident, "bar"), _)]\} in which the @{text [quotes] "\\n"} causes the second token to be in line 8. @@ -683,12 +681,12 @@ position and return it as part of the parser result. For example @{ML_matchresult_fake [display,gray] -"let - val input = filtered_input' \"where\" +\let + val input = filtered_input' "where" in - parse (Parse.position (Parse.$$$ \"where\")) input -end" -"((\"where\", {line=7, end_line=7}), [])"} + parse (Parse.position (Parse.$$$ "where")) input +end\ +\(("where", {line=7, end_line=7}), [])\} \begin{readmore} The functions related to positions are implemented in the file @@ -754,12 +752,12 @@ be parsed using the function @{ML_ind term in Parse}. For example: @{ML_matchresult_fake [display,gray] -"let - val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none \"foo\" +\let + val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "foo" in Parse.term input -end" -"(\"\", [])"} +end\ +\("", [])\} The function @{ML_ind prop in Parse} is similar, except that it gives a different @@ -767,15 +765,15 @@ the parsed string, but also some markup information. You can decode the information with the function @{ML_ind parse in YXML} in @{ML_structure YXML}. The result of the decoding is an XML-tree. You can see better what is going on if - you replace @{ML Position.none} by @{ML "Position.line 42"}, say: + you replace @{ML Position.none} by @{ML \Position.line 42\}, say: @{ML_matchresult_fake [display,gray] -"let - val input = Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 42) \"foo\" +\let + val input = Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 42) "foo" in YXML.parse (fst (Parse.term input)) -end" -"Elem (\"token\", [(\"line\", \"42\"), (\"end_line\", \"42\")], [XML.Text \"foo\"])"} +end\ +\Elem ("token", [("line", "42"), ("end_line", "42")], [XML.Text "foo"])\} The positional information is stored as part of an XML-tree so that code called later on will be able to give more precise error messages. @@ -833,20 +831,20 @@ definition of @{term even} and @{term odd}: @{ML_matchresult [display,gray] -"let +\let val input = filtered_input - (\"even and odd \" ^ - \"where \" ^ - \" even0[intro]: \\\"even 0\\\" \" ^ - \"| evenS[intro]: \\\"odd n \ even (Suc n)\\\" \" ^ - \"| oddS[intro]: \\\"even n \ odd (Suc n)\\\"\") + ("even and odd " ^ + "where " ^ + " even0[intro]: \"even 0\" " ^ + "| evenS[intro]: \"odd n \ even (Suc n)\" " ^ + "| oddS[intro]: \"even n \ odd (Suc n)\"") in parse spec_parser input -end" -"(([(even, NONE, NoSyn), (odd, NONE, NoSyn)], +end\ +\(([(even, NONE, NoSyn), (odd, NONE, NoSyn)], [((even0,_),_), ((evenS,_),_), - ((oddS,_),_)]), [])"} + ((oddS,_),_)]), [])\} \ ML \let @@ -865,19 +863,19 @@ \isacommand{and}-separated list of variables that can include optional type annotations and syntax translations. For example:\footnote{Note that in the code we need to write - \\"int \ bool\"\ in order to properly escape the double quotes + \"int \ bool"\ in order to properly escape the double quotes in the compound type.} @{ML_matchresult_fake [display,gray] -"let +\let val input = filtered_input - \"foo::\\\"int \ bool\\\" and bar::nat (\\\"BAR\\\" 100) and blonk\" + "foo::\"int \ bool\" and bar::nat (\"BAR\" 100) and blonk" in parse Parse.vars input -end" -"([(foo, SOME _, NoSyn), - (bar, SOME _, Mixfix (Source {text=\"BAR\",...}, [], 100, _)), - (blonk, NONE, NoSyn)],[])"} +end\ +\([(foo, SOME _, NoSyn), + (bar, SOME _, Mixfix (Source {text="BAR",...}, [], 100, _)), + (blonk, NONE, NoSyn)],[])\} \ text \ @@ -899,12 +897,12 @@ parsed by @{ML_ind prop in Parse}. However, they can include an optional theorem name plus some attributes. For example -@{ML_matchresult [display,gray] "let - val input = filtered_input \"foo_lemma[intro,dest!]:\" - val ((name, attrib), _) = parse (Parse_Spec.thm_name \":\") input +@{ML_matchresult [display,gray] \let + val input = filtered_input "foo_lemma[intro,dest!]:" + val ((name, attrib), _) = parse (Parse_Spec.thm_name ":") input in (name, map Token.name_of_src attrib) -end" "(foo_lemma, [(\"intro\", _), (\"dest\", _)])"} +end\ \(foo_lemma, [("intro", _), ("dest", _)])\} The function @{ML_ind opt_thm_name in Parse_Spec} is the ``optional'' variant of @{ML_ind thm_name in Parse_Spec}. Theorem names can contain attributes. The name @@ -1019,7 +1017,7 @@ behaviour of the command. In the code above we used a ``do-nothing''-function, which because of the parser @{ML_ind succeed in Scan} does not parse any argument, but immediately returns the simple - function @{ML "Local_Theory.background_theory I"}. We can replace + function @{ML \Local_Theory.background_theory I\}. We can replace this code by a function that first parses a proposition (using the parser @{ML Parse.prop}), then prints out some tracing information (using the function \trace_prop\) and finally does @@ -1051,8 +1049,7 @@ ``open up'' a proof in order to prove the proposition (for example \isacommand{lemma}) or prove some other properties (for example \isacommand{function}). To achieve this kind of behaviour, you have to use - the kind indicator @{ML_ind thy_goal in Keyword} and the function @{ML - "local_theory_to_proof" in Outer_Syntax} to set up the command. + the kind indicator @{ML_ind thy_goal in Keyword} and the function @{ML \local_theory_to_proof\ in Outer_Syntax} to set up the command. Below we show the command \isacommand{foobar\_goal} which takes a proposition as argument and then starts a proof in order to prove it. Therefore, we need to announce this command in the header @@ -1084,7 +1081,7 @@ @{ML_ind read_prop in Syntax}, which converts a string into a proper proposition. In Line 6 the function @{ML_ind theorem in Proof} starts the proof for the proposition. Its argument @{ML NONE} stands for a locale (which we chose to - omit); the argument @{ML "(K I)"} stands for a function that determines what + omit); the argument @{ML \(K I)\} stands for a function that determines what should be done with the theorem once it is proved (we chose to just forget about it).