BNFC / bnfc

BNF Converter
http://bnfc.digitalgrammars.com/
583 stars 165 forks source link

Open discussion about pretty printing #294

Open aspiwack opened 4 years ago

aspiwack commented 4 years ago

I'd like to talk about the generated pretty printing code. It doesn't appear to be documented all that much, so I may be saying nonsense, apologies in advance. I've only used the Haskell backend, so I'm going to only speak about it. I don't know if other backend have pretty-printing code as well.

My understanding is the following: BNFC gives me a class Print a, and an instance of Print for each exported grammar production.

However, the output of Print (or, rather, its method prt) is essentially a list of tokens. Which makes formatting the output rather difficult. The renderer is then left with recognising all the keywords, making groups, etc…

So that's my discussion proposal: could there be a better representation of the code to print, which would let me use a rich pretty printer to render the document?

andreasabel commented 4 years ago

could there be a better representation of the code to print, which would let me use a rich pretty printer to render the document?

Short answer, yes.

The Print class currently just produces a character stream, and the renderer does something ad-hoc to e.g. insert line breaks after semicolons, which is tailored towards C/Java style grammars.

aspiwack commented 4 years ago

Short answer, yes.

:slightly_smiling_face:

the renderer does something ad-hoc

Indeed, I've dropped the default renderer and replaced it with my own, which does its own ad hoc things for my language. Yet, it's not very satisfactory. I can't easily add behaviour for limited line length, unless I reparse most of the grammar.

But what would be a satisfactory design?

I see two families of approaches:

Are there others?

Personally, I think I like the rich-concrete syntax better. It is probably a bit more work, but the ability to target different rendering engine sounds useful. I use prettyprinter but it's not a universal choice.

andreasabel commented 4 years ago

I haven't really thought in a systematic way about printing yet. The only idea I had was that for lists, one should be able to state whether they should be laid out vertically or horizontally (the latter only if possible, of course). This could be realized by adding a qualifier to the list pragmas separator and terminator. (Less clear how to do this for plain CFG user-rolled list constructions.)

Another hack that should be replaced is spacing around operators/separators. The generated renderer does something ad-hoc for ; and ,, if I recall correctly. One could steer this more systematically by putting white-space into operator and separator symbols, like terminator Foo "; ".

But I agree that the whole printer should be rethought.

A caveat is the multi-backend nature of BNFC. We currently have uniform behavior of the printers generated by the different backends. This does not prevent to do something better for just one backend (like Haskell), but it is worthwhile investigating if we could not improve printing for all backends. Further, the default behavior of the printer should remain uniform for all backends, and the changes should be backwards-compatible in the sense that (for Haskell) there is still the overloaded printTree function giving the standard behavior.