blob: 33837a3cefa98f90a94249fbb15ded53fefe85df [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Hierarchical property system for IDL AST """
import re
import sys
from idl_log import ErrOut, InfoOut, WarnOut
from idl_option import GetOption, Option, ParseOptions
#
# IDLPropertyNode
#
# A property node is a hierarchically aware system for mapping
# keys to values, such that a local dictionary is search first,
# followed by parent dictionaries in order.
#
class IDLPropertyNode(object):
def __init__(self):
self.parents = []
self.property_map = {}
def Error(self, msg):
name = self.GetProperty('NAME', 'Unknown')
parents = [parent.GetProperty('NAME', '???') for parent in self.parents]
ErrOut.Log('%s [%s] : %s' % (name, ' '.join(parents), msg))
def AddParent(self, parent):
assert parent
self.parents.append(parent)
def SetProperty(self, name, val):
self.property_map[name] = val
def _GetProperty_(self, name):
# Check locally for the property, and return it if found.
prop = self.property_map.get(name, None)
if prop is not None: return prop
# If not, seach parents in order
for parent in self.parents:
prop = parent.GetProperty(name)
if prop is not None: return prop
# Otherwise, it can not be found.
return None
def GetProperty(self, name, default=None):
prop = self._GetProperty_(name)
if prop is None:
return default
else:
return prop
def GetPropertyLocal(self, name, default=None):
# Search for the property, but only locally, returning the
# default if not found.
prop = self.property_map.get(name, default)
return prop
# Regular expression to parse property keys in a string such that a string
# "My string $NAME$" will find the key "NAME".
regex_var = re.compile('(?P<src>[^\\$]+)|(?P<key>\\$\\w+\\$)')
def GetPropertyList(self):
return self.property_map.keys()
# Recursively expands text keys in the form of $KEY$ with the value
# of the property of the same name. Since this is done recursively
# one property can be defined in terms of another.
def Replace(self, text):
itr = IDLPropertyNode.regex_var.finditer(text)
out = ''
for m in itr:
(start, stop) = m.span()
if m.lastgroup == 'src':
out += text[start:stop]
if m.lastgroup == 'key':
key = text[start+1:stop-1]
val = self.GetProperty(key, None)
if not val:
self.Error('No property "%s"' % key)
out += self.Replace(str(val))
return out
#
# Testing functions
#
# Build a property node, setting the properties including a name, and
# associate the children with this new node.
#
def BuildNode(name, props, children=[], parents=[]):
node = IDLPropertyNode()
node.SetProperty('NAME', name)
for prop in props:
toks = prop.split('=')
node.SetProperty(toks[0], toks[1])
for child in children:
child.AddParent(node)
for parent in parents:
node.AddParent(parent)
return node
def ExpectProp(node, name, val):
found = node.GetProperty(name)
if found != val:
ErrOut.Log('Got property %s expecting %s' % (found, val))
return 1
return 0
#
# Verify property inheritance
#
def PropertyTest():
errors = 0
left = BuildNode('Left', ['Left=Left'])
right = BuildNode('Right', ['Right=Right'])
top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
errors += ExpectProp(top, 'Left', 'Top')
errors += ExpectProp(top, 'Right', 'Top')
errors += ExpectProp(left, 'Left', 'Left')
errors += ExpectProp(left, 'Right', 'Top')
errors += ExpectProp(right, 'Left', 'Top')
errors += ExpectProp(right, 'Right', 'Right')
if not errors: InfoOut.Log('Passed PropertyTest')
return errors
def ExpectText(node, text, val):
found = node.Replace(text)
if found != val:
ErrOut.Log('Got replacement %s expecting %s' % (found, val))
return 1
return 0
#
# Verify text replacement
#
def ReplaceTest():
errors = 0
left = BuildNode('Left', ['Left=Left'])
right = BuildNode('Right', ['Right=Right'])
top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
errors += ExpectText(top, '$Left$', 'Top')
errors += ExpectText(top, '$Right$', 'Top')
errors += ExpectText(left, '$Left$', 'Left')
errors += ExpectText(left, '$Right$', 'Top')
errors += ExpectText(right, '$Left$', 'Top')
errors += ExpectText(right, '$Right$', 'Right')
if not errors: InfoOut.Log('Passed ReplaceTest')
return errors
def MultiParentTest():
errors = 0
parent1 = BuildNode('parent1', ['PARENT1=parent1', 'TOPMOST=$TOP$'])
parent2 = BuildNode('parent2', ['PARENT1=parent2', 'PARENT2=parent2'])
child = BuildNode('child', ['CHILD=child'], parents=[parent1, parent2])
BuildNode('top', ['TOP=top'], children=[parent1])
errors += ExpectText(child, '$CHILD$', 'child')
errors += ExpectText(child, '$PARENT1$', 'parent1')
errors += ExpectText(child, '$PARENT2$', 'parent2')
# Verify recursive resolution
errors += ExpectText(child, '$TOPMOST$', 'top')
if not errors: InfoOut.Log('Passed MultiParentTest')
return errors
def Main():
errors = 0
errors += PropertyTest()
errors += ReplaceTest()
errors += MultiParentTest()
if errors:
ErrOut.Log('IDLNode failed with %d errors.' % errors)
return -1
return 0
if __name__ == '__main__':
sys.exit(Main())