diff options
Diffstat (limited to 'ppapi/generators')
-rw-r--r-- | ppapi/generators/idl_ast.py | 132 | ||||
-rw-r--r-- | ppapi/generators/idl_node.py | 445 | ||||
-rw-r--r-- | ppapi/generators/idl_version.py | 147 |
3 files changed, 553 insertions, 171 deletions
diff --git a/ppapi/generators/idl_ast.py b/ppapi/generators/idl_ast.py new file mode 100644 index 0000000..71aaf93 --- /dev/null +++ b/ppapi/generators/idl_ast.py @@ -0,0 +1,132 @@ +#!/usr/bin/python +# +# Copyright (c) 2011 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. + +"""Nodes for PPAPI IDL AST.""" + +from idl_namespace import IDLNamespace, IDLVersionMap +from idl_node import IDLAttribute, IDLFile, IDLNode +from idl_option import GetOption +from idl_visitor import IDLVisitor + +# +# IDL Predefined types +# +BuiltIn = set(['int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t', + 'uint16_t', 'uint32_t', 'uint64_t', 'double_t', 'float_t', + 'handle_t', 'interface_t', 'char', 'mem_t', 'str_t', 'void']) + + +# +# IDLNamespaceLabelResolver +# +# Once the AST is build, we need to resolve the namespace and version +# information. +# +class IDLNamespaceLabelResolver(IDLVisitor): + NamespaceSet = set(['AST', 'Callspec', 'Interface', 'Member', 'Struct']) + # + # When we arrive at a node we must assign it a namespace and if the + # node is named, then place it in the appropriate namespace. + # + def Arrive(self, node, parent_namespace): + # Set version min and max based on properties + vmin = node.GetProperty('version') + vmax = node.GetProperty('deprecate') + node.SetVersionRange(vmin, vmax) + + # If this object is not a namespace aware object, use the parent's one + if node.cls not in self.NamespaceSet: + node.namespace = parent_namespace + else: + # otherwise create one. + node.namespace = IDLNamespace(parent_namespace) + node.namespace.name = node.GetName() + + # If this node is named, place it in its parent's namespace + if parent_namespace and node.cls in IDLNode.NamedSet: + parent_namespace.AddNode(node) + + # Pass this namespace to each child in case they inherit it + return node.namespace + + # + # As we return from a node, if the node is a LabelItem we pass back + # the key=value pair representing the mapping of release to version. + # If the node is a Label take the lists of mapping and generate a + # version map which is assigned to the Labels parent as a property. + # + def Depart(self, node, data, childdata): + if node.IsA('LabelItem'): + return (node.GetName(), node.GetProperty('VALUE')) + if node.IsA('Label') and node.GetName() == GetOption('label'): + vmap = IDLVersionMap() + for release, version in childdata: + vmap.AddReleaseVersionMapping(release, float(version)) + node.parent.SetProperty("LABEL", vmap) + return None + + +class IDLFileTypeResolver(IDLVisitor): + def VisitFilter(self, node, data): + return not node.IsA('Comment', 'Copyright') + + def Arrive(self, node, filenode): + # Track the file node to update errors + if node.IsA('File'): + node.SetProperty('FILE', node) + + + # If this node has a TYPEREF, resolve it to a version list + typeref = node.property_node.GetPropertyLocal('TYPEREF') + if typeref: + node.typelist = node.parent.namespace.FindList(typeref) + if not node.typelist: + node.Error('Could not resolve %s.' % typeref) + else: + node.typelist = None + return filenode + + + +class IDLVersionMapDefault(IDLVersionMap): + def GetRelease(self, version): + return 'M13' + + def GetVersion(self, release): + return float(0.0) + +# +# IDLAst +# +# A specialized version of the IDLNode for containing the whole of the +# AST. The specialized BuildTree function pulls the per file namespaces +# into the global AST namespace and checks for collisions. +# +class IDLAst(IDLNode): + def __init__(self, children): + objs = [] + + for name in BuiltIn: + nameattr = IDLAttribute('NAME', name) + objs.append(IDLNode('Type', 'BuiltIn', 1, 0, [nameattr])) + + builtin = IDLFile('pp_stdint.idl', objs) + IDLNode.__init__(self, 'AST', 'BuiltIn', 1, 0, [builtin] + children) + self.SetProperty('LABEL', IDLVersionMapDefault()) + self.Resolve() + + def Resolve(self): + self.namespace = IDLNamespace(None) + self.namespace.name = 'AST' + IDLNamespaceLabelResolver().Visit(self, self.namespace) + IDLFileTypeResolver().Visit(self, None) + + def SetTypeInfo(self, name, properties): + node = self.namespace[name] + for prop in properties: + node.properties[prop] = properties[prop] + + diff --git a/ppapi/generators/idl_node.py b/ppapi/generators/idl_node.py index 93b6b4a..285307a 100644 --- a/ppapi/generators/idl_node.py +++ b/ppapi/generators/idl_node.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -""" Nodes for PPAPI IDL AST """ +"""Nodes for PPAPI IDL AST""" # # IDL Node @@ -17,13 +17,15 @@ # as the source data by the various generators. # -import re -import sys import hashlib +import sys from idl_log import ErrOut, InfoOut, WarnOut +from idl_propertynode import IDLPropertyNode +from idl_namespace import IDLNamespace +from idl_version import IDLVersion + -# # IDLAttribute # # A temporary object used by the parsing process to hold an Extended Attribute @@ -35,94 +37,93 @@ class IDLAttribute(object): self.name = name self.value = value -# Set of object IDLNode types which have a name and belong in the namespace. -NamedSet = set(['Enum', 'EnumItem', 'Function', 'Interface', 'Member', 'Param', - 'Struct', 'Type', 'Typedef']) + def __str__(self): + return '%s=%s' % (self.name, self.value) + # # IDLNode # -# A node in the AST. The Node is composed of children, implemented as a -# dictionary of lists of child types and a dictionary of local properties, -# a.k.a. ExtendedAttributes. +# This class implements the AST tree, providing the associations between +# parents and children. It also contains a namepsace and propertynode to +# allow for look-ups. IDLNode is derived from IDLVersion, so it is +# version aware. # -class IDLNode(object): - # 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+\\$)") +class IDLNode(IDLVersion): + + # Set of object IDLNode types which have a name and belong in the namespace. + NamedSet = set(['Enum', 'EnumItem', 'File', 'Function', 'Interface', + 'Member', 'Param', 'Struct', 'Type', 'Typedef']) + + show_versions = False + def __init__(self, cls, filename, lineno, pos, children=None): + # Initialize with no starting or ending Version + IDLVersion.__init__(self, None, None) - def __init__(self, cls, name, filename, lineno, pos, children): self.cls = cls - self.name = name self.lineno = lineno self.pos = pos self.filename = filename - - # Dictionary of type to in order child list - self.children = {} - - # Dictionary of child name to child object - self.namespace = {} - - # Dictionary of properties (ExtAttributes) - self.properties = { 'NAME' : name } - - self.hash = None - self.typeref = None + self.hashes = {} + self.deps = {} + self.errors = 0 + self.namespace = None + self.typelist = None self.parent = None - self.childlist = children - - if children: - for child in children: - # - # Copy children into local dictionary such that children of the - # same class are added in order to the per key list. All - # ExtAttributes are filtered and applied to a property dictionary - # instead of becoming children of IDLNode - # - if child.cls == 'ExtAttribute': - self.properties[child.name] = child.value - else: - if child.cls in self.children: - self.children[child.cls].append(child) - else: - self.children[child.cls] = [child] + self.property_node = IDLPropertyNode() + # self.children is a list of children ordered as defined + self.children = [] + # Process the passed in list of children, placing ExtAttributes into the + # property dictionary, and nodes into the local child list in order. In + # addition, add nodes to the namespace if the class is in the NamedSet. + if not children: children = [] + for child in children: + if child.cls == 'ExtAttribute': + self.SetProperty(child.name, child.value) + else: + child.SetParent(self) + self.children.append(child) + +# +# String related functions +# +# # Return a string representation of this node def __str__(self): - return "%s(%s)" % (self.cls, self.name) + name = self.GetName() + ver = IDLVersion.__str__(self) + if name is None: name = '' + if not IDLNode.show_versions: ver = '' + return '%s(%s%s)' % (self.cls, name, ver) + + # Return file and line number for where node was defined + def Location(self): + return '%s(%d)' % (self.filename, self.lineno) # Log an error for this object def Error(self, msg): - ErrOut.LogLine(self.filename, self.lineno, 0, " %s %s" % + self.errors += 1 + ErrOut.LogLine(self.filename, self.lineno, 0, ' %s %s' % (str(self), msg)) + if self.lineno == 46: raise Exception("huh?") # Log a warning for this object def Warning(self, msg): - WarnOut.LogLine(self.filename, self.lineno, 0, " %s %s" % + WarnOut.LogLine(self.filename, self.lineno, 0, ' %s %s' % (str(self), msg)) - # Get a list of objects for this key - def GetListOf(self, key): - return self.children.get(key, []) - - def GetOneOf(self, key): - children = self.children.get(key, None) - if children: - assert(len(children) == 1) - return children[0] - return None + def GetName(self): + return self.GetProperty('NAME') - # Get a list of all objects - def Children(self): - out = [] - for key in sorted(self.children.keys()): - out.extend(self.children[key]) - return out + def GetNameVersion(self): + name = self.GetProperty('NAME', default='') + ver = IDLVersion.__str__(self) + return '%s%s' % (name, ver) # Dump this object and its children def Dump(self, depth=0, comments=False, out=sys.stdout): - if self.cls == 'Comment' or self.cls == 'Copyright': + if self.cls in ['Comment', 'Copyright']: is_comment = True else: is_comment = False @@ -130,128 +131,230 @@ class IDLNode(object): # Skip this node if it's a comment, and we are not printing comments if not comments and is_comment: return - tab = "" - for t in range(depth): - tab += ' ' + tab = ''.rjust(depth * 2) if is_comment: - for line in self.name.split('\n'): - out.write("%s%s\n" % (tab, line)) + for line in self.GetName().split('\n'): + out.write('%s%s\n' % (tab, line)) else: - out.write("%s%s\n" % (tab, self)) + out.write('%s%s\n' % (tab, self)) + properties = self.property_node.GetProperyList() + if properties: + out.write('%s Properties\n' % tab) + for p in properties: + out.write('%s %s : %s\n' % (tab, p, self.GetProperty(p))) + for child in self.children: + child.Dump(depth+1, comments=comments, out=out) - if self.properties: - out.write("%s Properties\n" % tab) - for p in self.properties: - out.write("%s %s : %s\n" % (tab, p, self.properties[p])) +# +# Search related functions +# - for cls in sorted(self.children.keys()): - # Skip comments - if (cls == 'Comment' or cls == 'Copyright') and not comments: continue + # Check if node is of a given type + def IsA(self, *typelist): + if self.cls in typelist: return True + return False + + # Get a list of objects for this key + def GetListOf(self, *keys): + out = [] + for child in self.children: + if child.cls in keys: out.append(child) + return out - out.write("%s %ss\n" % (tab, cls)) - for c in self.children[cls]: - c.Dump(depth + 1, comments=comments, out=out) + def GetOneOf(self, *keys): + out = self.GetListOf(*keys) + if out: return out[0] + return None - # Link the parents and add the object to the parent's namespace - def BuildTree(self, parent): + def SetParent(self, parent): + self.property_node.AddParent(parent) self.parent = parent - for child in self.Children(): - child.BuildTree(self) - name = child.name - - # Add this child to the local namespace if it's a 'named' type - if child.cls in NamedSet: - if name in self.namespace: - other = self.namespace[name] - child.Error('Attempting to add %s to namespace of %s when already ' - 'declared in %s' % (name, str(self), str(other))) - self.namespace[name] = child - - def Resolve(self): - errs = 0 - typename = self.properties.get('TYPEREF', None) - - if typename: - if typename in self.namespace: - self.Error('Type collision in local namespace') - errs += 1 - - typeinfo = self.parent.Find(typename) - if not typeinfo: - self.Error('Unable to resolve typename %s.' % typename) - errs += 1 - sys.exit(-1) - self.typeinfo = typeinfo - else: - self.typeinfo = None - for child in self.Children(): - errs += child.Resolve() + # Get a list of all children + def GetChildren(self): + return self.children - return errs + # Get a list of all children of a given version + def GetChildrenVersion(self, version): + out = [] + for child in self.children: + if child.IsVersion(version): out.append(child) + return out - def Find(self, name): - if name in self.namespace: return self.namespace[name] - if self.parent: return self.parent.Find(name) - return None + # Get a list of all children in a given range + def GetChildrenRange(self, vmin, vmax): + out = [] + for child in self.children: + if child.IsRange(vmin, vmax): out.append(child) + return out - def Hash(self): - hash = hashlib.sha1() - for key, val in self.properties.iteritems(): - hash.update('%s=%s' % (key, str(val))) - for child in self.Children(): - hash.update(child.Hash()) - if self.typeref: hash.update(self.typeref.hash) - self.hash = hash.hexdigest() - return self.hash - - def GetProperty(self, name): - return self.properties.get(name, None) - - # 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 tems of another. - def Replace(self, text): - itr = IDLNode.regex_var.finditer(text) - out = "" - for m in itr: - (min,max) = m.span() - if m.lastgroup == "src": - out += text[min:max] - if m.lastgroup == "key": - key = text[min+1:max-1] - val = self.properties.get(key, None) - if not val: - self.Error("No property '%s'" % key) - out += self.Replace(str(val)) + def FindVersion(self, name, version): + node = self.namespace.FindNode(name, version) + if not node and self.parent: + node = self.parent.FindVersion(name, version) + return node + + def FindRange(self, name, vmin, vmax): + nodes = self.namespace.FindNodes(name, vmin, vmax) + if not nodes and self.parent: + nodes = self.parent.FindVersion(name, vmin, vmax) + return nodes + + def IsRelease(self, release): + label = self.GetLabel() + # Assume object is always available if there is no Label + if not label: + return True + + version = label.GetVersion(release) + out = self.IsVersion(version) return out + + def GetLabel(self): + label = self.GetProperty('LABEL') + if not label: + self.Error('No label availible.') + return None + return label + + def GetType(self, release): + label = self.GetLabel() + if not label: return None + if not self.typelist: return None + version = label.GetVersion(release) + return self.typelist.FindVersion(version) + + def GetHash(self, release): + hashval = self.hashes.get(release, None) + if hashval is None: + hashval = hashlib.sha1() + hashval.update(self.cls) + for key in self.property_node.GetPropertyList(): + val = self.GetProperty(key) + hashval.update('%s=%s' % (key, str(val))) + typeref = self.GetType(release) + if typeref: + hashval.update(typeref.GetHash(release)) + for child in self.GetChildren(): + if child.IsA('Copyright', 'Comment', 'Label'): continue + if not child.IsRelease(release): + continue + hashval.update( child.GetHash(release) ) + self.hashes[release] = hashval + return hashval.hexdigest() + + def GetDeps(self, release): + deps = self.deps.get(release, None) + if deps is None: + deps = set([self]) + for child in self.GetChildren(): + deps |= child.GetDeps(release) + typeref = self.GetType(release) + if typeref: deps |= typeref.GetDeps(release) + self.deps[release] = deps + return deps + + def GetRelease(self, version): + label = self.GetLabel() + if not label: return None + return label.GetRelease(version) + + def GetVersion(self, release): + label = self.GetLabel() + if not label: return None + return label.GetVersion(release) + + def SetProperty(self, name, val): + self.property_node.SetProperty(name, val) + + def GetProperty(self, name, default=None): + return self.property_node.GetProperty(name, default) + + def Traverse(self, data, func): + func(self, data) + for child in self.children: + child.Traverse(data, func) + + # -# IDL Predefined types +# IDLFile # -BuiltIn = set(['int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t', 'uint16_t', - 'uint32_t', 'uint64_t', 'double_t', 'float_t', 'handle_t', - 'mem_t', 'str_t', 'void', 'enum', 'struct', 'bool']) - +# A specialized version of IDLNode which tracks errors and warnings. # -# IDLAst +class IDLFile(IDLNode): + def __init__(self, name, children, errors=0): + attrs = [IDLAttribute('NAME', name), + IDLAttribute('ERRORS', errors)] + if not children: children = [] + IDLNode.__init__(self, 'File', name, 1, 0, attrs + children) + + # -# A specialized version of the IDLNode for containing the whole of the -# AST. The specialized BuildTree function pulls the per file namespaces -# into the global AST namespace and checks for collisions. +# Tests # -class IDLAst(IDLNode): - def __init__(self, children): - objs = [IDLNode('Type', name, 'BuiltIn', 1, 0, None) for name in BuiltIn] - IDLNode.__init__(self, 'AST', 'PPAPI', 'BuiltIn', 1, 0, objs + children) - - def BuildTree(self, parent): - IDLNode.BuildTree(self, parent) - for fileobj in self.GetListOf('File'): - for name, val in fileobj.namespace.iteritems(): - if name in self.namespace: - other = self.namespace[name] - val.Error('Attempting to add %s to namespace of %s when already ' - 'declared in %s' % (name, str(self), str(other))) - self.namespace[name] = val +def StringTest(): + errors = 0 + name_str = 'MyName' + text_str = 'MyNode(%s)' % name_str + name_node = IDLAttribute('NAME', name_str) + node = IDLNode('MyNode', 'no file', 1, 0, [name_node]) + if node.GetName() != name_str: + ErrOut.Log('GetName returned >%s< not >%s<' % (node.GetName(), name_str)) + errors += 1 + if node.GetProperty('NAME') != name_str: + ErrOut.Log('Failed to get name property.') + errors += 1 + if str(node) != text_str: + ErrOut.Log('str() returned >%s< not >%s<' % (str(node), text_str)) + errors += 1 + if not errors: InfoOut.Log('Passed StringTest') + return errors + + +def ChildTest(): + errors = 0 + child = IDLNode('child', 'no file', 1, 0) + parent = IDLNode('parent', 'no file', 1, 0, [child]) + + if child.parent != parent: + ErrOut.Log('Failed to connect parent.') + errors += 1 + + if [child] != parent.GetChildren(): + ErrOut.Log('Failed GetChildren.') + errors += 1 + + if child != parent.GetOneOf('child'): + ErrOut.Log('Failed GetOneOf(child)') + errors += 1 + + if parent.GetOneOf('bogus'): + ErrOut.Log('Failed GetOneOf(bogus)') + errors += 1 + + if not parent.IsA('parent'): + ErrOut.Log('Expecting parent type') + errors += 1 + + parent = IDLNode('parent', 'no file', 1, 0, [child, child]) + if [child, child] != parent.GetChildren(): + ErrOut.Log('Failed GetChildren2.') + errors += 1 + + if not errors: InfoOut.Log('Passed ChildTest') + return errors + + +def Main(): + errors = StringTest() + errors += ChildTest() + + if errors: + ErrOut.Log('IDLNode failed with %d errors.' % errors) + return -1 + return 0 + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/ppapi/generators/idl_version.py b/ppapi/generators/idl_version.py new file mode 100644 index 0000000..a67abd5c --- /dev/null +++ b/ppapi/generators/idl_version.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# +# Copyright (c) 2011 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. + +""" +IDLVersion for PPAPI + +This file defines the behavior of the AST namespace which allows for resolving +a symbol as one or more AST nodes given a version or range of versions. +""" + +import sys + +from idl_log import ErrOut, InfoOut, WarnOut +from idl_option import GetOption, Option, ParseOptions + +Option('version_debug', 'Debug version data') +Option('wgap', 'Ignore version gap warning') + + +# +# Module level functions and data used for testing. +# +error = None +warning = None +def ReportVersionError(msg): + global error + error = msg + +def ReportVersionWarning(msg): + global warning + warning = msg + +def ReportClear(): + global error, warning + error = None + warning = None + +# +# IDLVersion +# +# IDLVersion is an object which stores the association of a given symbol +# name, with an AST node for a range of versions for that object. +# +# A vmin value of None indicates that the object begins at the earliest +# available version number. The value of vmin is always inclusive. + +# A vmax value of None indicates that the object is never deprecated, so +# it exists until it is overloaded or until the latest available version. +# The value of vmax is always exclusive, representing the first version +# on which the object is no longer valid. +class IDLVersion(object): + def __init__(self, vmin, vmax): + self.vmin = vmin + self.vmax = vmax + + def __str__(self): + if not self.vmin: + vmin = '0' + else: + vmin = str(self.vmin) + if not self.vmax: + vmax = '+oo' + else: + vmax = str(self.vmax) + return '[%s,%s)' % (vmin, vmax) + + def SetVersionRange(self, vmin, vmax): + if vmin is not None: vmin = float(vmin) + if vmax is not None: vmax = float(vmax) + self.vmin = vmin + self.vmax = vmax + + # True, if version falls within the interval [self.vmin, self.vmax) + def IsVersion(self, version): + assert type(version) == float + + if self.vmax and self.vmax <= version: + return False + if self.vmin and self.vmin > version: + return False + if GetOption('version_debug'): + InfoOut.Log('%f is in %s' % (version, self)) + return True + + # True, if interval [vmin, vmax) overlaps interval [self.vmin, self.vmax) + def InRange(self, vmin, vmax): + assert type(vmin) == float + assert type(vmax) == float + assert vmin != vmax + + if self.vmax and self.vmax <= vmin: + return False + if self.vmin and self.vmin >= vmax: + return False + + if GetOption('version_debug'): + InfoOut.Log('%f to %f is in %s' % (vmin, vmax, self)) + return True + + def Error(self, msg): + ReportVersionError(msg) + + def Warning(self, msg): + ReportVersionWarning(msg) + + +# +# Test Code +# +def Main(args): + global errors + + FooXX = IDLVersion(None, None) + Foo1X = IDLVersion(1.0, None) + Foo23 = IDLVersion(2.0, 3.0) + + assert FooXX.IsVersion(0.0) + assert FooXX.IsVersion(1.0) + assert FooXX.InRange(0.0, 0.1) + assert FooXX.InRange(1.0,2.0) + + assert not Foo1X.IsVersion(0.0) + assert Foo1X.IsVersion(1.0) + assert Foo1X.IsVersion(2.0) + + assert not Foo1X.InRange(0.0, 1.0) + assert not Foo1X.InRange(0.5, 1.0) + assert Foo1X.InRange(1.0, 2.0) + assert Foo1X.InRange(2.0, 3.0) + + assert not Foo23.InRange(0.0, 1.0) + assert not Foo23.InRange(0.5, 1.0) + assert not Foo23.InRange(1.0, 2.0) + assert Foo23.InRange(2.0, 3.0) + assert Foo23.InRange(1.0, 2.1) + assert Foo23.InRange(2.9, 4.0) + assert not Foo23.InRange(3.0, 4.0) + + print "Passed" + return 0 + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) + |