File: Synopsis/ASG.py
  1#
  2# Copyright (C) 2000 Stefan Seefeld
  3# Copyright (C) 2000 Stephen Davies
  4# All rights reserved.
  5# Licensed to the public under the terms of the GNU LGPL (>= 2),
  6# see the file COPYING for details.
  7#
  8
  9"""Abstract Syntax Tree classes.
 10
 11This file contains classes which encapsulate nodes in the ASG. The base class
 12is the Declaration class that encapsulates a named declaration. All names used
 13are scoped tuples.
 14
 15Also defined in module scope are the constants DEFAULT, PUBLIC, PROTECTED and
 16PRIVATE.
 17"""
 18
 19# Accessibility constants
 20DEFAULT = 0
 21PUBLIC = 1
 22PROTECTED = 2
 23PRIVATE = 3
 24
 25def ccmp(a,b):
 26    """Compares classes of two objects"""
 27    return cmp(type(a),type(b)) or cmp(a.__class__,b.__class__)
 28
 29
 30class Error:
 31   """Exception class used by ASG internals."""
 32
 33   def __init__(self, err):
 34      self.err = err
 35   def __repr__(self):
 36      return self.err
 37
 38
 39class Debugger(type):
 40    """Wrap the object's 'accept' method, printing out the visitor's type.
 41    Useful for tracing visitors visiting declarations."""
 42
 43    def __init__(cls, name, bases, dict):
 44
 45        accept = dict['accept']
 46        "The original instancemethod."
 47
 48        def accept_wrapper(self, visitor):
 49            "The wrapper. The original 'accept' method is part of its closure."
 50            print '%s accepting %s.%s'%(self.__class__.__name__,
 51                                        visitor.__module__,
 52                                        visitor.__class__.__name__)
 53            accept(self, visitor)
 54
 55        setattr(cls, 'accept', accept_wrapper)
 56
 57
 58class TypeId(object):
 59    """Type-id abstract class."""
 60
 61    def __init__(self, language):
 62        self.language = language
 63
 64    def accept(self, visitor):
 65        """visitor pattern accept. @see Visitor"""
 66        pass
 67
 68    def __cmp__(self, other):
 69        "Comparison operator"
 70        return cmp(id(self),id(other))
 71
 72class NamedTypeId(TypeId):
 73    """Named type abstract class"""
 74
 75    def __init__(self, language, name):
 76        super(NamedTypeId, self).__init__(language)
 77        self.name = name
 78
 79class BuiltinTypeId(NamedTypeId):
 80    """Class for builtin type-ids"""
 81
 82    def __init__(self, language, name):
 83        super(BuiltinTypeId, self).__init__(language, name)
 84    def accept(self, visitor): visitor.visit_builtin_type_id(self)
 85    def __cmp__(self, other):
 86        "Comparison operator"
 87        return ccmp(self,other) or cmp(self.name,other.name)
 88    def __str__(self): return str(self.name)
 89
 90class DependentTypeId(NamedTypeId):
 91    """Class for template dependent type-ids"""
 92
 93    def __init__(self, language, name):
 94        super(DependentTypeId, self).__init__(language, name)
 95    def accept(self, visitor): visitor.visit_dependent_type_id(self)
 96    def __cmp__(self, other):
 97        "Comparison operator"
 98        return ccmp(self,other) or cmp(self.name,other.name)
 99    def __str__(self): return str(self.name)
100
101class UnknownTypeId(NamedTypeId):
102    """Class for not (yet) known type-ids."""
103    base = TypeId
104    def __init__(self, language, name):
105        super(UnknownTypeId, self).__init__(language, name)
106        self.link = name
107    def resolve(self, language, name, link):
108        """Associate this type-id with an external reference, instead of a declaration."""
109        self.base.language = language
110        self.name = name
111        self.link = link
112    def accept(self, visitor): visitor.visit_unknown_type_id(self)
113    def __cmp__(self, other):
114        "Comparison operator"
115        return ccmp(self,other) or cmp(self.name,other.name)
116    def __str__(self): return str(self.name)
117
118class DeclaredTypeId(NamedTypeId):
119    """Class for declared types"""
120
121    def __init__(self, language, name, declaration):
122        super(DeclaredTypeId, self).__init__(language, name)
123        self.declaration = declaration
124
125    def accept(self, visitor): visitor.visit_declared_type_id(self)
126    def __cmp__(self, other):
127        "Comparison operator"
128        return ccmp(self,other) or cmp(self.name,other.name)
129    def __str__(self): return str(self.name)
130
131class TemplateId(DeclaredTypeId):
132    """Class for template-ids."""
133
134    def __init__(self, language, name, declaration, parameters):
135
136        super(TemplateId, self).__init__(language, name, declaration)
137        self.parameters = parameters
138    def accept(self, visitor): visitor.visit_template_id(self)
139    def __cmp__(self, other):
140        "Comparison operator"
141        return ccmp(self,other) or cmp(self.parameters,other.parameters)
142    def __str__(self):
143        return "template<%s>%s"%(','.join([str(p) for p in self.parameters]), str(self.name))
144
145class ModifierTypeId(TypeId):
146    """Class for alias types with modifiers (such as 'const', '&', etc.)"""
147
148    def __init__(self, language, alias, premod, postmod):
149        super(ModifierTypeId, self).__init__(language)
150        self.alias = alias
151        self.premod = premod
152        self.postmod = postmod
153
154    def accept(self, visitor): visitor.visit_modifier_type_id(self)
155
156    def __cmp__(self, other):
157        "Comparison operator"
158        return (ccmp(self,other)
159                or cmp(self.alias,other.alias)
160                or cmp(self.premod,other.premod)
161                or cmp(self.postmod,other.postmod))
162    def __str__(self):
163        return "%s%s%s"%(''.join(['%s '%s for s in self.premod]),
164                         str(self.alias),
165                         ''.join(self.postmod))
166
167class ArrayTypeId(TypeId):
168    """A modifier that adds array dimensions to a type-id."""
169
170    def __init__(self, language, alias, sizes):
171        super(ArrayId, self).__init__(language)
172        self.alias = alias
173        self.sizes = sizes
174    def accept(self, visitor): visitor.visit_array_type_id(self)
175    def __cmp__(self, other):
176        "Comparison operator"
177        return (ccmp(self,other)
178                or cmp(self.alias,other.alias)
179                or cmp(self.sizes,other.sizes))
180    def __str__(self):
181        return "%s%s"%(str(self.alias),
182                       ''.join(['[%d]'%s for s in self.sizes]))
183
184class ParametrizedTypeId(TypeId):
185    """Class for parametrized type-id instances."""
186
187    def __init__(self, language, template, parameters):
188
189        super(ParametrizedTypeId, self).__init__(language)
190        self.template = template
191        self.parameters = parameters
192
193    def accept(self, visitor): visitor.visit_parametrized_type_id(self)
194
195    def __cmp__(self, other):
196        "Comparison operator"
197        return ccmp(self,other) or cmp(self.template,other.template)
198    def __str__(self):
199        return "%s<%s>"%('::'.join(self.template.name),
200                         ','.join([str(a) for a in self.parameters]))
201
202class FunctionTypeId(TypeId):
203    """Class for function (pointer) types."""
204    def __init__(self, language, return_type, premod, parameters):
205
206        super(FunctionTypeId, self).__init__(language)
207        self.return_type = return_type
208        self.premod = premod
209        self.parameters = parameters
210
211    def accept(self, visitor): visitor.visit_function_type_id(self)
212
213
214class Dictionary(dict):
215    """Dictionary extends the builtin 'dict' by adding a lookup method to it."""
216
217    def lookup(self, name, scopes):
218        """locate 'name' in one of the scopes"""
219        for s in scopes:
220            scope = list(s)
221            while len(scope) > 0:
222                if self.has_key(scope + name):
223                    return self[scope + name]
224                else: del scope[-1]
225        if self.has_key(name):
226            return self[name]
227        return None
228
229    def merge(self, dict):
230        """merge in a foreign dictionary, overriding already defined types only
231        if they are of type 'Unknown'."""
232        for i in dict.keys():
233            if self.has_key(i):
234                if isinstance(self[i], UnknownTypeId):
235                    self[i] = dict[i]
236                else:
237                    pass
238            else: self[i] = dict[i]
239
240
241class Declaration(object):
242    """Declaration base class. Every declaration has a name, type,
243    accessibility and annotations. The default accessibility is DEFAULT except for
244    C++ where the Parser always sets it to one of the other three. """
245
246    #__metaclass__ = Debugger
247
248    def __init__(self, file, line, type, name):
249
250        self.file  = file
251        """SourceFile instance this declaration is part of."""
252        self.line  = line
253        """The line number of this declaration."""
254        self.name = name
255        """The (fully qualified) name of the declared object."""
256        self.type = type
257        """A string describing the (language-specific) type of the declared object."""
258        self.accessibility = DEFAULT
259        """Accessibility descriptor for the declared object."""
260        self.annotations = {}
261        """A dictionary holding any annotations of this object."""
262
263    def accept(self, visitor):
264        """Visit the given visitor"""
265        visitor.visit_declaration(self)
266
267
268class Builtin(Declaration):
269   """A node for internal use only."""
270
271   def accept(self, visitor): visitor.visit_builtin(self)
272
273
274class UsingDirective(Builtin):
275   """Import one module's content into another."""
276
277   def accept(self, visitor): visitor.visit_using_directive(self)
278
279
280class UsingDeclaration(Builtin):
281    """Import a declaration into this module."""
282
283    def __init__(self, file, line, type, name, alias):
284
285        super(UsingDeclaration, self).__init__(file, line, type, name)
286        self.alias = alias
287
288    def accept(self, visitor):
289        visitor.visit_using_declaration(self)
290
291
292class Macro(Declaration):
293   """A preprocessor macro. Note that macros are not strictly part of the
294   ASG, and as such are always in the global scope. A macro is "temporary" if
295   it was #undefined in the same file it was #defined in."""
296
297   def __init__(self, file, line, type, name, parameters, text):
298
299      Declaration.__init__(self, file, line, type, name)
300      self.parameters = parameters
301      self.text = text
302
303   def accept(self, visitor): visitor.visit_macro(self)
304
305
306class Forward(Declaration):
307   """Forward declaration"""
308
309   def __init__(self, file, line, type, name, is_template_specialization = False):
310
311      Declaration.__init__(self, file, line, type, name)
312      self.template = None
313      self.is_template_specialization = is_template_specialization
314      self.primary_template = None
315      self.specializations = []
316
317   def accept(self, visitor):
318
319       visitor.visit_forward(self)
320
321
322class Group(Declaration):
323   """Base class for groups which contain declarations.
324   This class doesn't correspond to any language construct.
325   Rather, it may be used with comment-embedded grouping tags
326   to regroup declarations that are to appear together in the
327   manual."""
328
329   def __init__(self, file, line, type, name):
330
331      Declaration.__init__(self, file, line, type, name)
332      self.declarations = []
333
334   def accept(self, visitor):
335
336      visitor.visit_group(self)
337
338
339class Scope(Declaration):
340   """Base class for scopes (named groups)."""
341
342   def __init__(self, file, line, type, name):
343
344      Declaration.__init__(self, file, line, type, name)
345      self.declarations = []
346
347   def accept(self, visitor): visitor.visit_scope(self)
348
349
350class Module(Scope):
351   """Module class"""
352   def __init__(self, file, line, type, name):
353
354      Scope.__init__(self, file, line, type, name)
355
356   def accept(self, visitor): visitor.visit_module(self)
357
358
359class MetaModule(Module):
360   """Module Class that references all places where this Module occurs"""
361
362   def __init__(self, type, name):
363
364      Scope.__init__(self, None, "", type, name)
365      self.module_declarations = []
366
367   def accept(self, visitor): visitor.visit_meta_module(self)
368
369
370class Inheritance(object):
371   """Inheritance class. This class encapsulates the information about an
372   inheritance, such as attributes like 'virtual' and 'public' """
373
374   def __init__(self, type, parent, attributes):
375      self.type = type
376      self.parent = parent
377      self.attributes = attributes
378
379   def accept(self, visitor): visitor.visit_inheritance(self)
380
381
382class Class(Scope):
383
384   def __init__(self, file, line, type, name, is_template_specialization = False):
385
386      Scope.__init__(self, file, line, type, name)
387      self.parents = []
388      self.is_template_specialization = is_template_specialization
389      self.primary_template = None
390
391   def accept(self, visitor): visitor.visit_class(self)
392
393
394class ClassTemplate(Scope):
395
396   def __init__(self, file, line, type, name,
397                template = None, is_template_specialization = False):
398
399      Scope.__init__(self, file, line, type, name)
400      self.parents = []
401      self.template = template
402      self.is_template_specialization = is_template_specialization
403      self.primary_template = None
404      self.specializations = []
405
406   def accept(self, visitor): visitor.visit_class_template(self)
407
408
409class Typedef(Declaration):
410
411   def __init__(self, file, line, type, name, alias, constr):
412      Declaration.__init__(self, file, line, type, name)
413      self.alias = alias
414      self.constr = constr
415
416   def accept(self, visitor): visitor.visit_typedef(self)
417
418
419class Enumerator(Declaration):
420   """Enumerator of an Enum. Enumerators represent the individual names and
421   values in an enum."""
422
423   def __init__(self, file, line, name, value):
424      Declaration.__init__(self, file, line, "enumerator", name)
425      self.value = value
426
427   def accept(self, visitor): visitor.visit_enumerator(self)
428
429class Enum(Declaration):
430   """Enum declaration. The actual names and values are encapsulated by
431   Enumerator objects."""
432
433   def __init__(self, file, line, name, enumerators):
434
435      Declaration.__init__(self, file, line, "enum", name)
436      self.enumerators = enumerators[:]
437      #FIXME: the Cxx parser will append a Builtin('eos') to the
438      #list of enumerators which we need to extract here.
439      self.eos = None
440      if self.enumerators and isinstance(self.enumerators[-1], Builtin):
441         self.eos = self.enumerators.pop()
442
443   def accept(self, visitor): visitor.visit_enum(self)
444
445
446class Variable(Declaration):
447   """Variable definition"""
448
449   def __init__(self, file, line, type, name, vtype, constr):
450      Declaration.__init__(self, file, line, type, name)
451      self.vtype  = vtype
452      self.constr  = constr
453
454   def accept(self, visitor): visitor.visit_variable(self)
455
456
457class Const(Declaration):
458   """Constant declaration. A constant is a name with a type and value."""
459
460   def __init__(self, file, line, type, ctype, name, value):
461      Declaration.__init__(self, file, line, type, name)
462      self.ctype  = ctype
463      self.value = value
464
465   def accept(self, visitor): visitor.visit_const(self)
466
467
468class Parameter(object):
469   """Function Parameter"""
470
471   def __init__(self, premod, type, postmod, name='', value=''):
472      self.premodifier = premod
473      self.type = type
474      self.postmodifier = postmod
475      self.name = name
476      self.value = value
477
478   def accept(self, visitor): visitor.visit_parameter(self)
479
480   def __cmp__(self, other):
481      "Comparison operator"
482      #print "Parameter.__cmp__"
483      return cmp(self.type,other.type)
484   def __str__(self):
485      return "%s%s%s"%(' '.join(self.premodifier),
486                       str(self.type),
487                       ' '.join(self.postmodifier))
488
489class Function(Declaration):
490   """Function declaration.
491   Note that function names are stored in mangled form to allow overriding.
492   Formatters should use the real_name to extract the unmangled name."""
493
494   def __init__(self, file, line, type, premod, return_type, postmod, name, real_name):
495      Declaration.__init__(self, file, line, type, name)
496      self._real_name = real_name
497      self.premodifier = premod
498      self.return_type = return_type
499      self.parameters = []
500      self.postmodifier = postmod
501      self.exceptions = []
502
503   real_name = property(lambda self: self.name[:-1] + (self._real_name,))
504
505   def accept(self, visitor): visitor.visit_function(self)
506
507   def __cmp__(self, other):
508      "Recursively compares the typespec of the function"
509      return ccmp(self,other) or cmp(self.parameters, other.parameters)
510
511
512class FunctionTemplate(Function):
513
514   def __init__(self, file, line, type, premod, return_type, postmod, name, real_name, template = None):
515      Function.__init__(self, file, line, type, premod, return_type, postmod, name, real_name)
516      self.template = template
517
518   def accept(self, visitor): visitor.visit_function_template(self)
519
520
521class Operation(Function):
522   """Operation class. An operation is related to a Function and is currently
523   identical.
524   """
525   def __init__(self, file, line, type, premod, return_type, postmod, name, real_name):
526      Function.__init__(self, file, line, type, premod, return_type, postmod, name, real_name)
527
528   def accept(self, visitor): visitor.visit_operation(self)
529
530
531class OperationTemplate(Operation):
532
533   def __init__(self, file, line, type, premod, return_type, postmod, name, real_name, template = None):
534      Operation.__init__(self, file, line, type, premod, return_type, postmod, name, real_name)
535      self.template = template
536
537   def accept(self, visitor): visitor.visit_operation_template(self)
538
539
540class Visitor(object):
541   """Visitor for ASG nodes"""
542
543   def visit_builtin_type_id(self, type): pass
544   def visit_unknown_type_id(self, type): pass
545   def visit_declared_type_id(self, type): pass
546   def visit_modifier_type_id(self, type): pass
547   def visit_array_type_id(self, type): pass
548   def visit_template_id(self, type): pass
549   def visit_parametrized_type_id(self, type): pass
550   def visit_function_type_id(self, type): pass
551   def visit_dependent_type_id(self, type): pass
552
553   def visit_declaration(self, node): pass
554   def visit_builtin(self, node):
555      """Visit a Builtin instance. By default do nothing. Processors who
556      operate on Builtin nodes have to provide an appropriate implementation."""
557      pass
558   def visit_using_directive(self, node): self.visit_builtin(node)
559   def visit_using_declaration(self, node): self.visit_builtin(node)
560   def visit_macro(self, node): self.visit_declaration(node)
561   def visit_forward(self, node): self.visit_declaration(node)
562   def visit_group(self, node):
563      self.visit_declaration(node)
564      for d in node.declarations: d.accept(self)
565   def visit_scope(self, node):
566      self.visit_declaration(node)
567      for d in node.declarations: d.accept(self)
568   def visit_module(self, node): self.visit_scope(node)
569   def visit_meta_module(self, node): self.visit_module(node)
570   def visit_class(self, node): self.visit_scope(node)
571   def visit_class_template(self, node): self.visit_class(node)
572   def visit_typedef(self, node): self.visit_declaration(node)
573   def visit_enumerator(self, node): self.visit_declaration(node)
574   def visit_enum(self, node):
575      self.visit_declaration(node)
576      for e in node.enumerators:
577         e.accept(self)
578      if node.eos:
579         node.eos.accept(self)
580   def visit_variable(self, node): self.visit_declaration(node)
581   def visit_const(self, node): self.visit_declaration(node)
582   def visit_function(self, node):
583      self.visit_declaration(node)
584      for parameter in node.parameters: parameter.accept(self)
585   def visit_function_template(self, node): self.visit_function(node)
586   def visit_operation(self, node): self.visit_function(node)
587   def visit_operation_template(self, node): self.visit_operation(node)
588   def visit_parameter(self, node): pass
589   def visit_inheritance(self, node): pass
590
591class ASG(object):
592
593    def __init__(self, declarations = None, types = None):
594
595        self.declarations = declarations or []
596        self.types = types or Dictionary()
597
598    def copy(self):
599
600        return type(self)(self.declarations[:],
601                          self.types.copy())
602
603    def merge(self, other):
604
605        self.declarations.extend(other.declarations)
606        self.types.merge(other.types)
607
608