@@ -58,7 +58,7 @@ class FunctionNode {
58
58
59
59
this . validate ( ) ;
60
60
this . _string = null ;
61
- this . _whileIndex = 0 ;
61
+ this . _internalVariableNames = { } ;
62
62
}
63
63
64
64
validate ( ) {
@@ -178,7 +178,7 @@ class FunctionNode {
178
178
throw 'Missing JS to AST parser' ;
179
179
}
180
180
181
- const ast = Object . freeze ( inParser . parse ( ' const parser_' + this . name + ' = ' + this . source + ';' , {
181
+ const ast = Object . freeze ( inParser . parse ( ` const parser_${ this . name } = ${ this . source } ;` , {
182
182
locations : true
183
183
} ) ) ;
184
184
// take out the function object, outside the var declarations
@@ -200,7 +200,7 @@ class FunctionNode {
200
200
const argumentIndex = this . argumentNames . indexOf ( name ) ;
201
201
if ( argumentIndex === - 1 ) {
202
202
if ( this . declarations [ name ] ) {
203
- return this . declarations [ name ] ;
203
+ return this . declarations [ name ] . type ;
204
204
}
205
205
} else {
206
206
const argumentType = this . argumentTypes [ argumentIndex ] ;
@@ -218,7 +218,11 @@ class FunctionNode {
218
218
}
219
219
}
220
220
}
221
- return type === 'Float' ? 'Number' : type ;
221
+ if ( ! type ) {
222
+ // TODO: strict type detection mode?
223
+ // throw new Error(`Declaration of ${name} not found`);
224
+ }
225
+ return type ;
222
226
}
223
227
224
228
getConstantType ( constantName ) {
@@ -289,7 +293,12 @@ class FunctionNode {
289
293
* @returns {string }
290
294
*/
291
295
getType ( ast ) {
296
+ if ( Array . isArray ( ast ) ) {
297
+ return this . getType ( ast [ ast . length - 1 ] ) ;
298
+ }
292
299
switch ( ast . type ) {
300
+ case 'BlockStatement' :
301
+ return this . getType ( ast . body ) ;
293
302
case 'ArrayExpression' :
294
303
return `Array(${ ast . elements . length } )` ;
295
304
case 'Literal' :
@@ -307,6 +316,8 @@ class FunctionNode {
307
316
// modulos is Number
308
317
if ( ast . operator === '%' ) {
309
318
return 'Number' ;
319
+ } else if ( ast . operator === '>' || ast . operator === '<' ) {
320
+ return 'Boolean' ;
310
321
}
311
322
const type = this . getType ( ast . left ) ;
312
323
return typeLookupMap [ type ] || type ;
@@ -320,11 +331,21 @@ class FunctionNode {
320
331
return this . getType ( ast . id ) ;
321
332
case 'Identifier' :
322
333
if ( this . isAstVariable ( ast ) ) {
323
- if ( this . getVariableSignature ( ast ) === 'value' ) {
324
- return this . getVariableType ( ast . name ) ;
334
+ const signature = this . getVariableSignature ( ast ) ;
335
+ if ( signature === 'value' ) {
336
+ if ( this . argumentNames . indexOf ( ast . name ) > - 1 ) {
337
+ return this . getVariableType ( ast . name ) ;
338
+ } else if ( this . declarations [ ast . name ] ) {
339
+ return this . declarations [ ast . name ] . type ;
340
+ }
325
341
}
326
342
}
327
- throw this . astErrorOutput ( 'Unhandled Identifier' , ast ) ;
343
+ if ( ast . name === 'Infinity' ) {
344
+ return 'Integer' ;
345
+ }
346
+ return null ;
347
+ case 'ReturnStatement' :
348
+ return this . getType ( ast . argument ) ;
328
349
case 'MemberExpression' :
329
350
if ( this . isAstMathFunction ( ast ) ) {
330
351
switch ( ast . property . name ) {
@@ -383,13 +404,7 @@ class FunctionNode {
383
404
}
384
405
throw this . astErrorOutput ( 'Unhandled getType MemberExpression' , ast ) ;
385
406
case 'FunctionDeclaration' :
386
- if ( ast . body && ast . body . body ) {
387
- const possibleReturnStatement = ast . body . body [ ast . body . body . length - 1 ] ;
388
- if ( possibleReturnStatement . type === 'ReturnStatement' ) {
389
- return this . getType ( possibleReturnStatement . argument ) ;
390
- }
391
- }
392
- throw this . astErrorOutput ( `Unhandled getType Type "${ ast . type } "` , ast ) ;
407
+ return this . getType ( ast . body ) ;
393
408
case 'ConditionalExpression' :
394
409
return this . getType ( ast . consequent ) ;
395
410
default :
@@ -454,22 +469,84 @@ class FunctionNode {
454
469
return ast . type === 'Identifier' || ast . type === 'MemberExpression' ;
455
470
}
456
471
457
- getFirstVariable ( ast ) {
472
+ isSafe ( ast ) {
473
+ return this . isSafeDependencies ( this . getDependencies ( ast ) ) ;
474
+ }
475
+
476
+ isSafeDependencies ( dependencies ) {
477
+ return dependencies && dependencies . every ? dependencies . every ( dependency => dependency . isSafe ) : true ;
478
+ }
479
+
480
+ getDependencies ( ast , dependencies , isNotSafe ) {
481
+ if ( ! dependencies ) {
482
+ dependencies = [ ] ;
483
+ }
458
484
if ( ! ast ) return null ;
485
+ if ( Array . isArray ( ast ) ) {
486
+ for ( let i = 0 ; i < ast . length ; i ++ ) {
487
+ this . getDependencies ( ast [ i ] , dependencies , isNotSafe ) ;
488
+ }
489
+ return dependencies ;
490
+ }
459
491
switch ( ast . type ) {
492
+ case 'Literal' :
493
+ dependencies . push ( {
494
+ origin : 'literal' ,
495
+ value : ast . value ,
496
+ isSafe : isNotSafe === true ? false : ast . value > - Infinity && ast . value < Infinity && ! isNaN ( ast . value )
497
+ } ) ;
498
+ break ;
499
+ case 'VariableDeclarator' :
500
+ return this . getDependencies ( ast . init , dependencies , isNotSafe ) ;
460
501
case 'Identifier' :
461
- return ast ;
502
+ if ( this . declarations [ ast . name ] ) {
503
+ dependencies . push ( {
504
+ name : ast . name ,
505
+ origin : 'declaration' ,
506
+ isSafe : isNotSafe ? false : this . isSafeDependencies ( this . declarations [ ast . name ] . dependencies ) ,
507
+ } ) ;
508
+ } else if ( this . argumentNames . indexOf ( ast . name ) > - 1 ) {
509
+ dependencies . push ( {
510
+ name : ast . name ,
511
+ origin : 'argument' ,
512
+ isSafe : false ,
513
+ } ) ;
514
+ }
515
+ break ;
462
516
case 'FunctionDeclaration' :
463
- return this . getFirstVariable ( ast . body . body [ ast . body . body . length - 1 ] ) ;
517
+ return this . getDependencies ( ast . body . body [ ast . body . body . length - 1 ] , dependencies , isNotSafe ) ;
464
518
case 'ReturnStatement' :
465
- return this . getFirstVariable ( ast . argument ) ;
519
+ return this . getDependencies ( ast . argument , dependencies ) ;
466
520
case 'BinaryExpression' :
467
- return this . getFirstVariable ( ast . left ) ;
521
+ isNotSafe = ( ast . operator === '/' || ast . operator === '*' ) ;
522
+ this . getDependencies ( ast . left , dependencies , isNotSafe ) ;
523
+ this . getDependencies ( ast . right , dependencies , isNotSafe ) ;
524
+ return dependencies ;
468
525
case 'UpdateExpression' :
469
- return this . getFirstVariable ( ast . argument ) ;
526
+ return this . getDependencies ( ast . argument , dependencies , isNotSafe ) ;
527
+ case 'VariableDeclaration' :
528
+ return this . getDependencies ( ast . declarations , dependencies , isNotSafe ) ;
529
+ case 'ArrayExpression' :
530
+ dependencies . push ( {
531
+ origin : 'declaration' ,
532
+ isSafe : true ,
533
+ } ) ;
534
+ return dependencies ;
535
+ case 'CallExpression' :
536
+ dependencies . push ( {
537
+ origin : 'function' ,
538
+ isSafe : true ,
539
+ } ) ;
540
+ return dependencies ;
541
+ case 'MemberExpression' :
542
+ const details = this . getMemberExpressionDetails ( ast ) ;
543
+ if ( details ) {
544
+ return details . type ;
545
+ }
470
546
default :
471
- throw this . astErrorOutput ( ' Unhandled variable lookup' , ast ) ;
547
+ throw this . astErrorOutput ( ` Unhandled type ${ ast . type } in getAllVariables` , ast ) ;
472
548
}
549
+ return dependencies ;
473
550
}
474
551
475
552
getVariableSignature ( ast ) {
@@ -719,7 +796,7 @@ class FunctionNode {
719
796
* @returns {Array } the append retArr
720
797
*/
721
798
astBreakStatement ( brNode , retArr ) {
722
- retArr . push ( 'break;\n ' ) ;
799
+ retArr . push ( 'break;' ) ;
723
800
return retArr ;
724
801
}
725
802
/**
@@ -741,7 +818,53 @@ class FunctionNode {
741
818
astDoWhileStatement ( ast , retArr ) {
742
819
return retArr ;
743
820
}
744
- astVariableDeclaration ( ast , retArr ) {
821
+ /**
822
+ * @desc Parses the abstract syntax tree for *Variable Declaration*
823
+ * @param {Object } varDecNode - An ast Node
824
+ * @param {Array } retArr - return array string
825
+ * @returns {Array } the append retArr
826
+ */
827
+ astVariableDeclaration ( varDecNode , retArr ) {
828
+ const declarations = varDecNode . declarations ;
829
+ if ( ! declarations || ! declarations [ 0 ] || ! declarations [ 0 ] . init ) {
830
+ throw this . astErrorOutput ( 'Unexpected expression' , varDecNode ) ;
831
+ }
832
+ const result = [ ] ;
833
+ const firstDeclaration = declarations [ 0 ] ;
834
+ const init = firstDeclaration . init ;
835
+ let type = this . isState ( 'in-for-loop-init' ) ? 'Integer' : this . getType ( init ) ;
836
+ if ( type === 'LiteralInteger' ) {
837
+ // We had the choice to go either float or int, choosing float
838
+ type = 'Number' ;
839
+ }
840
+ const markupType = typeMap [ type ] ;
841
+ if ( ! markupType ) {
842
+ throw this . astErrorOutput ( `Markup type ${ markupType } not handled` , varDecNode ) ;
843
+ }
844
+ let dependencies = this . getDependencies ( firstDeclaration . init ) ;
845
+ this . declarations [ firstDeclaration . id . name ] = Object . freeze ( {
846
+ type,
847
+ dependencies,
848
+ isSafe : dependencies . every ( dependency => dependency . isSafe )
849
+ } ) ;
850
+ const initResult = [ `${ type } user_${ firstDeclaration . id . name } =` ] ;
851
+ this . astGeneric ( init , initResult ) ;
852
+ result . push ( initResult . join ( '' ) ) ;
853
+
854
+ // first declaration is done, now any added ones setup
855
+ for ( let i = 1 ; i < declarations . length ; i ++ ) {
856
+ const declaration = declarations [ i ] ;
857
+ dependencies = this . getDependencies ( declaration ) ;
858
+ this . declarations [ declaration . id . name ] = Object . freeze ( {
859
+ type,
860
+ dependencies,
861
+ isSafe : false
862
+ } ) ;
863
+ this . astGeneric ( declaration , result ) ;
864
+ }
865
+
866
+ retArr . push ( retArr , result . join ( ',' ) ) ;
867
+ retArr . push ( ';' ) ;
745
868
return retArr ;
746
869
}
747
870
/**
@@ -992,6 +1115,17 @@ class FunctionNode {
992
1115
throw this . astErrorOutput ( 'Unexpected expression' , ast ) ;
993
1116
}
994
1117
}
1118
+
1119
+ getInternalVariableName ( name ) {
1120
+ if ( ! this . _internalVariableNames . hasOwnProperty ( name ) ) {
1121
+ this . _internalVariableNames [ name ] = 0 ;
1122
+ }
1123
+ this . _internalVariableNames [ name ] ++ ;
1124
+ if ( this . _internalVariableNames [ name ] === 1 ) {
1125
+ return name ;
1126
+ }
1127
+ return name + this . _internalVariableNames [ name ] ;
1128
+ }
995
1129
}
996
1130
997
1131
const typeLookupMap = {
0 commit comments