Skip to content

Commit 8a6241c

Browse files
authored
Better print format (#1157)
* Omit parenthesis based on operator precedence * Improve expression list printing * Add print format tests
1 parent 284a672 commit 8a6241c

File tree

10 files changed

+179
-140
lines changed

10 files changed

+179
-140
lines changed

src/LuaPrinter.ts

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,42 @@ export class LuaPrinter {
120120
[lua.SyntaxKind.BitwiseLeftShiftOperator]: "<<",
121121
[lua.SyntaxKind.BitwiseNotOperator]: "~",
122122
};
123+
private static operatorPrecedence: Record<lua.Operator, number> = {
124+
[lua.SyntaxKind.OrOperator]: 1,
125+
[lua.SyntaxKind.AndOperator]: 2,
126+
127+
[lua.SyntaxKind.EqualityOperator]: 3,
128+
[lua.SyntaxKind.InequalityOperator]: 3,
129+
[lua.SyntaxKind.LessThanOperator]: 3,
130+
[lua.SyntaxKind.LessEqualOperator]: 3,
131+
[lua.SyntaxKind.GreaterThanOperator]: 3,
132+
[lua.SyntaxKind.GreaterEqualOperator]: 3,
133+
134+
[lua.SyntaxKind.BitwiseOrOperator]: 4,
135+
[lua.SyntaxKind.BitwiseExclusiveOrOperator]: 5,
136+
[lua.SyntaxKind.BitwiseAndOperator]: 6,
137+
138+
[lua.SyntaxKind.BitwiseLeftShiftOperator]: 7,
139+
[lua.SyntaxKind.BitwiseRightShiftOperator]: 7,
140+
141+
[lua.SyntaxKind.ConcatOperator]: 8,
142+
143+
[lua.SyntaxKind.AdditionOperator]: 9,
144+
[lua.SyntaxKind.SubtractionOperator]: 9,
145+
146+
[lua.SyntaxKind.MultiplicationOperator]: 10,
147+
[lua.SyntaxKind.DivisionOperator]: 10,
148+
[lua.SyntaxKind.FloorDivisionOperator]: 10,
149+
[lua.SyntaxKind.ModuloOperator]: 10,
150+
151+
[lua.SyntaxKind.NotOperator]: 11,
152+
[lua.SyntaxKind.LengthOperator]: 11,
153+
[lua.SyntaxKind.NegationOperator]: 11,
154+
[lua.SyntaxKind.BitwiseNotOperator]: 11,
155+
156+
[lua.SyntaxKind.PowerOperator]: 12,
157+
};
158+
private static rightAssociativeOperators = new Set([lua.SyntaxKind.ConcatOperator, lua.SyntaxKind.PowerOperator]);
123159

124160
private currentIndent = "";
125161
private luaFile: string;
@@ -676,34 +712,49 @@ export class LuaPrinter {
676712
const chunks: SourceChunk[] = [];
677713

678714
chunks.push(this.printOperator(expression.operator));
679-
chunks.push(this.printExpressionInParenthesesIfNeeded(expression.operand));
715+
chunks.push(
716+
this.printExpressionInParenthesesIfNeeded(
717+
expression.operand,
718+
LuaPrinter.operatorPrecedence[expression.operator]
719+
)
720+
);
680721

681722
return this.createSourceNode(expression, chunks);
682723
}
683724

684725
public printBinaryExpression(expression: lua.BinaryExpression): SourceNode {
685726
const chunks: SourceChunk[] = [];
686-
687-
chunks.push(this.printExpressionInParenthesesIfNeeded(expression.left));
727+
const isRightAssociative = LuaPrinter.rightAssociativeOperators.has(expression.operator);
728+
const precedence = LuaPrinter.operatorPrecedence[expression.operator];
729+
chunks.push(
730+
this.printExpressionInParenthesesIfNeeded(expression.left, isRightAssociative ? precedence + 1 : precedence)
731+
);
688732
chunks.push(" ", this.printOperator(expression.operator), " ");
689-
chunks.push(this.printExpressionInParenthesesIfNeeded(expression.right));
733+
chunks.push(
734+
this.printExpressionInParenthesesIfNeeded(
735+
expression.right,
736+
isRightAssociative ? precedence : precedence + 1
737+
)
738+
);
690739

691740
return this.createSourceNode(expression, chunks);
692741
}
693742

694-
private printExpressionInParenthesesIfNeeded(expression: lua.Expression): SourceNode {
695-
return this.needsParenthesis(expression)
743+
private printExpressionInParenthesesIfNeeded(expression: lua.Expression, minPrecedenceToOmit?: number): SourceNode {
744+
return this.needsParenthesis(expression, minPrecedenceToOmit)
696745
? this.createSourceNode(expression, ["(", this.printExpression(expression), ")"])
697746
: this.printExpression(expression);
698747
}
699748

700-
private needsParenthesis(expression: lua.Expression): boolean {
701-
return (
702-
lua.isBinaryExpression(expression) ||
703-
lua.isFunctionExpression(expression) ||
704-
lua.isTableExpression(expression) ||
705-
(lua.isUnaryExpression(expression) && expression.operator === lua.SyntaxKind.NotOperator)
706-
);
749+
private needsParenthesis(expression: lua.Expression, minPrecedenceToOmit?: number): boolean {
750+
if (lua.isBinaryExpression(expression) || lua.isUnaryExpression(expression)) {
751+
return (
752+
minPrecedenceToOmit === undefined ||
753+
LuaPrinter.operatorPrecedence[expression.operator] < minPrecedenceToOmit
754+
);
755+
} else {
756+
return lua.isFunctionExpression(expression) || lua.isTableExpression(expression);
757+
}
707758
}
708759

709760
public printCallExpression(expression: lua.CallExpression): SourceNode {
@@ -769,10 +820,19 @@ export class LuaPrinter {
769820
return intersperse(chunks, ", ");
770821
}
771822

823+
/**
824+
* Returns true if the expression list (table field or parameters) should be printed on one line.
825+
*/
826+
protected isSimpleExpressionList(expressions: lua.Expression[]): boolean {
827+
if (expressions.length <= 1) return true;
828+
if (expressions.length > 4) return false;
829+
return expressions.every(isSimpleExpression);
830+
}
831+
772832
protected printExpressionList(expressions: lua.Expression[]): SourceChunk[] {
773833
const chunks: SourceChunk[] = [];
774834

775-
if (expressions.every(isSimpleExpression)) {
835+
if (this.isSimpleExpressionList(expressions)) {
776836
chunks.push(...this.joinChunksWithComma(expressions.map(e => this.printExpression(e))));
777837
} else {
778838
chunks.push("\n");

test/translation/__snapshots__/transformation.spec.ts.snap

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ backQuoteInTemplateString = \\"\` \` \`\\"
2121
escapedCharsInQuotes = \\"\\\\\\\\ \\\\0 \\\\b \\\\t \\\\n \\\\v \\\\f \\\\\\" ' \`\\"
2222
escapedCharsInDoubleQuotes = \\"\\\\\\\\ \\\\0 \\\\b \\\\t \\\\n \\\\v \\\\f \\\\\\" ' \`\\"
2323
escapedCharsInTemplateString = \\"\\\\\\\\ \\\\0 \\\\b \\\\t \\\\n \\\\v \\\\f \\\\\\" ' \`\\"
24-
nonEmptyTemplateString = (\\"Level 0: \\\\n\\\\t \\" .. ((\\"Level 1: \\\\n\\\\t\\\\t \\" .. ((\\"Level 3: \\\\n\\\\t\\\\t\\\\t \\" .. \\"Last level \\\\n --\\") .. \\" \\\\n --\\")) .. \\" \\\\n --\\")) .. \\" \\\\n --\\""
24+
nonEmptyTemplateString = (\\"Level 0: \\\\n\\\\t \\" .. (\\"Level 1: \\\\n\\\\t\\\\t \\" .. (\\"Level 3: \\\\n\\\\t\\\\t\\\\t \\" .. \\"Last level \\\\n --\\") .. \\" \\\\n --\\") .. \\" \\\\n --\\") .. \\" \\\\n --\\""
2525
`;
2626

2727
exports[`Transformation (exportStatement) 1`] = `
@@ -230,6 +230,30 @@ return ____exports"
230230

231231
exports[`Transformation (modulesVariableNoExport) 1`] = `"foo = \\"bar\\""`;
232232

233+
exports[`Transformation (printFormat) 1`] = `
234+
"stringConcat = ((\\"a\\" .. \\"b\\" .. \\"c\\") .. \\"d\\") .. \\"e\\"
235+
numbers = 2 * 2 + 3 + 4 * (5 + 6) ~= 7
236+
function func(...)
237+
end
238+
func(function()
239+
local b = \\"A function\\"
240+
end)
241+
func(func())
242+
array = {func()}
243+
array2 = {
244+
func(),
245+
func()
246+
}
247+
object = {a = 1, b = 2, c = 3}
248+
bigObject = {
249+
a = 1,
250+
b = 2,
251+
c = 3,
252+
d = \\"value1\\",
253+
e = \\"value2\\"
254+
}"
255+
`;
256+
233257
exports[`Transformation (returnDefault) 1`] = `
234258
"function myFunc(self)
235259
return
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const stringConcat = "a" + ("b" + "c") + "d" + "e";
2+
const numbers = 2 * 2 + 3 + 4 * (5 + 6) !== 7;
3+
4+
function func(this: void, ...args: any) {}
5+
6+
func(() => {
7+
const b = "A function";
8+
});
9+
10+
func(func());
11+
12+
const array = [func()];
13+
const array2 = [func(), func()];
14+
15+
const object = {
16+
a: 1,
17+
b: 2,
18+
c: 3,
19+
};
20+
21+
const bigObject = {
22+
a: 1,
23+
b: 2,
24+
c: 3,
25+
d: "value1",
26+
e: "value2",
27+
};

test/unit/__snapshots__/expressions.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ return ____exports"
1414

1515
exports[`Binary expressions ordering parentheses ("1*(3+4*2)") 1`] = `
1616
"local ____exports = {}
17-
____exports.__result = 1 * (3 + (4 * 2))
17+
____exports.__result = 1 * (3 + 4 * 2)
1818
return ____exports"
1919
`;
2020

2121
exports[`Binary expressions ordering parentheses ("1*30+4") 1`] = `
2222
"local ____exports = {}
23-
____exports.__result = (1 * 30) + 4
23+
____exports.__result = 1 * 30 + 4
2424
return ____exports"
2525
`;
2626

test/unit/__snapshots__/spread.spec.ts.snap

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ function ____exports.__main(self)
9090
end
9191
return test(
9292
nil,
93-
{
94-
fn = function(____, arg) return arg end
95-
},
93+
{fn = function(____, arg) return arg end},
9694
\\"foobar\\"
9795
)
9896
end
@@ -108,11 +106,9 @@ function ____exports.__main(self)
108106
end
109107
local function test(self, ...)
110108
do
111-
pcall(
112-
function()
113-
error(\\"foobar\\", 0)
114-
end
115-
)
109+
pcall(function()
110+
error(\\"foobar\\", 0)
111+
end)
116112
do
117113
return pick(nil, ...)
118114
end

test/unit/__snapshots__/switch.spec.ts.snap

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ function ____exports.__main(self)
5050
result = hoisted(nil)
5151
break
5252
end
53-
____cond3 = ____cond3 or (____switch3 == 2)
53+
____cond3 = ____cond3 or ____switch3 == 2
5454
if ____cond3 then
5555
result = \\"2\\"
5656
end
5757
if ____cond3 then
5858
result = \\"default\\"
5959
end
60-
____cond3 = ____cond3 or (____switch3 == 3)
60+
____cond3 = ____cond3 or ____switch3 == 3
6161
if ____cond3 then
6262
result = \\"3\\"
6363
break
@@ -88,14 +88,14 @@ function ____exports.__main(self)
8888
result = hoisted(nil)
8989
break
9090
end
91-
____cond3 = ____cond3 or (____switch3 == 2)
91+
____cond3 = ____cond3 or ____switch3 == 2
9292
if ____cond3 then
9393
result = \\"2\\"
9494
end
9595
if ____cond3 then
9696
result = \\"default\\"
9797
end
98-
____cond3 = ____cond3 or (____switch3 == 3)
98+
____cond3 = ____cond3 or ____switch3 == 3
9999
if ____cond3 then
100100
result = \\"3\\"
101101
break
@@ -118,19 +118,19 @@ function ____exports.__main(self)
118118
local out = {}
119119
repeat
120120
local ____switch3 = 0
121-
local ____cond3 = ((____switch3 == 0) or (____switch3 == 1)) or (____switch3 == 2)
121+
local ____cond3 = ____switch3 == 0 or ____switch3 == 1 or ____switch3 == 2
122122
if ____cond3 then
123123
__TS__ArrayPush(out, \\"0,1,2\\")
124124
break
125125
end
126-
____cond3 = ____cond3 or (____switch3 == 3)
126+
____cond3 = ____cond3 or ____switch3 == 3
127127
if ____cond3 then
128128
do
129129
__TS__ArrayPush(out, \\"3\\")
130130
break
131131
end
132132
end
133-
____cond3 = ____cond3 or (____switch3 == 4)
133+
____cond3 = ____cond3 or ____switch3 == 4
134134
if ____cond3 then
135135
break
136136
end

0 commit comments

Comments
 (0)