Three ways to pretty print Perl 6

I’ve had my head down working on the “Grammars” chapter, and along
the way I’ve been using dd to look at things. It’s better than nothing, but it’s also very compact and unwrapped. I really wanted a pretty printer, and I was even prepared to write my own. But, I don’t have to. Here are three modules that can do it for you.

But Jeff Goff has already written Pretty::Printer. I wasn’t able to install it with panda or zef (and it’s not listed on modules.perl6.org). But, you can get it from the perl6-pp GitHub repo.

Here’s a structure that I want to inspect. It includes a Match object with a named capture:

use v6;
use lib qw/lib/;

my $match = ('123456789' ~~ m:g/$<digit>=(\d+:)/);

my %hash =
	dog => 'Nikki',
	cat => 'Buster',
	butterfly => 'Hamadryas',
	array => [ 5 .. 10 ],
	complex => {
		one => 37,
		two => "Buster",
		three => [ 3..7 ],
		four => {
			array => [ 4..7 ],
			hash  => {
				dog => 'Newfie',
				}
			},
		},
	match => $match;
	;

I can use the built-in dd to get this very compact representation that just needs whitespace in the right places:

Hash %hash = {:array($[5, 6, 7, 8, 9, 10]), :butterfly("Hamadryas"), :cat("Buster"), :complex(${:four(${:array($[4, 5, 6, 7]), :hash(${:dog("Newfie")})}), :one(37), :three($[3, 4, 5, 6, 7]), :two("Buster")}), :dog("Nikki"), :match($(Match.new(ast => Any, list => (), hash => Map.new((:digit(Match.new(ast => Any, list => (), hash => Map.new(()), orig => "123456789", to => 9, from => 0)))), orig => "123456789", to => 9, from => 0),))}

With Pretty::Printer (using the pull request I just sent), I can choose some formatting options:

use Pretty::Printer;

my $pp = Pretty::Printer.new(
	pre-item-spacing       => "\n",
	post-item-spacing      => "\n",

	intra-group-spacing    => "",

	pre-separator-spacing  => '',
	post-separator-spacing => "\n",

	indent-style           => "   ",
	);

say $pp.pp( %hash );

The output is a bit rough, but it’s much easier for me to read, especially with the match stuff:

${
   :array($[
      5,
      6,
      7,
      8,
      9,
      10
   ]),
   :butterfly("Hamadryas"),
   :cat("Buster"),
   :complex(${
      :four(${
         :array($[
            4,
            5,
            6,
            7
         ]),
         :hash(${
            :dog("Newfie")
         })
      }),
      :one(37),
      :three($[
         3,
         4,
         5,
         6,
         7
      ]),
      :two("Buster")
   }),
   :dog("Nikki"),
   :match($(
         Match.new(${
                    :ast(Any),
                    :from(0),
                    :hash(Map.new(
                       :digit(Match.new(${
                              :ast(Any),
                              :from(0),
                              :hash(Map.new()),
                              :list($()),
                              :orig("123456789"),
                              :to(9)
                       })))),
                    :list($()),
                    :orig("123456789"),
                    :to(9)
         })
   ))
}

There’s also Data::Dump (which you can install with panda:

use Data::Dump;
say Dump %hash;

I find the output is a bit too detailed for most of my needs, but does fine with the Match object. Although you can’t see it here, the output can be colored if your terminal supports that:

{
  array     => [
    5.Int,
    6.Int,
    7.Int,
    8.Int,
    9.Int,
    10.Int,
  ],
  butterfly => "Hamadryas".Str,
  cat       => "Buster".Str,
  complex   => {
    four  => {
      array => [
        4.Int,
        5.Int,
        6.Int,
        7.Int,
      ],
      hash  => {
        dog => "Newfie".Str,
      },
    },
    one   => 37.Int,
    three => [
      3.Int,
      4.Int,
      5.Int,
      6.Int,
      7.Int,
    ],
    two   => "Buster".Str,
  },
  dog       => "Nikki".Str,
  match     => [
    Match :: (
      $!CURSOR => undefined,
      $!from  => 0.Int,
      $!made  => (Any),
      $!orig  => "123456789".Str,
      $!to    => 9.Int,
      %!hash  => {
        digit => Match :: (
          $!CURSOR => undefined,
          $!from  => 0.Int,
          $!made  => (Any),
          $!orig  => "123456789".Str,
          $!to    => 9.Int,
          %!hash  => { },
          @!list  => [ ],

          method ACCEPTS () returns Mu {...},
          method BUILD (:@list, :%hash) returns Nil {...},
          method Bool () returns Mu {...},
          method Bool () returns Mu {...},
          method CURSOR () returns Mu {...},
          method Capture () returns Mu {...},
          method FLATTENABLE_HASH () returns Mu {...},
          method FLATTENABLE_LIST () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Method+{}.new () returns Mu {...},
          method Numeric () returns Mu {...},
          method Numeric () returns Mu {...},
          method Str () returns Mu {...},
          method Str () returns Mu {...},
          method WHICH () returns Mu {...},
          method ast () returns Mu {...},
          method caps () returns Mu {...},
          method chunks () returns Mu {...},
          method elems () returns Mu {...},
          method from () returns Mu {...},
          method from-args () returns Mu {...},
          method gist () returns Mu {...},
          method gist () returns Mu {...},
          method hash () returns Mu {...},
          method list () returns Mu {...},
          method made () returns Mu {...},
          method make (Mu \made) returns Mu {...},
          method new (:$orig, :$from, :$to, :$CURSOR, :$made) returns Mu {...},
          method new (:@list, :%hash) returns Mu {...},
          method orig () returns Mu {...},
          method perl () returns Mu {...},
          method perl () returns Mu {...},
          method postmatch () returns Mu {...},
          method prematch () returns Mu {...},
          method to () returns Mu {...},
        ),
      },
      @!list  => [ ],

      method ACCEPTS () returns Mu {...},
      method BUILD (:@list, :%hash) returns Nil {...},
      method Bool () returns Mu {...},
      method Bool () returns Mu {...},
      method CURSOR () returns Mu {...},
      method Capture () returns Mu {...},
      method FLATTENABLE_HASH () returns Mu {...},
      method FLATTENABLE_LIST () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Method+{<anon|140185815714976>}.new () returns Mu {...},
      method Numeric () returns Mu {...},
      method Numeric () returns Mu {...},
      method Str () returns Mu {...},
      method Str () returns Mu {...},
      method WHICH () returns Mu {...},
      method ast () returns Mu {...},
      method caps () returns Mu {...},
      method chunks () returns Mu {...},
      method elems () returns Mu {...},
      method from () returns Mu {...},
      method from-args () returns Mu {...},
      method gist () returns Mu {...},
      method gist () returns Mu {...},
      method hash () returns Mu {...},
      method list () returns Mu {...},
      method made () returns Mu {...},
      method make (Mu \made) returns Mu {...},
      method new (:$orig, :$from, :$to, :$CURSOR, :$made) returns Mu {...},
      method new (:@list, :%hash) returns Mu {...},
      method orig () returns Mu {...},
      method perl () returns Mu {...},
      method perl () returns Mu {...},
      method postmatch () returns Mu {...},
      method prematch () returns Mu {...},
      method to () returns Mu {...},
    ),
  ],
}

And, there’s Data::Dump::Tree, which is also colorized.

use Data::Dump::Tree;
say dump %hash;

It might be more comfortable for people used to the Microsoft hierarchical interfaces, but notice how it’s not that interesting for Match objects:

{6} @0
├ array => [6] @1
│ ├ 0 = 5.Int
│ ├ 1 = 6.Int
│ ├ 2 = 7.Int
│ ├ 3 = 8.Int
│ ├ 4 = 9.Int
│ └ 5 = 10.Int
├ butterfly => Hamadryas.Str
├ cat => Buster.Str
├ complex => {4} @2
│ ├ four => {2} @3
│ │ ├ array => [4] @4
│ │ │ ├ 0 = 4.Int
│ │ │ ├ 1 = 5.Int
│ │ │ ├ 2 = 6.Int
│ │ │ └ 3 = 7.Int
│ │ └ hash => {1} @5
│ │   └ dog => Newfie.Str
│ ├ one => 37.Int
│ ├ three => [5] @6
│ │ ├ 0 = 3.Int
│ │ ├ 1 = 4.Int
│ │ ├ 2 = 5.Int
│ │ ├ 3 = 6.Int
│ │ └ 4 = 7.Int
│ └ two => Buster.Str
├ dog => Nikki.Str
└ match => (1) @7
  └ 0 = 123456789[0..9|

4 comments

  1. Well, It’s interesting to read that Data::Dump::Tree is more interesting for users that like Microsoft type of display(at least I had a good laugh 🙂 ).

    If you want Match object details use the right role, as described in the documentation. The idea is to not have the match objects when you do not want them.

    a lighter introduction in this advent entry https://perl6advent.wordpress.com/2016/12/21/ with a good example of how matches are handled compared to other dumpers

    or the full documentation here: https://github.com/nkh/P6-Data-Dump-Tree

  2. About the very last line of the article:
    match => (1) @7
    └ 0 = 123456789[0..9|

    Surely that should be [0..8] or [0..^9]. According to https://docs.perl6.org/type/Range, “1 .. 5; # 1 <= $x <= 5", and 123456789 only has nine elements, with indices '0' .. '8'.

    I realize it's the Data:;Dump::Tree writer's problem, not yours, but you published it 🙂

    Tom

    1. Hi Tom,

      the final glyph is ‘|’, not ‘]’, the pipe symbol. it is the same as what .gistl uses and means ‘excluding’ or ‘up to’; so the above dump is correct.

      I also find that disturbing so it was changed in Data::Dump::Tree a five weeks ago and now it is displayed ‘inclusive’ with matching square brackets and no ‘..’ if the range contains only one element.

Leave a Reply to Tom Legrady Cancel reply

Your email address will not be published. Required fields are marked *