Skip to content

Commit 422a36a

Browse files
authored
output cleanup (#501)
* removed empty string literals from template strings and unneeded parenthesis from expressions * stripping parenthesis from casts * fixed indenting * additional binary expression test * function cleanup - using `function foo()` syntax instead of `foo = function()` - Exception made for assigned function expressions - inlining arrow functions with no body - also stripping empty do...end statements * removing continue tags from loops with no continue statements * fixed lint error * addressed feedback and did some cleanup * formatting table literals and added some comments * updates based on feedback * added comment to isValidFunctionDeclarationName * refactored function expression stuff to avoid needing inline parameter on printReturnStatement * moved parens out of printFunctionParameters
1 parent b34142a commit 422a36a

File tree

11 files changed

+427
-290
lines changed

11 files changed

+427
-290
lines changed

src/LuaAST.ts

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,17 @@ export function cloneNode<T extends Node>(node: T): T {
121121
return Object.assign({}, node);
122122
}
123123

124+
export function setNodePosition<T extends Node>(node: T, position: TextRange): T {
125+
node.line = position.line;
126+
node.column = position.column;
127+
128+
return node;
129+
}
130+
124131
export function setNodeOriginal<T extends Node>(node: T, tsOriginal: ts.Node): T {
125132
const sourcePosition = getSourcePosition(tsOriginal);
126133
if (sourcePosition) {
127-
node.line = sourcePosition.line;
128-
node.column = sourcePosition.column;
134+
setNodePosition(node, sourcePosition);
129135
}
130136

131137
return node;
@@ -589,20 +595,19 @@ export function createStringLiteral(value: string | ts.__String, tsOriginal?: ts
589595
return expression;
590596
}
591597

592-
// There is no export function statement/declaration because those are just syntax sugar
593-
//
594-
// `function f () body end` becomes `f = function () body` end
595-
// `function t.a.b.c.f () body end` becomes `t.a.b.c.f = function () body end`
596-
// `local function f () body end` becomes `local f; f = function () body end` NOT `local f = function () body end`
597-
// See https://www.lua.org/manual/5.3/manual.html 3.4.11
598-
//
599-
// We should probably create helper functions to create the different export function declarations
598+
export enum FunctionExpressionFlags {
599+
None = 0x0,
600+
Inline = 0x1, // Keep function on same line
601+
Declaration = 0x2, // Prefer declaration syntax `function foo()` over assignment syntax `foo = function()`
602+
}
603+
600604
export interface FunctionExpression extends Expression {
601605
kind: SyntaxKind.FunctionExpression;
602606
params?: Identifier[];
603607
dots?: DotsLiteral;
604608
restParamName?: Identifier;
605609
body: Block;
610+
flags: FunctionExpressionFlags;
606611
}
607612

608613
export function isFunctionExpression(node: Node): node is FunctionExpression {
@@ -614,6 +619,7 @@ export function createFunctionExpression(
614619
params?: Identifier[],
615620
dots?: DotsLiteral,
616621
restParamName?: Identifier,
622+
flags = FunctionExpressionFlags.None,
617623
tsOriginal?: ts.Node,
618624
parent?: Node
619625
): FunctionExpression
@@ -627,6 +633,7 @@ export function createFunctionExpression(
627633
expression.dots = dots;
628634
setParent(restParamName, expression);
629635
expression.restParamName = restParamName;
636+
expression.flags = flags;
630637
return expression;
631638
}
632639

@@ -862,3 +869,27 @@ export function createTableIndexExpression(
862869
}
863870

864871
export type IdentifierOrTableIndexExpression = Identifier | TableIndexExpression;
872+
873+
export type FunctionDefinition = (VariableDeclarationStatement | AssignmentStatement) & {
874+
right: [FunctionExpression];
875+
};
876+
877+
export function isFunctionDefinition(statement: VariableDeclarationStatement | AssignmentStatement)
878+
: statement is FunctionDefinition
879+
{
880+
return statement.left.length === 1
881+
&& statement.right
882+
&& statement.right.length === 1
883+
&& isFunctionExpression(statement.right[0]);
884+
}
885+
886+
export type InlineFunctionExpression = FunctionExpression & {
887+
body: { statements: [ReturnStatement]; };
888+
};
889+
890+
export function isInlineFunctionExpression(expression: FunctionExpression) : expression is InlineFunctionExpression {
891+
return expression.body.statements
892+
&& expression.body.statements.length === 1
893+
&& isReturnStatement(expression.body.statements[0])
894+
&& (expression.flags & FunctionExpressionFlags.Inline) !== 0;
895+
}

src/LuaPrinter.ts

Lines changed: 89 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -213,25 +213,37 @@ export class LuaPrinter {
213213

214214
private printDoStatement(statement: tstl.DoStatement): SourceNode {
215215
const chunks: SourceChunk[] = [];
216-
chunks.push(this.indent("do\n"));
217-
this.pushIndent();
218-
chunks.push(...this.ignoreDeadStatements(statement.statements).map(s => this.printStatement(s)));
219-
this.popIndent();
220-
chunks.push(this.indent("end\n"));
216+
217+
if (statement.statements && statement.statements.length > 0) {
218+
chunks.push(this.indent("do\n"));
219+
this.pushIndent();
220+
chunks.push(...this.ignoreDeadStatements(statement.statements).map(s => this.printStatement(s)));
221+
this.popIndent();
222+
chunks.push(this.indent("end\n"));
223+
}
221224

222225
return this.concatNodes(...chunks);
223226
}
224227

225228
private printVariableDeclarationStatement(statement: tstl.VariableDeclarationStatement): SourceNode {
226229
const chunks: SourceChunk[] = [];
230+
227231
chunks.push(this.indent("local "));
228-
chunks.push(...this.joinChunks(", ", statement.left.map(e => this.printExpression(e))));
229232

230-
if (statement.right) {
231-
chunks.push(" = ");
232-
chunks.push(...this.joinChunks(", ", statement.right.map(e => this.printExpression(e))));
233+
if (tstl.isFunctionDefinition(statement)) {
234+
// Print all local functions as `local function foo()` instead of `local foo = function` to allow recursion
235+
chunks.push(this.printFunctionDefinition(statement));
236+
chunks.push("\n");
237+
238+
} else {
239+
chunks.push(...this.joinChunks(", ", statement.left.map(e => this.printExpression(e))));
240+
241+
if (statement.right) {
242+
chunks.push(" = ");
243+
chunks.push(...this.joinChunks(", ", statement.right.map(e => this.printExpression(e))));
244+
}
245+
chunks.push(";\n");
233246
}
234-
chunks.push(";\n");
235247

236248
return this.concatNodes(...chunks);
237249
}
@@ -240,6 +252,19 @@ export class LuaPrinter {
240252
const chunks: SourceChunk[] = [];
241253

242254
chunks.push(this.indent());
255+
256+
if (tstl.isFunctionDefinition(statement)
257+
&& (statement.right[0].flags & tstl.FunctionExpressionFlags.Declaration) !== 0)
258+
{
259+
// Use `function foo()` instead of `foo = function()`
260+
const name = this.printExpression(statement.left[0]);
261+
if (tsHelper.isValidLuaFunctionDeclarationName(name.toString())) {
262+
chunks.push(this.printFunctionDefinition(statement));
263+
chunks.push("\n");
264+
return this.createSourceNode(statement, chunks);
265+
}
266+
}
267+
243268
chunks.push(...this.joinChunks(", ", statement.left.map(e => this.printExpression(e))));
244269
chunks.push(" = ");
245270
chunks.push(...this.joinChunks(", ", statement.right.map(e => this.printExpression(e))));
@@ -359,7 +384,6 @@ export class LuaPrinter {
359384
const chunks: SourceChunk[] = [];
360385

361386
chunks.push(...this.joinChunks(", ", statement.expressions.map(e => this.printExpression(e))));
362-
363387
chunks.push(";\n");
364388

365389
return this.createSourceNode(statement, [this.indent(), "return ", ...chunks]);
@@ -436,7 +460,7 @@ export class LuaPrinter {
436460
}
437461
}
438462

439-
private printFunctionExpression(expression: tstl.FunctionExpression): SourceNode {
463+
private printFunctionParameters(expression: tstl.FunctionExpression): SourceChunk[] {
440464
const parameterChunks: SourceNode[] = expression.params
441465
? expression.params.map(i => this.printIdentifier(i))
442466
: [];
@@ -445,10 +469,46 @@ export class LuaPrinter {
445469
parameterChunks.push(this.printDotsLiteral(expression.dots));
446470
}
447471

472+
return this.joinChunks(", ", parameterChunks);
473+
}
474+
475+
private printFunctionExpression(expression: tstl.FunctionExpression): SourceNode {
448476
const chunks: SourceChunk[] = [];
449477

450478
chunks.push("function(");
451-
chunks.push(...this.joinChunks(", ", parameterChunks));
479+
chunks.push(...this.printFunctionParameters(expression));
480+
chunks.push(")");
481+
482+
if (tstl.isInlineFunctionExpression(expression)) {
483+
const returnStatement = expression.body.statements[0];
484+
chunks.push(" ");
485+
const returnNode: SourceChunk[] = [
486+
"return ",
487+
...this.joinChunks(", ", returnStatement.expressions.map(e => this.printExpression(e))),
488+
";",
489+
];
490+
chunks.push(this.createSourceNode(returnStatement, returnNode));
491+
chunks.push(" end");
492+
493+
} else {
494+
chunks.push("\n");
495+
this.pushIndent();
496+
chunks.push(this.printBlock(expression.body));
497+
this.popIndent();
498+
chunks.push(this.indent("end"));
499+
}
500+
501+
return this.createSourceNode(expression, chunks);
502+
}
503+
504+
private printFunctionDefinition(statement: tstl.FunctionDefinition): SourceNode {
505+
const expression = statement.right[0];
506+
const chunks: SourceChunk[] = [];
507+
508+
chunks.push("function ");
509+
chunks.push(this.printExpression(statement.left[0]));
510+
chunks.push("(");
511+
chunks.push(...this.printFunctionParameters(expression));
452512
chunks.push(")\n");
453513

454514
this.pushIndent();
@@ -482,14 +542,18 @@ export class LuaPrinter {
482542

483543
chunks.push("{");
484544

485-
if (expression.fields) {
486-
expression.fields.forEach((f, i) => {
487-
if (i < expression.fields.length - 1) {
488-
chunks.push(this.printTableFieldExpression(f), ", ");
489-
} else {
490-
chunks.push(this.printTableFieldExpression(f));
491-
}
492-
});
545+
if (expression.fields && expression.fields.length > 0) {
546+
if (expression.fields.length === 1) {
547+
// Inline tables with only one entry
548+
chunks.push(this.printTableFieldExpression(expression.fields[0]));
549+
550+
} else {
551+
chunks.push("\n");
552+
this.pushIndent();
553+
expression.fields.forEach(f => chunks.push(this.indent(), this.printTableFieldExpression(f), ",\n"));
554+
this.popIndent();
555+
chunks.push(this.indent());
556+
}
493557
}
494558

495559
chunks.push("}");
@@ -501,41 +565,21 @@ export class LuaPrinter {
501565
const chunks: SourceChunk[] = [];
502566

503567
chunks.push(this.printOperator(expression.operator));
504-
505-
if (this.needsParentheses(expression.operand)) {
506-
chunks.push("(", this.printExpression(expression.operand), ")");
507-
} else {
508-
chunks.push(this.printExpression(expression.operand));
509-
}
568+
chunks.push(this.printExpression(expression.operand));
510569

511570
return this.createSourceNode(expression, chunks);
512571
}
513572

514573
private printBinaryExpression(expression: tstl.BinaryExpression): SourceNode {
515574
const chunks: SourceChunk[] = [];
516575

517-
if (this.needsParentheses(expression.left)) {
518-
chunks.push("(", this.printExpression(expression.left), ")");
519-
} else {
520-
chunks.push(this.printExpression(expression.left));
521-
}
522-
576+
chunks.push(this.printExpression(expression.left));
523577
chunks.push(" ", this.printOperator(expression.operator), " ");
524-
525-
if (this.needsParentheses(expression.right)) {
526-
chunks.push("(", this.printExpression(expression.right), ")");
527-
} else {
528-
chunks.push(this.printExpression(expression.right));
529-
}
578+
chunks.push(this.printExpression(expression.right));
530579

531580
return this.createSourceNode(expression, chunks);
532581
}
533582

534-
private needsParentheses(expression: tstl.Expression): boolean {
535-
return tstl.isBinaryExpression(expression) || tstl.isUnaryExpression(expression)
536-
|| tstl.isFunctionExpression(expression);
537-
}
538-
539583
private printParenthesizedExpression(expression: tstl.ParenthesizedExpression): SourceNode {
540584
return this.createSourceNode(expression, ["(", this.printExpression(expression.innerEpxression), ")"]);
541585
}
@@ -544,11 +588,7 @@ export class LuaPrinter {
544588
const chunks = [];
545589
const parameterChunks = this.joinChunks(", ", expression.params.map(e => this.printExpression(e)));
546590

547-
if (this.needsParentheses(expression.expression)) {
548-
chunks.push("(", this.printExpression(expression.expression), ")(", ...parameterChunks, ")");
549-
} else {
550-
chunks.push(this.printExpression(expression.expression), "(", ...parameterChunks, ")");
551-
}
591+
chunks.push(this.printExpression(expression.expression), "(", ...parameterChunks, ")");
552592

553593
return this.concatNodes(...chunks);
554594
}

0 commit comments

Comments
 (0)