11#!/usr/bin/env python
22# -*- coding: utf-8 -*-
33
4+ from typing import NamedTuple
45from ast_ import NodeVisitor
56from .config import OPTIONS
67import api .errmsg
@@ -24,28 +25,8 @@ def __init__(self, obj):
2425 self .obj = obj
2526
2627
27- class OptimizerVisitor (NodeVisitor ):
28- """ Implements some optimizations
29- """
30- NOP = symbols .NOP () # Return this for "erased" nodes
31-
32- @staticmethod
33- def TYPE (type_ ):
34- """ Converts a backend type (from api.constants)
35- to a SymbolTYPE object (taken from the SYMBOL_TABLE).
36- If type_ is already a SymbolTYPE object, nothing
37- is done.
38- """
39- if isinstance (type_ , symbols .TYPE ):
40- return type_
41-
42- assert TYPE .is_valid (type_ )
43- return gl .SYMBOL_TABLE .basic_types [type_ ]
44-
28+ class GenericVisitor (NodeVisitor ):
4529 def visit (self , node ):
46- if self .O_LEVEL < 1 : # Optimize only if O1 or above
47- return node
48-
4930 stack = [ToVisit (node )]
5031 last_result = None
5132
@@ -76,6 +57,31 @@ def _visit(self, node):
7657
7758 return meth (node .obj )
7859
60+
61+ class OptimizerVisitor (GenericVisitor ):
62+ """ Implements some optimizations
63+ """
64+ NOP = symbols .NOP () # Return this for "erased" nodes
65+
66+ @staticmethod
67+ def TYPE (type_ ):
68+ """ Converts a backend type (from api.constants)
69+ to a SymbolTYPE object (taken from the SYMBOL_TABLE).
70+ If type_ is already a SymbolTYPE object, nothing
71+ is done.
72+ """
73+ if isinstance (type_ , symbols .TYPE ):
74+ return type_
75+
76+ assert TYPE .is_valid (type_ )
77+ return gl .SYMBOL_TABLE .basic_types [type_ ]
78+
79+ def visit (self , node ):
80+ if self .O_LEVEL < 1 : # Optimize only if O1 or above
81+ return node
82+
83+ return super ().visit (node )
84+
7985 @property
8086 def O_LEVEL (self ):
8187 return OPTIONS .optimization .value
@@ -196,7 +202,7 @@ def visit_IF(self, node):
196202 if not block_accessed and chk .is_number (expr_ ): # constant condition
197203 if expr_ .value : # always true (then_)
198204 yield then_
199- else : # always false (else_)
205+ else : # always false (else_)
200206 yield else_
201207 return
202208
@@ -282,3 +288,70 @@ def _update_bound_status(self, arg: symbols.VARARRAY):
282288
283289 if arg .scope == SCOPE .local and not arg .byref :
284290 arg .scopeRef .owner .locals_size = api .symboltable .SymbolTable .compute_offsets (arg .scopeRef )
291+
292+
293+ class VarDependency (NamedTuple ):
294+ parent : symbols .VAR
295+ dependency : symbols .VAR
296+
297+
298+ class VariableVisitor (GenericVisitor ):
299+ _original_variable = None
300+ _parent_variable = None
301+ _visited = set ()
302+
303+ @staticmethod
304+ def generic_visit (node ):
305+ if node not in VariableVisitor ._visited :
306+ VariableVisitor ._visited .add (node )
307+ for i in range (len (node .children )):
308+ node .children [i ] = yield ToVisit (node .children [i ])
309+ yield node
310+
311+ def has_circular_dependency (self , var_dependency : VarDependency ) -> bool :
312+ if var_dependency .dependency == VariableVisitor ._original_variable :
313+ api .errmsg .error (VariableVisitor ._original_variable .lineno ,
314+ "Circular dependency between '{}' and '{}'" .format (
315+ VariableVisitor ._original_variable .name , var_dependency .parent ))
316+ return True
317+
318+ return False
319+
320+ def get_var_dependencies (self , var_entry : symbols .VAR ):
321+ visited = set ()
322+ result = set ()
323+
324+ def visit_var (entry ):
325+ if entry in visited :
326+ return
327+
328+ visited .add (entry )
329+ if not isinstance (entry , symbols .VAR ):
330+ for child in entry .children :
331+ visit_var (child )
332+ if isinstance (child , symbols .VAR ):
333+ result .add (VarDependency (parent = VariableVisitor ._parent_variable , dependency = child ))
334+ return
335+
336+ VariableVisitor ._parent_variable = entry
337+ if entry .alias is not None :
338+ result .add (VarDependency (parent = entry , dependency = entry .alias ))
339+ visit_var (entry .alias )
340+ elif entry .addr is not None :
341+ visit_var (entry .addr )
342+
343+ visit_var (var_entry )
344+ return result
345+
346+ def visit_VARDECL (self , node : symbols .VARDECL ):
347+ """ Checks for cyclic dependencies in aliasing variables
348+ """
349+ VariableVisitor ._visited = set ()
350+ VariableVisitor ._original_variable = node .entry
351+ for dependency in self .get_var_dependencies (node .entry ):
352+ if self .has_circular_dependency (dependency ):
353+ break
354+
355+ VariableVisitor ._visited = set ()
356+ VariableVisitor ._original_variable = VariableVisitor ._parent_variable = None
357+ yield node
0 commit comments