lspitzner / brittany

haskell source code formatter
GNU Affero General Public License v3.0
690 stars 72 forks source link

Brittany doesn't generate blank lines for `do {stmts;;;stmts}` #270

Closed bredelings closed 4 years ago

bredelings commented 4 years ago

Hi, I'm generating Haskell code with braces and semi-colons and then using brittany to remove braces and semicolons and use braces instead.

I'm trying to figure out how to generate code so that after brittany processes it there will be spaces between things. I've tried ; ;, ; ;, \n; \n;. In all cases, brittany consumes the extra ; (and the newline) and generates lines with no space between them.

Any idea how I can generate do { stmts; <something>; stmts} and get

  do
    stmts

    stmts

out?

eborden commented 4 years ago

I'm very curious why this is your workflow. It seems very non-standard and rather a niche issue.

bredelings commented 4 years ago

I have a C++ program that translates a simple input language into Haskell and then runs it in a special interpreter that tracks execution traces, in order to implement probabilistic programming.

BTW, my recollection (I think from the Haskell 2010 report) was that the reason Haskell handles layout by translating whitespace to {. ;. and } is precisely so that Haskell can be machine-generated. I'm not sure generation of Haskell code in general is "very non-standard".

I have two questions. First, does the implementation ignore all blank lines inside a { } block? Second, what kind of work would be required to handle blank lines like this?

bredelings commented 4 years ago

Hmm, it seems that brittany will preserve whitespace before comments. So both the whitespace and the comment are preserved below.

sample_imodel_1 = do {arg_mean_length <- shifted_exponential 10.0 1.0
;arg_log_rate <- Probability.laplace (-4.0) 0.707

-- This is another comment
;return (IModel.rs07 arg_log_rate arg_mean_length, ["rs07:log_rate" %=% arg_log_rate, "rs07:mean_length" %=% arg_mean_length])}
eborden commented 4 years ago

I'm not sure generation of Haskell code in general is "very non-standard".

Aha! You are right. I had not thought of that use case until you mentioned it.

lspitzner commented 4 years ago
do { a; ; ; b ; c }

in the AST looks like

HsDo
  NoExt
  DoExpr
  A Nothing
    [ A Just (Ann (DP (0,1)) [] [] [(AnnSemiSep,DP (0,0)),(AnnSemiSep,DP (0,1)),(AnnSemiSep,DP (0,1))] Nothing Nothing)
        BodyStmt{..}    -- boils down to binding "a"
    , A Just (Ann (DP (0,1)) [] [] [(AnnSemiSep,DP (0,1))] Nothing Nothing)
        BodyStmt{..}    -- boils down to binding "b"
    , A Just (Ann (DP (0,1)) [] [] [] Nothing Nothing)
        BodyStmt{..}    -- boils down to binding "c"
    ]

So this involves somehow turning AnnSemiSeps into newlines. Either by pre-processing on the annotations or by treating it like an "after-element-comment" that consists only of newlines.

lspitzner commented 4 years ago

@bredelings please test if the semicolon-newlines branch works for you (you will need to enable the new config flag in addition to running that branch).

bredelings commented 4 years ago

Indeed, it works! I added the line lconfig_experimentalSemicolonNewlines: true to the config.

lspitzner commented 4 years ago

implemented in 0.12.1.1