1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
|
#
# Copyright (C) 2000 Stefan Seefeld
# Copyright (C) 2000 Stephen Davies
# All rights reserved.
# Licensed to the public under the terms of the GNU LGPL (>= 2),
# see the file COPYING for details.
#
"""Abstract Syntax Tree classes.
This file contains classes which encapsulate nodes in the AST. The base class
is the Declaration class that encapsulates a named declaration. All names used
are scoped tuples.
Also defined in module scope are the constants DEFAULT, PUBLIC, PROTECTED and
PRIVATE.
"""
import Util, Type
import string, sys, cPickle, types, stat
# The version of the file format - this should be increased everytime
# incompatible changes are made to the AST or Type classes
FILE_VERSION = 5
# Accessibility constants
DEFAULT = 0
PUBLIC = 1
PROTECTED = 2
PRIVATE = 3
def ccmp(a,b):
"""Compares classes of two objects"""
return cmp(type(a),type(b)) or cmp(a.__class__,b.__class__)
def load(filename):
"""Loads an AST object from the given filename"""
try:
file = open(filename, 'rb')
unpickler = cPickle.Unpickler(file)
version = unpickler.load()
if version is not FILE_VERSION:
file.close()
raise Exception, 'Wrong file version'
deps = unpickler.load() # ignored here
ast = unpickler.load()
file.close()
return ast
except:
exc, msg = sys.exc_info()[0:2]
if exc is Exception:
raise Exception, "Loading AST from '%s': %s"%(filename, msg)
raise Exception, "Loading AST from '%s', %s: %s"%(filename, exc, msg)
def load_deps(filename):
"""Loads a dependencies object from the given filename. The file will be
an AST save file (usually *.syn), but this method only reads up to the
dependencies object stored before the actual AST. The object returned is a
list of (filename, timestamp) pairs."""
try:
file = open(filename, 'rb')
unpickler = cPickle.Unpickler(file)
version = unpickler.load()
if version is not FILE_VERSION:
file.close()
raise Exception, 'Wrong file version'
deps = unpickler.load()
# stop here, before loading the (probably huge) AST object
file.close()
return deps
except:
exc, msg = sys.exc_info()[0:2]
if exc is Exception:
raise Exception, "Loading dependencies from '%s': %s"%(filename, msg)
raise Exception, "Loading dependencies from '%s', %s: %s"%(filename, exc, msg)
def save(filename, ast):
"""Saves an AST object to the given filename"""
try:
deps = make_deps(ast)
file = open(filename, 'wb')
pickler = cPickle.Pickler(file, 1)
pickler.dump(FILE_VERSION)
pickler.dump(deps)
pickler.dump(ast)
file.close()
except:
exc, msg = sys.exc_info()[0:2]
raise Exception, "Saving '%s', %s: %s"%(filename, exc, msg)
def make_deps(ast):
"""Creates the dependencies object to save in the .syn file from the given
AST. The dependencies are a list of (filename, timestamp) pairs, extracted
from ast.files()"""
deps = []
for file in ast.files().values():
filename = file.full_filename()
try:
info = os.stat(filename)
except:
# Ignore any file we can't stat
continue
time = info[stat.ST_MTIME]
deps.append( (filename, time) )
return deps
class AST:
"""Top-level Abstract Syntax Tree.
The AST represents the whole AST for a file or group of files as a list of
declarations and a types dictionary.
"""
def __init__(self, files=None, declarations=None, typedict=None):
"""Constructor"""
if files is None: files = {}
if declarations is None: declarations = []
if type(files) is not types.DictType: raise TypeError, "files parameter must be a dict of SourceFile objects"
if type(declarations) != type([]): raise TypeError, "declarations parameter must be a list of declarations"
if typedict is None: typedict = Type.Dictionary()
elif not isinstance(typedict, Type.Dictionary): raise TypeError, "types must be an instance of Type.Dictionary"
self.__files = files
self.__declarations = list(declarations)
self.__types = typedict
def files(self):
"""The files this AST represents. Returns a dictionary mapping
filename to SourceFile objects."""
return self.__files
def declarations(self):
"""List of Declaration objects. These are the declarations at file scope"""
return self.__declarations
def types(self):
"""Dictionary of types. This is a Type.Dictionary object"""
return self.__types
def accept(self, visitor):
"""Accept the given visitor"""
visitor.visitAST(self)
def merge(self, other_ast):
"""Merges another AST. Files and declarations are appended to those in
this AST, and types are merged by overwriting existing types -
Unduplicator is responsible for sorting out the mess this may cause :)"""
self.__types.merge(other_ast.types())
self.__declarations.extend(other_ast.declarations())
#merge files
replacement = {}
for filename, file in other_ast.files().items():
if not self.__files.has_key(filename):
self.__files[filename] = file
continue
myfile = self.__files[filename]
replacement[file] = myfile
# the 'main' flag dominates...
myfile.set_is_main(myfile.is_main() or file.is_main())
myfile.declarations().extend(file.declarations())
myfile.includes().extend(file.includes())
# fix dangling inclusions of 'old' files
for r in replacement:
for f in self.__files.values():
for i in f.includes():
if i.target() == r: i.set_target(replacement[r])
class Include:
"""Information about an include directive in a SourceFile.
If the include directive required a macro expansion to get the filename,
the is_macro will return true. If the include directive was actually an
include_next, then is_next will return true.
"""
def __init__(self, target, name, is_macro, is_next):
if not isinstance(target, SourceFile):
raise TypeError, "target parameter must be a SourceFile"
self.__target = target
self.__name = name
self.__is_macro = is_macro
self.__is_next = is_next
def target(self):
return self.__target
def set_target(self, target):
self.__target = target
def name(self):
"""return the name as it appears in the include statement"""
return self.__name
def is_macro(self):
return self.__is_macro
def is_next(self):
return self.__is_next
class MacroCall:
"""A class to support mapping from positions in a preprocessed file
back to positions in the original file."""
def __init__(self, name, start, end, diff):
self.name = name
self.start = start
self.end = end
self.diff = diff
class SourceFile:
"""The information about a file that the AST was generated from.
Contains filename, all declarations from this file (even nested ones) and
includes (aka imports) from this file."""
def __init__(self, filename, full_filename, language):
"""Constructor"""
if type(filename) is not types.StringType: raise TypeError, "filename parameter must be a string filename"
if type(full_filename) is not types.StringType: raise TypeError, "full_filename parameter must be a string filename"
if type(language) is not types.StringType: raise TypeError, "language parameter must be a string language"
self.__filename = filename
self.__full_filename = full_filename
self.__language = language
self.__includes = []
self.__declarations = []
self.__is_main = 0
self.__macro_calls = {}
def is_main(self):
"""Returns whether this was a main file. A source file is a main file
if it was parsed directly or as an extra file. A source file is not a
main file if it was just included. A source file that had no actual
declarations but was given to the parser as either the main source
file or an extra file is still a main file."""
return self.__is_main
def set_is_main(self, value):
"""Sets the is_main flag. Typically only called once, and then may by
the linker if it discovers that a file is actually a main file
elsewhere."""
self.__is_main = value
def filename(self):
"""Returns the filename of this file. The filename can be absolute or
relative, depending on the settings for the Parser"""
return self.__filename
def full_filename(self):
"""Returns the full_filename of this file. The filename can be absolute or
relative, depending on the filename given to the Parser. This filename
does not have any basename stripped from it, and should be accessible
from the current directory as is whether absolute or relative."""
return self.__full_filename
def language(self):
"""Returns the language for this file as a string"""
return self.__language
def declarations(self):
"""Returns a list of declarations declared in this file"""
return self.__declarations
def includes(self):
"""Returns a the files included by this file. These may be
absolute or relative, depending on the settings for the Parser. This
may also include system files. The return value is a list of tuples,
with each tuple being a pair (included-from-filename,
include-filename). This allows distinction of files directly included
(included-from-filename == self.filename()) but also allows dependency
tracking since *all* files read while parsing this file are included
in the list (even system files)."""
return self.__includes
def macro_calls(self):
return self.__macro_calls
class Declaration:
"""Declaration base class. Every declaration has a name, comments,
accessibility and type. The default accessibility is DEFAULT except for
C++ where the Parser always sets it to one of the other three. """
def __init__(self, file, line, language, strtype, name):
if file is not None:
if type(file) is not types.InstanceType or not isinstance(file, SourceFile):
raise TypeError, "file must be a SourceFile object"
self.__file = file
self.__line = line
self.__language = language
self.__name = tuple(name)
self.__type = strtype
self.__comments = []
self.__accessibility = DEFAULT
def file(self):
"""The SourceFile this declaration appeared in"""
return self.__file
def line(self):
"""The line of the file this declaration started at"""
return self.__line
def language(self):
"""The language this declaration is in"""
return self.__language
def type(self):
"""A string name of the type of this declaration"""
return self.__type
def name(self):
"""The scoped tuple name of this declaration"""
return self.__name
def comments(self):
"""A list of Comment objects"""
return self.__comments
def accept(self, visitor):
"""Visit the given visitor"""
visitor.visitDeclaration(self)
def accessibility(self):
"""One of the accessibility constants.
This may be one of DEFAULT, PUBLIC, PROTECTED or PRIVATE, which are
defined at module scope (Synopsis.AST)"""
return self.__accessibility
def set_name(self, name):
"""Change the name of the declaration. If you do want to change the
name (and you probably don't!) then make sure you update your 'types'
dictionary too!"""
self.__name = tuple(name)
def set_accessibility(self, axs):
"""Change the accessibility"""
self.__accessibility = axs
class Builtin (Declaration):
"""An ast node for internal use only."""
def __init__(self, file, line, language, type, name):
"""Constructor"""
Declaration.__init__(self, file, line, language, type, name)
def accept(self, visitor): visitor.visitBuiltin(self)
class Macro (Declaration):
"""A preprocessor macro. Note that macros are not strictly part of the
AST, and as such are always in the global scope. A macro is "temporary" if
it was #undefined in the same file it was #defined in."""
def __init__(self, file, line, language, type, name, parameters, text):
"""Constructor"""
Declaration.__init__(self, file, line, language, type, name)
self.__parameters = parameters
self.__text = text
def parameters(self):
"""Returns a list of parameter names (strings) for this macro if it
has any. Note that if the macro is not a function-like macro, this
method will return None. If it is a function-like macro but with no
parameters, an empty list will be returned."""
return self.__parameters
def text(self):
"""Returns the replacement text for this macro as a string"""
return self.__text
def accept(self, visitor): visitor.visitMacro(self)
class Forward (Declaration):
"""Forward declaration"""
def __init__(self, file, line, language, type, name):
Declaration.__init__(self, file, line, language, type, name)
def accept(self, visitor): visitor.visitForward(self)
class Group (Declaration):
"""Base class for groups which contain declarations.
This class doesn't correspond to any language construct.
Rather, it may be used with comment-embedded grouping tags
to regroup declarations that are to appear together in the
manual."""
def __init__(self, file, line, language, type, name):
Declaration.__init__(self, file, line, language, type, name)
self.__declarations = []
def declarations(self):
"""The list of declarations in this group"""
return self.__declarations
def accept(self, visitor):
#print "group accept", visitor
visitor.visitGroup(self)
class Scope (Group):
"""Base class for scopes (named groups)."""
def __init__(self, file, line, language, type, name):
Group.__init__(self, file, line, language, type, name)
def accept(self, visitor): visitor.visitScope(self)
class Module (Scope):
"""Module class"""
def __init__(self, file, line, language, type, name):
Scope.__init__(self, file, line, language, type, name)
def accept(self, visitor): visitor.visitModule(self)
class MetaModule (Module):
"""Module Class that references all places where this Module occurs"""
def __init__(self, lang, type, name):
Scope.__init__(self, None, "", lang, type, name)
self.__module_declarations = []
def module_declarations(self):
"""The module declarations this metamodule subsumes"""
return self.__module_declarations
def accept(self, visitor): visitor.visitMetaModule(self)
class Inheritance:
"""Inheritance class. This class encapsulates the information about an
inheritance, such as attributes like 'virtual' and 'public' """
def __init__(self, type, parent, attributes):
self.__type = type
self.__parent = parent
self.__attributes = attributes
def type(self):
"""The type of inheritance ('implements', 'extends' etc)"""
return self.__type
def parent(self):
"""The parent class or typedef declaration"""
return self.__parent
def attributes(self):
"""Attributes such as 'virtual', 'public' etc"""
return self.__attributes
def accept(self, visitor): visitor.visitInheritance(self)
def set_parent(self, parent): self.__parent = parent
class Class (Scope):
"""Class class."""
def __init__(self, file, line, language, type, name):
Scope.__init__(self, file, line, language, type, name)
self.__parents = []
self.__template = None
def parents(self):
"""The list of Inheritance objects representing base classes"""
return self.__parents
def template(self):
"""The Template Type if this is a template, or None"""
return self.__template
def set_template(self, template):
self.__template = template
def accept(self, visitor): visitor.visitClass(self)
class Typedef (Declaration):
"""Typedef class.
alias() -- the type object referenced by this alias
constr() -- boolean: true if the alias type was constructed within this typedef declaration."""
def __init__(self, file, line, language, type, name, alias, constr):
Declaration.__init__(self, file, line, language, type, name)
self.__alias = alias
self.__constr = constr
def alias(self):
"""The Type object aliased by this typedef"""
return self.__alias
def constr(self):
"""True if alias type was constructed here.
For example, typedef struct _Foo {} Foo;"""
return self.__constr
def accept(self, visitor): visitor.visitTypedef(self)
def set_alias(self, type): self.__alias = type
class Enumerator (Declaration):
"""Enumerator of an Enum. Enumerators represent the individual names and
values in an enum."""
def __init__(self, file, line, language, name, value):
Declaration.__init__(self, file, line, language, "enumerator", name)
self.__value = value
def value(self):
"""The string value of this enumerator"""
return self.__value
def accept(self, visitor): visitor.visitEnumerator(self)
class Enum (Declaration):
"""Enum declaration. The actual names and values are encapsulated by
Enumerator objects."""
def __init__(self, file, line, language, name, enumerators):
Declaration.__init__(self, file, line, language, "enum", name)
self.__enumerators = enumerators[:]
#FIXME: the Cxx parser will append a Builtin('eos') to the
#list of enumerators which we need to extract here.
self.eos = None
if self.__enumerators and isinstance(self.__enumerators[-1], Builtin):
self.eos = self.__enumerators.pop()
def enumerators(self):
"""List of Enumerator objects"""
return self.__enumerators
def accept(self, visitor): visitor.visitEnum(self)
class Variable (Declaration):
"""Variable definition"""
def __init__(self, file, line, language, type, name, vtype, constr):
Declaration.__init__(self, file, line, language, type, name)
self.__vtype = vtype
self.__constr = constr
def vtype(self):
"""The Type object for this variable"""
return self.__vtype
def constr(self):
"""True if the type was constructed here.
For example: struct Foo {} myFoo;"""
return self.__constr
def accept(self, visitor): visitor.visitVariable(self)
def set_vtype(self, vtype): self.__vtype = vtype
class Const (Declaration):
"""Constant declaration. A constant is a name with a type and value."""
def __init__(self, file, line, language, type, ctype, name, value):
Declaration.__init__(self, file, line, language, type, name)
self.__ctype = ctype
self.__value = value
def ctype(self):
"""Type object for this const"""
return self.__ctype
def value(self):
"""The string value of this type"""
return self.__value
def accept(self, visitor): visitor.visitConst(self)
def set_ctype(self, ctype): self.__ctype = ctype
class Parameter:
"""Function Parameter"""
def __init__(self, premod, type, postmod, identifier='', value=''):
self.__premodifier = premod
self.__type = type
self.__postmodifier = postmod
self.__identifier = identifier
self.__value = value
def premodifier(self):
"""List of premodifiers such as 'in' or 'out'"""
return self.__premodifier
def type(self):
"""The Type object"""
return self.__type
def postmodifier(self):
"""Post modifiers..."""
return self.__postmodifier
def identifier(self):
"""The string name of this parameter"""
return self.__identifier
def value(self):
"""The string value of this parameter"""
return self.__value
def accept(self, visitor): visitor.visitParameter(self)
def set_type(self, type): self.__type = type
def __cmp__(self, other):
"Comparison operator"
#print "Parameter.__cmp__"
return cmp(self.type(),other.type())
def __str__(self):
return "%s%s%s"%(self.__premodifier,str(self.__type),self.__postmodifier)
class Function (Declaration):
"""Function declaration.
Note that function names are stored in mangled form to allow overriding.
Formatters should use the realname() method to extract the unmangled name."""
def __init__(self, file, line, language, type, premod, returnType, name, realname):
Declaration.__init__(self, file, line, language, type, name)
self.__realname = realname
self.__premodifier = premod
self.__returnType = returnType
self.__parameters = []
self.__postmodifier = []
self.__exceptions = []
self.__template = None
def premodifier(self):
"""List of premodifiers such as 'oneway'"""
return self.__premodifier
def returnType(self):
"""Type object for the return type of this function"""
return self.__returnType
def realname(self):
"""The unmangled scoped name tuple of this function"""
name = list(self.name())
name[-1] = self.__realname
return tuple(name)
def parameters(self):
"""The list of Parameter objects of this function"""
return self.__parameters
def postmodifier(self):
"""The list of postmodifier strings"""
return self.__postmodifier
def exceptions(self):
"""The list of exception Types"""
return self.__exceptions
def template(self):
"""The Template Type if this is a template, or None"""
return self.__template
def set_template(self, template):
self.__template = template
def accept(self, visitor): visitor.visitFunction(self)
def set_returnType(self, type): self.__returnType = type
def __cmp__(self, other):
"Recursively compares the typespec of the function"
return ccmp(self,other) or cmp(self.parameters(), other.parameters())
class Operation (Function):
"""Operation class. An operation is related to a Function and is currently
identical.
"""
def __init__(self, file, line, language, type, premod, returnType, name, realname):
Function.__init__(self, file, line, language, type, premod, returnType, name, realname)
def accept(self, visitor): visitor.visitOperation(self)
class CommentTag:
"""Information about a tag in a comment. Tags can represent meta-information
about a comment or extra attributes related to a declaration. For example,
some tags can nominate a comment as belonging to another declaration,
while others indicate information such as parameter and return type
descriptions."""
def __init__(self, name, text):
"""Constructor. Name is the name of tag, eg: 'class', 'param'. Text is
the rest of the text for a tag."""
self.__name = name
self.__text = text
def name(self):
"""Returns the name of this tag"""
return self.__name
def text(self):
"""Returns the text of this tag"""
return self.__text
class Comment:
"""Information about a comment related to a declaration.
The comments are extracted verbatim by the parsers, and various Linker
CommentProcessors can select comments with appropriate formatting (eg: /**
style comments, //. style comments, or all // style comments).
The text field is text of the comment, less any tags that have been
extracted.
The summary field contains a summary of the comment, which may be equal to
the comment text if there is no extra detail. The summary field is only
set by Linker.Comments.Summarizer, which also ensures that there is only
one comment for the declaration first.
The list of tags in a comment can be extracted by a Linker
CommentProcessor, or is an empty list if not set.
C++ Comments may be suspect, which means that they were not immediately
before a declaration, but the extract_tails option was set so they were
kept for the Linker to deal with.
"""
def __init__(self, text, file, line, suspect=0):
self.__file = file
self.__line = line
self.__text = text
self.__summary = None
self.__tags = []
self.__suspect = suspect
def text(self):
"""The text of the comment"""
return self.__text
def set_text(self, text):
"""Changes the text"""
self.__text = text
def summary(self):
"""The summary of the comment"""
return self.__summary
def set_summary(self, summary):
"""Changes the summary"""
self.__summary = summary
def tags(self):
"""The tags of the comment. Only CommentTag instances should be added
to this list."""
return self.__tags
def __str__(self):
"""Returns the text of the comment"""
return self.__text
def file(self):
"""The file it was defined in"""
return self.__file
def line(self):
"""The line it was defined at"""
return self.__line
def is_suspect(self):
"""Returns true if the comment is suspect"""
return self.__suspect
def set_suspect(self, suspect):
"""Sets whether the comment is suspect"""
self.__suspect = suspect
class Visitor :
"""Visitor for AST nodes"""
def visitAST(self, node):
for declaration in node.declarations(): declaration.accept(self)
def visitDeclaration(self, node): return
def visitBuiltin(self, node): return
def visitMacro(self, node): self.visitDeclaration(node)
def visitForward(self, node): self.visitDeclaration(node)
def visitGroup(self, node):
self.visitDeclaration(node)
for declaration in node.declarations(): declaration.accept(self)
def visitScope(self, node): self.visitGroup(node)
def visitModule(self, node): self.visitScope(node)
def visitMetaModule(self, node): self.visitModule(node)
def visitClass(self, node): self.visitScope(node)
def visitTypedef(self, node): self.visitDeclaration(node)
def visitEnumerator(self, node): self.visitDeclaration(node)
def visitEnum(self, node):
self.visitDeclaration(node)
for enum in node.enumerators(): enum.accept(self)
if node.eos: node.eos.accept(self)
def visitVariable(self, node): self.visitDeclaration(node)
def visitConst(self, node): self.visitDeclaration(node)
def visitFunction(self, node):
self.visitDeclaration(node)
for parameter in node.parameters(): parameter.accept(self)
def visitOperation(self, node): self.visitFunction(node)
def visitParameter(self, node): return
def visitComment(self, node): return
def visitInheritance(self, node): return
|