From fd6ed348048d9a2b683700a4240cc53c19d6e0df Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 16 Nov 2023 14:51:52 -0800 Subject: [PATCH] Better debug output for piece trees. (#1324) * Better debug output for piece trees. The old formatter has pretty nice debug output, but the new one is still pretty rudimentary. This improves that situation somewhat from: ``` Sequence(Type(`class Foo` Block(`{` Sequence(Var(`static late final int` `i;`) Var(`static late int` `i;`) Var(`static late var` `i;`) Var(`covariant late var` `i;`) Var(`covariant late int` `i;`) Var(`late final int` `i;`) Var(`late int` `i;`) Var(`late var` `i;`)) `}`))) ``` To: ``` Seq( Type( `class Foo` Block( `{` Seq( Var(`static late final int` `i;`) Var(`static late int` `i;`) Var(`static late var` `i;`) Var(`covariant late var` `i;`) Var(`covariant late int` `i;`) Var(`late final int` `i;`) Var(`late int` `i;`) Var(`late var` `i;`) ) `}` ) ) ) ``` It also eliminates a bunch of pointless `toString()` implementations that are only used for debugging and can be inferred from runtimeType. * Update doc comment. --- lib/src/debug.dart | 74 ++++++++++++++++++++++++++++++------- lib/src/piece/adjacent.dart | 3 -- lib/src/piece/assign.dart | 3 -- lib/src/piece/block.dart | 3 -- lib/src/piece/chain.dart | 3 -- lib/src/piece/clause.dart | 6 --- lib/src/piece/do_while.dart | 3 -- lib/src/piece/for.dart | 3 -- lib/src/piece/function.dart | 2 +- lib/src/piece/if.dart | 3 -- lib/src/piece/infix.dart | 3 -- lib/src/piece/list.dart | 3 -- lib/src/piece/piece.dart | 8 ++++ lib/src/piece/postfix.dart | 3 -- lib/src/piece/sequence.dart | 2 +- lib/src/piece/type.dart | 3 -- lib/src/piece/variable.dart | 2 +- 17 files changed, 72 insertions(+), 55 deletions(-) diff --git a/lib/src/debug.dart b/lib/src/debug.dart index 0b16c954..7fcfaf2b 100644 --- a/lib/src/debug.dart +++ b/lib/src/debug.dart @@ -268,25 +268,73 @@ void dumpLines(List chunks, SplitSet splits) { /// Build a string representation of the [piece] tree. String pieceTree(Piece piece) { var buffer = StringBuffer(); + _PieceDebugTree(piece).write(buffer, 0); + return buffer.toString(); +} - void traverse(Piece piece) { - buffer.write(piece); +/// A stringified representation of a tree of pieces for debug output. +class _PieceDebugTree { + final String label; + final List<_PieceDebugTree> children = []; - if (piece is! TextPiece) { - var children = []; - piece.forEachChild(children.add); + _PieceDebugTree(Piece piece) : label = piece.toString() { + piece.forEachChild((child) { + children.add(_PieceDebugTree(child)); + }); + } - buffer.write('('); - for (var child in children) { - if (child != children.first) buffer.write(' '); - traverse(child); - } - buffer.write(')'); + /// The approximate number of characters of output needed to print this tree + /// on a single line. + /// + /// Used to determine when to show a tree's children inline or split. Note + /// that this is O(n^2), but we don't really care since it's only used for + /// debug output. + int get width { + var result = label.length; + for (var child in children) { + result += child.width; } + return result; } - traverse(piece); - return buffer.toString(); + void write(StringBuffer buffer, int indent) { + buffer.write(label); + if (children.isEmpty) return; + + buffer.write('('); + + // Split the tree if it is too long. + var isSplit = indent * 2 + width > 80; + if (isSplit) { + indent++; + buffer.writeln(); + buffer.write(' ' * indent); + } + + var first = true; + for (var child in children) { + if (!first) { + if (isSplit) { + buffer.writeln(); + buffer.write(' ' * indent); + } else { + buffer.write(' '); + } + } + + child.write(buffer, indent); + + first = false; + } + + if (isSplit) { + indent--; + buffer.writeln(); + buffer.write(' ' * indent); + } + + buffer.write(')'); + } } String _color(String ansiEscape) => useAnsiColors ? ansiEscape : ''; diff --git a/lib/src/piece/adjacent.dart b/lib/src/piece/adjacent.dart index 47ada2a9..24bf2fdc 100644 --- a/lib/src/piece/adjacent.dart +++ b/lib/src/piece/adjacent.dart @@ -21,7 +21,4 @@ class AdjacentPiece extends Piece { void forEachChild(void Function(Piece piece) callback) { _pieces.forEach(callback); } - - @override - String toString() => 'Adjacent'; } diff --git a/lib/src/piece/assign.dart b/lib/src/piece/assign.dart index a17ac862..0bf2cf73 100644 --- a/lib/src/piece/assign.dart +++ b/lib/src/piece/assign.dart @@ -129,7 +129,4 @@ class AssignPiece extends Piece { callback(target); callback(value); } - - @override - String toString() => 'Assign'; } diff --git a/lib/src/piece/block.dart b/lib/src/piece/block.dart index b2ac2967..d6cc9a16 100644 --- a/lib/src/piece/block.dart +++ b/lib/src/piece/block.dart @@ -55,7 +55,4 @@ class BlockPiece extends Piece { callback(contents); callback(rightBracket); } - - @override - String toString() => 'Block'; } diff --git a/lib/src/piece/chain.dart b/lib/src/piece/chain.dart index e4612f2b..d5e9a984 100644 --- a/lib/src/piece/chain.dart +++ b/lib/src/piece/chain.dart @@ -46,7 +46,4 @@ class ChainPiece extends Piece { void forEachChild(void Function(Piece piece) callback) { _operations.forEach(callback); } - - @override - String toString() => 'Chain'; } diff --git a/lib/src/piece/clause.dart b/lib/src/piece/clause.dart index 63834b7e..09006755 100644 --- a/lib/src/piece/clause.dart +++ b/lib/src/piece/clause.dart @@ -121,9 +121,6 @@ class ClausesPiece extends Piece { void forEachChild(void Function(Piece piece) callback) { _clauses.forEach(callback); } - - @override - String toString() => 'Clauses'; } /// A keyword followed by a comma-separated list of items described by that @@ -156,7 +153,4 @@ class ClausePiece extends Piece { callback(_keyword); _parts.forEach(callback); } - - @override - String toString() => 'Clause'; } diff --git a/lib/src/piece/do_while.dart b/lib/src/piece/do_while.dart index 10a9ce16..892ccd78 100644 --- a/lib/src/piece/do_while.dart +++ b/lib/src/piece/do_while.dart @@ -25,7 +25,4 @@ class DoWhilePiece extends Piece { callback(_body); callback(_condition); } - - @override - String toString() => 'DoWhile'; } diff --git a/lib/src/piece/for.dart b/lib/src/piece/for.dart index 2e87fdff..ecc48df6 100644 --- a/lib/src/piece/for.dart +++ b/lib/src/piece/for.dart @@ -59,7 +59,4 @@ class ForPiece extends Piece { callback(_forParts); callback(_body); } - - @override - String toString() => 'For'; } diff --git a/lib/src/piece/function.dart b/lib/src/piece/function.dart index 5a519800..a42aa373 100644 --- a/lib/src/piece/function.dart +++ b/lib/src/piece/function.dart @@ -56,5 +56,5 @@ class FunctionPiece extends Piece { } @override - String toString() => 'Fn'; + String get debugName => 'Fn'; } diff --git a/lib/src/piece/if.dart b/lib/src/piece/if.dart index 32d84abe..9e3a8d2b 100644 --- a/lib/src/piece/if.dart +++ b/lib/src/piece/if.dart @@ -75,9 +75,6 @@ class IfPiece extends Piece { callback(section.statement); } } - - @override - String toString() => 'If'; } /// A single section in a chain of if-elses. diff --git a/lib/src/piece/infix.dart b/lib/src/piece/infix.dart index de29e6ea..9bc2ef17 100644 --- a/lib/src/piece/infix.dart +++ b/lib/src/piece/infix.dart @@ -42,7 +42,4 @@ class InfixPiece extends Piece { void forEachChild(void Function(Piece piece) callback) { operands.forEach(callback); } - - @override - String toString() => 'Infix'; } diff --git a/lib/src/piece/list.dart b/lib/src/piece/list.dart index b5b9d2ff..c46a13e2 100644 --- a/lib/src/piece/list.dart +++ b/lib/src/piece/list.dart @@ -123,9 +123,6 @@ class ListPiece extends Piece { if (_after case var after?) callback(after); } - - @override - String toString() => 'List'; } /// An element in a [ListPiece]. diff --git a/lib/src/piece/piece.dart b/lib/src/piece/piece.dart index a5031a13..e7ef9075 100644 --- a/lib/src/piece/piece.dart +++ b/lib/src/piece/piece.dart @@ -59,6 +59,14 @@ abstract class Piece { void pin(State state) { _pinnedState = state; } + + /// The name of this piece as it appears in debug output. + /// + /// By default, this is the class's name with `Piece` removed. + String get debugName => runtimeType.toString().replaceAll('Piece', ''); + + @override + String toString() => debugName; } /// A simple atomic piece of code. diff --git a/lib/src/piece/postfix.dart b/lib/src/piece/postfix.dart index 432ea012..ca6242e2 100644 --- a/lib/src/piece/postfix.dart +++ b/lib/src/piece/postfix.dart @@ -46,7 +46,4 @@ class PostfixPiece extends Piece { void forEachChild(void Function(Piece piece) callback) { pieces.forEach(callback); } - - @override - String toString() => 'Post'; } diff --git a/lib/src/piece/sequence.dart b/lib/src/piece/sequence.dart index 6dc2cf45..c428d45c 100644 --- a/lib/src/piece/sequence.dart +++ b/lib/src/piece/sequence.dart @@ -39,7 +39,7 @@ class SequencePiece extends Piece { } @override - String toString() => 'Sequence'; + String get debugName => 'Seq'; } /// An element inside a [SequencePiece]. diff --git a/lib/src/piece/type.dart b/lib/src/piece/type.dart index 88803009..c037c0c0 100644 --- a/lib/src/piece/type.dart +++ b/lib/src/piece/type.dart @@ -39,7 +39,4 @@ class TypePiece extends Piece { if (_clauses case var clauses?) callback(clauses); callback(_body); } - - @override - String toString() => 'Type'; } diff --git a/lib/src/piece/variable.dart b/lib/src/piece/variable.dart index 3eb88881..ec0d65a8 100644 --- a/lib/src/piece/variable.dart +++ b/lib/src/piece/variable.dart @@ -95,5 +95,5 @@ class VariablePiece extends Piece { } @override - String toString() => 'Var'; + String get debugName => 'Var'; }