
On 2019-04-29 3:54 a.m., 佐藤玄基 wrote:
Hello,
I found that the layout interpretation algorithm in Section 10.3 of Report 2010 produces parse-error when applied to the following code snippet:
[Snippet 1 : The code that fails to be parsed by Report] main = print t where { t = s where s = 1 :: Int } [/Snippet 1]
GHC 8.4.4 and GHC 8.6.4 accept this code, which is a good behavior in my opinion.
It's worth noting that the snippet is accepted by GHC even with -XAlternativeLayoutRule. Overall, I think I'd rather just have the concrete AlternativeLayoutRule algorithm in the specification than tweak the current broken and vague pseudo-code.
If we regard the behavior of those current GHCs as correct, this "bug" in the Report lies in the following lines in the definition of the function L:
[Snippet 2 : A part of the layout interpretation algorithm in the Report] L (} : ts) (0 : ms) = } : L ts ms (Note 3) L (} : ts) ms = parse-error (Note 3) [/Snippet 2]
I suggest this be modified to what follows:
[Snippet 3 : A Suggested modification to Snippet 2] L (} : ts) (m : ms) | m == 0 = } : L ts ms | m > 0 = } : L (} : ts) ms L (} : ts) [] = parse-error [/Snippet 3]
Let me explain in detail. First of all, how is Snippet 1 refused by the Report 2010 algorithm? Let us emulate it by hand. Firstly pre-process Snippet 1:
[Snippet 4 : Pre-processed Snippet 1] {1} main = print t where { t = s where {36} s = 1 :: Int } [/Snippet 4]
I assumed that Snippet 1 is the only line in a file. We may compute:
[Computation 5 : Apply L to Snippet 4] L
[] = "{ main = print t where { t = s where { s = 1 :: Int" ++ L "}" [36,0,1] [/Computation 5] Wait here. What does L do next? Looking from top to bottom, we hit the second line in Snippet 2, which leads us to parse-error. Now you might expect that the line
[Snippet 6 : Another part of the Report algorithm] L (t : ts) (m : ms) = } : (L (t : ts) ms) if m /= 0 and parse-error(t) (Note 5) [/Snippet 6]
would help, but unfortunately it doesn't work. Even if we put aside the fact that Snippet 6 is lower in the text than Snippet 2 and has lower priority of execution according to the usual Haskell matching rule, this line in L won't be triggered by this parse error at all, since *parse-error('}') is false!* Let us go back to the definition of parse-error(t).
[Quotation 7 : Note 5 in the Report algorithm] The side condition parse-error(t) is to be interpreted as follows: if the tokens generated so far by L together with the next token t represent an invalid prefix of the Haskell grammar, and the tokens generated so far by L followed by the token "}" represent a valid prefix of the Haskell grammar, then parse-error(t) is true. [/Quotation 7]
Now, this is the point. In this case, "the tokens generated so far by L together with the next token t" is:
[Snippet 8] { main = print t where { t = s where { s = 1 :: Int } [/Snippet 8]
This is a *valid prefix of the Haskell grammar*, and hence parse-error('}') is false.
Therefore, any Haskell2010-compliant compiler should reject Snippet 1, and this doesn't seem to be any sensible choice of specification. Speaking generally, I guess the Report 2010's authors wanted the case where a inner implicit brace and a outer explicit brace is closed at the same time to be processed by the rule in Snippet 6, but it doesn't work since Snippet 2 is before Snippet 6 in the definition of L and parse-error(t) is always false if t = '}'.
The fix of this problem is easy: replace Snippet 2 with Snippet 3. The added case of m > 0 is doing almost the same thing as Snippet 6, but parse-error(t) is removed. If we distinguish implicit close-braces and explicit close-braces, the condition m > 0 fully does the job of parse-error('}'), so I expect there will be no problem with this modification.
I apologize you if this long text has exhausted your eyes. I hope this suggestion would help.
Sincerely yours, Genki SATO
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-prime