diff -r 95b42288294e -r 438703674711 ProgTutorial/Parsing.thy --- a/ProgTutorial/Parsing.thy Fri May 17 11:21:09 2019 +0200 +++ b/ProgTutorial/Parsing.thy Tue May 21 14:37:39 2019 +0200 @@ -69,9 +69,9 @@ or raise the exception \FAIL\ if no string can be consumed. For example trying to parse - @{ML_matchresult_fake [display,gray] + @{ML_response [display,gray] \($$ "x") (Symbol.explode "world")\ - \Exception FAIL raised\} + \exception FAIL NONE 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. @@ -97,14 +97,13 @@ sequences, for example \\\, that have a special meaning in Isabelle. To see the difference consider -@{ML_matchresult_fake [display,gray] -\let - val input = "\ bar" -in - (String.explode input, Symbol.explode input) -end\ -\(["\\", "<", "f", "o", "o", ">", " ", "b", "a", "r"], - ["\", " ", "b", "a", "r"])\} +@{ML_matchresult [display,gray] +\String.explode "\ bar"\ +\[#"\\", #"<", #"f", #"o", #"o", #">", #" ", #"b", #"a", #"r"]\} + +@{ML_matchresult [display,gray] +\Symbol.explode "\ bar"\ +\["\", " ", "b", "a", "r"]\} Slightly more general than the parser @{ML \$$\} is the function @{ML_ind one in Scan}, in that it takes a predicate as argument and @@ -236,16 +235,16 @@ 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_response [display,gray] \(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\ + \exception ABORT fn 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] + @{ML_response [display,gray] \Scan.error (!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\ - \Exception Error "foo" raised\} + \foo\} This kind of ``prefixing'' to see the correct error message is usually done by wrappers such as @{ML_ind local_theory in Outer_Syntax} @@ -269,8 +268,8 @@ @{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_response [display,gray] \Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "holle")\ + \h is not followed by e\} produces the correct error message. Running it with @@ -318,9 +317,9 @@ 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_response [display,gray] \Scan.unless ($$ "h") ($$ "w") (Symbol.explode "hello")\ + "exception FAIL NONE raised"} fails, while @@ -501,11 +500,11 @@ messages. The following code -@{ML_matchresult_fake [display,gray] \Token.explode +@{ML_response [display,gray] \Token.explode (Thy_Header.get_keywords' @{context}) Position.none "hello world"\ -\[Token (_,(Ident, "hello"),_), - Token (_,(Space, " "),_), - Token (_,(Ident, "world"),_)]\} +\[Token (\(Ident, "hello"),\), + Token (\(Space, " "),\), + Token (\(Ident, "world"),\)]\} produces three tokens where the first and the last are identifiers, since @{text [quotes] "hello"} and @{text [quotes] "world"} do not match any @@ -523,24 +522,24 @@ text \ then lexing @{text [quotes] "hello world"} will produce - @{ML_matchresult_fake [display,gray] \Token.explode + @{ML_response [display,gray] \Token.explode (Thy_Header.get_keywords' @{context}) Position.none "hello world"\ -\[Token (_,(Keyword, "hello"),_), - Token (_,(Space, " "),_), - Token (_,(Ident, "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 functions @{ML filter} and @{ML_ind is_proper in Token} to do this. For example: -@{ML_matchresult_fake [display,gray] +@{ML_response [display,gray] \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"), _)]\} +\[Token (\(Keyword, "hello"), \), Token (\(Ident, "world"), \)]\} For convenience we define the function: \ @@ -552,11 +551,11 @@ text \ If you now parse -@{ML_matchresult_fake [display,gray] +@{ML_response [display,gray] \filtered_input "inductive | for"\ -\[Token (_,(Command, "inductive"),_), - Token (_,(Keyword, "|"),_), - Token (_,(Keyword, "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, @@ -637,15 +636,15 @@ 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] +@{ML_response [display,gray] \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\ +\Outer syntax error: keyword "|" expected, +but keyword in was found\ } \begin{exercise} (FIXME) @@ -669,10 +668,10 @@ text \ 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"), _)]\} +@{ML_response [display,gray] +\filtered_input' "foo \n bar"\ +\[Token (("foo", ({line=7, offset=1, end_offset=4}, {line=7, offset=4})), (Ident, "foo"),\), + Token (("bar", ({line=8, offset=7, end_offset=10}, {line=8, offset=10})), (Ident, "bar"),\)]\} in which the @{text [quotes] "\\n"} causes the second token to be in line 8. @@ -680,13 +679,13 @@ By using the parser @{ML position in Parse} you can access the token position and return it as part of the parser result. For example -@{ML_matchresult_fake [display,gray] +@{ML_response [display,gray] \let val input = filtered_input' "where" in parse (Parse.position (Parse.$$$ "where")) input end\ -\(("where", {line=7, end_line=7}), [])\} +\(("where", {line=7, offset=1, end_offset=6}), [])\} \begin{readmore} The functions related to positions are implemented in the file @@ -751,7 +750,7 @@ for terms and types: you can just call the predefined parsers. Terms can be parsed using the function @{ML_ind term in Parse}. For example: -@{ML_matchresult_fake [display,gray] +@{ML_response [display,gray] \let val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "foo" in @@ -767,13 +766,13 @@ 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: -@{ML_matchresult_fake [display,gray] +@{ML_response [display,gray] \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"])\} +\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. @@ -847,13 +846,6 @@ ((oddS,_),_)]), [])\} \ -ML \let - val input = filtered_input - "foo::\"int \ bool\" and bar::nat (\"BAR\" 100) and blonk" -in - parse Parse.vars input -end\ - text \ As you see, the result is a pair consisting of a list of variables with optional type-annotation and syntax-annotation, and a list of @@ -866,16 +858,16 @@ \"int \ bool"\ in order to properly escape the double quotes in the compound type.} -@{ML_matchresult_fake [display,gray] +@{ML_response [display,gray] \let val input = filtered_input "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)],[])\} +\([("foo", SOME \, NoSyn), + ("bar", SOME \, Mixfix (Source {\text = "BAR"}, [], 100, \)), + ("blonk", NONE, NoSyn)], [])\} \ text \ @@ -953,11 +945,7 @@ text \ Often new commands, for example for providing new definitional principles, - need to be implemented. While this is not difficult on the ML-level and for - jEdit, in order to be backwards compatible, new commands need also to be recognised - by Proof-General. This results in some subtle configuration issues, which we will - explain in the next section. Here we just describe how to define new commands - to work with jEdit. + need to be implemented. Let us start with a ``silly'' command that does nothing at all. We shall name this command \isacommand{foobar}. Before you can @@ -1000,9 +988,7 @@ text \ but of course you will not see anything since \isacommand{foobar} is - not intended to do anything. Remember, however, that this only - works in jEdit. In order to enable also Proof-General recognise this - command, a keyword file needs to be generated (see next section). + not intended to do anything. As it stands, the command \isacommand{foobar} is not very useful. Let us refine it a bit next by letting it take a proposition as argument