Home | History | Annotate | Download | only in generators
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """ Hierarchical property system for IDL AST """
      7 import re
      8 import sys
      9 
     10 from idl_log import ErrOut, InfoOut, WarnOut
     11 from idl_option import GetOption, Option, ParseOptions
     12 
     13 #
     14 # IDLPropertyNode
     15 #
     16 # A property node is a hierarchically aware system for mapping
     17 # keys to values, such that a local dictionary is search first,
     18 # followed by parent dictionaries in order.
     19 #
     20 class IDLPropertyNode(object):
     21   def __init__(self):
     22     self.parents = []
     23     self.property_map = {}
     24 
     25   def Error(self, msg):
     26     name = self.GetProperty('NAME', 'Unknown')
     27     parents = [parent.GetProperty('NAME', '???') for parent in self.parents]
     28     ErrOut.Log('%s [%s] : %s' % (name, ' '.join(parents), msg))
     29 
     30   def AddParent(self, parent):
     31     assert parent
     32     self.parents.append(parent)
     33 
     34   def SetProperty(self, name, val):
     35     self.property_map[name] = val
     36 
     37   def _GetProperty_(self, name):
     38     # Check locally for the property, and return it if found.
     39     prop = self.property_map.get(name, None)
     40     if prop is not None: return prop
     41     # If not, seach parents in order
     42     for parent in self.parents:
     43       prop = parent.GetProperty(name)
     44       if prop is not None: return prop
     45     # Otherwise, it can not be found.
     46     return None
     47 
     48   def GetProperty(self, name, default=None):
     49     prop = self._GetProperty_(name)
     50     if prop is None:
     51       return default
     52     else:
     53       return prop
     54 
     55   def GetPropertyLocal(self, name, default=None):
     56     # Search for the property, but only locally, returning the
     57     # default if not found.
     58     prop = self.property_map.get(name, default)
     59     return prop
     60 
     61   # Regular expression to parse property keys in a string such that a string
     62   #  "My string $NAME$" will find the key "NAME".
     63   regex_var = re.compile('(?P<src>[^\\$]+)|(?P<key>\\$\\w+\\$)')
     64 
     65   def GetPropertyList(self):
     66     return self.property_map.keys()
     67 
     68   # Recursively expands text keys in the form of $KEY$ with the value
     69   # of the property of the same name.  Since this is done recursively
     70   # one property can be defined in terms of another.
     71   def Replace(self, text):
     72     itr = IDLPropertyNode.regex_var.finditer(text)
     73     out = ''
     74     for m in itr:
     75       (start, stop) = m.span()
     76       if m.lastgroup == 'src':
     77         out += text[start:stop]
     78       if m.lastgroup == 'key':
     79         key = text[start+1:stop-1]
     80         val = self.GetProperty(key, None)
     81         if not val:
     82           self.Error('No property "%s"' % key)
     83         out += self.Replace(str(val))
     84     return out
     85 
     86 
     87 #
     88 # Testing functions
     89 #
     90 
     91 # Build a property node, setting the properties including a name, and
     92 # associate the children with this new node.
     93 #
     94 def BuildNode(name, props, children=[], parents=[]):
     95   node = IDLPropertyNode()
     96   node.SetProperty('NAME', name)
     97   for prop in props:
     98     toks = prop.split('=')
     99     node.SetProperty(toks[0], toks[1])
    100   for child in children:
    101     child.AddParent(node)
    102   for parent in parents:
    103     node.AddParent(parent)
    104   return node
    105 
    106 def ExpectProp(node, name, val):
    107   found = node.GetProperty(name)
    108   if found != val:
    109     ErrOut.Log('Got property %s expecting %s' % (found, val))
    110     return 1
    111   return 0
    112 
    113 #
    114 # Verify property inheritance
    115 #
    116 def PropertyTest():
    117   errors = 0
    118   left = BuildNode('Left', ['Left=Left'])
    119   right = BuildNode('Right', ['Right=Right'])
    120   top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
    121 
    122   errors += ExpectProp(top, 'Left', 'Top')
    123   errors += ExpectProp(top, 'Right', 'Top')
    124 
    125   errors += ExpectProp(left, 'Left', 'Left')
    126   errors += ExpectProp(left, 'Right', 'Top')
    127 
    128   errors += ExpectProp(right, 'Left', 'Top')
    129   errors += ExpectProp(right, 'Right', 'Right')
    130 
    131   if not errors: InfoOut.Log('Passed PropertyTest')
    132   return errors
    133 
    134 
    135 def ExpectText(node, text, val):
    136   found = node.Replace(text)
    137   if found != val:
    138     ErrOut.Log('Got replacement %s expecting %s' % (found, val))
    139     return 1
    140   return 0
    141 
    142 #
    143 # Verify text replacement
    144 #
    145 def ReplaceTest():
    146   errors = 0
    147   left = BuildNode('Left', ['Left=Left'])
    148   right = BuildNode('Right', ['Right=Right'])
    149   top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
    150 
    151   errors += ExpectText(top, '$Left$', 'Top')
    152   errors += ExpectText(top, '$Right$', 'Top')
    153 
    154   errors += ExpectText(left, '$Left$', 'Left')
    155   errors += ExpectText(left, '$Right$', 'Top')
    156 
    157   errors += ExpectText(right, '$Left$', 'Top')
    158   errors += ExpectText(right, '$Right$', 'Right')
    159 
    160   if not errors: InfoOut.Log('Passed ReplaceTest')
    161   return errors
    162 
    163 
    164 def MultiParentTest():
    165   errors = 0
    166 
    167   parent1 = BuildNode('parent1', ['PARENT1=parent1', 'TOPMOST=$TOP$'])
    168   parent2 = BuildNode('parent2', ['PARENT1=parent2', 'PARENT2=parent2'])
    169   child = BuildNode('child', ['CHILD=child'], parents=[parent1, parent2])
    170   BuildNode('top', ['TOP=top'], children=[parent1])
    171 
    172   errors += ExpectText(child, '$CHILD$', 'child')
    173   errors += ExpectText(child, '$PARENT1$', 'parent1')
    174   errors += ExpectText(child, '$PARENT2$', 'parent2')
    175 
    176   # Verify recursive resolution
    177   errors += ExpectText(child, '$TOPMOST$', 'top')
    178 
    179   if not errors: InfoOut.Log('Passed MultiParentTest')
    180   return errors
    181 
    182 
    183 def Main():
    184   errors = 0
    185   errors += PropertyTest()
    186   errors += ReplaceTest()
    187   errors += MultiParentTest()
    188 
    189   if errors:
    190     ErrOut.Log('IDLNode failed with %d errors.' % errors)
    191     return  -1
    192   return 0
    193 
    194 
    195 if __name__ == '__main__':
    196   sys.exit(Main())
    197 
    198