Skip to content

Commit 19f42a9

Browse files
Revamp the PieceWriter API some. (#1298)
Revamp the PieceWriter API some. The current API is pretty confusing around when you should use `pop()`, `split()`, or both. If you get it wrong, sometimes it silently works but may leave a bug for later, or sometimes it fails it seemingly unrelated ways. Also, it's not clear whether you should call `pop()` first or `split()`. (It actually didn't matter.) I tried to simplify the API some. It's still more subtle and brittle than I would like, but it's not clear to me if the complexity is essential. I'm trying to build Piece trees that don't have unnecessary nodes, so some slippage between AST node boundaries and Piece tree boundaries is essential. I hope this API is better. The changes are: - Make `split()` implicitly `pop()` the previous piece. This means you never need to call both `split()` and `pop()`. Just call `split()`. You do still have to know when to prefer `pop()` instead of `split()`, but I added some documentation to try to clarify that. - Rename the main PieceWriter field from `writer` to `pieces`. I think this reads a little better at the callsites and makes it clearer what is being pushed, popped, and split. "Writer" doesn't really mean much. - Rename `space()` to `writeSpace()`. Then add a helper method in PieceFactory called `space()`. This way, almost all calls on `pieces` deal with pieces and calls that recurse into or write pieces of an AST are bare function calls: `token()`, `visit()`, and `space()`. - Change `push()` to `give()` and `pop()` to `take()`. Co-authored-by: Nate Bosch <[email protected]>
1 parent cca55a3 commit 19f42a9

File tree

6 files changed

+256
-208
lines changed

6 files changed

+256
-208
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 53 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,11 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
3737
final LineInfo lineInfo;
3838

3939
@override
40-
final PieceWriter writer;
40+
final PieceWriter pieces;
4141

42-
/// Initialize a newly created visitor to write source code representing
43-
/// the visited nodes to the given [writer].
42+
/// Create a new visitor that will be called to visit the code in [source].
4443
AstNodeVisitor(DartFormatter formatter, this.lineInfo, SourceCode source)
45-
: writer = PieceWriter(formatter, source);
44+
: pieces = PieceWriter(formatter, source);
4645

4746
/// Visits [node] and returns the formatted result.
4847
///
@@ -86,10 +85,10 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
8685
// Write any comments at the end of the code.
8786
sequence.addCommentsBefore(node.endToken.next!);
8887

89-
writer.push(sequence.build());
88+
pieces.give(sequence.build());
9089

9190
// Finish writing and return the complete result.
92-
return writer.finish();
91+
return pieces.finish();
9392
}
9493

9594
@override
@@ -130,7 +129,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
130129
@override
131130
void visitAssignmentExpression(AssignmentExpression node) {
132131
visit(node.leftHandSide);
133-
writer.space();
132+
space();
134133
finishAssignment(node.operator, node.rightHandSide);
135134
}
136135

@@ -219,19 +218,17 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
219218
@override
220219
void visitConditionalExpression(ConditionalExpression node) {
221220
visit(node.condition);
222-
var condition = writer.pop();
223-
writer.split();
221+
var condition = pieces.split();
224222

225223
token(node.question);
226-
writer.space();
224+
space();
227225
visit(node.thenExpression);
228-
var thenBranch = writer.pop();
229-
writer.split();
226+
var thenBranch = pieces.split();
230227

231228
token(node.colon);
232-
writer.space();
229+
space();
233230
visit(node.elseExpression);
234-
var elseBranch = writer.pop();
231+
var elseBranch = pieces.take();
235232

236233
var piece = InfixPiece([condition, thenBranch, elseBranch]);
237234

@@ -243,13 +240,13 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
243240
piece.pin(State.split);
244241
}
245242

246-
writer.push(piece);
243+
pieces.give(piece);
247244
}
248245

249246
@override
250247
void visitConfiguration(Configuration node) {
251248
token(node.ifKeyword);
252-
writer.space();
249+
space();
253250
token(node.leftParenthesis);
254251

255252
if (node.equalToken case var equals?) {
@@ -259,7 +256,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
259256
}
260257

261258
token(node.rightParenthesis);
262-
writer.space();
259+
space();
263260
visit(node.uri);
264261
}
265262

@@ -311,20 +308,19 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
311308
@override
312309
void visitDoStatement(DoStatement node) {
313310
token(node.doKeyword);
314-
writer.space();
311+
space();
315312
visit(node.body);
316-
writer.space();
313+
space();
317314
token(node.whileKeyword);
318-
var body = writer.pop();
319-
writer.split();
315+
var body = pieces.split();
320316

321317
token(node.leftParenthesis);
322318
visit(node.condition);
323319
token(node.rightParenthesis);
324320
token(node.semicolon);
325-
var condition = writer.pop();
321+
var condition = pieces.take();
326322

327-
writer.push(DoWhilePiece(body, condition));
323+
pieces.give(DoWhilePiece(body, condition));
328324
}
329325

330326
@override
@@ -417,7 +413,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
417413
}
418414

419415
builder.rightBracket(node.rightParenthesis, delimiter: node.rightDelimiter);
420-
writer.push(builder.build());
416+
pieces.give(builder.build());
421417
}
422418

423419
@override
@@ -428,8 +424,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
428424
@override
429425
void visitForStatement(ForStatement node) {
430426
token(node.forKeyword);
431-
writer.split();
432-
var forKeyword = writer.pop();
427+
var forKeyword = pieces.split();
433428

434429
// Treat the for loop parts sort of as an argument list where each clause
435430
// is a separate argument. This means that when they split, they split like:
@@ -485,13 +480,13 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
485480
}
486481

487482
partsList.rightBracket(node.rightParenthesis);
488-
writer.split();
489483
var forPartsPiece = partsList.build();
490484

485+
pieces.split();
491486
visit(node.body);
492-
var body = writer.pop();
487+
var body = pieces.take();
493488

494-
writer.push(ForPiece(forKeyword, forPartsPiece, body,
489+
pieces.give(ForPiece(forKeyword, forPartsPiece, body,
495490
hasBlockBody: node.body is Block));
496491
}
497492

@@ -649,14 +644,14 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
649644
}
650645

651646
sequence.visit(node.statement);
652-
writer.push(sequence.build());
647+
pieces.give(sequence.build());
653648
}
654649

655650
@override
656651
void visitLibraryDirective(LibraryDirective node) {
657652
createDirectiveMetadata(node);
658653
token(node.libraryKeyword);
659-
visit(node.name2, before: writer.space);
654+
visit(node.name2, before: space);
660655
token(node.semicolon);
661656
}
662657

@@ -794,7 +789,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
794789
void visitPartDirective(PartDirective node) {
795790
createDirectiveMetadata(node);
796791
token(node.partKeyword);
797-
writer.space();
792+
space();
798793
visit(node.uri);
799794
token(node.semicolon);
800795
}
@@ -803,9 +798,9 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
803798
void visitPartOfDirective(PartOfDirective node) {
804799
createDirectiveMetadata(node);
805800
token(node.partKeyword);
806-
writer.space();
801+
space();
807802
token(node.ofKeyword);
808-
writer.space();
803+
space();
809804

810805
// Part-of may have either a name or a URI. Only one of these will be
811806
// non-null. We visit both since visit() ignores null.
@@ -908,7 +903,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
908903
token(node.returnKeyword);
909904

910905
if (node.expression case var expression) {
911-
writer.space();
906+
space();
912907
visit(expression);
913908
}
914909

@@ -922,7 +917,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
922917
// come at the top of the file, we don't have to worry about preceding
923918
// comments or whitespace.
924919
// TODO(new-ir): Update selection if inside the script tag.
925-
writer.write(node.scriptTag.lexeme.trim());
920+
pieces.write(node.scriptTag.lexeme.trim());
926921
}
927922

928923
@override
@@ -948,14 +943,12 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
948943
if ((node.type, node.name) case (var type?, var name?)) {
949944
// Have both a type and name, so allow splitting between them.
950945
visit(type);
951-
var typePiece = writer.pop();
952-
writer.split();
946+
var typePiece = pieces.split();
953947

954948
token(name);
955-
var namePiece = writer.pop();
956-
writer.split();
949+
var namePiece = pieces.take();
957950

958-
writer.push(VariablePiece(typePiece, [namePiece], hasType: true));
951+
pieces.give(VariablePiece(typePiece, [namePiece], hasType: true));
959952
} else {
960953
// Only one of name or type so just write whichever there is.
961954
visit(node.type);
@@ -1005,23 +998,23 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
1005998

1006999
createSwitchValue(node.switchKeyword, node.leftParenthesis, node.expression,
10071000
node.rightParenthesis);
1008-
writer.space();
1001+
space();
10091002
list.leftBracket(node.leftBracket);
10101003

10111004
for (var member in node.cases) {
10121005
list.visit(member);
10131006
}
10141007

10151008
list.rightBracket(node.rightBracket);
1016-
writer.push(list.build());
1009+
pieces.give(list.build());
10171010
}
10181011

10191012
@override
10201013
void visitSwitchExpressionCase(SwitchExpressionCase node) {
10211014
if (node.guardedPattern.whenClause != null) throw UnimplementedError();
10221015

10231016
visit(node.guardedPattern.pattern);
1024-
writer.space();
1017+
space();
10251018
finishAssignment(node.arrow, node.expression);
10261019
}
10271020

@@ -1032,10 +1025,9 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
10321025

10331026
// Attach the ` {` after the `)` in the [ListPiece] created by
10341027
// [createSwitchValue()].
1035-
writer.space();
1028+
space();
10361029
token(node.leftBracket);
1037-
writer.split();
1038-
var switchPiece = writer.pop();
1030+
var switchPiece = pieces.split();
10391031

10401032
var sequence = SequenceBuilder(this);
10411033
for (var member in node.members) {
@@ -1047,10 +1039,10 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
10471039
token(member.keyword);
10481040

10491041
if (member is SwitchCase) {
1050-
writer.space();
1042+
space();
10511043
visit(member.expression);
10521044
} else if (member is SwitchPatternCase) {
1053-
writer.space();
1045+
space();
10541046

10551047
if (member.guardedPattern.whenClause != null) {
10561048
throw UnimplementedError();
@@ -1063,8 +1055,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
10631055
}
10641056

10651057
token(member.colon);
1066-
var casePiece = writer.pop();
1067-
writer.split();
1058+
var casePiece = pieces.split();
10681059

10691060
// Don't allow any blank lines between the `case` line and the first
10701061
// statement in the case (or the next case if this case has no body).
@@ -1079,9 +1070,9 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
10791070
sequence.addCommentsBefore(node.rightBracket);
10801071

10811072
token(node.rightBracket);
1082-
var rightBracketPiece = writer.pop();
1073+
var rightBracketPiece = pieces.take();
10831074

1084-
writer.push(BlockPiece(switchPiece, sequence.build(), rightBracketPiece,
1075+
pieces.give(BlockPiece(switchPiece, sequence.build(), rightBracketPiece,
10851076
alwaysSplit: node.members.isNotEmpty));
10861077
}
10871078

@@ -1126,7 +1117,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
11261117
void visitTypeParameter(TypeParameter node) {
11271118
token(node.name);
11281119
if (node.bound case var bound?) {
1129-
writer.space();
1120+
space();
11301121
modifier(node.extendsKeyword);
11311122
visit(bound);
11321123
}
@@ -1157,17 +1148,17 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
11571148
// argument list or a function type's parameter list) affect the indentation
11581149
// and splitting of the surrounding variable declaration.
11591150
visit(node.type);
1160-
var header = writer.pop();
1151+
var header = pieces.take();
11611152

11621153
var variables = <Piece>[];
11631154
for (var variable in node.variables) {
1164-
writer.split();
1155+
pieces.split();
11651156
visit(variable);
11661157
commaAfter(variable);
1167-
variables.add(writer.pop());
1158+
variables.add(pieces.take());
11681159
}
11691160

1170-
writer.push(VariablePiece(header, variables, hasType: node.type != null));
1161+
pieces.give(VariablePiece(header, variables, hasType: node.type != null));
11711162
}
11721163

11731164
@override
@@ -1179,19 +1170,18 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
11791170
@override
11801171
void visitWhileStatement(WhileStatement node) {
11811172
token(node.whileKeyword);
1182-
writer.space();
1173+
space();
11831174
token(node.leftParenthesis);
11841175
visit(node.condition);
11851176
token(node.rightParenthesis);
1186-
var condition = writer.pop();
1187-
writer.split();
1177+
var condition = pieces.split();
11881178

11891179
visit(node.body);
1190-
var body = writer.pop();
1180+
var body = pieces.take();
11911181

11921182
var piece = IfPiece();
11931183
piece.add(condition, body, isBlock: node.body is Block);
1194-
writer.push(piece);
1184+
pieces.give(piece);
11951185
}
11961186

11971187
@override

lib/src/front_end/comment_writer.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import 'piece_writer.dart';
4343
// TODO(tall): When argument lists and their comment handling is supported,
4444
// mention that here.
4545
mixin CommentWriter {
46-
PieceWriter get writer;
46+
PieceWriter get pieces;
4747

4848
LineInfo get lineInfo;
4949

@@ -78,20 +78,20 @@ mixin CommentWriter {
7878

7979
if (comments.isHanging(i)) {
8080
// Attach the comment to the previous token.
81-
writer.space();
82-
writer.writeComment(comment, hanging: true);
81+
pieces.writeSpace();
82+
pieces.writeComment(comment, hanging: true);
8383
} else {
84-
writer.writeNewline();
85-
writer.writeComment(comment);
84+
pieces.writeNewline();
85+
pieces.writeComment(comment);
8686
}
8787

8888
if (comment.type == CommentType.line || comment.type == CommentType.doc) {
89-
writer.writeNewline();
89+
pieces.writeNewline();
9090
}
9191
}
9292

9393
if (comments.isNotEmpty && _needsSpaceAfterComment(token.lexeme)) {
94-
writer.space();
94+
pieces.writeSpace();
9595
}
9696
}
9797

0 commit comments

Comments
 (0)