File: Synopsis/Parsers/IDL/omni.py
  1#
  2# Copyright (C) 2000 Stefan Seefeld
  3# All rights reserved.
  4# Licensed to the public under the terms of the GNU LGPL (>= 2),
  5# see the file COPYING for details.
  6#
  7
  8from Synopsis import IR, ASG
  9from Synopsis.QualifiedName import QualifiedCxxName as QName
 10from Synopsis.SourceFile import *
 11import idlast, idltype, idlvisitor, idlutil
 12import _omniidl
 13import sys, getopt, os, os.path, string, types
 14
 15sourcefile = None
 16
 17def strip_filename(filename):
 18   "This is aliased as strip if -b used and basename set"
 19
 20   if len(basename) > len(filename): return filename
 21   if filename[:len(basename)] == basename:
 22      return filename[len(basename):]
 23   return filename
 24
 25class TypeTranslator(idlvisitor.TypeVisitor):
 26   """maps idltype objects to ASG.TypeId objects in a ASG.Dictionary"""
 27
 28   def __init__(self, types):
 29      self.types = types
 30      self.__result = None
 31      self.__basetypes = {idltype.tk_void:       QName(('void',)),
 32                          idltype.tk_short:      QName(('short',)),
 33                          idltype.tk_long:       QName(('long',)),
 34                          idltype.tk_ushort:     QName(('unsigned short',)),
 35                          idltype.tk_ulong:      QName(('unsigned long',)),
 36                          idltype.tk_float:      QName(('float',)),
 37                          idltype.tk_double:     QName(('double',)),
 38                          idltype.tk_boolean:    QName(('boolean',)),
 39                          idltype.tk_char:       QName(('char',)),
 40                          idltype.tk_octet:      QName(('octet',)),
 41                          idltype.tk_any:        QName(('any',)),
 42                          idltype.tk_TypeCode:   QName(('CORBA','TypeCode',)),
 43                          idltype.tk_Principal:  QName(('CORBA','Principal',)),
 44                          idltype.tk_longlong:   QName(('long long',)),
 45                          idltype.tk_ulonglong:  QName(('unsigned long long',)),
 46                          idltype.tk_longdouble: QName(('long double',)),
 47                          idltype.tk_wchar:      QName(('wchar',))}
 48
 49   def internalize(self, idltype):
 50
 51      idltype.accept(self)
 52      return self.__result
 53
 54   def has_key(self, name): return self.types.has_key(name)
 55
 56   def add(self, name, type):
 57      self.types[name] = type
 58
 59   def get(self, name):
 60      return self.types[name]
 61
 62   def visitBaseType(self, idltype):
 63
 64      type = ASG.BuiltinTypeId('IDL', self.__basetypes[idltype.kind()])
 65      self.types[type.name] = type
 66      self.__result = type.name
 67
 68   def visitStringType(self, idltype):
 69
 70      # FIXME: Should we create a ParametrizedTypeId with the appropriate bound parameters ?
 71      if idltype.bound() == 0:
 72         qname = QName(('string',))
 73      else:
 74         qname = QName(('string<%s>'%idltype.bound(),))
 75      if qname not in self.types:
 76         self.types[qname] = ASG.BuiltinTypeId('IDL', qname)
 77      self.__result = qname
 78
 79   def visitWStringType(self, idltype):
 80
 81      # FIXME: Should we create a ParametrizedTypeId with the appropriate bound parameters ?
 82      if idltype.bound() == 0:
 83         qname = QName(('wstring',))
 84      else:
 85         qname = QName(('wstring<%s>'%idltype.bound(),))
 86      if qname not in self.types:
 87         self.types[qname] = ASG.BuiltinTypeId('IDL', qname)
 88      self.__result = qname
 89
 90   def visitSequenceType(self, idltype):
 91
 92      qname = QName(('sequence',))
 93      if not self.types.has_key(qname):
 94         self.types[qname] = ASG.BuiltinTypeId("IDL", qname)
 95      idltype.seqType().accept(self)
 96      ptype = self.types[self.__result]
 97      type = ASG.ParametrizedTypeId("IDL", self.types[qname], [ptype])
 98      qname  = QName(('sequence<%s>'%str(ptype.name),))
 99      self.types[qname] = type
100      self.__result = qname
101
102   def visitDeclaredType(self, idltype):
103
104      self.__result = QName(idltype.decl().scopedName())
105
106class ASGTranslator(idlvisitor.AstVisitor):
107
108   def __init__(self, declarations, types, primary_file_only):
109
110      self.declarations = declarations
111      self.primary_file_only = primary_file_only
112      self.types = types
113      self.__scope = []
114      self.__operation = None
115      self.__enum = None
116
117   def scope(self): return self.__scope[-1].name
118
119   def add_declaration(self, declaration):
120      self.__scope[-1].declarations.append(declaration)
121
122   def addType(self, name, type):
123
124      if self.types.has_key(name):
125         if isinstance(self.types.get(name), ASG.UnknownTypeId):
126            self.types.add(name, type)
127         else:
128            pass
129         return
130      self.types.add(name, type)
131
132   def getType(self, name): return self.types.get(QName(name))
133   def visitAST(self, node):
134
135      self.__scope.append(ASG.Scope(sourcefile, 0, 'file', QName()))
136      # add an 'Object' Type to the Type Dictionary. Don't declare it in the ASG since
137      # there is no corresponding declaration
138      qname = QName(('CORBA', 'Object'))
139      object = ASG.Class(sourcefile, 0, 'interface', qname)
140      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, object))
141      for n in node.declarations():
142         n.accept(self)
143      for d in self.__scope[-1].declarations:
144         self.declarations.append(d)
145
146   def visitModule(self, node):
147
148      visible = node.mainFile() or not self.primary_file_only
149      qname = QName(list(self.scope()) + [node.identifier()])
150      module = ASG.Module(sourcefile, node.line(), 'module', qname)
151      if visible:
152         self.add_declaration(module)
153      self.__scope.append(module)
154      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, module))
155      if not self.primary_file_only or node.mainFile():
156         comments = [c.text() for c in node.comments()]
157         if comments:
158            module.annotations['comments'] = comments
159      for n in node.definitions():
160         n.accept(self)
161      self.__scope.pop()
162
163   def visitInterface(self, node):
164
165      visible = node.mainFile() or not self.primary_file_only
166      qname = QName(list(self.scope()) + [node.identifier()])
167      class_ = ASG.Class(sourcefile, node.line(), 'interface', qname)
168      if visible:
169         self.add_declaration(class_)
170      self.__scope.append(class_)
171      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, class_))
172      if not self.primary_file_only or node.mainFile():
173         comments = [c.text() for c in node.comments()]
174         if comments:
175            class_.annotations['comments'] = comments
176      for i in node.inherits():
177         parent = self.getType(i.scopedName())
178         class_.parents.append(ASG.Inheritance("", parent, []))
179      for c in node.contents(): c.accept(self)
180      self.__scope.pop()
181
182   def visitForward(self, node):
183
184      visible = node.mainFile() or not self.primary_file_only
185      name = list(self.scope())
186      qname = QName(name + [node.identifier()])
187      forward = ASG.Forward(sourcefile, node.line(), 'interface', qname)
188      if visible:
189         self.add_declaration(forward)
190      self.addType(qname, ASG.UnknownTypeId('IDL', qname))
191
192   def visitConst(self, node):
193
194      visible = node.mainFile() or not self.primary_file_only
195      name = list(self.scope())
196      qname = QName(name + [node.identifier()])
197      type = self.types.internalize(node.constType())
198      if node.constType().kind() == idltype.tk_enum:
199         value = "::" + idlutil.ccolonName(node.value().scopedName())
200      else:
201         value = str(node.value())
202      const = ASG.Const(sourcefile, node.line(), 'const',
203                        self.getType(type), qname, value)
204      if visible:
205         self.add_declaration(const)
206      comments = [c.text() for c in node.comments()]
207      if comments:
208         const.annotations['comments'] = comments
209
210   def visitTypedef(self, node):
211
212      visible = node.mainFile() or not self.primary_file_only
213      # if this is an inline constructed type, it is a 'Declared' type
214      # and we need to visit the declaration first
215      if node.constrType():
216         node.aliasType().decl().accept(self)
217      type = self.types.internalize(node.aliasType())
218      comments = [c.text() for c in node.comments()]
219      for d in node.declarators():
220         # reinit the type for this declarator, as each declarator of
221         # a single typedef declaration can have a different type. *sigh*
222         dtype = type
223         if d.sizes():
224            array = ASG.ArrayTypeId('IDL', self.getType(type), [str(s) for s in d.sizes()])
225            dtype = map(None, type[:-1])
226            dtype.append(type[-1] + string.join(map(lambda s:"["+ str(s) +"]", d.sizes()),''))
227            self.addType(QName(dtype), array)
228         name = list(self.scope())
229         qname = QName(name + [d.identifier()])
230         typedef = ASG.Typedef(sourcefile, node.line(), 'typedef', qname, self.getType(dtype), node.constrType())
231         d_comments = comments + [c.text() for c in d.comments()]
232         if d_comments:
233            typedef.annotations['comments'] = d_comments
234         self.addType(qname, ASG.DeclaredTypeId('IDL', qname, typedef))
235         if visible:
236            self.add_declaration(typedef)
237
238   def visitMember(self, node):
239
240      visible = node.mainFile() or not self.primary_file_only
241      # if this is an inline constructed type, it is a 'Declared' type
242      # and we need to visit the declaration first
243      if node.constrType():
244         node.memberType().decl().accept(self)
245      type = self.types.internalize(node.memberType())
246      comments = [c.text() for c in node.comments()]
247      for d in node.declarators():
248         # reinit the type for this declarator, as each declarator of
249         # a single typedef declaration can have a different type. *sigh*
250         dtype = type
251         if d.sizes():
252            array = ASG.ArrayTypeId('IDL', self.getType(type), [str(s) for s in node.sizes()])
253            dtype = type[:-1]
254            dtype.append(type[-1] + string.join(map(lambda s:"["+s+"]", d.sizes()),''))
255            self.addType(dtype, array)
256         qname = QName(list(self.scope()) + [d.identifier()])
257         member = ASG.Variable(sourcefile, node.line(), 'variable', qname, self.getType(dtype), node.constrType())
258         d_comments = comments + [c.text() for c in d.comments()]
259         if d_comments:
260            member.annotations['comments'] = d_comments
261         self.addType(qname, ASG.DeclaredTypeId('IDL', qname, member))
262         if visible:
263            self.add_declaration(member)
264
265   def visitStruct(self, node):
266
267      visible = node.mainFile() or not self.primary_file_only
268      qname = QName(list(self.scope()) + [node.identifier()])
269      if self.primary_file_only and not node.mainFile():
270         forward = ASG.Forward(sourcefile, node.line(), 'struct', qname)
271         if visible:
272            self.add_declaration(forward)
273         self.addType(qname, ASG.DeclaredTypeId('IDL', qname, forward))
274         return
275      struct = ASG.Class(sourcefile, node.line(), 'struct', qname)
276      if visible:
277         self.add_declaration(struct)
278      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, struct))
279      comments = [c.text() for c in node.comments()]
280      if comments:
281         struct.annotations['comments'] = comments
282      self.__scope.append(struct)
283      for member in node.members(): member.accept(self)
284      self.__scope.pop()
285
286   def visitException(self, node):
287
288      visible = node.mainFile() or not self.primary_file_only
289      qname = QName(list(self.scope()) + [node.identifier()])
290      if self.primary_file_only and not node.mainFile():
291         forward = ASG.Forward(sourcefile, node.line(), 'exception', qname)
292         if visible:
293            self.add_declaration(forward)
294         self.addType(qname, ASG.DeclaredTypeId('IDL', qname, forward))
295         return
296      exc = ASG.Class(sourcefile, node.line(), 'exception', qname)
297      if visible:
298         self.add_declaration(exc)
299      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, exc))
300      self.__scope.append(exc)
301      comments = [c.text() for c in node.comments()]
302      if comments:
303         exc.annotations['comments'] = comments
304      for member in node.members(): member.accept(self)
305      self.__scope.pop()
306
307    #    def visitCaseLabel(self, node):    return
308
309   def visitUnionCase(self, node):
310
311      # if this is an inline constructed type, it is a 'Declared' type
312      # and we need to visit the declaration first
313      if node.constrType():
314         node.caseType().decl().accept(self)
315      type = self.types.internalize(node.caseType())
316      declarator = node.declarator()
317      if declarator.sizes():
318         array = ASG.ArrayTypeId('IDL', self.getType(type), [str(s) for s in declarator.sizes()])
319         type = type[:-1]
320         type.append(type[-1] + string.join(map(lambda s:"["+s+"]",node.sizes()),''))
321         self.addType(type, array)
322      qname = QName(list(self.scope()) + [node.declarator().identifier()])
323      self.__scope[-1].declarations.append(
324         ASG.Operation(sourcefile, node.line(), 'case',
325                       [], self.getType(type), [], qname, qname[-1]))
326
327   def visitUnion(self, node):
328
329      visible = node.mainFile() or not self.primary_file_only
330      qname = QName(list(self.scope()) + [node.identifier()])
331      if self.primary_file_only and not node.mainFile():
332         forward = ASG.Forward(sourcefile, node.line(), 'union', qname)
333         if visible:
334            self.add_declaration(forward)
335         self.addType(qname, ASG.DeclaredTypeId('IDL', qname, forward))
336         return
337      class_ = ASG.Class(sourcefile, node.line(), 'union', qname)
338      self.add_declaration(class_)
339      self.__scope.append(class_)
340      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, class_))
341      comments = [c.text() for c in node.comments()]
342      if comments:
343         class_.annotations['comments'] = comments
344      for c in node.cases(): c.accept(self)
345      self.__scope.pop()
346
347   def visitEnumerator(self, node):
348
349      qname = QName(list(self.scope()) + [node.identifier()])
350      enum = ASG.Enumerator(sourcefile, node.line(), qname, '')
351      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, enum))
352      self.__enum.enumerators.append(enum)
353
354   def visitEnum(self, node):
355
356      visible = node.mainFile() or not self.primary_file_only
357      qname = QName(list(self.scope()) + [node.identifier()])
358      if self.primary_file_only and not node.mainFile():
359         forward = ASG.Forward(sourcefile, node.line(), 'enum', qname)
360         if visible:
361            self.add_declaration(forward)
362         self.addType(qname, ASG.DeclaredTypeId('IDL', qname, forward))
363         return
364      self.__enum = ASG.Enum(sourcefile, node.line(), qname, [])
365      if visible:
366         self.add_declaration(self.__enum)
367      self.addType(qname, ASG.DeclaredTypeId('IDL', qname, self.__enum))
368      comments = [c.text() for c in node.comments()]
369      if comments:
370         self.__enum.annotations['comments'] = comments
371      for enumerator in node.enumerators(): enumerator.accept(self)
372      self.__enum = None
373
374   def visitAttribute(self, node):
375
376      visible = node.mainFile() or not self.primary_file_only
377      scopename = list(self.scope())
378      if self.primary_file_only and not node.mainFile(): return
379      # Add real Operation objects
380      pre = []
381      if node.readonly(): pre.append("readonly")
382      type = self.types.internalize(node.attrType())
383      comments = [c.text() for c in node.comments()]
384      for id in node.identifiers():
385         qname = QName(scopename + [id])
386         attr = ASG.Operation(sourcefile, node.line(), 'attribute',
387                              pre, self.getType(type), [], qname, qname[-1])
388         if comments:
389            attr.annotations['comments'] = comments
390         if visible:
391            self.add_declaration(attr)
392
393   def visitParameter(self, node):
394
395      operation = self.__operation
396      pre = []
397      if node.direction() == 0: pre.append("in")
398      elif node.direction() == 1: pre.append("out")
399      else: pre.append("inout")
400      post = []
401      name = self.types.internalize(node.paramType())
402      operation.parameters.append(ASG.Parameter(pre, self.getType(name), post, node.identifier()))
403
404   def visitOperation(self, node):
405
406      visible = node.mainFile() or not self.primary_file_only
407      pre = []
408      if node.oneway(): pre.append("oneway")
409      return_type = self.types.internalize(node.returnType())
410      qname = QName(list(self.scope()) + [node.identifier()])
411      self.__operation = ASG.Operation(sourcefile, node.line(), 'operation', pre, self.getType(return_type), [], qname, qname[-1])
412      comments = [c.text() for c in node.comments()]
413      if comments:
414         self.__operation.annotations['comments'] = comments
415      for p in node.parameters(): p.accept(self)
416      for e in node.raises():
417         exception = self.getType(e.scopedName())
418         self.__operation.exceptions.append(exception)
419
420      if visible:
421         self.add_declaration(self.__operation)
422      self.__operation = None
423
424#    def visitNative(self, node):       return
425#    def visitStateMember(self, node):  return
426#    def visitFactory(self, node):      return
427#    def visitValueForward(self, node): return
428#    def visitValueBox(self, node):     return
429#    def visitValueAbs(self, node):     return
430#    def visitValue(self, node):        return
431
432def parse(ir, cppfile, src, primary_file_only,
433          base_path, verbose, debug):
434   global basename, strip, sourcefile
435
436   if base_path:
437      basename = base_path
438
439   _omniidl.keepComments(1)
440   _omniidl.noForwardWarning()
441   tree = _omniidl.compile(open(cppfile, 'r+'))
442   if tree == None:
443      sys.stderr.write("omni: Error parsing %s\n"%cppfile)
444      sys.exit(1)
445
446   sourcefile = SourceFile(strip_filename(src), src, 'IDL')
447   sourcefile.annotations['primary'] = True
448   new_ir = IR.IR()
449   new_ir.files[sourcefile.name] = sourcefile
450   type_trans = TypeTranslator(new_ir.asg.types)
451   ast_trans = ASGTranslator(new_ir.asg.declarations, type_trans, primary_file_only)
452   tree.accept(ast_trans)
453   sourcefile.declarations[:] = new_ir.asg.declarations
454   ir.merge(new_ir)
455   _omniidl.clear()
456   return ir
457