@@ -3032,7 +3032,18 @@ export class LuaTransformer {
30323032 }
30333033 }
30343034
3035- protected transformAssignment ( lhs : ts . Expression , right ?: tstl . Expression ) : tstl . Statement {
3035+ protected transformAssignment ( lhs : ts . Expression , right : tstl . Expression , parent ?: ts . Expression ) : tstl . Statement {
3036+ if ( tsHelper . isArrayLength ( lhs , this . checker , this . program ) ) {
3037+ return tstl . createExpressionStatement (
3038+ this . transformLuaLibFunction (
3039+ LuaLibFeature . ArraySetLength ,
3040+ parent ,
3041+ this . transformExpression ( lhs . expression ) ,
3042+ right
3043+ )
3044+ ) ;
3045+ }
3046+
30363047 return tstl . createAssignmentStatement (
30373048 this . transformExpression ( lhs ) as tstl . AssignmentLeftHandSideExpression ,
30383049 right ,
@@ -3047,44 +3058,283 @@ export class LuaTransformer {
30473058 this . validateFunctionAssignment ( expression . right , rightType , leftType ) ;
30483059 this . validatePropertyAssignment ( expression ) ;
30493060
3050- if ( tsHelper . isArrayLengthAssignment ( expression , this . checker , this . program ) ) {
3051- // array.length = x
3052- return tstl . createExpressionStatement (
3053- this . transformLuaLibFunction (
3054- LuaLibFeature . ArraySetLength ,
3055- expression ,
3056- this . transformExpression ( expression . left . expression ) ,
3057- this . transformExpression ( expression . right )
3058- )
3061+ if ( tsHelper . isAssignmentPattern ( expression . left ) ) {
3062+ // Destructuring assignment
3063+ const flattenable = tsHelper . isValidFlattenableDestructuringAssignmentLeftHandSide (
3064+ expression as ts . DestructuringAssignment ,
3065+ this . checker ,
3066+ this . program
30593067 ) ;
3060- }
30613068
3062- if ( ts . isArrayLiteralExpression ( expression . left ) ) {
3063- // Destructuring assignment
3064- const left =
3065- expression . left . elements . length > 0
3066- ? expression . left . elements . map ( e => this . transformArrayBindingElement ( e ) )
3067- : [ tstl . createAnonymousIdentifier ( expression . left ) ] ;
3068- let right : tstl . Expression [ ] ;
3069- if ( ts . isArrayLiteralExpression ( expression . right ) ) {
3070- if ( expression . right . elements . length > 0 ) {
3071- const visitResults = expression . right . elements . map ( e => this . transformExpression ( e ) ) ;
3072- right = this . filterUndefined ( visitResults ) ;
3073- } else {
3074- right = [ tstl . createNilLiteral ( ) ] ;
3069+ if ( flattenable ) {
3070+ const expressionType = this . checker . getTypeAtLocation ( expression . right ) ;
3071+ let right = this . transformExpression ( expression . right ) ;
3072+
3073+ if (
3074+ ! tsHelper . isTupleReturnCall ( expression . right , this . checker ) &&
3075+ tsHelper . isArrayType ( expressionType , this . checker , this . program )
3076+ ) {
3077+ right = this . createUnpackCall ( right , expression . right ) ;
30753078 }
3076- } else if ( tsHelper . isTupleReturnCall ( expression . right , this . checker ) ) {
3077- right = [ this . transformExpression ( expression . right ) ] ;
3078- } else {
3079- right = [ this . createUnpackCall ( this . transformExpression ( expression . right ) , expression . right ) ] ;
3079+
3080+ return this . transformFlattenableDestructuringAssignment (
3081+ expression as ts . DestructuringAssignment ,
3082+ right
3083+ ) ;
3084+ }
3085+
3086+ let right = this . transformExpression ( expression . right ) ;
3087+ const rootIdentifier = tstl . createAnonymousIdentifier ( expression . left ) ;
3088+
3089+ if ( tsHelper . isTupleReturnCall ( expression . right , this . checker ) ) {
3090+ right = this . wrapInTable ( right ) ;
30803091 }
3081- return tstl . createAssignmentStatement ( left as tstl . AssignmentLeftHandSideExpression [ ] , right , expression ) ;
3092+
3093+ const rootDeclaration = tstl . createVariableDeclarationStatement ( rootIdentifier , right ) ;
3094+
3095+ const statements = this . transformDestructuringAssignment (
3096+ expression as ts . DestructuringAssignment ,
3097+ rootIdentifier
3098+ ) ;
3099+ statements . unshift ( rootDeclaration ) ;
3100+
3101+ return statements ;
30823102 } else {
30833103 // Simple assignment
30843104 return this . transformAssignment ( expression . left , this . transformExpression ( expression . right ) ) ;
30853105 }
30863106 }
30873107
3108+ protected transformFlattenableDestructuringAssignment (
3109+ node : ts . DestructuringAssignment ,
3110+ right : tstl . Expression | tstl . Expression [ ]
3111+ ) : tstl . Statement {
3112+ if ( ts . isArrayLiteralExpression ( node . left ) ) {
3113+ const left : tstl . AssignmentLeftHandSideExpression [ ] = node . left . elements . map (
3114+ element => this . transformExpression ( element ) as tstl . AssignmentLeftHandSideExpression
3115+ ) ;
3116+ return tstl . createAssignmentStatement ( left , right , node ) ;
3117+ }
3118+
3119+ throw TSTLErrors . NonFlattenableDestructure ( node ) ;
3120+ }
3121+
3122+ protected transformDestructuringAssignment (
3123+ node : ts . DestructuringAssignment ,
3124+ root : tstl . Expression
3125+ ) : tstl . Statement [ ] {
3126+ switch ( node . left . kind ) {
3127+ case ts . SyntaxKind . ObjectLiteralExpression :
3128+ return this . transformObjectDestructuringAssignment ( node as ts . ObjectDestructuringAssignment , root ) ;
3129+ case ts . SyntaxKind . ArrayLiteralExpression :
3130+ return this . transformArrayDestructuringAssignment ( node as ts . ArrayDestructuringAssignment , root ) ;
3131+ }
3132+ }
3133+
3134+ protected transformObjectDestructuringAssignment (
3135+ node : ts . ObjectDestructuringAssignment ,
3136+ root : tstl . Expression
3137+ ) : tstl . Statement [ ] {
3138+ return this . transformObjectLiteralAssignmentPattern ( node . left , root ) ;
3139+ }
3140+
3141+ protected transformArrayDestructuringAssignment (
3142+ node : ts . ArrayDestructuringAssignment ,
3143+ root : tstl . Expression
3144+ ) : tstl . Statement [ ] {
3145+ return this . transformArrayLiteralAssignmentPattern ( node . left , root ) ;
3146+ }
3147+
3148+ protected transformShorthandPropertyAssignment (
3149+ node : ts . ShorthandPropertyAssignment ,
3150+ root : tstl . Expression
3151+ ) : tstl . Statement [ ] {
3152+ const result : tstl . Statement [ ] = [ ] ;
3153+ const assignmentVariableName = this . transformIdentifier ( node . name ) ;
3154+ const extractionIndex = tstl . createStringLiteral ( node . name . text ) ;
3155+ const variableExtractionAssignmentStatement = tstl . createAssignmentStatement (
3156+ assignmentVariableName ,
3157+ tstl . createTableIndexExpression ( root , extractionIndex )
3158+ ) ;
3159+
3160+ result . push ( variableExtractionAssignmentStatement ) ;
3161+
3162+ const defaultInitializer = node . objectAssignmentInitializer
3163+ ? this . transformExpression ( node . objectAssignmentInitializer )
3164+ : undefined ;
3165+
3166+ if ( defaultInitializer ) {
3167+ const nilCondition = tstl . createBinaryExpression (
3168+ assignmentVariableName ,
3169+ tstl . createNilLiteral ( ) ,
3170+ tstl . SyntaxKind . EqualityOperator
3171+ ) ;
3172+
3173+ const assignment = tstl . createAssignmentStatement ( assignmentVariableName , defaultInitializer ) ;
3174+
3175+ const ifBlock = tstl . createBlock ( [ assignment ] ) ;
3176+
3177+ result . push ( tstl . createIfStatement ( nilCondition , ifBlock , undefined , node ) ) ;
3178+ }
3179+
3180+ return result ;
3181+ }
3182+
3183+ protected transformObjectLiteralAssignmentPattern (
3184+ node : ts . ObjectLiteralExpression ,
3185+ root : tstl . Expression
3186+ ) : tstl . Statement [ ] {
3187+ const result : tstl . Statement [ ] = [ ] ;
3188+
3189+ for ( const property of node . properties ) {
3190+ switch ( property . kind ) {
3191+ case ts . SyntaxKind . ShorthandPropertyAssignment :
3192+ result . push ( ...this . transformShorthandPropertyAssignment ( property , root ) ) ;
3193+ break ;
3194+ case ts . SyntaxKind . PropertyAssignment :
3195+ result . push ( ...this . transformPropertyAssignment ( property , root ) ) ;
3196+ break ;
3197+ case ts . SyntaxKind . SpreadAssignment :
3198+ throw TSTLErrors . ForbiddenEllipsisDestruction ( property ) ;
3199+ default :
3200+ throw TSTLErrors . UnsupportedKind ( "Object Destructure Property" , property . kind , property ) ;
3201+ }
3202+ }
3203+
3204+ return result ;
3205+ }
3206+
3207+ protected transformArrayLiteralAssignmentPattern (
3208+ node : ts . ArrayLiteralExpression ,
3209+ root : tstl . Expression
3210+ ) : tstl . Statement [ ] {
3211+ const result : tstl . Statement [ ] = [ ] ;
3212+
3213+ node . elements . forEach ( ( element , index ) => {
3214+ const indexedRoot = tstl . createTableIndexExpression (
3215+ root as tstl . Expression ,
3216+ tstl . createNumericLiteral ( index + 1 ) ,
3217+ element
3218+ ) ;
3219+
3220+ switch ( element . kind ) {
3221+ case ts . SyntaxKind . ObjectLiteralExpression :
3222+ result . push (
3223+ ...this . transformObjectLiteralAssignmentPattern (
3224+ element as ts . ObjectLiteralExpression ,
3225+ indexedRoot
3226+ )
3227+ ) ;
3228+ break ;
3229+ case ts . SyntaxKind . ArrayLiteralExpression :
3230+ result . push (
3231+ ...this . transformArrayLiteralAssignmentPattern (
3232+ element as ts . ArrayLiteralExpression ,
3233+ indexedRoot
3234+ )
3235+ ) ;
3236+ break ;
3237+ case ts . SyntaxKind . BinaryExpression :
3238+ const assignedVariable = tstl . createIdentifier ( "____bindingAssignmentValue" ) ;
3239+
3240+ const assignedVariableDeclaration = tstl . createVariableDeclarationStatement (
3241+ assignedVariable ,
3242+ indexedRoot
3243+ ) ;
3244+
3245+ const nilCondition = tstl . createBinaryExpression (
3246+ assignedVariable ,
3247+ tstl . createNilLiteral ( ) ,
3248+ tstl . SyntaxKind . EqualityOperator
3249+ ) ;
3250+
3251+ const defaultAssignmentStatement = this . transformAssignment (
3252+ ( element as ts . BinaryExpression ) . left ,
3253+ this . transformExpression ( ( element as ts . BinaryExpression ) . right )
3254+ ) ;
3255+
3256+ const elseAssignmentStatement = this . transformAssignment (
3257+ ( element as ts . BinaryExpression ) . left ,
3258+ assignedVariable
3259+ ) ;
3260+
3261+ const ifBlock = tstl . createBlock ( [ defaultAssignmentStatement ] ) ;
3262+
3263+ const elseBlock = tstl . createBlock ( [ elseAssignmentStatement ] ) ;
3264+
3265+ const ifStatement = tstl . createIfStatement ( nilCondition , ifBlock , elseBlock , node ) ;
3266+
3267+ result . push ( assignedVariableDeclaration ) ;
3268+ result . push ( ifStatement ) ;
3269+ break ;
3270+ case ts . SyntaxKind . Identifier :
3271+ case ts . SyntaxKind . PropertyAccessExpression :
3272+ case ts . SyntaxKind . ElementAccessExpression :
3273+ const assignmentStatement = this . transformAssignment ( element , indexedRoot ) ;
3274+
3275+ result . push ( assignmentStatement ) ;
3276+ break ;
3277+ case ts . SyntaxKind . OmittedExpression :
3278+ break ;
3279+ default :
3280+ throw TSTLErrors . UnsupportedKind ( "Array Destructure Assignment Element" , element . kind , element ) ;
3281+ }
3282+ } ) ;
3283+
3284+ return result ;
3285+ }
3286+
3287+ protected transformPropertyAssignment ( node : ts . PropertyAssignment , root : tstl . Expression ) : tstl . Statement [ ] {
3288+ const result : tstl . Statement [ ] = [ ] ;
3289+
3290+ if ( tsHelper . isAssignmentPattern ( node . initializer ) ) {
3291+ const propertyAccessString = this . transformPropertyName ( node . name ) ;
3292+ const newRootAccess = tstl . createTableIndexExpression ( root , propertyAccessString ) ;
3293+
3294+ if ( ts . isObjectLiteralExpression ( node . initializer ) ) {
3295+ return this . transformObjectLiteralAssignmentPattern ( node . initializer , newRootAccess ) ;
3296+ }
3297+
3298+ if ( ts . isArrayLiteralExpression ( node . initializer ) ) {
3299+ return this . transformArrayLiteralAssignmentPattern ( node . initializer , newRootAccess ) ;
3300+ }
3301+ }
3302+
3303+ let leftExpression : ts . Expression ;
3304+ if ( ts . isBinaryExpression ( node . initializer ) ) {
3305+ leftExpression = node . initializer . left ;
3306+ } else {
3307+ leftExpression = node . initializer ;
3308+ }
3309+
3310+ const variableToExtract = this . transformPropertyName ( node . name ) ;
3311+ const extractingExpression = tstl . createTableIndexExpression ( root , variableToExtract ) ;
3312+
3313+ const destructureAssignmentStatement = this . transformAssignment ( leftExpression , extractingExpression ) ;
3314+
3315+ result . push ( destructureAssignmentStatement ) ;
3316+
3317+ if ( ts . isBinaryExpression ( node . initializer ) ) {
3318+ const assignmentLeftHandSide = this . transformExpression ( node . initializer . left ) ;
3319+
3320+ const nilCondition = tstl . createBinaryExpression (
3321+ assignmentLeftHandSide ,
3322+ tstl . createNilLiteral ( ) ,
3323+ tstl . SyntaxKind . EqualityOperator
3324+ ) ;
3325+
3326+ const assignmentStatements = this . statementVisitResultToArray (
3327+ this . transformAssignmentStatement ( node . initializer )
3328+ ) ;
3329+
3330+ const ifBlock = tstl . createBlock ( assignmentStatements ) ;
3331+
3332+ result . push ( tstl . createIfStatement ( nilCondition , ifBlock , undefined , node ) ) ;
3333+ }
3334+
3335+ return result ;
3336+ }
3337+
30883338 protected transformAssignmentExpression (
30893339 expression : ts . BinaryExpression
30903340 ) : tstl . CallExpression | tstl . MethodCallExpression {
@@ -3093,7 +3343,7 @@ export class LuaTransformer {
30933343 const leftType = this . checker . getTypeAtLocation ( expression . left ) ;
30943344 this . validateFunctionAssignment ( expression . right , rightType , leftType ) ;
30953345
3096- if ( tsHelper . isArrayLengthAssignment ( expression , this . checker , this . program ) ) {
3346+ if ( tsHelper . isArrayLength ( expression . left , this . checker , this . program ) ) {
30973347 // array.length = x
30983348 return this . transformLuaLibFunction (
30993349 LuaLibFeature . ArraySetLength ,
@@ -3103,34 +3353,24 @@ export class LuaTransformer {
31033353 ) ;
31043354 }
31053355
3106- if ( ts . isArrayLiteralExpression ( expression . left ) ) {
3356+ if ( tsHelper . isAssignmentPattern ( expression . left ) ) {
31073357 // Destructuring assignment
3108- // (function() local ${tmps} = ${right}; ${left} = ${tmps}; return {${tmps}} end)()
3109- const left =
3110- expression . left . elements . length > 0
3111- ? expression . left . elements . map ( e => this . transformExpression ( e ) )
3112- : [ tstl . createAnonymousIdentifier ( expression . left ) ] ;
3113- let right : tstl . Expression [ ] ;
3114- if ( ts . isArrayLiteralExpression ( expression . right ) ) {
3115- right =
3116- expression . right . elements . length > 0
3117- ? expression . right . elements . map ( e => this . transformExpression ( e ) )
3118- : [ tstl . createNilLiteral ( ) ] ;
3119- } else if ( tsHelper . isTupleReturnCall ( expression . right , this . checker ) ) {
3120- right = [ this . transformExpression ( expression . right ) ] ;
3121- } else {
3122- right = [ this . createUnpackCall ( this . transformExpression ( expression . right ) , expression . right ) ] ;
3358+ const rootIdentifier = tstl . createAnonymousIdentifier ( expression . left ) ;
3359+
3360+ let right = this . transformExpression ( expression . right ) ;
3361+ if ( tsHelper . isTupleReturnCall ( expression . right , this . checker ) ) {
3362+ right = this . wrapInTable ( right ) ;
31233363 }
3124- const tmps = left . map ( ( _ , i ) => tstl . createIdentifier ( `____tmp${ i } ` ) ) ;
3125- const statements : tstl . Statement [ ] = [
3126- tstl . createVariableDeclarationStatement ( tmps , right ) ,
3127- tstl . createAssignmentStatement ( left as tstl . AssignmentLeftHandSideExpression [ ] , tmps ) ,
3128- ] ;
3129- return this . createImmediatelyInvokedFunctionExpression (
3130- statements ,
3131- tstl . createTableExpression ( tmps . map ( t => tstl . createTableFieldExpression ( t ) ) ) ,
3132- expression
3364+
3365+ const rootDeclaration = tstl . createVariableDeclarationStatement ( rootIdentifier , right ) ;
3366+
3367+ const statements = this . transformDestructuringAssignment (
3368+ expression as ts . DestructuringAssignment ,
3369+ rootIdentifier
31333370 ) ;
3371+ statements . unshift ( rootDeclaration ) ;
3372+
3373+ return this . createImmediatelyInvokedFunctionExpression ( statements , rootIdentifier , expression ) ;
31343374 }
31353375
31363376 if ( ts . isPropertyAccessExpression ( expression . left ) || ts . isElementAccessExpression ( expression . left ) ) {
0 commit comments