Skip to content

Commit edd6e3d

Browse files
committed
Added AST printer
1 parent 4464228 commit edd6e3d

File tree

1 file changed

+302
-0
lines changed

1 file changed

+302
-0
lines changed

src/LuaPrinter.ts

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
import * as tstl from "./LuaAST";
2+
3+
export class LuaPrinter {
4+
5+
/* tslint:disable:object-literal-sort-keys */
6+
private static operatorMap: {[key in tstl.Operator]: string} = {
7+
[tstl.SyntaxKind.AdditionOperator]: "+",
8+
[tstl.SyntaxKind.SubractionOperator]: "-",
9+
[tstl.SyntaxKind.MultiplicationOperator]: "*",
10+
[tstl.SyntaxKind.DivisionOperator]: "/",
11+
[tstl.SyntaxKind.FloorDivisionOperator]: "//",
12+
[tstl.SyntaxKind.ModuloOperator]: "%",
13+
[tstl.SyntaxKind.PowerOperator]: "^",
14+
[tstl.SyntaxKind.NegationOperator]: "-",
15+
[tstl.SyntaxKind.ConcatOperator]: "..",
16+
[tstl.SyntaxKind.LengthOperator]: "#",
17+
[tstl.SyntaxKind.EqualityOperator]: "==",
18+
[tstl.SyntaxKind.InequalityOperator]: "~=",
19+
[tstl.SyntaxKind.LessThanOperator]: "<",
20+
[tstl.SyntaxKind.LessEqualOperator]: "<=",
21+
[tstl.SyntaxKind.GreaterThanOperator]: ">",
22+
[tstl.SyntaxKind.GreaterEqualOperator]: ">=",
23+
[tstl.SyntaxKind.AndOperator]: "and",
24+
[tstl.SyntaxKind.OrOperator]: "or",
25+
[tstl.SyntaxKind.NotOperator]: "not",
26+
[tstl.SyntaxKind.BitwiseAndOperator]: "&",
27+
[tstl.SyntaxKind.BitwiseOrOperator]: "|",
28+
[tstl.SyntaxKind.BitwiseExclusiveOrOperator]: "~",
29+
[tstl.SyntaxKind.BitwiseRightShiftOperator]: ">>",
30+
[tstl.SyntaxKind.BitwiseLeftShiftOperator]: "<<",
31+
[tstl.SyntaxKind.BitwiseNotOperator]: "~",
32+
};
33+
/* tslint:enable:object-literal-sort-keys */
34+
35+
private currentIndent: string;
36+
37+
private pushIndent(): void {
38+
this.currentIndent = this.currentIndent + " ";
39+
}
40+
41+
private popIndent(): void {
42+
this.currentIndent = this.currentIndent.slice(4);
43+
}
44+
45+
private indent(input: string): string {
46+
return this.currentIndent + input;
47+
}
48+
49+
private printBlock(block: tstl.Block): string {
50+
return block.statements.map(s => this.printStatement(s)).join();
51+
}
52+
53+
private printStatement(statement: tstl.Statement): string {
54+
switch (statement.kind) {
55+
case tstl.SyntaxKind.DoStatement:
56+
return this.printDoStatement(statement as tstl.DoStatement);
57+
case tstl.SyntaxKind.VariableDeclarationStatement:
58+
return this.printVariableDeclarationStatement(statement as tstl.VariableDeclarationStatement);
59+
case tstl.SyntaxKind.VariableAssignmentStatement:
60+
return this.printVariableAssignmentStatement(statement as tstl.VariableAssignmentStatement);
61+
case tstl.SyntaxKind.IfStatement:
62+
return this.printIfStatement(statement as tstl.IfStatement);
63+
case tstl.SyntaxKind.WhileStatement:
64+
return this.printWhileStatement(statement as tstl.WhileStatement);
65+
case tstl.SyntaxKind.RepeatStatement:
66+
return this.printRepeatStatement(statement as tstl.RepeatStatement);
67+
case tstl.SyntaxKind.ForStatement:
68+
return this.printForStatement(statement as tstl.ForStatement);
69+
case tstl.SyntaxKind.ForInStatement:
70+
return this.printForInStatement(statement as tstl.ForInStatement);
71+
case tstl.SyntaxKind.GotoStatement:
72+
return this.printGotoStatement(statement as tstl.GotoStatement);
73+
case tstl.SyntaxKind.LabelStatement:
74+
return this.printLabelStatement(statement as tstl.LabelStatement);
75+
case tstl.SyntaxKind.ReturnStatement:
76+
return this.printReturnStatement(statement as tstl.ReturnStatement);
77+
case tstl.SyntaxKind.BreakStatement:
78+
return this.printBreakStatement(statement as tstl.BreakStatement);
79+
case tstl.SyntaxKind.ExpressionStatement:
80+
return this.printExpressionStatement(statement as tstl.ExpressionStatement);
81+
}
82+
}
83+
private printDoStatement(statement: tstl.DoStatement): string {
84+
let result = this.indent("do\n");
85+
this.pushIndent();
86+
result += statement.statements.map(s => this.printStatement(s)).join();
87+
this.popIndent();
88+
result += this.indent("end\n");
89+
90+
return result;
91+
}
92+
private printVariableDeclarationStatement(statement: tstl.VariableDeclarationStatement): string {
93+
return this.indent(`"local" ${statement.left.map(e => this.printExpression(e)).join(", ")} =` +
94+
`${statement.right.map(e => this.printExpression(e)).join(", ")};\n`);
95+
}
96+
private printVariableAssignmentStatement(statement: tstl.VariableAssignmentStatement): string {
97+
return this.indent(`${statement.left.map(e => this.printExpression(e)).join(", ")} =` +
98+
`${statement.right.map(e => this.printExpression(e)).join(", ")};\n`);
99+
}
100+
private printIfStatement(statement: tstl.IfStatement): string {
101+
let result = this.indent(`if ${this.printExpression(statement.condtion)} then\n`);
102+
this.pushIndent();
103+
result += this.printBlock(statement.ifBlock);
104+
this.popIndent();
105+
if (statement.elseBlock) {
106+
if (tstl.isIfStatement(statement.elseBlock)) {
107+
result += this.printIfStatement(statement.elseBlock);
108+
} else {
109+
result += this.indent("else\n");
110+
this.pushIndent();
111+
result += this.printBlock(statement.elseBlock);
112+
this.popIndent();
113+
}
114+
}
115+
result += this.indent("end\n");
116+
117+
return result;
118+
}
119+
private printWhileStatement(statement: tstl.WhileStatement): string {
120+
let result = this.indent(`while ${this.printExpression(statement.condtion)} do\n`);
121+
this.pushIndent();
122+
result += this.printBlock(statement.body);
123+
this.popIndent();
124+
result += this.indent("end\n");
125+
126+
return result;
127+
}
128+
private printRepeatStatement(statement: tstl.RepeatStatement): string {
129+
let result = this.indent(`repeat\n`);
130+
this.pushIndent();
131+
result += this.printBlock(statement.body);
132+
this.popIndent();
133+
result += this.indent(`until ${this.printExpression(statement.condtion)};\n`);
134+
135+
return result;
136+
}
137+
private printForStatement(statement: tstl.ForStatement): string {
138+
const ctrlVar = this.printExpression(statement.controlVariable);
139+
const ctrlVarInit = this.printExpression(statement.controlVariableInitializer);
140+
const limit = this.printExpression(statement.limitExpression);
141+
142+
let result = this.indent(`for ${ctrlVar} = ${ctrlVarInit}, ${limit}`);
143+
if (statement.stepExpression) {
144+
const step = this.printExpression(statement.stepExpression);
145+
result += `, ${step}`;
146+
}
147+
result += ` do\n`;
148+
149+
this.pushIndent();
150+
result += this.printBlock(statement.body);
151+
this.popIndent();
152+
result += this.indent("end\n");
153+
154+
return result;
155+
}
156+
private printForInStatement(statement: tstl.ForInStatement): string {
157+
const names = statement.names.map(i => this.printIdentifier(i)).join(", ");
158+
const expressions = statement.names.map(e => this.printExpression(e)).join(", ");
159+
160+
let result = this.indent(`for ${names} in ${expressions} do`);
161+
this.pushIndent();
162+
result += this.printBlock(statement.body);
163+
this.popIndent();
164+
result += this.indent("end\n");
165+
166+
return result;
167+
}
168+
private printGotoStatement(statement: tstl.GotoStatement): string {
169+
return this.indent(`goto ${statement.label};\n`);
170+
}
171+
private printLabelStatement(statement: tstl.LabelStatement): string {
172+
return this.indent(`::${statement.name}::\n`);
173+
}
174+
private printReturnStatement(statement: tstl.ReturnStatement): string {
175+
if (!statement.expressions) {
176+
return this.indent(`return;\n`);
177+
}
178+
return this.indent(`return ${statement.expressions.map(e => this.printExpression(e)).join(", ")};\n`);
179+
}
180+
private printBreakStatement(statement: tstl.BreakStatement): string {
181+
return this.indent("break;\n");
182+
}
183+
private printExpressionStatement(statement: tstl.ExpressionStatement): string {
184+
return this.indent(`${this.printExpression(statement.expression)};\n`);
185+
}
186+
// Expressions
187+
private printExpression(expression: tstl.Expression): string {
188+
switch (expression.kind) {
189+
case tstl.SyntaxKind.StringLiteral:
190+
return this.printStringLiteral(expression as tstl.StringLiteral);
191+
case tstl.SyntaxKind.NumericLiteral:
192+
return this.printNumericLiteral(expression as tstl.NumericLiteral);
193+
case tstl.SyntaxKind.NilKeyword:
194+
return this.printNilLiteral(expression as tstl.NilLiteral);
195+
case tstl.SyntaxKind.DotsKeyword:
196+
return this.printDotsLiteral(expression as tstl.DotsLiteral);
197+
case tstl.SyntaxKind.TrueKeyword:
198+
case tstl.SyntaxKind.FalseKeyword:
199+
return this.printBooleanLiteral(expression as tstl.BooleanLiteral);
200+
case tstl.SyntaxKind.FunctionExpression:
201+
return this.printFunctionExpression(expression as tstl.FunctionExpression);
202+
case tstl.SyntaxKind.TableFieldExpression:
203+
return this.printTableFieldExpression(expression as tstl.TableFieldExpression);
204+
case tstl.SyntaxKind.TableExpression:
205+
return this.printTableExpression(expression as tstl.TableExpression);
206+
case tstl.SyntaxKind.UnaryExpression:
207+
return this.printUnaryExpression(expression as tstl.UnaryExpression);
208+
case tstl.SyntaxKind.BinaryExpression:
209+
return this.printBinaryExpression(expression as tstl.BinaryExpression);
210+
case tstl.SyntaxKind.ParenthesizedExpression:
211+
return this.printParenthesizedExpression(expression as tstl.ParenthesizedExpression);
212+
case tstl.SyntaxKind.CallExpression:
213+
return this.printCallExpression(expression as tstl.CallExpression);
214+
case tstl.SyntaxKind.MethodCallExpression:
215+
return this.printMethodCallExpression(expression as tstl.MethodCallExpression);
216+
case tstl.SyntaxKind.Identifier:
217+
return this.printIdentifier(expression as tstl.Identifier);
218+
case tstl.SyntaxKind.TableIndexExpression:
219+
return this.printTableIndexExpression(expression as tstl.TableIndexExpression);
220+
}
221+
}
222+
private printStringLiteral(expression: tstl.StringLiteral): string {
223+
return `"${expression.value}"`;
224+
}
225+
private printNumericLiteral(expression: tstl.NumericLiteral): string {
226+
return `${expression.value}`;
227+
}
228+
private printNilLiteral(expression: tstl.NilLiteral): string {
229+
return "nil";
230+
}
231+
private printDotsLiteral(expression: tstl.DotsLiteral): string {
232+
return "...";
233+
}
234+
private printBooleanLiteral(expression: tstl.BooleanLiteral): string {
235+
if (expression.kind === tstl.SyntaxKind.TrueKeyword) {
236+
return "true";
237+
} else {
238+
return "false";
239+
}
240+
}
241+
private printFunctionExpression(expression: tstl.FunctionExpression): string {
242+
const paramterArr: string[] = expression.params.map(i => this.printIdentifier(i));
243+
if (expression.dots) {
244+
paramterArr.push(this.printDotsLiteral(expression.dots));
245+
}
246+
247+
let result = this.indent(`function (${paramterArr.join(", ")})\n`);
248+
this.pushIndent();
249+
result += this.printBlock(expression.body);
250+
this.popIndent();
251+
result += this.indent("end\n");
252+
253+
return result;
254+
}
255+
private printTableFieldExpression(expression: tstl.TableFieldExpression): string {
256+
const value = this.printExpression(expression.value);
257+
258+
if (expression.key) {
259+
return `[${this.printExpression(expression.key)}] = ${value}`;
260+
} else {
261+
return value;
262+
}
263+
}
264+
private printTableExpression(expression: tstl.TableExpression): string {
265+
return `{${expression.fields.map(f => this.printTableFieldExpression(f)).join(", ")}}`;
266+
}
267+
private printUnaryExpression(expression: tstl.UnaryExpression): string {
268+
return `${this.printOperator(expression.operator)}${this.printExpression(expression.operand)}`;
269+
}
270+
private printBinaryExpression(expression: tstl.BinaryExpression): string {
271+
const left = this.printExpression(expression.left);
272+
const operator = this.printOperator(expression.operator);
273+
const right = this.printExpression(expression.right);
274+
return `${left} ${operator} ${right}`;
275+
}
276+
private printParenthesizedExpression(expression: tstl.ParenthesizedExpression): string {
277+
return `(${this.printExpression(expression.innerEpxression)})`;
278+
}
279+
private printCallExpression(expression: tstl.CallExpression): string {
280+
const params = expression.params.map(e => this.printExpression(e)).join(", ");
281+
return `${this.printExpression(expression.expression)}(${params}))`;
282+
}
283+
private printMethodCallExpression(expression: tstl.MethodCallExpression): string {
284+
const params = expression.params.map(e => this.printExpression(e)).join(", ");
285+
const prefix = this.printExpression(expression.prefixExpression);
286+
const name = this.printIdentifier(expression.name);
287+
return `${prefix}:${name}(${params}))`;
288+
}
289+
private printIdentifier(expression: tstl.Identifier): string {
290+
return expression.text;
291+
}
292+
private printTableIndexExpression(expression: tstl.TableIndexExpression): string {
293+
const table = this.printExpression(expression.table);
294+
if (tstl.isIdentifier(expression.index)) {
295+
return `${table}.${this.printIdentifier(expression.index)}`;
296+
}
297+
return `${table}[${this.printExpression(expression.index)}]`;
298+
}
299+
private printOperator(kind: tstl.Operator): string {
300+
return LuaPrinter.operatorMap[kind];
301+
}
302+
}

0 commit comments

Comments
 (0)