@@ -318,3 +318,312 @@ def handle_or_else(self, orelse, test):
318
318
else_connect_statements = self .stmt_star_handler (orelse )
319
319
test .connect (else_connect_statements .first_statement )
320
320
return else_connect_statements .last_statements
321
+
322
+ def remove_breaks (self , last_statements ):
323
+ """Remove all break statements in last_statements."""
324
+ return [n for n in last_statements if type (n ) is not BreakNode ]
325
+
326
+ def visit_If (self , node ):
327
+ label_visitor = LabelVisitor ()
328
+ label_visitor .visit (node .test )
329
+
330
+ test = self .append_node (Node (label_visitor .result , node , line_number = node .lineno , path = self .filenames [- 1 ]))
331
+
332
+ self .add_if_label (test )
333
+
334
+ body_connect_stmts = self .stmt_star_handler (node .body )
335
+ if isinstance (body_connect_stmts , IgnoredNode ):
336
+ body_connect_stmts = ConnectStatements (first_statement = test , last_statements = [], break_statements = [])
337
+ test .connect (body_connect_stmts .first_statement )
338
+
339
+ if node .orelse :
340
+ orelse_last_nodes = self .handle_or_else (node .orelse , test )
341
+ body_connect_stmts .last_statements .extend (orelse_last_nodes )
342
+ else :
343
+ body_connect_stmts .last_statements .append (test ) # if there is no orelse, test needs an edge to the next_node
344
+
345
+ last_statements = self .remove_breaks (body_connect_stmts .last_statements )
346
+
347
+ return ControlFlowNode (test , last_statements , break_statements = body_connect_stmts .break_statements )
348
+
349
+ def visit_NameConstant (self , node ):
350
+ label_visitor = LabelVisitor ()
351
+ label_visitor .visit (node )
352
+
353
+ return self .append_node (Node (label_visitor .result , node .__class__ .__name__ , node , line_number = node .lineno , path = self .filenames [- 1 ]))
354
+
355
+ def visit_Raise (self , node ):
356
+ label = LabelVisitor ()
357
+ label .visit (node )
358
+
359
+ return self .append_node (RaiseNode (label .result , node , line_number = node .lineno , path = self .filenames [- 1 ]))
360
+
361
+ def handle_stmt_star_ignore_node (self , body , fallback_cfg_node ):
362
+ try :
363
+ fallback_cfg_node .connect (body .first_statement )
364
+ except AttributeError :
365
+ body = ConnectStatements ([fallback_cfg_node ], [fallback_cfg_node ], list ())
366
+ return body
367
+
368
+ def visit_Try (self , node ):
369
+ try_node = self .append_node (Node ('Try' , node , line_number = node .lineno , path = self .filenames [- 1 ]))
370
+
371
+ body = self .stmt_star_handler (node .body )
372
+ body = self .handle_stmt_star_ignore_node (body , try_node )
373
+
374
+ last_statements = list ()
375
+ for handler in node .handlers :
376
+ try :
377
+ name = handler .type .id
378
+ except AttributeError :
379
+ name = ''
380
+ handler_node = self .append_node (Node ('except ' + name + ':' , handler , line_number = handler .lineno , path = self .filenames [- 1 ]))
381
+ for body_node in body .last_statements :
382
+ body_node .connect (handler_node )
383
+ handler_body = self .stmt_star_handler (handler .body )
384
+ handler_body = self .handle_stmt_star_ignore_node (handler_body , handler_node )
385
+ last_statements .extend (handler_body .last_statements )
386
+
387
+ if node .orelse :
388
+ orelse_last_nodes = self .handle_or_else (node .orelse , body .last_statements [- 1 ])
389
+ body .last_statements .extend (orelse_last_nodes )
390
+
391
+ if node .finalbody :
392
+ finalbody = self .stmt_star_handler (node .finalbody )
393
+ for last in last_statements :
394
+ last .connect (finalbody .first_statement )
395
+
396
+ for last in body .last_statements :
397
+ last .connect (finalbody .first_statement )
398
+
399
+ body .last_statements .extend (finalbody .last_statements )
400
+
401
+ last_statements .extend (self .remove_breaks (body .last_statements ))
402
+
403
+ return ControlFlowNode (try_node , last_statements , break_statements = body .break_statements )
404
+
405
+ def get_names (self , node , result ):
406
+ """Recursively finds all names."""
407
+ if isinstance (node , ast .Name ):
408
+ return node .id + result
409
+ elif isinstance (node , ast .Subscript ):
410
+ return result
411
+ else :
412
+ return self .get_names (node .value , result + '.' + node .attr )
413
+
414
+ def extract_left_hand_side (self , target ):
415
+ """Extract the left hand side varialbe from a target.
416
+
417
+ Removes list indexes, stars and other left hand side elements.
418
+ """
419
+ left_hand_side = self .get_names (target , '' )
420
+
421
+ left_hand_side .replace ('*' , '' )
422
+ if '[' in left_hand_side :
423
+ index = left_hand_side .index ('[' )
424
+ left_hand_side = target [0 :index ]
425
+
426
+ return left_hand_side
427
+
428
+ def assign_tuple_target (self , node , right_hand_side_variables ):
429
+ new_assignment_nodes = list ()
430
+ for i , target in enumerate (node .targets [0 ].elts ):
431
+ value = node .value .elts [i ]
432
+
433
+ label = LabelVisitor ()
434
+ label .visit (target )
435
+
436
+ if isinstance (value , ast .Call ):
437
+ new_ast_node = ast .Assign (target , value )
438
+ new_ast_node .lineno = node .lineno
439
+
440
+ new_assignment_nodes .append ( self .assignment_call_node (label .result , new_ast_node ))
441
+
442
+ else :
443
+ label .result += ' = '
444
+ label .visit (value )
445
+
446
+ new_assignment_nodes .append (self .append_node (AssignmentNode (label .result , self .extract_left_hand_side (target ), ast .Assign (target , value ), right_hand_side_variables , line_number = node .lineno , path = self .filenames [- 1 ])))
447
+
448
+
449
+ self .connect_nodes (new_assignment_nodes )
450
+ return ControlFlowNode (new_assignment_nodes [0 ], [new_assignment_nodes [- 1 ]], []) # return the last added node
451
+
452
+ def assign_multi_target (self , node , right_hand_side_variables ):
453
+ new_assignment_nodes = list ()
454
+
455
+ for target in node .targets :
456
+ label = LabelVisitor ()
457
+ label .visit (target )
458
+ left_hand_side = label .result
459
+ label .result += ' = '
460
+ label .visit (node .value )
461
+
462
+ new_assignment_nodes .append (self .append_node (AssignmentNode (label .result , left_hand_side , ast .Assign (target , node .value ), right_hand_side_variables , line_number = node .lineno , path = self .filenames [- 1 ])))
463
+
464
+ self .connect_nodes (new_assignment_nodes )
465
+ return ControlFlowNode (new_assignment_nodes [0 ], [new_assignment_nodes [- 1 ]], []) # return the last added node
466
+
467
+ def visit_Assign (self , node ):
468
+ rhs_visitor = RHSVisitor ()
469
+ rhs_visitor .visit (node .value )
470
+ if isinstance (node .targets [0 ], ast .Tuple ): # x,y = [1,2]
471
+ if isinstance (node .value , ast .Tuple ):
472
+ return self .assign_tuple_target (node , rhs_visitor .result )
473
+ elif isinstance (node .value , ast .Call ):
474
+ call = None
475
+ for element in node .targets [0 ].elts :
476
+ label = LabelVisitor ()
477
+ label .visit (element )
478
+ call = self .assignment_call_node (label .result , node )
479
+ return call
480
+ elif len (node .targets ) > 1 : # x = y = 3
481
+ return self .assign_multi_target (node , rhs_visitor .result )
482
+ else :
483
+ if isinstance (node .value , ast .Call ): # x = call()
484
+
485
+ label = LabelVisitor ()
486
+ label .visit (node .targets [0 ])
487
+ return self .assignment_call_node (label .result , node )
488
+ else : # x = 4
489
+ label = LabelVisitor ()
490
+ label .visit (node )
491
+ return self .append_node (AssignmentNode (label .result , self .extract_left_hand_side (node .targets [0 ]), node , rhs_visitor .result , line_number = node .lineno , path = self .filenames [- 1 ]))
492
+
493
+ def assignment_call_node (self , left_hand_label , ast_node ):
494
+ """Handle assignments that contain a function call on its right side."""
495
+ self .undecided = True # Used for handling functions in assignments
496
+
497
+ rhs_visitor = RHSVisitor ()
498
+ rhs_visitor .visit (ast_node .value )
499
+
500
+ call = self .visit (ast_node .value )
501
+
502
+ call_label = ''
503
+ call_assignment = None
504
+ if isinstance (call , AssignmentNode ): # assignment after returned nonbuiltin
505
+ call_label = call .left_hand_side
506
+ call_assignment = AssignmentNode (left_hand_label + ' = ' + call_label , left_hand_label , ast_node , [call .left_hand_side ], line_number = ast_node .lineno , path = self .filenames [- 1 ])
507
+ call .connect (call_assignment )
508
+ else : # assignment to builtin
509
+ call_label = call .label
510
+ call_assignment = AssignmentNode (left_hand_label + ' = ' + call_label , left_hand_label , ast_node , rhs_visitor .result , line_number = ast_node .lineno , path = self .filenames [- 1 ])
511
+
512
+ self .nodes .append (call_assignment )
513
+
514
+ self .undecided = False
515
+
516
+ return call_assignment
517
+
518
+ def visit_AugAssign (self , node ):
519
+ label = LabelVisitor ()
520
+ label .visit (node )
521
+
522
+ rhs_visitor = RHSVisitor ()
523
+ rhs_visitor .visit (node .value )
524
+
525
+ return self .append_node (AssignmentNode (label .result , self .extract_left_hand_side (node .target ), node , rhs_visitor .result , line_number = node .lineno , path = self .filenames [- 1 ]))
526
+
527
+ def loop_node_skeleton (self , test , node ):
528
+ """Common handling of looped structures, while and for."""
529
+ body_connect_stmts = self .stmt_star_handler (node .body )
530
+
531
+ test .connect (body_connect_stmts .first_statement )
532
+ test .connect_predecessors (body_connect_stmts .last_statements )
533
+
534
+ # last_nodes is used for making connections to the next node in the parent node
535
+ # this is handled in stmt_star_handler
536
+ last_nodes = list ()
537
+ last_nodes .extend (body_connect_stmts .break_statements )
538
+
539
+ if node .orelse :
540
+ orelse_connect_stmts = self .stmt_star_handler (node .orelse )
541
+
542
+ test .connect (orelse_connect_stmts .first_statement )
543
+ last_nodes .extend (orelse_connect_stmts .last_statements )
544
+ else :
545
+ last_nodes .append (test ) # if there is no orelse, test needs an edge to the next_node
546
+
547
+ return ControlFlowNode (test , last_nodes , list ())
548
+
549
+ def add_while_label (self , node ):
550
+ """Prepend 'while' and append ':' to the label of a node."""
551
+ node .label = 'while ' + node .label + ':'
552
+
553
+ def visit_While (self , node ):
554
+ label_visitor = LabelVisitor ()
555
+ label_visitor .visit (node .test )
556
+
557
+ test = self .append_node (Node (label_visitor .result , node , line_number = node .lineno , path = self .filenames [- 1 ]))
558
+
559
+ self .add_while_label (test )
560
+
561
+ return self .loop_node_skeleton (test , node )
562
+
563
+ def visit_For (self , node ):
564
+ self .undecided = True # Used for handling functions in for loops
565
+
566
+ #issue23
567
+ iterator_label = LabelVisitor ()
568
+ iterator = iterator_label .visit (node .iter )
569
+ self .undecided = False
570
+
571
+ target_label = LabelVisitor ()
572
+ target = target_label .visit (node .target )
573
+
574
+ for_node = self .append_node (Node ("for " + target_label .result + " in " + iterator_label .result + ':' , node , line_number = node .lineno , path = self .filenames [- 1 ]))
575
+
576
+
577
+
578
+ if isinstance (node .iter , ast .Call ) and get_call_names_as_string (node .iter .func ) in self .function_names :
579
+ last_node = self .visit (node .iter )
580
+ last_node .connect (for_node )
581
+
582
+
583
+ return self .loop_node_skeleton (for_node , node )
584
+
585
+ def visit_Expr (self , node ):
586
+ return self .visit (node .value )
587
+
588
+ def add_builtin (self , node ):
589
+ label = LabelVisitor ()
590
+ label .visit (node )
591
+ builtin_call = Node (label .result , node , line_number = node .lineno , path = self .filenames [- 1 ])
592
+
593
+ if not self .undecided :
594
+ self .nodes .append (builtin_call )
595
+ self .undecided = False
596
+ return builtin_call
597
+
598
+ def visit_Name (self , node ):
599
+ label = LabelVisitor ()
600
+ label .visit (node )
601
+
602
+ return self .append_node (Node (label .result , node , line_number = node .lineno , path = self .filenames [- 1 ]))
603
+
604
+ def visit_With (self , node ):
605
+ label_visitor = LabelVisitor ()
606
+ label_visitor .visit (node .items [0 ])
607
+
608
+ with_node = self .append_node (Node (label_visitor .result , node , line_number = node .lineno , path = self .filenames [- 1 ]))
609
+ connect_statements = self .stmt_star_handler (node .body )
610
+ with_node .connect (connect_statements .first_statement )
611
+ return ControlFlowNode (with_node , connect_statements .last_statements , connect_statements .break_statements )
612
+
613
+ def visit_Str (self , node ):
614
+ return IgnoredNode ()
615
+
616
+ def visit_Break (self , node ):
617
+ return self .append_node (BreakNode (node , line_number = node .lineno , path = self .filenames [- 1 ]))
618
+
619
+ def visit_Pass (self , node ):
620
+ return self .append_node (Node ('pass' , node , line_number = node .lineno , path = self .filenames [- 1 ]))
621
+
622
+ def visit_Continue (self , node ):
623
+ return self .append_node (Node ('continue' , node , line_number = node .lineno , path = self .filenames [- 1 ]))
624
+
625
+ def visit_Delete (self , node ):
626
+ labelVisitor = LabelVisitor ()
627
+ for expr in node .targets :
628
+ labelVisitor .visit (expr )
629
+ return self .append_node (Node ('del ' + labelVisitor .result , node , line_number = node .lineno , path = self .filenames [- 1 ]))
0 commit comments