diff --git a/modules/compiler/src/main/java/config/parser/ConfigAstBuilder.java b/modules/compiler/src/main/java/config/parser/ConfigAstBuilder.java index 168c24b..b5fe9b1 100644 --- a/modules/compiler/src/main/java/config/parser/ConfigAstBuilder.java +++ b/modules/compiler/src/main/java/config/parser/ConfigAstBuilder.java @@ -726,6 +726,8 @@ private Expression pathClosureElement(Expression expression, Expression closure) private Expression pathArgumentsElement(Expression caller, ArgumentsContext ctx) { var arguments = argumentList(ctx.argumentList()); + if( ctx.COMMA() != null ) + arguments.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); return ast( methodCall(caller, arguments), caller, ctx ); } @@ -952,6 +954,8 @@ private Expression gstringPath(ParserRuleContext ctx) { private Expression creator(CreatorContext ctx) { var type = createdName(ctx.createdName()); var arguments = argumentList(ctx.arguments().argumentList()); + if( ctx.arguments().COMMA() != null ) + arguments.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); return ctorX(type, arguments); } @@ -971,7 +975,10 @@ private Expression list(ListContext ctx) { if( ctx.COMMA() != null && ctx.expressionList() == null ) throw createParsingFailedException("Empty list literal should not contain any comma(,)", ctx.COMMA()); - return listX(expressionList(ctx.expressionList())); + var result = listX(expressionList(ctx.expressionList())); + if( ctx.COMMA() != null ) + result.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); + return result; } private List expressionList(ExpressionListContext ctx) { @@ -990,7 +997,10 @@ private Expression map(MapContext ctx) { var entries = ctx.mapEntryList().mapEntry().stream() .map(this::mapEntry) .collect(Collectors.toList()); - return mapX(entries); + var result = mapX(entries); + if( ctx.COMMA() != null ) + result.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); + return result; } private MapEntryExpression mapEntry(MapEntryContext ctx) { @@ -1398,6 +1408,7 @@ public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, private static final String INSIDE_PARENTHESES_LEVEL = "_INSIDE_PARENTHESES_LEVEL"; private static final String LEADING_COMMENTS = "_LEADING_COMMENTS"; private static final String QUOTE_CHAR = "_QUOTE_CHAR"; + private static final String TRAILING_COMMA = "_TRAILING_COMMA"; private static final String VERBATIM_TEXT = "_VERBATIM_TEXT"; private static final List GROOVY_KEYWORDS = List.of( diff --git a/modules/compiler/src/main/java/script/formatter/Formatter.java b/modules/compiler/src/main/java/script/formatter/Formatter.java index 3d04f8e..5e29a38 100644 --- a/modules/compiler/src/main/java/script/formatter/Formatter.java +++ b/modules/compiler/src/main/java/script/formatter/Formatter.java @@ -179,12 +179,12 @@ else if( !(node.getElseBlock() instanceof EmptyStatement) ) { } } - private Expression currentStmtExpr; + private Expression currentRootExpr; @Override public void visitExpressionStatement(ExpressionStatement node) { - var cse = currentStmtExpr; - currentStmtExpr = node.getExpression(); + var cre = currentRootExpr; + currentRootExpr = node.getExpression(); appendLeadingComments(node); appendIndent(); if( node.getStatementLabels() != null ) { @@ -195,16 +195,19 @@ public void visitExpressionStatement(ExpressionStatement node) { } visit(node.getExpression()); appendNewLine(); - currentStmtExpr = cse; + currentRootExpr = cre; } @Override public void visitReturnStatement(ReturnStatement node) { + var cre = currentRootExpr; + currentRootExpr = node.getExpression(); appendLeadingComments(node); appendIndent(); append("return "); visit(node.getExpression()); appendNewLine(); + currentRootExpr = cre; } @Override @@ -359,9 +362,13 @@ public void visitBinaryExpression(BinaryExpression node) { inVariableDeclaration = true; visit(node.getLeftExpression()); inVariableDeclaration = false; - if( !(node.getRightExpression() instanceof EmptyExpression) ) { + var source = node.getRightExpression(); + if( !(source instanceof EmptyExpression) ) { append(" = "); - visit(node.getRightExpression()); + var cre = currentRootExpr; + currentRootExpr = source; + visit(source); + currentRootExpr = cre; } return; } @@ -378,12 +385,6 @@ public void visitBinaryExpression(BinaryExpression node) { if( beginWrappedPipeChain ) inWrappedPipeChain = true; - Expression cse = null; - if( currentStmtExpr == node && node.getOperation().isA(Types.ASSIGNMENT_OPERATOR) ) { - cse = currentStmtExpr; - currentStmtExpr = node.getRightExpression(); - } - visit(node.getLeftExpression()); if( inWrappedPipeChain ) { @@ -399,15 +400,21 @@ public void visitBinaryExpression(BinaryExpression node) { var iwpc = inWrappedPipeChain; inWrappedPipeChain = false; - visit(node.getRightExpression()); + if( node.getOperation().isA(Types.ASSIGNMENT_OPERATOR) ) { + var source = node.getRightExpression(); + var cre = currentRootExpr; + currentRootExpr = source; + visit(source); + currentRootExpr = cre; + } + else { + visit(node.getRightExpression()); + } inWrappedPipeChain = iwpc; if( inWrappedPipeChain ) decIndent(); - if( cse != null ) - currentStmtExpr = cse; - if( beginWrappedPipeChain ) inWrappedPipeChain = false; } @@ -485,7 +492,7 @@ public void visitParameters(Parameter[] parameters) { } append(param.getName()); if( param.hasInitialExpression() ) { - append('='); + append(" = "); visit(param.getInitialExpression()); } if( i + 1 < parameters.length ) @@ -510,7 +517,7 @@ public void visitTupleExpression(TupleExpression node) { @Override public void visitListExpression(ListExpression node) { - var wrap = shouldWrapExpression(node); + var wrap = hasTrailingComma(node) || shouldWrapExpression(node); append('['); if( wrap ) incIndent(); @@ -525,13 +532,14 @@ public void visitListExpression(ListExpression node) { protected void visitPositionalArgs(List args, boolean wrap) { var comma = wrap ? "," : ", "; + var trailingComma = wrap && args.size() > 1; for( int i = 0; i < args.size(); i++ ) { if( wrap ) { appendNewLine(); appendIndent(); } visit(args.get(i)); - if( i + 1 < args.size() ) + if( trailingComma || i + 1 < args.size() ) append(comma); } } @@ -542,7 +550,7 @@ public void visitMapExpression(MapExpression node) { append("[:]"); return; } - var wrap = shouldWrapExpression(node); + var wrap = hasTrailingComma(node) || shouldWrapExpression(node); append('['); if( wrap ) incIndent(); @@ -557,13 +565,14 @@ public void visitMapExpression(MapExpression node) { protected void visitNamedArgs(List args, boolean wrap) { var comma = wrap ? "," : ", "; + var trailingComma = wrap && args.size() > 1; for( int i = 0; i < args.size(); i++ ) { if( wrap ) { appendNewLine(); appendIndent(); } visit(args.get(i)); - if( i + 1 < args.size() ) + if( trailingComma || i + 1 < args.size() ) append(comma); } } @@ -713,6 +722,10 @@ public void visit(Expression node) { // helpers + private static boolean hasTrailingComma(Expression node) { + return node.getNodeMetaData(TRAILING_COMMA) != null; + } + public static boolean isLegacyType(ClassNode cn) { return cn.getNodeMetaData(LEGACY_TYPE) != null; } @@ -722,13 +735,15 @@ private boolean shouldWrapExpression(Expression node) { } private boolean shouldWrapMethodCall(MethodCallExpression node) { + if( hasTrailingComma(node.getArguments()) ) + return true; var start = node.getMethod(); var end = node.getArguments(); return start.getLineNumber() < end.getLastLineNumber(); } private boolean shouldWrapMethodChain(MethodCallExpression node) { - if( currentStmtExpr != node ) + if( currentRootExpr != node ) return false; if( !shouldWrapExpression(node) ) return false; @@ -746,7 +761,7 @@ private boolean shouldWrapMethodChain(MethodCallExpression node) { } private boolean shouldWrapPipeExpression(BinaryExpression node) { - return currentStmtExpr == node && node.getOperation().isA(Types.PIPE) && shouldWrapExpression(node); + return currentRootExpr == node && node.getOperation().isA(Types.PIPE) && shouldWrapExpression(node); } private static final String SLASH_STR = "/"; @@ -761,6 +776,7 @@ private boolean shouldWrapPipeExpression(BinaryExpression node) { private static final String LEADING_COMMENTS = "_LEADING_COMMENTS"; private static final String LEGACY_TYPE = "_LEGACY_TYPE"; private static final String QUOTE_CHAR = "_QUOTE_CHAR"; + private static final String TRAILING_COMMA = "_TRAILING_COMMA"; private static final String TRAILING_COMMENT = "_TRAILING_COMMENT"; private static final String VERBATIM_TEXT = "_VERBATIM_TEXT"; diff --git a/modules/compiler/src/main/java/script/parser/ScriptAstBuilder.java b/modules/compiler/src/main/java/script/parser/ScriptAstBuilder.java index 925d91f..c479d0e 100644 --- a/modules/compiler/src/main/java/script/parser/ScriptAstBuilder.java +++ b/modules/compiler/src/main/java/script/parser/ScriptAstBuilder.java @@ -1077,6 +1077,8 @@ private Expression pathClosureElement(Expression expression, Expression closure) private Expression pathArgumentsElement(Expression caller, ArgumentsContext ctx) { var arguments = argumentList(ctx.argumentList()); + if( ctx.COMMA() != null ) + arguments.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); return ast( methodCall(caller, arguments), caller, ctx ); } @@ -1303,6 +1305,8 @@ private Expression gstringPath(ParserRuleContext ctx) { private Expression creator(CreatorContext ctx) { var type = createdName(ctx.createdName()); var arguments = argumentList(ctx.arguments().argumentList()); + if( ctx.arguments().COMMA() != null ) + arguments.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); return ctorX(type, arguments); } @@ -1347,7 +1351,10 @@ private Expression list(ListContext ctx) { if( ctx.COMMA() != null && ctx.expressionList() == null ) throw createParsingFailedException("Empty list literal should not contain any comma(,)", ctx.COMMA()); - return listX(expressionList(ctx.expressionList())); + var result = listX(expressionList(ctx.expressionList())); + if( ctx.COMMA() != null ) + result.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); + return result; } private List expressionList(ExpressionListContext ctx) { @@ -1366,7 +1373,10 @@ private Expression map(MapContext ctx) { var entries = ctx.mapEntryList().mapEntry().stream() .map(this::mapEntry) .collect(Collectors.toList()); - return mapX(entries); + var result = mapX(entries); + if( ctx.COMMA() != null ) + result.putNodeMetaData(TRAILING_COMMA, Boolean.TRUE); + return result; } private MapEntryExpression mapEntry(MapEntryContext ctx) { @@ -1824,6 +1834,7 @@ public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, private static final String LEADING_COMMENTS = "_LEADING_COMMENTS"; private static final String LEGACY_TYPE = "_LEGACY_TYPE"; private static final String QUOTE_CHAR = "_QUOTE_CHAR"; + private static final String TRAILING_COMMA = "_TRAILING_COMMA"; private static final String TRAILING_COMMENT = "_TRAILING_COMMENT"; private static final String VERBATIM_TEXT = "_VERBATIM_TEXT";