Modules |
Files |
Inheritance Tree |
Inheritance Graph |
Name Index |
Config
File: Synopsis/Parser/IDL/omni.py
1| # $Id: omni.py,v 1.36 2003/01/20 06:43:02 chalky Exp $
2| #
3| # This file is a part of Synopsis.
4| # Copyright (C) 2000, 2001 Stefan Seefeld
5| #
6| # Synopsis is free software; you can redistribute it and/or modify it
7| # under the terms of the GNU General Public License as published by
8| # the Free Software Foundation; either version 2 of the License, or
9| # (at your option) any later version.
10| #
11| # This program is distributed in the hope that it will be useful,
12| # but WITHOUT ANY WARRANTY; without even the implied warranty of
13| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14| # General Public License for more details.
15| #
16| # You should have received a copy of the GNU General Public License
17| # along with this program; if not, write to the Free Software
18| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19| # 02111-1307, USA.
20| #
21| # $Log: omni.py,v $
22| # Revision 1.36 2003/01/20 06:43:02 chalky
23| # Refactored comment processing. Added AST.CommentTag. Linker now determines
24| # comment summary and extracts tags. Increased AST version number.
25| #
26| # Revision 1.35 2003/01/16 17:14:10 chalky
27| # Increase AST version number. SourceFiles store full filename. Executor/Project
28| # uses it to check timestamp for all included files when deciding whether to
29| # reparse input files.
30| #
31| # Revision 1.34 2002/12/12 17:25:35 chalky
32| # Implemented Include support for C++ parser. A few other minor fixes.
33| #
34| # Revision 1.33 2002/12/10 07:27:47 chalky
35| # Fixed for new AST structure (SourceFiles)
36| #
37| # Revision 1.32 2002/12/09 04:01:03 chalky
38| # Added multiple file support to parsers, changed AST datastructure to handle
39| # new information, added a demo to demo/C++. AST Declarations now have a
40| # reference to a SourceFile (which includes a filename) instead of a filename.
41| #
42| # Revision 1.31 2002/11/03 12:43:33 chalky
43| # Fix procedure for finding omnicpp to use the PATH variable
44| #
45| # Revision 1.30 2001/07/19 05:10:39 chalky
46| # Use filenames stored in AST object
47| #
48| # Revision 1.29 2001/07/19 04:03:18 chalky
49| # New .syn file format.
50| #
51| # Revision 1.28 2001/06/13 13:02:36 chalky
52| # A couple of fixes for new realname() stuff
53| #
54| # Revision 1.27 2001/06/13 01:55:11 stefan
55| # modify the realName member to contain only the unscoped name. This has the nice effect that pruning the scope will affect the name and realname at once, since the realName() method computes the scoped name tuple on-the-fly
56| #
57| # Revision 1.26 2001/05/25 13:45:49 stefan
58| # fix problem with getopt error reporting
59| #
60| # Revision 1.25 2001/04/03 23:03:53 chalky
61| # IDL parser now uses config objects. Changed IDL demo accordingly.
62| #
63| # Revision 1.24 2001/02/12 04:08:09 chalky
64| # Added config options to HTML and Linker. Config demo has doxy and synopsis styles.
65| #
66| # Revision 1.23 2001/01/31 06:51:24 stefan
67| # add support for '-v' to all modules; modified toc lookup to use additional url as prefix
68| #
69| # Revision 1.22 2001/01/29 20:22:00 stefan
70| # fixed getopt bug
71| #
72| # Revision 1.21 2001/01/28 02:23:49 chalky
73| # Added -b basename
74| #
75| # Revision 1.20 2001/01/26 17:25:19 stefan
76| # represent array sizes as strings, not ints (to be elaborated...)
77| #
78| # Revision 1.19 2001/01/25 18:27:47 stefan
79| # added Type.Array type and removed AST.Declarator. Adjusted the IDL parser to that.
80| #
81| # Revision 1.18 2001/01/22 19:54:41 stefan
82| # better support for help message
83| #
84| # Revision 1.17 2001/01/22 17:06:15 stefan
85| # added copyright notice, and switched on logging
86| #
87|
88| from omniidl import idlast, idltype, idlvisitor, idlutil
89| import _omniidl
90| import sys, getopt, os, os.path, string, types
91| from Synopsis.Core import Type, AST, Util
92|
93| verbose = 0
94|
95| # A dummy function that doesn't modify filename. use -b to change it
96| def strip(filename): return filename
97|
98| def strip_filename(filename):
99| "This is aliased as strip if -b used and basename set"
100| if len(basename) > len(filename): return filename
101| if filename[:len(basename)] == basename:
102| return filename[len(basename):]
103| return filename
104|
105| class TypeTranslator (idlvisitor.TypeVisitor):
106| """maps idltype objects to Synopsis.Type objects in a Type.Dictionary"""
107| def __init__(self, types):
108| self.types = types
109| self.__result = None
110| self.__basetypes = {
111| idltype.tk_void: "void",
112| idltype.tk_short: "short",
113| idltype.tk_long: "long",
114| idltype.tk_ushort: "unsigned short",
115| idltype.tk_ulong: "unsigned long",
116| idltype.tk_float: "float",
117| idltype.tk_double: "double",
118| idltype.tk_boolean: "boolean",
119| idltype.tk_char: "char",
120| idltype.tk_octet: "octet",
121| idltype.tk_any: "any",
122| idltype.tk_TypeCode: "CORBA::TypeCode",
123| idltype.tk_Principal: "CORBA::Principal",
124| idltype.tk_longlong: "long long",
125| idltype.tk_ulonglong: "unsigned long long",
126| idltype.tk_longdouble: "long double",
127| idltype.tk_wchar: "wchar"
128| }
129|
130| def internalize(self, idltype):
131| idltype.accept(self)
132| return self.__result
133|
134| def add(self, name, type):
135| self.types[name] = type
136| def get(self, name):
137| return self.types[name]
138|
139| def visitBaseType(self, idltype):
140| type = Type.Base("IDL", (self.__basetypes[idltype.kind()],))
141| self.types[type.name()] = type
142| self.__result = type.name()
143|
144| def visitStringType(self, idltype):
145| if not self.types.has_key(["string"]):
146| self.types[["string"]] = Type.Base("IDL", ("string",))
147| self.__result = ["string"]
148| #if idltype.bound() == 0:
149| # self.__result_type = "string"
150| #else:
151| # self.__result_type = "string<" + str(type.bound()) + ">"
152|
153| def visitWStringType(self, idltype):
154| if not self.types.has_key(["wstring"]):
155| self.types[["wstring"]] = Type.Base("IDL", ("wstring",))
156| self.__result = ["wstring"]
157| #if type.bound() == 0:
158| # self.__result_type = "wstring"
159| #else:
160| # self.__result_type = "wstring<" + str(type.bound()) + ">"
161|
162| def visitSequenceType(self, idltype):
163| if not self.types.has_key(["sequence"]):
164| self.types[["sequence"]] = Type.Base("IDL", ("sequence",))
165| idltype.seqType().accept(self)
166| ptype = self.types[self.__result]
167| #if type.bound() == 0:
168| # self.__result_type = "sequence<" + self.__result_type + ">"
169| #else:
170| # self.__result_type = "sequence<" + self.__result_type + ", " +\
171| # str(type.bound()) + ">"
172| type = Type.Parametrized("IDL", self.types[["sequence"]], [ptype])
173| name = ["sequence<" + Util.ccolonName(ptype.name()) + ">"]
174| self.types[name] = type
175| self.__result = name
176|
177| def visitDeclaredType(self, idltype):
178| self.__result = idltype.decl().scopedName()
179|
180| class ASTTranslator (idlvisitor.AstVisitor):
181| def __init__(self, declarations, types, mainfile_only):
182| self.declarations = declarations
183| self.__mainfile_only = mainfile_only
184| self.__types = types
185| self.__scope = []
186| self.__operation = None
187| self.__enum = None
188|
189| def scope(self): return self.__scope[-1].name()
190| def addDeclaration(self, declaration): self.__scope[-1].declarations().append(declaration)
191| def addType(self, name, type):
192| if self.__types.types.has_key(name):
193| if isinstance(self.__types.get(name), Type.Unknown):
194| self.__types.add(name, type)
195| else:
196| pass
197| return
198| self.__types.add(name, type)
199| def getType(self, name): return self.__types.get(name)
200| def visitAST(self, node):
201| self.__scope.append(AST.Scope(sourcefile, 0, "IDL", "file", []))
202| # add an 'Object' Type to the Type Dictionary. Don't declare it in the AST since
203| # there is no corresponding declaration
204| object = AST.Class(sourcefile, 0, "IDL", "interface", ["CORBA", "Object"])
205| self.addType(["CORBA", "Object"], Type.Declared("IDL", ["CORBA", "Object"], object))
206| for n in node.declarations():
207| n.accept(self)
208| for d in self.__scope[-1].declarations():
209| self.declarations.append(d)
210|
211| def visitModule(self, node):
212| #if self.__mainfile_only and not node.mainFile(): return
213| name = list(self.scope()) + [node.identifier()]
214| module = AST.Module(sourcefile, node.line(), "IDL", "module", name)
215| self.addDeclaration(module)
216| self.__scope.append(module)
217| self.addType(name, Type.Declared("IDL", name, module))
218| if not self.__mainfile_only or node.mainFile():
219| for c in node.comments():
220| module.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
221| for n in node.definitions():
222| n.accept(self)
223| self.__scope.pop()
224|
225| def visitInterface(self, node):
226| name = list(self.scope()) + [node.identifier()]
227| clas = AST.Class(sourcefile, node.line(), "IDL", "interface", name)
228| self.addDeclaration(clas)
229| self.__scope.append(clas)
230| self.addType(name, Type.Declared("IDL", name, clas))
231| if not self.__mainfile_only or node.mainFile():
232| for c in node.comments():
233| clas.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
234| for i in node.inherits():
235| parent = self.getType(i.scopedName())
236| clas.parents().append(AST.Inheritance("", parent, []))
237| for c in node.contents(): c.accept(self)
238| self.__scope.pop()
239|
240| def visitForward(self, node):
241| #if self.__mainfile_only and not node.mainFile(): return
242| name = list(self.scope())
243| name.append(node.identifier())
244| forward = AST.Forward(sourcefile, node.line(), "IDL", "interface", name)
245| self.addDeclaration(forward)
246| self.addType(name, Type.Unknown("IDL", name))
247|
248| def visitConst(self, node):
249| if self.__mainfile_only and not node.mainFile(): return
250| name = list(self.scope())
251| name.append(node.identifier())
252| type = self.__types.internalize(node.constType())
253| if node.constType().kind() == idltype.tk_enum:
254| value = "::" + idlutil.ccolonName(node.value().scopedName())
255| else:
256| value = str(node.value())
257| const = AST.Const(sourcefile, node.line(), "IDL", "const",
258| self.getType(type), name, value)
259| self.addDeclaration(const)
260| for c in node.comments():
261| const.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
262|
263| def visitTypedef(self, node):
264| #if self.__mainfile_only and not node.mainFile(): return
265| # if this is an inline constructed type, it is a 'Declared' type
266| # and we need to visit the declaration first
267| if node.constrType():
268| node.memberType().decl().accept(self)
269| type = self.__types.internalize(node.aliasType())
270| comments = []
271| for c in node.comments():
272| comments.append(AST.Comment(c.text(), strip(c.file()), c.line()))
273| for d in node.declarators():
274| # reinit the type for this declarator, as each declarator of
275| # a single typedef declaration can have a different type. *sigh*
276| dtype = type
277| if d.sizes():
278| array = Type.Array("IDL", self.getType(type), map(lambda s:str(s), d.sizes()))
279| dtype = map(None, type[:-1])
280| dtype.append(type[-1] + string.join(map(lambda s:"["+ str(s) +"]", d.sizes()),''))
281| self.addType(dtype, array)
282| dname = list(self.scope())
283| dname.append(d.identifier())
284| typedef = AST.Typedef(sourcefile, node.line(), "IDL", "typedef", dname, self.getType(dtype), node.constrType())
285| typedef.comments().extend(comments)
286| for c in d.comments():
287| typedef.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
288| self.addType(typedef.name(), Type.Declared("IDL", typedef.name(), typedef))
289| self.addDeclaration(typedef)
290|
291| def visitMember(self, node):
292| if self.__mainfile_only and not node.mainFile(): return
293| # if this is an inline constructed type, it is a 'Declared' type
294| # and we need to visit the declaration first
295| if node.constrType():
296| node.memberType().decl().accept(self)
297| type = self.__types.internalize(node.memberType())
298| comments = []
299| for c in node.comments():
300| comments.append(AST.Comment(c.text(), strip(c.file()), c.line()))
301| for d in node.declarators():
302| # reinit the type for this declarator, as each declarator of
303| # a single typedef declaration can have a different type. *sigh*
304| dtype = type
305| if d.sizes():
306| array = Type.Array("IDL", self.getType(type), map(lambda s:str(s), node.sizes()))
307| dtype = type[:-1]
308| dtype.append(type[-1] + string.join(map(lambda s:"["+s+"]", d.sizes()),''))
309| self.addType(dtype, array)
310| dname = list(self.scope())
311| dname.append(d.identifier())
312| member = AST.Variable(sourcefile, node.line(), "IDL", "variable", dname, self.getType(dtype), node.constrType())
313| member.comments().extend(comments)
314| for c in d.comments():
315| member.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
316| self.addType(member.name(), Type.Declared("IDL", member.name(), member))
317| self.addDeclaration(member)
318|
319| def visitStruct(self, node):
320| name = list(self.scope()) + [node.identifier()]
321| if self.__mainfile_only and not node.mainFile():
322| forward = AST.Forward(sourcefile, node.line(), "IDL", "struct", name)
323| self.addDeclaration(forward)
324| self.addType(name, Type.Declared("IDL", name, forward))
325| return
326| struct = AST.Class(sourcefile, node.line(), "IDL", "struct", name)
327| self.addDeclaration(struct)
328| self.addType(name, Type.Declared("IDL", name, struct))
329| for c in node.comments():
330| struct.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
331| self.__scope.append(struct)
332| for member in node.members(): member.accept(self)
333| self.__scope.pop()
334|
335| def visitException(self, node):
336| name = list(self.scope()) + [node.identifier()]
337| if self.__mainfile_only and not node.mainFile():
338| forward = AST.Forward(sourcefile, node.line(), "IDL", "exception", name)
339| self.addDeclaration(forward)
340| self.addType(name, Type.Declared("IDL", name, forward))
341| return
342| exc = AST.Class(sourcefile, node.line(), "IDL", "exception", name)
343| self.addDeclaration(exc)
344| self.addType(name, Type.Declared("IDL", name, exc))
345| self.__scope.append(exc)
346| for c in node.comments():
347| exc.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
348| for member in node.members(): member.accept(self)
349| self.__scope.pop()
350|
351| # def visitCaseLabel(self, node): return
352| def visitUnionCase(self, node):
353| if self.__mainfile_only and not node.mainFile(): return
354| # if this is an inline constructed type, it is a 'Declared' type
355| # and we need to visit the declaration first
356| if node.constrType():
357| node.caseType().decl().accept(self)
358| type = self.__types.internalize(node.caseType())
359| declarator = node.declarator()
360| if declarator.sizes():
361| array = Type.Array("IDL", self.getType(type), map(lambda s:str(s), declarator.sizes()))
362| type = type[:-1]
363| type.append(type[-1] + string.join(map(lambda s:"["+s+"]",node.sizes()),''))
364| self.addType(type, array)
365| name = list(self.scope())
366| name.append(node.declarator().identifier())
367| self.__scope[-1].declarations().append(
368| AST.Operation(sourcefile, node.line(), "IDL", "case",
369| [], self.getType(type), name, name[-1]))
370| def visitUnion(self, node):
371| name = list(self.scope()) + [node.identifier()]
372| if self.__mainfile_only and not node.mainFile():
373| forward = AST.Forward(sourcefile, node.line(), "IDL", "union", name)
374| self.addDeclaration(forward)
375| self.addType(name, Type.Declared("IDL", name, forward))
376| return
377| clas = AST.Class(sourcefile, node.line(), "IDL", "union", name)
378| self.addDeclaration(clas)
379| self.__scope.append(clas)
380| self.addType(name, Type.Declared("IDL", name, clas))
381| for c in node.comments():
382| clas.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
383| for c in node.cases(): c.accept(self)
384| self.__scope.pop()
385|
386| def visitEnumerator(self, node):
387| if self.__mainfile_only and not node.mainFile(): return
388| name = list(self.scope())
389| name.append(node.identifier())
390| enum = AST.Enumerator(sourcefile, node.line(), "IDL", name, "")
391| self.addType(name, Type.Declared("IDL", name, enum))
392| self.__enum.enumerators().append(enum)
393|
394| def visitEnum(self, node):
395| name = list(self.scope()) + [node.identifier()]
396| if self.__mainfile_only and not node.mainFile():
397| forward = AST.Forward(sourcefile, node.line(), "IDL", "enum", name)
398| self.addDeclaration(forward)
399| self.addType(name, Type.Declared("IDL", name, forward))
400| return
401| self.__enum = AST.Enum(sourcefile, node.line(), "IDL", name, [])
402| self.addDeclaration(self.__enum)
403| self.addType(name, Type.Declared("IDL", name, self.__enum))
404| for c in node.comments():
405| self.__enum.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
406| for enumerator in node.enumerators(): enumerator.accept(self)
407| self.__enum = None
408|
409| def visitAttribute(self, node):
410| scopename = list(self.scope())
411| if self.__mainfile_only and not node.mainFile(): return
412| # Add real Operation objects
413| pre = []
414| if node.readonly(): pre.append("readonly")
415| type = self.__types.internalize(node.attrType())
416| comments = []
417| for c in node.comments():
418| comments.append(AST.Comment(c.text(), strip(c.file()), c.line()))
419| for id in node.identifiers():
420| name = scopename + [id]
421| attr = AST.Operation(sourcefile, node.line(), "IDL", "attribute",
422| pre, self.getType(type), name, name[-1])
423| attr.comments().extend(comments)
424| self.addDeclaration(attr)
425|
426| def visitParameter(self, node):
427| operation = self.__operation
428| pre = []
429| if node.direction() == 0: pre.append("in")
430| elif node.direction() == 1: pre.append("out")
431| else: pre.append("inout")
432| post = []
433| name = self.__types.internalize(node.paramType())
434| operation.parameters().append(AST.Parameter(pre, self.getType(name), post, node.identifier()))
435|
436| def visitOperation(self, node):
437| pre = []
438| if node.oneway(): pre.append("oneway")
439| returnType = self.__types.internalize(node.returnType())
440| name = list(self.scope())
441| name.append(node.identifier())
442| self.__operation = AST.Operation(sourcefile, node.line(), "IDL", "operation", pre, self.getType(returnType), name, name[-1])
443| for c in node.comments():
444| self.__operation.comments().append(AST.Comment(c.text(), strip(c.file()), c.line()))
445| for p in node.parameters(): p.accept(self)
446| for e in node.raises():
447| exception = self.getType(e.scopedName())
448| self.__operation.exceptions().append(exception)
449|
450| self.addDeclaration(self.__operation)
451| self.__operation = None
452|
453| # def visitNative(self, node): return
454| # def visitStateMember(self, node): return
455| # def visitFactory(self, node): return
456| # def visitValueForward(self, node): return
457| # def visitValueBox(self, node): return
458| # def visitValueAbs(self, node): return
459| # def visitValue(self, node): return
460|
461| def usage():
462| print \
463| """
464| -I<path> Specify include path to be used by the preprocessor
465| -m Unly keep declarations from the main file
466| -k Comments after declarations are kept for the back-ends
467| -K Comments before declarations are kept for the back-ends
468| -b <basename> Strip the basename from all filenames"""
469|
470| def __parseArgs(args, config_obj):
471| global preprocessor_args, mainfile_only, basename, strip, verbose
472|
473| preprocessor_args = []
474| mainfile_only = 0
475| basename = ""
476|
477| # Try config object first
478| if hasattr(config_obj, 'keep_comments') and config_obj.keep_comments:
479| preprocessor_args.append("-C")
480| _omniidl.keepComments(1)
481| if hasattr(config_obj, 'main_file') and config_obj.main_file:
482| mainfile_only = 1
483| if hasattr(config_obj, 'verbose') and config_obj.verbose:
484| verbose = 1
485| if hasattr(config_obj, 'basename') and config_obj.basename:
486| basename = config_obj.basename
487| strip = strip_filename
488| if hasattr(config_obj, 'include_path') and config_obj.include_path:
489| paths = config_obj.include_path
490| if type(paths) != types.ListType:
491| sys.stderr.write("Config include_path must be a list of strings")
492| sys.exit(1)
493| for path in paths:
494| if type(path) != types.StringType:
495| sys.stderr.write("Config include_path must be a list of strings")
496| sys.exit(1)
497| preprocessor_args.append("-I" + path)
498|
499|
500| # Parse args
501| try:
502| opts,remainder = Util.getopt_spec(args, "I:b:mkKv")
503| except getopt.error, e:
504| sys.stderr.write("Error in arguments: " + str(e) + "\n")
505| sys.exit(1)
506|
507| for opt in opts:
508| o,a = opt
509|
510| if o == "-I":
511| preprocessor_args.append("-I" + a)
512| if o == "-m":
513| mainfile_only = 1
514| elif o == "-k":
515| preprocessor_args.append("-C")
516| _omniidl.keepComments(0)
517| elif o == "-K":
518| preprocessor_args.append("-C")
519| _omniidl.keepComments(1)
520| elif o == "-b":
521| basename = a
522| strip = strip_filename
523| elif o == "-v": verbose = 1
524|
525| def parse(file, extra_files, args, config_obj):
526| global preprocessor_args, mainfile_only, sourcefile
527| __parseArgs(args, config_obj)
528| path = string.split(os.getenv('PATH'), os.pathsep)
529| # Add Synopsis' bindir
530| path.insert(0, os.path.dirname(sys.argv[0]))
531| if hasattr(_omniidl, "__file__"):
532| # Add path to omniidl module
533| dirname = os.path.dirname(_omniidl.__file__)
534| path.insert(0, dirname)
535| # If, eg, /usr/lib/pythonX.X/site-packages, add /usr/lib
536| dirnames = string.split(dirname, os.sep)
537| if len(dirnames) > 2 and dirnames[-1] == 'site-packages' \
538| and dirnames[-2][:6] == 'python':
539| path.insert(0, string.join(dirnames[:-2], os.sep))
540|
541| preprocessor = None
542| for directory in path:
543| preprocessor = os.path.join(directory, "omnicpp")
544| if os.access(preprocessor, os.R_OK | os.X_OK):
545| break
546| preprocessor = None
547| if not preprocessor:
548| # Try ordinary cpp
549| print "Error: unable to find omnicpp in path:"
550| print string.join(path, os.pathsep)
551| sys.exit(1)
552| preprocessor_cmd = preprocessor + " -lang-c++ -undef -D__OMNIIDL__=" + _omniidl.version
553| preprocessor_cmd = preprocessor_cmd + " " + string.join(preprocessor_args, " ") + " " + file
554| fd = os.popen(preprocessor_cmd, "r")
555|
556| _omniidl.noForwardWarning()
557| tree = _omniidl.compile(fd)
558| if tree == None:
559| sys.stderr.write("omni: Error parsing " + file + "\n")
560| sys.exit(1)
561|
562| sourcefile = AST.SourceFile(strip(file), file, "IDL")
563| sourcefile.set_is_main(1)
564| ast = AST.AST()
565| ast.files()[sourcefile.filename()] = sourcefile
566| type_trans = TypeTranslator(ast.types())
567| ast_trans = ASTTranslator(ast.declarations(), type_trans, mainfile_only)
568| tree.accept(ast_trans)
569| sourcefile.declarations()[:] = ast.declarations()
570| return ast