diff --git a/src/LuaAST.ts b/src/LuaAST.ts index 4d854ee5d..679f88bd7 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -110,16 +110,12 @@ export interface Node extends TextRange { } export function createNode(kind: SyntaxKind, tsOriginal?: ts.Node, parent?: Node): Node { - let line: number | undefined; - let column: number | undefined; - // TODO figure out why tsOriginal.getSourceFile() - // can return udnefined in the first place instead of catching it here - if (tsOriginal && tsOriginal.getSourceFile()) { - const lineAndCharacter = ts.getLineAndCharacterOfPosition(tsOriginal.getSourceFile(), tsOriginal.pos); - line = lineAndCharacter.line; - column = lineAndCharacter.character; + const sourcePosition = getSourcePosition(tsOriginal); + if (sourcePosition) { + return {kind, parent, line: sourcePosition.line, column: sourcePosition.column}; + } else { + return {kind, parent}; } - return {kind, parent, line, column}; } export function cloneNode(node: T): T { @@ -127,9 +123,12 @@ export function cloneNode(node: T): T { } export function setNodeOriginal(node: T, tsOriginal: ts.Node): T { - const lineAndCharacter = ts.getLineAndCharacterOfPosition(tsOriginal.getSourceFile(), tsOriginal.pos); - node.line = lineAndCharacter.line; - node.column = lineAndCharacter.character; + const sourcePosition = getSourcePosition(tsOriginal); + if (sourcePosition) { + node.line = sourcePosition.line; + node.line = sourcePosition.line; + } + return node; } @@ -140,20 +139,32 @@ export function setParent(node: Node | Node[] | undefined, parent: Node): void if (Array.isArray(node)) { node.forEach(n => { n.parent = parent; - if (!n.line || !n.column) { - n.line = parent.line; - n.column = parent.column; - } }); } else { node.parent = parent; - if (!node.line || !node.column) { - node.line = parent.line; - node.column = parent.column; - } } } +function getSourcePosition(sourceNode: ts.Node): TextRange | undefined { + if (sourceNode !== undefined && sourceNode.getSourceFile() !== undefined && sourceNode.pos >= 0) { + + const { line, character } = ts.getLineAndCharacterOfPosition( + sourceNode.getSourceFile(), + sourceNode.pos + sourceNode.getLeadingTriviaWidth() + ); + + return { line, column: character }; + } +} + +export function getOriginalPos(node: Node): TextRange { + while (node.line === undefined && node.parent !== undefined) { + node = node.parent; + } + + return { line: node.line, column: node.column }; +} + export interface Block extends Node { kind: SyntaxKind.Block; statements?: Statement[]; @@ -816,8 +827,8 @@ export function createIdentifier( return expression; } -export function cloneIdentifier(identifier: Identifier): Identifier { - return createIdentifier(identifier.text, undefined, identifier.symbolId); +export function cloneIdentifier(identifier: Identifier, tsOriginal?: ts.Node): Identifier { + return createIdentifier(identifier.text, tsOriginal, identifier.symbolId); } export function createAnnonymousIdentifier(tsOriginal?: ts.Node, parent?: Node): Identifier { diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 16a040e52..d69c0a6d3 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -9,6 +9,8 @@ import { LuaLibFeature, LuaLib } from "./LuaLib"; import { CompilerOptions } from "./CompilerOptions"; import { LuaLibImportKind } from "./CompilerOptions"; +type SourceChunk = string | SourceNode; + export class LuaPrinter { /* tslint:disable:object-literal-sort-keys */ private static operatorMap: {[key in tstl.Operator]: string} = { @@ -60,7 +62,7 @@ export class LuaPrinter { luaLibFeatures?: Set, sourceFile?: string): [string, string] { - const codeWithMap = + const codeWithMap = this.printImplementation(block, luaLibFeatures, sourceFile) // TODO is the file: part really required? and should this be handled in the printer? .toStringWithSourceMap({file: path.basename(sourceFile, path.extname(sourceFile)) + ".lua"}); @@ -93,9 +95,11 @@ export class LuaPrinter { } } - this.sourceFile = sourceFile; + this.sourceFile = path.basename(sourceFile); + + const blockNode = this.createSourceNode(block, this.printBlock(block)); - return this.createSourceNode(undefined, undefined, this.sourceFile, [header, this.printBlock(block)]); + return this.concatNodes(header, blockNode); } private pushIndent(): void { @@ -106,28 +110,27 @@ export class LuaPrinter { this.currentIndent = this.currentIndent.slice(4); } - private indent(input: string): string { - return this.currentIndent + input; + private indent(input: SourceChunk = ""): SourceChunk { + return this.concatNodes(this.currentIndent, input); } - private createSourceNode( - line: number | undefined, - column: number | undefined, - sourceFile: string, - chunks: Array<(string | SourceNode)> | SourceNode | string): SourceNode { + private createSourceNode(node: tstl.Node, chunks: SourceChunk | SourceChunk[]): SourceNode { + const originalPos = tstl.getOriginalPos(node); - line = line !== undefined ? line + 1 : line; - column = column !== undefined ? column + 1 : column; + return originalPos !== undefined + ? new SourceNode(originalPos.line + 1, originalPos.column, this.sourceFile, chunks) + : new SourceNode(undefined, undefined, this.sourceFile, chunks); + } - return new SourceNode(line, column, sourceFile, chunks); + private concatNodes(...chunks: SourceChunk[]): SourceNode { + return new SourceNode(undefined, undefined, this.sourceFile, chunks); } private printBlock(block: tstl.Block): SourceNode { return this.createSourceNode( - undefined, - undefined, - this.sourceFile, - this.ignoreDeadStatements(block.statements).map(s => this.printStatement(s))); + block, + this.ignoreDeadStatements(block.statements).map(s => this.printStatement(s)) + ); } private printStatement(statement: tstl.Statement): SourceNode { @@ -158,72 +161,50 @@ export class LuaPrinter { return this.printBreakStatement(statement as tstl.BreakStatement); case tstl.SyntaxKind.ExpressionStatement: return this.printExpressionStatement(statement as tstl.ExpressionStatement); + default: + throw new Error(`Tried to print unknown statement kind: ${tstl.SyntaxKind[statement.kind]}`); } } private printDoStatement(statement: tstl.DoStatement): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.indent("do\n")); this.pushIndent(); chunks.push(...this.ignoreDeadStatements(statement.statements).map(s => this.printStatement(s))); this.popIndent(); chunks.push(this.indent("end\n")); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printVariableDeclarationStatement(statement: tstl.VariableDeclarationStatement): SourceNode { - const chunks: Array<(string | SourceNode)> = []; - chunks.push("local "); - statement.left.forEach((e, i) => { - if (i < statement.left.length - 1) { - chunks.push(this.printExpression(e), ", "); - } else { - chunks.push(this.printExpression(e)); - } - }); + const chunks: SourceChunk[] = []; + chunks.push(this.indent("local ")); + chunks.push(...this.joinChunks(", ", statement.left.map(e => this.printExpression(e)))); if (statement.right) { chunks.push(" = "); - statement.right.forEach((e, i) => { - if (i < statement.right.length - 1) { - chunks.push(this.printExpression(e), ", "); - } else { - chunks.push(this.printExpression(e)); - } - }); + chunks.push(...this.joinChunks(", ", statement.right.map(e => this.printExpression(e)))); } chunks.push(";\n"); - - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printVariableAssignmentStatement(statement: tstl.AssignmentStatement): SourceNode { - const chunks: Array<(string | SourceNode)> = []; - statement.left.forEach((e, i) => { - if (i < statement.left.length - 1) { - chunks.push(this.printExpression(e), ", "); - } else { - chunks.push(this.printExpression(e)); - } - }); + const chunks: SourceChunk[] = []; + chunks.push(this.indent()); + chunks.push(...this.joinChunks(", ", statement.left.map(e => this.printExpression(e)))); chunks.push(" = "); - statement.right.forEach((e, i) => { - if (i < statement.right.length - 1) { - chunks.push(this.printExpression(e), ", "); - } else { - chunks.push(this.printExpression(e)); - } - }); + chunks.push(...this.joinChunks(", ", statement.right.map(e => this.printExpression(e)))); chunks.push(";\n"); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.createSourceNode(statement, chunks); } private printIfStatement(statement: tstl.IfStatement, isElseIf?: boolean): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; const prefix = isElseIf ? "elseif" : "if"; @@ -247,11 +228,11 @@ export class LuaPrinter { chunks.push(this.indent("end\n")); } - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printWhileStatement(statement: tstl.WhileStatement): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.indent("while "), this.printExpression(statement.condtion), " do\n"); @@ -261,11 +242,11 @@ export class LuaPrinter { chunks.push(this.indent("end\n")); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printRepeatStatement(statement: tstl.RepeatStatement): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.indent(`repeat\n`)); @@ -275,7 +256,7 @@ export class LuaPrinter { chunks.push(this.indent("until "), this.printExpression(statement.condtion), ";\n"); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printForStatement(statement: tstl.ForStatement): SourceNode { @@ -283,7 +264,7 @@ export class LuaPrinter { const ctrlVarInit = this.printExpression(statement.controlVariableInitializer); const limit = this.printExpression(statement.limitExpression); - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.indent("for "), ctrlVar, " = ", ctrlVarInit, ", ", limit); @@ -298,14 +279,14 @@ export class LuaPrinter { chunks.push(this.indent("end\n")); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printForInStatement(statement: tstl.ForInStatement): SourceNode { const names = statement.names.map(i => this.printIdentifier(i)).join(", "); const expressions = statement.expressions.map(e => this.printExpression(e)).join(", "); - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.indent("for "), names, " in ", expressions, " do\n"); @@ -314,65 +295,37 @@ export class LuaPrinter { this.popIndent(); chunks.push(this.indent("end\n")); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.createSourceNode(statement, chunks); } private printGotoStatement(statement: tstl.GotoStatement): SourceNode { - return this.createSourceNode( - statement.line, - statement.column, - this.sourceFile, - [this.indent("goto "), statement.label, ";\n"]); + return this.createSourceNode(statement, [this.indent("goto "), statement.label, ";\n"]); } private printLabelStatement(statement: tstl.LabelStatement): SourceNode { - return this.createSourceNode( - statement.line, - statement.column, - this.sourceFile, - [this.indent("::"), statement.name, "::\n"]); + return this.createSourceNode(statement, [this.indent("::"), statement.name, "::\n"]); } private printReturnStatement(statement: tstl.ReturnStatement): SourceNode { - if (!statement.expressions) { - return this.createSourceNode( - statement.line, - statement.column, - this.sourceFile, - this.indent("return;\n")); + if (!statement.expressions || statement.expressions.length === 0) { + return this.createSourceNode(statement, this.indent("return;\n")); } - const chunks: Array<(string | SourceNode)> = []; - - chunks.push(this.indent("return ")); + const chunks: SourceChunk[] = []; - statement.expressions.forEach((e, i) => { - if (i < statement.expressions.length - 1) { - chunks.push(this.printExpression(e), ", "); - } else { - chunks.push(this.printExpression(e)); - } - }); + chunks.push(...this.joinChunks(", ", statement.expressions.map(e => this.printExpression(e)))); chunks.push(";\n"); - return this.createSourceNode(statement.line, statement.column, this.sourceFile, chunks); + return this.createSourceNode(statement, [this.indent(), "return ", ...chunks]); } private printBreakStatement(statement: tstl.BreakStatement): SourceNode { - return this.createSourceNode( - statement.line, - statement.column, - this.sourceFile, - this.indent("break;\n")); + return this.createSourceNode(statement, this.indent("break;\n")); } private printExpressionStatement(statement: tstl.ExpressionStatement): SourceNode { - return this.createSourceNode( - statement.line, - statement.column, - this.sourceFile, - [this.printExpression(statement.expression), ";\n"]); + return this.concatNodes(this.indent(), this.printExpression(statement.expression), ";\n"); } // Expressions @@ -409,72 +362,48 @@ export class LuaPrinter { return this.printIdentifier(expression as tstl.Identifier); case tstl.SyntaxKind.TableIndexExpression: return this.printTableIndexExpression(expression as tstl.TableIndexExpression); + default: + throw new Error(`Tried to print unknown statement kind: ${tstl.SyntaxKind[expression.kind]}`); } } private printStringLiteral(expression: tstl.StringLiteral): SourceNode { - return this.createSourceNode( - expression.line, - expression.column, - this.sourceFile, - expression.value); + return this.createSourceNode(expression, `"${expression.value}"`); } private printNumericLiteral(expression: tstl.NumericLiteral): SourceNode { - return this.createSourceNode( - expression.line, - expression.column, - this.sourceFile, - String(expression.value)); + return this.createSourceNode(expression, String(expression.value)); } private printNilLiteral(expression: tstl.NilLiteral): SourceNode { - return this.createSourceNode(expression.line, - expression.column, - this.sourceFile, - "nil"); + return this.createSourceNode(expression, "nil"); } private printDotsLiteral(expression: tstl.DotsLiteral): SourceNode { - return this.createSourceNode( - expression.line, - expression.column, - this.sourceFile, - "..."); + return this.createSourceNode(expression, "..."); } private printBooleanLiteral(expression: tstl.BooleanLiteral): SourceNode { if (expression.kind === tstl.SyntaxKind.TrueKeyword) { - return this.createSourceNode( - expression.line, - expression.column, - this.sourceFile, - "true"); + return this.createSourceNode(expression, "true"); } else { - return this.createSourceNode( - expression.line, - expression.column, - this.sourceFile, - "false"); + return this.createSourceNode(expression, "false"); } } private printFunctionExpression(expression: tstl.FunctionExpression): SourceNode { - const paramterArr: SourceNode[] = expression.params ? expression.params.map(i => this.printIdentifier(i)) : []; + const parameterChunks: SourceNode[] = expression.params + ? expression.params.map(i => this.printIdentifier(i)) + : []; + if (expression.dots) { - paramterArr.push(this.printDotsLiteral(expression.dots)); + parameterChunks.push(this.printDotsLiteral(expression.dots)); } - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push("function("); - paramterArr.forEach((p, i) => { - if (i < paramterArr.length - 1) { - chunks.push(p, ", "); - } else { - chunks.push(p); - } - }); + chunks.push(...this.joinChunks(", ", parameterChunks)); chunks.push(")\n"); this.pushIndent(); @@ -482,11 +411,11 @@ export class LuaPrinter { this.popIndent(); chunks.push(this.indent("end")); - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.createSourceNode(expression, chunks); } private printTableFieldExpression(expression: tstl.TableFieldExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; const value = this.printExpression(expression.value); @@ -500,11 +429,11 @@ export class LuaPrinter { chunks.push(value); } - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.createSourceNode(expression, chunks); } private printTableExpression(expression: tstl.TableExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push("{"); @@ -520,11 +449,11 @@ export class LuaPrinter { chunks.push("}"); - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.createSourceNode(expression, chunks); } private printUnaryExpression(expression: tstl.UnaryExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.printOperator(expression.operator)); @@ -534,11 +463,11 @@ export class LuaPrinter { chunks.push(this.printExpression(expression.operand)); } - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.createSourceNode(expression, chunks); } private printBinaryExpression(expression: tstl.BinaryExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; if (this.needsParentheses(expression.left)) { chunks.push("(", this.printExpression(expression.left), ")"); @@ -554,7 +483,7 @@ export class LuaPrinter { chunks.push(this.printExpression(expression.right)); } - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.createSourceNode(expression, chunks); } private needsParentheses(expression: tstl.Expression): boolean { @@ -563,70 +492,44 @@ export class LuaPrinter { } private printParenthesizedExpression(expression: tstl.ParenthesizedExpression): SourceNode { - return this.createSourceNode( - expression.line, - expression.column, - this.sourceFile, - ["(", this.printExpression(expression.innerEpxression), ")"]); + return this.createSourceNode(expression, ["(", this.printExpression(expression.innerEpxression), ")"]); } private printCallExpression(expression: tstl.CallExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; - - const params: Array<(string | SourceNode)> = []; - - expression.params.forEach((p, i) => { - if (i < expression.params.length - 1) { - params.push(this.printExpression(p), ", "); - } else { - params.push(this.printExpression(p)); - } - }); + const chunks = []; + const parameterChunks = this.joinChunks(", ", expression.params.map(e => this.printExpression(e))); if (this.needsParentheses(expression.expression)) { - chunks.push("(", this.printExpression(expression.expression), ")(", ...params, ")"); + chunks.push("(", this.printExpression(expression.expression), ")(", ...parameterChunks, ")"); } else { - chunks.push(this.printExpression(expression.expression), "(", ...params, ")"); + chunks.push(this.printExpression(expression.expression), "(", ...parameterChunks, ")"); } - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.concatNodes(...chunks); } private printMethodCallExpression(expression: tstl.MethodCallExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; - - const params: Array<(string | SourceNode)> = []; - - expression.params.forEach((p, i) => { - if (i < expression.params.length - 1) { - params.push(this.printExpression(p), ", "); - } else { - params.push(this.printExpression(p)); - } - }); - const prefix = this.printExpression(expression.prefixExpression); + const parameterChunks = this.joinChunks(", ", expression.params.map(e => this.printExpression(e))); const name = this.printIdentifier(expression.name); - chunks.push(prefix, ":", name, "(", ...params, ")"); - - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.concatNodes(prefix, ":", name, "(", ...parameterChunks, ")"); } private printIdentifier(expression: tstl.Identifier): SourceNode { - return this.createSourceNode(expression.line, expression.column, this.sourceFile, expression.text); + return this.createSourceNode(expression, expression.text); } private printTableIndexExpression(expression: tstl.TableIndexExpression): SourceNode { - const chunks: Array<(string | SourceNode)> = []; + const chunks: SourceChunk[] = []; chunks.push(this.printExpression(expression.table)); if (tstl.isStringLiteral(expression.index) && tsHelper.isValidLuaIdentifier(expression.index.value)) { - chunks.push(".", expression.index.value); + chunks.push(".", this.createSourceNode(expression.index, expression.index.value)); } else { chunks.push("[", this.printExpression(expression.index), "]"); } - return this.createSourceNode(expression.line, expression.column, this.sourceFile, chunks); + return this.createSourceNode(expression, chunks); } private printOperator(kind: tstl.Operator): string { @@ -643,4 +546,15 @@ export class LuaPrinter { } return aliveStatements; } + + private joinChunks(separator: string, chunks: SourceChunk[]): SourceChunk[] { + const result = []; + for (let i = 0; i < chunks.length; i++) { + result.push(chunks[i]); + if (i < chunks.length - 1) { + result.push(separator); + } + } + return result; + } } diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index c3b01e7ca..9bb5ebea5 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -503,7 +503,8 @@ export class LuaTransformer { const fieldAssign = tstl.createAssignmentStatement( classField, - value + value, + field ); result.push(fieldAssign); @@ -590,12 +591,13 @@ export class LuaTransformer { const result: tstl.Statement[] = []; // className = className or {} - let classTable: tstl.Expression = tstl.createTableExpression(); + let classTable: tstl.Expression = tstl.createTableExpression([], statement); if (!noClassOr) { classTable = tstl.createBinaryExpression( this.addExportToIdentifier(className), // Use original identifier node in declaration classTable, - tstl.SyntaxKind.OrOperator + tstl.SyntaxKind.OrOperator, + statement ); } @@ -608,7 +610,8 @@ export class LuaTransformer { if (statement.members.some(m => ts.isGetAccessor(m) && tsHelper.isStatic(m))) { const classGetters = tstl.createTableIndexExpression( createClassNameWithExport(), - tstl.createStringLiteral("____getters") + tstl.createStringLiteral("____getters"), + statement ); const assignClassGetters = tstl.createAssignmentStatement( classGetters, @@ -623,7 +626,8 @@ export class LuaTransformer { // className.__index = className const classIndex = tstl.createTableIndexExpression( createClassNameWithExport(), - tstl.createStringLiteral("__index") + tstl.createStringLiteral("__index"), + statement ); const assignClassIndex = tstl.createAssignmentStatement(classIndex, createClassNameWithExport(), statement); result.push(assignClassIndex); @@ -647,14 +651,16 @@ export class LuaTransformer { // className.prototype = className.prototype or {} const createClassPrototype = () => tstl.createTableIndexExpression( createClassNameWithExport(), - tstl.createStringLiteral("prototype") + tstl.createStringLiteral("prototype"), + statement ); const classPrototypeTable = noClassOr - ? tstl.createTableExpression() + ? tstl.createTableExpression([], statement) : tstl.createBinaryExpression( createClassPrototype(), tstl.createTableExpression(), - tstl.SyntaxKind.OrOperator + tstl.SyntaxKind.OrOperator, + statement ); const assignClassPrototype = tstl.createAssignmentStatement(createClassPrototype(), classPrototypeTable); result.push(assignClassPrototype); @@ -663,11 +669,13 @@ export class LuaTransformer { if (statement.members.some(m => ts.isGetAccessor(m) && !tsHelper.isStatic(m))) { const classPrototypeGetters = tstl.createTableIndexExpression( createClassPrototype(), - tstl.createStringLiteral("____getters") + tstl.createStringLiteral("____getters"), + statement ); const assignClassPrototypeGetters = tstl.createAssignmentStatement( classPrototypeGetters, - tstl.createTableExpression() + tstl.createTableExpression(), + statement ); result.push(assignClassPrototypeGetters); } @@ -680,7 +688,8 @@ export class LuaTransformer { // className.prototype.__index = __TS_Index(className.prototype) const assignClassPrototypeIndex = tstl.createAssignmentStatement( classPrototypeIndex, - this.transformLuaLibFunction(LuaLibFeature.Index, undefined, createClassPrototype()) + this.transformLuaLibFunction(LuaLibFeature.Index, undefined, createClassPrototype()), + statement ); result.push(assignClassPrototypeIndex); @@ -688,7 +697,8 @@ export class LuaTransformer { // className.prototype.__index = className.prototype const assignClassPrototypeIndex = tstl.createAssignmentStatement( classPrototypeIndex, - createClassPrototype() + createClassPrototype(), + statement ); result.push(assignClassPrototypeIndex); } @@ -697,11 +707,13 @@ export class LuaTransformer { // className.prototype.____setters = {} const classPrototypeSetters = tstl.createTableIndexExpression( createClassPrototype(), - tstl.createStringLiteral("____setters") + tstl.createStringLiteral("____setters"), + statement ); const assignClassPrototypeSetters = tstl.createAssignmentStatement( classPrototypeSetters, - tstl.createTableExpression() + tstl.createTableExpression(), + statement ); result.push(assignClassPrototypeSetters); } @@ -741,7 +753,8 @@ export class LuaTransformer { // className.____super = baseName const createClassBase = () => tstl.createTableIndexExpression( createClassNameWithExport(), - tstl.createStringLiteral("____super") + tstl.createStringLiteral("____super"), + statement ); const assignClassBase = tstl.createAssignmentStatement(createClassBase(), baseName, statement); result.push(assignClassBase); @@ -795,13 +808,15 @@ export class LuaTransformer { // setmetatable(className.prototype, className.____super.prototype) const basePrototype = tstl.createTableIndexExpression( createClassBase(), - tstl.createStringLiteral("prototype") + tstl.createStringLiteral("prototype"), + statement ); const setClassPrototypeMetatable = tstl.createExpressionStatement( tstl.createCallExpression( tstl.createIdentifier("setmetatable"), [createClassPrototype(), basePrototype] - ) + ), + statement ); result.push(setClassPrototypeMetatable); @@ -844,7 +859,8 @@ export class LuaTransformer { tstl.createCallExpression( tstl.createIdentifier("setmetatable"), [tstl.createTableExpression(), createClassPrototype()] - ) + ), + statement ); newFuncStatements.push(assignSelf); @@ -854,12 +870,13 @@ export class LuaTransformer { this.createSelfIdentifier(), tstl.createIdentifier("____constructor"), [tstl.createDotsLiteral()] - ) + ), + statement ); newFuncStatements.push(callConstructor); // return self - const returnSelf = tstl.createReturnStatement([this.createSelfIdentifier()]); + const returnSelf = tstl.createReturnStatement([this.createSelfIdentifier()], statement); newFuncStatements.push(returnSelf); // function className.new(construct, ...) ... end @@ -874,7 +891,8 @@ export class LuaTransformer { tstl.createDotsLiteral(), undefined, statement - ) + ), + statement ); result.push(newFunc); @@ -898,7 +916,7 @@ export class LuaTransformer { const selfIndex = tstl.createTableIndexExpression(this.createSelfIdentifier(), fieldName); // self[fieldName] = value - const assignClassField = tstl.createAssignmentStatement(selfIndex, value); + const assignClassField = tstl.createAssignmentStatement(selfIndex, value, f); statements.push(assignClassField); } @@ -1088,7 +1106,8 @@ export class LuaTransformer { tstl.createBlock(body), paramNames, dots, - restParamName + restParamName, + node.body ); const classNameWithExport = this.addExportToIdentifier(tstl.cloneIdentifier(className)); @@ -1493,20 +1512,6 @@ export class LuaTransformer { resumeCall) ); - //coroutine.status(____co) ~= "dead"; - const coStatus = tstl.createCallExpression( - tstl.createTableIndexExpression( - tstl.createIdentifier("coroutine"), - tstl.createStringLiteral("status") - ), - [coroutineIdentifier] - ); - const status = tstl.createBinaryExpression( - coStatus, - tstl.createStringLiteral("dead"), - tstl.SyntaxKind.EqualityOperator - ); - nextBody.push(status); //if(not ____err){error(____value)} const errorCheck = tstl.createIfStatement( tstl.createUnaryExpression( @@ -1523,7 +1528,22 @@ export class LuaTransformer { ]) ); nextBody.push(errorCheck); - //{done = coroutine.status(____co) ~= "dead"; value = ____value} + + //coroutine.status(____co) == "dead"; + const coStatus = tstl.createCallExpression( + tstl.createTableIndexExpression( + tstl.createIdentifier("coroutine"), + tstl.createStringLiteral("status") + ), + [coroutineIdentifier] + ); + const status = tstl.createBinaryExpression( + coStatus, + tstl.createStringLiteral("dead"), + tstl.SyntaxKind.EqualityOperator + ); + + //{done = coroutine.status(____co) == "dead"; value = ____value} const iteratorResult = tstl.createTableExpression([ tstl.createTableFieldExpression( status, @@ -1801,10 +1821,10 @@ export class LuaTransformer { return tstl.createReturnStatement([expression]); } } - return tstl.createReturnStatement([this.transformExpression(statement.expression)]); + return tstl.createReturnStatement([this.transformExpression(statement.expression)], statement); } else { // Empty return - return tstl.createReturnStatement(); + return tstl.createReturnStatement([], statement); } } @@ -3107,7 +3127,7 @@ export class LuaTransformer { return this.transformLuaLibFunction(LuaLibFeature.Symbol, node, ...parameters); } - const callExpression = tstl.createCallExpression(callPath, parameters); + const callExpression = tstl.createCallExpression(callPath, parameters, node); return wrapResult ? this.wrapInTable(callExpression) : callExpression; } @@ -3198,13 +3218,17 @@ export class LuaTransformer { // table:name() return tstl.createMethodCallExpression( table, - tstl.createIdentifier(name), + this.transformIdentifier(node.expression.name), parameters, node ); } else { // table.name() - const callPath = tstl.createTableIndexExpression(table, tstl.createStringLiteral(name)); + const callPath = tstl.createTableIndexExpression( + table, + tstl.createStringLiteral(name), + node.expression + ); return tstl.createCallExpression(callPath, parameters, node); } } @@ -3752,7 +3776,7 @@ export class LuaTransformer { public transformStringLiteral(literal: ts.StringLiteralLike): tstl.StringLiteral { const text = tsHelper.escapeString(literal.text); - return tstl.createStringLiteral(text); + return tstl.createStringLiteral(text, literal); } public transformNumericLiteral(literal: ts.NumericLiteral): tstl.NumericLiteral { @@ -3805,13 +3829,13 @@ export class LuaTransformer { const value = Number(propertyName.text); return tstl.createNumericLiteral(value, propertyName); } else { - return tstl.createStringLiteral(this.transformIdentifier(propertyName).text); + return tstl.createStringLiteral(this.transformIdentifier(propertyName).text, propertyName); } } public transformIdentifier(expression: ts.Identifier): tstl.Identifier { if (expression.originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) { - return tstl.createIdentifier("nil"); // TODO this is a hack that allows use to keep Identifier + return tstl.createIdentifier("nil", expression); // TODO this is a hack that allows use to keep Identifier // as return time as changing that would break a lot of stuff. // But this should be changed to retun tstl.createNilLiteral() // at some point. @@ -4016,7 +4040,6 @@ export class LuaTransformer { // exported if (!rhs) { return []; - } else if (Array.isArray(lhs)) { assignment = tstl.createAssignmentStatement( lhs.map(i => this.createExportedIdentifier(i)), @@ -4243,10 +4266,11 @@ export class LuaTransformer { if (scope.functionDefinitions) { for (const [functionSymbolId, functionDefinition] of scope.functionDefinitions) { + const { line, column } = tstl.getOriginalPos(functionDefinition.assignment); const assignmentPos = ts.getPositionOfLineAndCharacter( this.currentSourceFile, - functionDefinition.assignment.line, - functionDefinition.assignment.column); + line, + column); if (functionSymbolId !== symbolId // Don't recurse into self && declaration.pos < assignmentPos // Ignore functions before symbol declaration && functionDefinition.referencedSymbols.has(symbolId) diff --git a/test/compiler/watchmode.spec.ts b/test/compiler/watchmode.spec.ts index 7b838316b..105f02fdb 100644 --- a/test/compiler/watchmode.spec.ts +++ b/test/compiler/watchmode.spec.ts @@ -23,8 +23,8 @@ export class CompilerWatchModeTest { Expect(fs.existsSync(fileToChangeOut)).toBe(true); - const initialResultLua = fs.readFileSync(fileToChangeOut); - const originalTS = fs.readFileSync(fileToChange); + const initialResultLua = fs.readFileSync(fileToChangeOut, "utf-8"); + const originalTS = fs.readFileSync(fileToChange, "utf-8"); fs.unlinkSync(fileToChangeOut); @@ -33,7 +33,7 @@ export class CompilerWatchModeTest { await this.waitForFileExists(fileToChangeOut, 5000) .catch(err => console.error(err)); - const updatedResultLua = fs.readFileSync(fileToChangeOut).toString(); + const updatedResultLua = fs.readFileSync(fileToChangeOut, "utf-8").toString(); Expect(initialResultLua).not.toEqual(updatedResultLua);