[ANN] PrettyPrint.jl: handy and extensible pretty print, also simple

I’m now developing a parser generator library and I’ve found debugging extremely disturbing for the lack of nested datatypes’ pretty print, which motivates me to create a pretty print library.

using PrettyPrint

struct S1
    i :: Int
    f :: Float64
end

struct S2
    s  :: Vector{String}
    s1 :: S1
end

data = S2(
    ["114514", "as we can"],
    S1(42, 9.96)
)
pprint(data) # or print(pformat(data))

=>

S2(
  s=[
    "114514",
    "as we cam",
  ],
  s1=S1(
    i=42,
    f=9.96,
  ),
)

Extension is simple, too…

using PrettyPrint
struct Account
    username :: String
    password :: String
end

@info :before_extension
pprint(
        [Account("van", "gd"), Account("thautwarm", "996icu")]
)
println()
PrettyPrint.pprint_impl(io, account::Account, indent::Int, newline::Bool) = print(io, "Account($(account.username))")

@info :after_extension
pprint(
        [Account("van", "gd"), Account("thautwarm", "996icu")]
)
println()

=>

[ Info: before_extension
[
  Account(
    username="van",
    password="gd",
  ),
  Account(
    username="thautwarm",
    password="996icu",
  ),
]
[ Info: after_extension
[
  Account(van),
  Account(thautwarm),
]
5 Likes

Omg, I’ve just found a package called PrettyPrinting.jl a

3 Likes

Okay, mine is simpler, within fewer features.

Unrelated to pretty printing, I’m interested in your RBNF package, can I find more info about it apart from in the code about what you’re trying to do and, for instance, how it compares to PEG packages like PEG.jl or the older PEGParser.jl or Parsimonious.jl or ParserCombinator (I imagine there may be more such packages too).

Sorry if they’re completely different, I don’t know a lot about parsers, but I’d love to know more about the differences and use-cases. Thanks!

3 Likes

Congratulations on tackling this hard problem. Having pretty printed structures is really important for the development and documentation of works. I’m sure the Julia ecosystem would be very well served with various options to how this problem could be approached. Furthermore, there is always the matter of aesthetics – to each person, some ways just appeal more than others.

PrettyPrinting.jl is based upon the algorithm described by Phillip M. Yelland’s A New Approach to Optimal Code Formatting. I believe the algorithm minimizes ugliness across two dimensions. You can define options for how your custom types could be shown vertically and horizontally, then the core algorithm can pick/choose those options based upon how instances of that type would fit into a broader composition.The hard part is finding an algorithm and implementation that is complex enough to handle the common challenges, but has a simple user interface with the minimum necessary code that it could be maintained and understood.

Kyrylo Simonov wrote Pretty Printing to support our work on DataKnots.jl. We needed to show complex query plans that were optimized to the size of the screen. Probably it won’t scale to very large outputs, but it’s a very nice algorithm for reformatting code, such as the popular gofmt or rfmt. We’ve also found the approach to not support beautifully formatted tables, and have had to write our own code for that. That said, Pretty Printing is especially useful in documentation style test suites, such as NarrativeTest.jl, which we use for DataKnot’s documentation. Readable, informative, and succinct documentation is the cornerstone of accessible open source projects.

Anyway, if you’re looking to help explain, document, provide test cases, and otherwise improve the implementation of pretty printing approaches, you’d be very welcome to contribute to PrettyPrinting. Some of the most important work in an open source project isn’t the initial bootstrap, it’s the ongoing explanations, assistance, documentation, showcases, testing, and what not. Given that you’ve already implemented something similar, this puts you in an even more beloved position as far as other collaborators are concerned. Pretty Printing was written of necessity, could use much more refinement, and is under the MIT Expat license for everyone to enjoy, use, extend, etc. Good luck and have fun!

3 Likes

Pay attention that as we can in the code became as we cam after printing (Both in the opening post and on the GitHub page).

Have you looked at PrettyTables.jl?

2 Likes

Yes, in original post I made this mistake. I think I’ve fixed this and it’s a meme(

Hi. I’ve been in awe of PrettyTables.jl. In fact, being able to use this and import/export to CSV and DataFrame is what prompted me to add support for Tables.jl. When exporting tabular data after a query, having a beautiful format is delicious.

Sadly, our knot structure isn’t really table, it’s a tree (and soon to be a directed graph). So, we pretty much had to write custom layout code. To get an idea of the kinds of outputs we need, you could scan our tutorial – it’s mostly tabular, except when it isn’t: some outputs there are trees that are flattened with undocumented conventions using ; and ,. The eventual visualization in a notebook would be dynamic using HTML, since any tree would necessarily have to be projected as a series of nested tables reflecting particular cross-section of the output.

Perhaps we could have some shared code to visualize trees? The cases include: simple scalars, a singular tuple (not a vector), a vector of scalars, an actual table, and then, nested versions of the above where any cell could be one of those. We have called the projection of a tree onto a set of nested tables a “focus”, since you necessarily have to go down one path or the output would be misleading (ie, parallel, nested tables really confuse users, they see unintended correlations).

2 Likes

Hi, RBNF is invented on my own, which is similar to EBNF but not the same though. I created this to address the complexity of taking advantage of ASTs(abstract syntax trees) that’re given by the generated parser.
I mean what the parser generated by RBNF can produce reasonable and easy-to-use data structures, for instance, following rule defines a grammar for parsing syntaxes like let a = 1 in a + 1, and the parsing output is simply a Let(bind=..., value=..., body=..) instead of a S-Expr/CST like ['let', someIdentifierNode, '=', someExprNode, 'in', someExprNode].

Let := 'let' bind=Identifier '=' value=Expr 'in'  body=Expr 

Also, many other handy features are provided by RBNF, say, auto-generated lexers, you can see in the README I didn’t write lexers like that in YACC/Antlr.

Another I’d say is the context sensitive part, however in RBNF.jl it’s not implemented, but you can check https://github.com/thautwarm/RBNF, which also supports arbitrary left recursions. My friends and I have created a python bytecode compiler written in pure python, whose parser was written in RBNF.py and has benefited from context sensitive parsing.

You might want to check a bunch of mildly context-sensitive parsers, my prospective mentor has told me that RBNF might be one of them, and I’m also planning to make RBNF my first graduate paper.

If you’re interested in this immature technique, we can discuss more about this in Slack. My real name(also Slack name) is Taine Zhao and you can contact me there!

2 Likes

Thanks for your kind words, in fact I didn’t know such a history of pretty print previously. And you’ve referred to the matter of aesthetics, which I think an interesting task to work with. Maybe we can find out a way to customize the style in module level, then the pprint could be more fantastic to each individual.

The hard part is finding an algorithm and implementation that is complex enough to handle the common challenges.

Don’t have an idea about that, but in this domain I know a pretty good practice in F# language, called StructuredFormat. However the documentations of Microsoft are always not that good(but I think in terms of only techniques they are awesome).
I appreciate your works on DataKnots.jl, in fact I do have some experience in writing query plans or SQL parsing and am capable of understanding its value and complexity.

Also, the NarrativeTest.jl is funny, I like it! I think it does help the tests that’re not elaborate enough.

However, I don’t think PrettyPrinting.jl needs more iterations, it has a theoretic basis and has been used for many existing packages. A funny story is, in my local dev, I used to find PrettyPrinting.jl, but at that time I thought I might name my project incorrectly or use PkgTemplates.jl incorrectly, and I didn’t notice that it’s an existing pretty print package! Before I made PrettyPrint.jl I did search in Google about Julia pretty print but, unfortunately, got nothing(only this extremely outdated package: https://github.com/samuelcolvin/PrettyPrint.jl). If I found there was already PrettyPrinting.jl, I wouldn’t write PrettyPrint.jl.

1 Like