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
6import sys
7
8#
9# IDL Node
10#
11# IDL Node defines the IDLAttribute and IDLNode objects which are constructed
12# by the parser as it processes the various 'productions'.  The IDLAttribute
13# objects are assigned to the IDLNode's property dictionary instead of being
14# applied as children of The IDLNodes, so they do not exist in the final tree.
15# The AST of IDLNodes is the output from the parsing state and will be used
16# as the source data by the various generators.
17#
18
19
20#
21# CopyToList
22#
23# Takes an input item, list, or None, and returns a new list of that set.
24def CopyToList(item):
25  # If the item is 'Empty' make it an empty list
26  if not item:
27    item = []
28
29  # If the item is not a list
30  if type(item) is not type([]):
31    item = [item]
32
33  # Make a copy we can modify
34  return list(item)
35
36
37# IDLSearch
38#
39# A temporary object used by the parsing process to hold an Extended Attribute
40# which will be passed as a child to a standard IDLNode.
41#
42class IDLSearch(object):
43  def __init__(self):
44    self.depth = 0
45
46  def Enter(self, node):
47    pass
48
49  def Exit(self, node):
50    pass
51
52
53# IDLAttribute
54#
55# A temporary object used by the parsing process to hold an Extended Attribute
56# which will be passed as a child to a standard IDLNode.
57#
58class IDLAttribute(object):
59  def __init__(self, name, value):
60    self._cls = 'Property'
61    self.name = name
62    self.value = value
63
64  def __str__(self):
65    return '%s=%s' % (self.name, self.value)
66
67  def GetClass(self):
68    return self._cls
69
70#
71# IDLNode
72#
73# This class implements the AST tree, providing the associations between
74# parents and children.  It also contains a namepsace and propertynode to
75# allow for look-ups.  IDLNode is derived from IDLRelease, so it is
76# version aware.
77#
78class IDLNode(object):
79  def __init__(self, cls, filename, lineno, pos, children=None):
80    self._cls = cls
81    self._properties = {
82      'ERRORS' : [],
83      'WARNINGS': [],
84      'FILENAME': filename,
85      'LINENO' : lineno,
86      'POSSITION' : pos,
87    }
88
89    self._children = []
90    self._parent = None
91    self.AddChildren(children)
92
93#
94#
95#
96  # Return a string representation of this node
97  def __str__(self):
98    name = self.GetProperty('NAME','')
99    return '%s(%s)' % (self._cls, name)
100
101  def GetLogLine(self, msg):
102    filename, lineno = self.GetFileAndLine()
103    return '%s(%d) : %s\n' % (filename, lineno, msg)
104
105  # Log an error for this object
106  def Error(self, msg):
107    self.GetProperty('ERRORS').append(msg)
108    sys.stderr.write(self.GetLogLine('error: ' + msg))
109
110  # Log a warning for this object
111  def Warning(self, msg):
112    self.GetProperty('WARNINGS').append(msg)
113    sys.stdout.write(self.GetLogLine('warning:' + msg))
114
115  # Return file and line number for where node was defined
116  def GetFileAndLine(self):
117    return self.GetProperty('FILENAME'), self.GetProperty('LINENO')
118
119  def GetClass(self):
120    return self._cls
121
122  def GetName(self):
123    return self.GetProperty('NAME')
124
125  def GetParent(self):
126    return self._parent
127
128  def Traverse(self, search, filter_nodes):
129    if self._cls in filter_nodes:
130      return ''
131
132    search.Enter(self)
133    search.depth += 1
134    for child in self._children:
135      child.Traverse(search, filter_nodes)
136    search.depth -= 1
137    search.Exit(self)
138
139
140  def Tree(self, filter_nodes=None, accept_props=None):
141    class DumpTreeSearch(IDLSearch):
142      def __init__(self, props):
143        IDLSearch.__init__(self)
144        self.out = []
145        self.props = props
146
147      def Enter(self, node):
148        tab = ''.rjust(self.depth * 2)
149        self.out.append(tab + str(node))
150        if self.props:
151          proplist = []
152          for key, value in node.GetProperties().iteritems():
153            if key in self.props:
154              proplist.append(tab + '    %s: %s' % (key, str(value)))
155          if proplist:
156            self.out.append(tab + '  PROPERTIES')
157            self.out.extend(proplist)
158
159    if filter_nodes == None:
160      filter_nodes = ['Comment', 'Copyright']
161
162    search = DumpTreeSearch(accept_props)
163    self.Traverse(search, filter_nodes)
164    return search.out
165
166#
167# Search related functions
168#
169  # Check if node is of a given type
170  def IsA(self, *typelist):
171    if self._cls in typelist:
172      return True
173    return False
174
175  # Get a list of all children
176  def GetChildren(self):
177    return self._children
178
179  def GetListOf(self, *keys):
180    out = []
181    for child in self.GetChildren():
182      if child.GetClass() in keys:
183        out.append(child)
184    return out
185
186  def GetOneOf(self, *keys):
187    out = self.GetListOf(*keys)
188    if out:
189      return out[0]
190    return None
191
192  def AddChildren(self, children):
193    children = CopyToList(children)
194    for child in children:
195      if not child:
196        continue
197      if type(child) == IDLAttribute:
198        self.SetProperty(child.name, child.value)
199        continue
200      if type(child) == IDLNode:
201        child._parent = self
202        self._children.append(child)
203        continue
204      raise RuntimeError('Adding child of type %s.\n' % type(child).__name__)
205
206
207#
208# Property Functions
209#
210  def SetProperty(self, name, val):
211    self._properties[name] = val
212
213  def GetProperty(self, name, default=None):
214    return self._properties.get(name, default)
215
216  def GetProperties(self):
217    return self._properties
218