psddl/src/HddlReader.py

Go to the documentation of this file.
00001 #--------------------------------------------------------------------------
00002 # File and Version Information:
00003 #  $Id: HddlReader.py 11679 2016-04-14 20:59:24Z davidsch@SLAC.STANFORD.EDU $
00004 #
00005 # Description:
00006 #  Module HddlReader...
00007 #
00008 #------------------------------------------------------------------------
00009 
00010 """Parser for DLL files.
00011 
00012 This software was developed for the SIT project.  If you use all or 
00013 part of it, please give an appropriate acknowledgment.
00014 
00015 @version $Id: HddlReader.py 11679 2016-04-14 20:59:24Z davidsch@SLAC.STANFORD.EDU $
00016 
00017 @author Andy Salnikov
00018 """
00019 
00020 
00021 #------------------------------
00022 #  Module's version from CVS --
00023 #------------------------------
00024 __version__ = "$Revision: 11679 $"
00025 
00026 #--------------------------------
00027 #  Imports of standard modules --
00028 #--------------------------------
00029 import sys
00030 import os
00031 import os.path
00032 import logging
00033 
00034 #---------------------------------
00035 #  Imports of base class module --
00036 #---------------------------------
00037 
00038 #-----------------------------
00039 # Imports for other modules --
00040 #-----------------------------
00041 from psddl.Attribute import Attribute
00042 from psddl.Bitfield import Bitfield
00043 from psddl.Constant import Constant
00044 from psddl.Constructor import Constructor, CtorArg, CtorInit
00045 from psddl.Enum import Enum
00046 from psddl.ExprVal import ExprVal
00047 from psddl.Method import Method
00048 from psddl.Namespace import Namespace
00049 from psddl.Package import Package
00050 from psddl.Type import Type
00051 from psddl.H5Type import H5Type
00052 from psddl.H5Dataset import H5Dataset
00053 from psddl.H5Attribute import H5Attribute
00054 from psddl.HddlYacc import HddlYacc
00055 from psddl.HddlYacc import QID
00056 
00057 #----------------------------------
00058 # Local non-exported definitions --
00059 #----------------------------------
00060 
00061 # list of integer types    
00062 _intTypes = ["int8_t", "uint8_t",  "int16_t", "uint16_t", 
00063              "int32_t", "uint32_t", "int64_t", "uint64_t",
00064              ]
00065 
00066 class _error(SyntaxError):
00067     def __init__(self, filename, lineno, message):
00068         msg = "{0}:{1}: {2}".format(filename, lineno, message)
00069         SyntaxError.__init__(self, msg)
00070 #         self.filename = filename
00071 #         self.lineno = lineno
00072 
00073 # mapping of the HDDL tag names to internal tag names
00074 _tagmap = {
00075     'no_sizeof':    'no-sizeof',
00076     'cpp_name':     'c++-name',
00077     'value_type':   'value-type',
00078     'config_type':  'config-type',
00079 }
00080 
00081 def _tags(decl):
00082     ''' 
00083     Makes dictionary of tags from declaration. Filters out 'doc' tags.
00084     If tag has more than one argument then tag value will be a tuple of 
00085     arguments, otherwise it will be a value of first argument (or None)
00086     '''
00087     tags = {}
00088     for tag in decl['tags']:
00089         name = tag['name']
00090         name = _tagmap.get(name, name)
00091         args = tag['args']
00092         if name == 'doc': continue
00093         if args is None: 
00094             tags[name] = None
00095         elif len(args) == 1:
00096             tags[name] = args[0]
00097         else:
00098             tags[name] = args
00099     return tags
00100 
00101 
00102 def _tagval(decl, tagname, default = None):
00103     ''' 
00104     Get tag values for a declaration, returns a list or None.
00105     If tag appears multiple times or has multiple arguments they are all
00106     merged into a single list. Tags without arguments are skipped.
00107     
00108     [[tag(a), tag(b)]] [[tag]] -> [a, b]
00109     [[tag]] -> []
00110     [[tag(a, b), tag(c)]] -> [a, b, c]
00111     '''
00112     values = None
00113     for tag in decl['tags']:
00114         if tag['name'] == tagname:
00115             if tag['args'] is not None:
00116                 if values is None: values = []
00117                 values += tag['args']
00118     if values is None: values = default
00119     return values
00120 
00121 
00122 def _doc(decl):
00123     ''' 
00124     extract doc string from a declaration, this is done by merging all 'doc' tags together.
00125     '''
00126     return '\n'.join(_tagval(decl, 'doc', []))
00127 
00128 def _constExprToString(decl):
00129     ''' convert parsed constant expression into string representation '''
00130     op = decl['op']     # opname, string
00131     lhs = decl['left']  # None or dict
00132     rhs = decl['right'] # dict or IDENTIFIER or QID
00133     if op is None:
00134         # number or identifier
00135         return str(rhs)
00136     elif lhs is None:
00137         if op == '(':
00138             return '(' + _constExprToString(rhs) + ')'
00139         else:
00140             # unary op
00141             return op + _constExprToString(rhs)
00142     else:
00143         # binary op
00144         if op == 'LSHIFT': 
00145             op = '<<'
00146         elif op == 'RSHIFT': 
00147             op = '>>'
00148         return _constExprToString(lhs) + op + _constExprToString(rhs)
00149 
00150 def _cmpFiles(f1, f2):
00151     """Brute force file compare"""
00152     c1 = file(f1).read()
00153     c2 = file(f2).read()
00154     return c1 == c2
00155 
00156 def _lineno(decl):
00157     ''' Return line number for a declaration '''
00158     return decl['pos'][0][0]
00159 
00160 def _access(decl):
00161     ''' Return line number for a declaration '''
00162     for tag in decl['tags']:
00163         tagname = tag['name']
00164         if tagname in ['public', 'protected', 'private']: return tagname
00165     return 'public'
00166 
00167 def _hasDevelTag(decl):
00168     for tag in decl['tags']:
00169         if tag['name'] == 'devel': 
00170             return True
00171     return False
00172 
00173 #------------------------
00174 # Exported definitions --
00175 #------------------------
00176 
00177 #---------------------
00178 #  Class definition --
00179 #---------------------
00180 class HddlReader ( object ) :
00181 
00182     #----------------
00183     #  Constructor --
00184     #----------------
00185     def __init__ ( self, ddlfiles, inc_dir, parseDevel=False ) :
00186         self.files = ddlfiles
00187         self.inc_dir = inc_dir
00188         self.parseDevelTypes = parseDevel
00189 
00190         # list of all files already processed (or being processed)
00191         # each item is a tuple (name, data)
00192         self.processed = []
00193         
00194         # stack (LIFO) of file names currently in processing
00195         self.location = []  
00196 
00197         # list of devel types encountered
00198         self.develTypes = []
00199     
00200     #-------------------
00201     #  Public methods --
00202     #-------------------
00203 
00204     def _isDevelType(self, typename, pkg):
00205         for develType in self.develTypes:
00206             if develType['typeName']==typename and pkg.fullName()==develType['pkgName']:
00207                 return True
00208         return False
00209 
00210     def _processed(self, file):
00211         ''' 
00212         Check if file is already processed, just compare file data 
00213         byte-by-byte with the data from files already processed 
00214         '''
00215         data = open(file).read()
00216         for f, d in self.processed:
00217             if data == d:
00218                 return True
00219         return False
00220 
00221     def read( self ) :
00222         '''
00223         Read all files and build a model
00224         '''
00225 
00226         # model is the global namespace
00227         model = Package('')
00228         self._initTypes(model)
00229 
00230         for file in self.files:
00231             if self._processed(file): continue 
00232             logging.debug("HddlReader.read: opening file %s", file)
00233             self._readFile( file, model, False )
00234 
00235         # return the model
00236         return model
00237 
00238 
00239     def _readFile( self, file, model, included ) :
00240         """Read one file and parse its contents.
00241         
00242           @param file     name of the file to read
00243           @param model    model instance (global namespace)
00244           @param included  if true then we are processing included file
00245         """
00246 
00247         # opne file and read its data
00248         data = open(file).read()
00249 
00250         # remember current file name 
00251         self.location.append(file)
00252         self.processed.append((file, data))
00253 
00254         parser = HddlYacc(debug=0)
00255         
00256         try:
00257 
00258             # parse data, build tree
00259             tree = parser.parse(data, file)
00260     
00261             # parse all includes first
00262             for include in tree['includes']:
00263                 self._parseInclude(include, model)
00264     
00265             # top level namespace is a kind of package because it can 
00266             # contain all the same stuff but it does not have a parent
00267             parent = None
00268             self._parsePackage(tree, model, parent, included)
00269                 
00270         except EOFError, ex:
00271             raise
00272         except SyntaxError, ex:
00273             print >>sys.stderr, ex
00274             for loc in reversed(self.location[:-1]):
00275                 print >>sys.stderr, "    included from:", loc
00276             # special type of exception will stop processing
00277             raise EOFError
00278                 
00279 
00280         del self.location[-1]
00281 
00282 
00283     def _parseInclude(self, indict, model):
00284         '''
00285         process include statement
00286         '''
00287 
00288         # check tag names
00289         self._checktags(indict, ['headers'])
00290 
00291         file = indict['name']
00292         headers = _tagval(indict, 'headers', [])
00293 
00294         logging.debug("HddlReader._parseInclude: locating include file %s", file)
00295         path = self._findInclude(file)
00296         if path is None:
00297             msg = "Cannot locate include file '{0}'".format(file)
00298             raise _error(self.location[-1], _lineno(indict), msg)
00299         logging.debug("HddlReader._parseInclude: found file %s", path)
00300 
00301         # if this file was processed already just skip it
00302         if self._processed(path):
00303             logging.debug("HddlReader._parseInclude: file %s was already included", file)
00304             return
00305 
00306         # if the include file is in the list of regular files then process it as regular file
00307         included = True
00308         for f in self.files:
00309             if not self._processed(f) and _cmpFiles(path, f) :
00310                 included = False
00311                 break
00312 
00313         # remember all includes
00314         model.use.append(dict(file=file, cpp_headers=headers))
00315 
00316         # get data from file
00317         logging.debug("HddlReader._parseInclude: reading file %s", path)
00318         self._readFile( path, model, included )
00319 
00320 
00321 
00322     def _parsePackage(self, pkgdict, model, parent, included):
00323         ''' Parse package definition
00324           
00325           @param pkgdict dictionary with package declaration
00326           @param model   full model instance
00327           @param parent  parent package or None when parsing top-level namespace
00328           @param included  true if in included file
00329         '''
00330         
00331         pkgname = pkgdict['name']  # QID instance, not a string
00332 
00333         # check tag names
00334         self._checktags(pkgdict, ['external', 'cpp_name', 'doc'])
00335 
00336         # make package object if it does not exist yet
00337         if parent:
00338             obj = parent.localName(pkgname)
00339             if obj is None:
00340                 pkg = Package(pkgname, parent, 
00341                               comment = _doc(pkgdict),
00342                               tags = _tags(pkgdict))
00343             else:
00344                 # make sure that existing name is a package
00345                 if not isinstance(obj, Package):
00346                     msg = "while defining new package - package '{0}' already contains name '{1}' which is not a package".format(pkg.name, pkgname)
00347                     raise _error(self.location[-1], _lineno(pkgdict), msg)
00348                 pkg = obj
00349         else:
00350             # it's top-level package, we fill model itself
00351             pkg = model
00352 
00353         for decl in pkgdict['declarations']:
00354 
00355             # package children can be types, constants or enums
00356             
00357             if decl['decl'] == 'package' :
00358 
00359                 self._parsePackage(decl, model, pkg, included)
00360 
00361             elif decl['decl'] == 'type' :
00362 
00363                 self._parseType(decl, pkg, included)
00364 
00365             elif decl['decl'] == 'h5schema' :
00366 
00367                 self._parseH5Type(decl, pkg, included)
00368 
00369             elif decl['decl'] == 'const' :
00370 
00371                 self._parseConstant(decl, pkg, included)
00372 
00373             elif decl['decl'] == 'enum' :
00374 
00375                 self._parseEnum(decl, pkg, included)
00376 
00377             else:
00378                 
00379                 msg = "Package '{0}' contains unexpected declaration: {1}".format(pkgname, decl['decl'])
00380                 raise _error(self.location[-1], _lineno(decl), msg)
00381 
00382 
00383     def _parseType(self, typedict, pkg, included):
00384 
00385         # check tag names
00386         self._checktags(typedict, ['value_type', 'devel', 'config_type', 'config', 'pack', 'no_sizeof', 'external', 'type_id', 'cpp_name', 'doc'])
00387         if _hasDevelTag(typedict):
00388             self.develTypes.append({'pkgName':pkg.fullName(), 'typeName':typedict['name']})
00389             if not self.parseDevelTypes: return
00390         # every type must have a name
00391         typename = typedict['name']
00392         
00393         # check the name
00394         if pkg.localName(typename):
00395             msg = "Name '{0}' is already defined in package {1}".format(typename, pkg.name)
00396             raise _error(self.location[-1], _lineno(typedict), msg)
00397 
00398         base = typedict['base']
00399         if base: 
00400             base = pkg.lookup(str(base), Type)
00401             if not base:
00402                 msg = "Failed to resolve name of a base type '{0}'".format(typedict['base'])
00403                 raise _error(self.location[-1], _lineno(typedict), msg)
00404 
00405         # check type_id tag
00406         xtc_type_id, xtc_version = self._getTypeId(typedict)
00407 
00408         # make new type object
00409         type = Type(typename,
00410                     version = xtc_version,
00411                     type_id = xtc_type_id,
00412                     levels = [],
00413                     pack = self._getIntTag(typedict, 'pack'),
00414                     base = base,
00415                     xtcConfig = self._getConfigTypes(typedict, pkg),
00416                     comment = _doc(typedict),
00417                     tags = _tags(typedict),
00418                     package = pkg,
00419                     included = included,
00420                     location = self.location[-1] )
00421 
00422         # first parse enums and constants
00423         for decl in typedict['declarations']:
00424 
00425             if decl['decl'] == 'const':
00426                 
00427                 self._parseConstant(decl, type, included)
00428                 
00429             elif decl['decl'] == 'enum':
00430                 
00431                 self._parseEnum(decl, type, included)
00432                 
00433 
00434         # next do members and methods as they may depend on other types (enums)
00435         for decl in typedict['declarations']:
00436             
00437             if decl['decl'] == 'member' :
00438                 
00439                 self._parseAttr(decl, type)
00440 
00441             elif decl['decl'] == 'method' :
00442                 
00443                 self._parseMeth(decl, type)
00444 
00445         # constructors need to be parsed last as they may depend on other types (methods and enums)
00446         for decl in typedict['declarations']:
00447             
00448             if decl['decl'] == 'ctor':
00449                 
00450                 self._parseCtor(decl, type)
00451 
00452         # calculate offsets for the data members
00453         type.calcOffsets()
00454     
00455 
00456     def _parseH5Type(self, schemadict, pkg, included):
00457         """Method which parses definition of h5schema"""
00458 
00459         # check tag names
00460         self._checktags(schemadict, ['version', 'embedded', 'default', 'external', 'doc'])
00461 
00462         # every type must have a name
00463         schemaname = schemadict['name']
00464 
00465         # find corresponding pstype
00466         pstype = pkg.lookup(schemaname, Type)
00467         if not pstype: 
00468             msg = "Failed to lookup name of a type '{0}', check that type exist and include its definition.".format(schemaname)
00469             raise _error(self.location[-1], _lineno(schemadict), msg)
00470 
00471         version = self._getIntTag(schemadict, 'version')
00472         if version is None: version = 0
00473 
00474         # make new type object
00475         type = H5Type(schemaname,
00476                       package = pkg,
00477                       pstype = pstype,
00478                       version = version,
00479                       tags = _tags(schemadict),
00480                       included = included,
00481                       location = self.location[-1] )
00482         pstype.h5schemas.append(type)
00483 
00484         # loop over sub-elements
00485         for decl in schemadict['declarations']:
00486 
00487             if decl['decl'] == "h5ds" :
00488                 
00489                 self._parseH5Dataset(decl, type, pstype)
00490 
00491             elif decl['decl'] == 'enum_remap' :
00492 
00493                 self._parseH5EnumMap(decl, type, pstype)
00494 
00495                 
00496     def _parseH5EnumMap(self, edecl, h5type, pstype):
00497         '''Method which parses enum remapping'''
00498         
00499         # check tag names
00500         self._checktags(edecl, ['doc'])
00501 
00502         enum_name = edecl['name']
00503         
00504         # find enum type
00505         enum_type = pstype.lookup(enum_name, Enum)
00506         if not enum_type: 
00507             msg = "Failed to resolve name of enum type '{0}'".format(enum_name)
00508             raise _error(self.location[-1], _lineno(edecl), msg)
00509 
00510         # constants defined in psana type        
00511         constants = enum_type.constants()
00512         
00513         for remap in edecl['remaps']:
00514             
00515             psname = remap['from']
00516             h5name = remap['to']
00517 
00518             # check that name is defined
00519             match = [c for c in constants if c.name == psname] + [None]
00520             match = match[0]
00521             if match is None: 
00522                 msg = "Failed to resolve enum constant name '{0}'".format(psname)
00523                 raise _error(self.location[-1], _lineno(remap), msg)
00524 
00525             h5type.enum_map.setdefault(enum_name, {})[psname] = h5name
00526         
00527     def _parseH5Dataset(self, dsdecl, h5type, pstype):
00528         """Method which parses definition of h5 dataset"""
00529 
00530         # check tag names
00531         self._checktags(dsdecl, ['method', 'vlen', 'external', 'zero_dims', 'doc', 'method_domain'])
00532 
00533         dsname = dsdecl['name']
00534 
00535         logging.debug("HddlReader._parseH5Dataset: new dataset: %s", dsname)
00536 
00537         dstype = dsdecl['type']
00538         if dstype:
00539             dstype = pstype.lookup(dstype, (Type, Enum))
00540             if not dstype: 
00541                 msg = "Failed to resolve dataset type name '{0}'".format(dsdecl['type'])
00542                 raise _error(self.location[-1], _lineno(dsdecl), msg)
00543 
00544         # it may specify method name via tags, argument may be QID, we need string
00545         method = _tagval(dsdecl, 'method', [None])[0]
00546         if method: method = str(method)
00547             
00548         # shape can be specified as a rank only (number or None)
00549         rank = dsdecl['shape']
00550         if rank is None: rank = -1
00551 
00552         domain_for_method = None
00553         if not method:
00554             method_domain = _tagval(dsdecl, 'method_domain', None)
00555             if method_domain:
00556                 assert len(method_domain)==2, "ERROR: method_domain tag requires 2 args (method, len)"
00557                 method = str(method_domain[0])
00558                 assert rank is -1, "ERROR: processing method_domain tag, but rank is not -1? unexpected"
00559                 rank = 1
00560                 domain_for_method = str(method_domain[1])
00561                 assert domain_for_method, "ERROR: 2nd argument for method_domain is empty"
00562 
00563         # optional schema version in tags
00564         schema_version = self._getIntTag(dsdecl, 'schema_version', 0)
00565 
00566         # make dataset
00567         ds = H5Dataset(name = dsname, 
00568                        parent = h5type, 
00569                        pstype = pstype,
00570                        type = dstype,
00571                        method = method,
00572                        rank = rank,
00573                        domain_for_method = domain_for_method,
00574                        schema_version = schema_version,
00575                        tags = _tags(dsdecl))
00576         h5type.datasets.append(ds)
00577 
00578         # loop over sub-elements
00579         if dsdecl['attributes'] is not None:
00580             for adecl in dsdecl['attributes']:
00581                 self._parseH5Attribute(adecl, ds, pstype)
00582 
00583     def _parseH5Attribute(self, adecl, ds, pstype):
00584         """Method which parses definition of h5 attribute"""
00585 
00586         # check tag names
00587         self._checktags(adecl, ['external', 'method', 'vlen', 'doc'])
00588 
00589         # attribute must have a name
00590         aname = adecl['name']
00591 
00592         logging.debug("HddlReader._parseH5Attribute: new attribute: %s", aname)
00593 
00594         atype = adecl['type']
00595         if atype:
00596             atype = pstype.lookup(str(atype), (Type, Enum))
00597             if not atype: 
00598                 msg = "Failed to resolve attribute type name '{0}'".format(adecl['type'])
00599                 raise _error(self.location[-1], _lineno(adecl), msg)
00600 
00601         # it may specify method name via tags, argument may be QID, we need string
00602         method = _tagval(adecl, 'method', [None])[0]
00603         if method: method = str(method)
00604 
00605         # shape can be specified as a rank only or as list of dimensions expressions (strings for now)
00606         rank = -1
00607         shape = None
00608         shapedecl = adecl['shape']
00609         if shapedecl is None:
00610             # nothing
00611             pass
00612         elif isinstance(shapedecl, int):
00613             # just a rank
00614             rank = shapedecl
00615         elif isinstance(shapedecl, list):
00616             # list of dimension expressions (strings)
00617             shape = ','.join(shapedecl)
00618             rank = len(shapedecl)
00619         else:
00620             msg = "Unexpected shape or rank declaration for attribute '{0}'".format(aname)
00621             raise _error(self.location[-1], _lineno(adecl), msg)
00622 
00623         # optional schema version in tags
00624         schema_version = self._getIntTag(adecl, 'schema_version', 0)
00625 
00626         # make attribute
00627         attr = H5Attribute(name = aname, 
00628                            parent = ds,
00629                            type = atype,
00630                            method = method or aname,
00631                            rank = rank,
00632                            shape = shape,
00633                            schema_version = schema_version,
00634                            tags = _tags(adecl))
00635         ds.attributes.append(attr)
00636 
00637 
00638 
00639     def _parseAttr(self, adecl, type):
00640         ''' Parse defintion of a single data member '''
00641         
00642         # check tag names
00643         self._checktags(adecl, ['public', 'private', 'protected', 'shape_method', 'doc'])
00644 
00645         # every attribute must have a name
00646         attrname = adecl['name']
00647         
00648         # find type object
00649         atypename = str(adecl['type'])
00650         atype = type.lookup(atypename, (Type, Enum))
00651         if not atype: 
00652             msg = "Failed to resolve member type name '{0}'".format(atypename)
00653             raise _error(self.location[-1], _lineno(adecl), msg)
00654 
00655         # shape can be specified as a rank only or as list of dimensions expressions (strings for now)
00656         shape = None
00657         if adecl['shape'] is not None:
00658             shape = ','.join(adecl['shape'])
00659 
00660         # it may specify shape_method via tags, argument may be QID, we need string
00661         shape_method = _tagval(adecl, 'shape_method', [None])[0]
00662         if shape_method: shape_method = str(shape_method)
00663 
00664         accessor = adecl['method']
00665 
00666         # create attribute
00667         attr = Attribute( attrname,
00668                           type = atype,
00669                           parent = type, 
00670                           shape = shape, 
00671                           comment = _doc(adecl), 
00672                           tags = _tags(adecl),
00673                           shape_method = shape_method,
00674                           accessor_name = accessor )
00675         logging.debug("HddlReader._parseAttr: new attribute: %s", attr)
00676 
00677         # accessor method for it
00678         if accessor :
00679             rank = 0
00680             if adecl['shape']: rank = len(adecl['shape'])
00681             method = Method(accessor, 
00682                             attribute = attr, 
00683                             parent = type, 
00684                             type = atype,
00685                             rank = rank,
00686                             access = _access(adecl),
00687                             comment = attr.comment)
00688             attr.accessor = method
00689             logging.debug("HddlReader._parseAttr: new method: %s", method)
00690 
00691         # get bitfields
00692         bfoff = 0
00693         for bfdecl in (adecl['bitfields'] or []):
00694         
00695             # check tag names
00696             self._checktags(bfdecl, ['public', 'private', 'protected', 'doc'])
00697 
00698             size = bfdecl['size']
00699             bftypename = str(bfdecl['type'])
00700             bftype = type.lookup(bftypename, (Type, Enum))
00701             if not bftype: 
00702                 msg = "Failed to resolve bitfield type name '{0}'".format(bftypename)
00703                 raise _error(self.location[-1], _lineno(bfdecl), msg)
00704 
00705             bf = Bitfield(bfdecl['name'], 
00706                           offset = bfoff, 
00707                           size = size,
00708                           parent = attr,
00709                           type = bftype,
00710                           comment = _doc(bfdecl))
00711             logging.debug("HddlReader._parseAttr: new bitfield: %s", bf)
00712             bfoff += size
00713             
00714             accessor = bfdecl['method']
00715             if accessor :
00716                 method = Method(accessor, 
00717                                 bitfield = bf, 
00718                                 parent = type, 
00719                                 type = bftype,
00720                                 access = _access(bfdecl),
00721                                 comment = bf.comment)
00722                 bf.accessor = method
00723                 logging.debug("HddlReader._parseAttr: new method: %s", method)
00724 
00725     def _parseCtor(self, ctordecl, parent):
00726 
00727         # check tag names
00728         self._checktags(ctordecl, ['auto', 'inline', 'force_definition', 'external', 'doc'])
00729 
00730     
00731         # can specify initialization values for some attributes
00732         attr_init = []
00733         init2dest = {}
00734         for initdecl in ctordecl['inits']:
00735 
00736             # resolve destination, must be a member or bitfield
00737             destname = initdecl['dest']
00738             dest = [d for d in parent.attributes_and_bitfields() if d.name == destname]
00739             if not dest: 
00740                 msg = "Failed to resolve destination name '{0}' in constructor initializers list.".format(destname)
00741                 raise _error(self.location[-1], _lineno(ctordecl), msg)
00742             dest = dest[0]
00743 
00744             # convert expression back to string form
00745             expr = _constExprToString(initdecl['expr'])
00746             
00747             attr_init.append(CtorInit(dest, expr))
00748             
00749             # remember it, may be needed later
00750             init2dest[expr] = dest
00751 
00752 
00753         # collect arguments
00754         args = []
00755         for argdecl in ctordecl['args']:
00756 
00757             # check tag names
00758             self._checktags(argdecl, ['method', 'doc'])
00759 
00760             # name and optional type
00761             argname = argdecl['name']
00762             atype = None
00763             if argdecl['type']:
00764                 atype = parent.lookup(str(argdecl['type']), (Type, Enum))
00765                 if not atype: 
00766                     msg = "Failed to resolve argument type name '{0}' for constructor argument '{1}'".format(argdecl['type'], argname)
00767                     raise _error(self.location[-1], _lineno(ctordecl), msg)
00768             
00769             # destination can be given
00770             dest = None
00771             if argdecl['dest']:
00772                 dest = [d for d in parent.attributes_and_bitfields() if d.name == argdecl['dest']]
00773                 if not dest: 
00774                     msg = "Failed to resolve destination name '{0}' for constructor argument '{1}'".format(argdecl['dest'], argname)
00775                     raise _error(self.location[-1], _lineno(ctordecl), msg)
00776                 dest = dest[0]
00777             if not dest:
00778                 # destination can also be given in initializer list, try to find init expression 
00779                 # which is just the name of the argument
00780                 dest = init2dest.get(argname)
00781 
00782             # shape can be specified as a rank only (number or None)
00783             rank = argdecl['rank']
00784             if rank is None: rank = -1
00785             
00786             # it may specify method via tags
00787             meth = _tagval(argdecl, 'method', [None])[0]
00788             if meth:
00789                 meth = parent.lookup(str(meth), Method)
00790                 if not meth: 
00791                     msg = "Failed to resolve method name '{0}' for constructor argument '{1}'".format(meth, argname)
00792                     raise _error(self.location[-1], _lineno(ctordecl), msg)
00793             elif dest:
00794                 meth = dest.accessor
00795             
00796 #             if not meth: 
00797 #                 msg = "Constructor argument requires to have either a method name ([[method()]]) or destination with accessor method, for constructor argument '{0}'".format(argname)
00798 #                 raise _error(self.location[-1], _lineno(ctordecl), msg)
00799 
00800             # rank is not used yet
00801             args.append(CtorArg(argname, dest, atype, meth, None))
00802 
00803 
00804         ctor = Constructor(parent, 
00805                            args = args,
00806                            attr_init = attr_init,
00807                            tags = _tags(ctordecl),
00808                            comment = _doc(ctordecl))
00809         logging.debug("HddlReader._parseCtor: new constructor: %s", ctor)
00810 
00811                         
00812     def _parseMeth(self, methdecl, type):
00813     
00814         # check tag names
00815         self._checktags(methdecl, ['inline', 'external', 'language', 'doc'])
00816 
00817         # every method must have a name
00818         name = methdecl['name']
00819         
00820         # find type object
00821         mtype = None
00822         typename = str(methdecl['type'])
00823         if typename != 'void':
00824             mtype = type.lookup(typename, (Type, Enum))
00825             if not mtype:
00826                 msg = "Failed to resolve method return type '{0}'".format(typename)
00827                 raise _error(self.location[-1], _lineno(methdecl), msg)
00828 
00829         args = []
00830         for argdecl in methdecl['args']:
00831             argname = argdecl['name']
00832             typename = str(argdecl['type'])
00833             atype = type.lookup(typename, (Type, Enum))
00834             if not atype: 
00835                 msg = "Failed to resolve argument type name '{0}' for method argument '{1}'".format(typename, argname)
00836                 raise _error(self.location[-1], _lineno(methdecl), msg)
00837             arank = argdecl['rank']
00838             args.append((argname, atype))
00839 
00840         codes = {}
00841         if methdecl['bodies']:
00842             for i, codeblock in enumerate(methdecl['bodies']):
00843 
00844                 # check tag names
00845                 self._checktags(codeblock, ['language', 'doc'])
00846                 
00847                 lang = _tagval(codeblock, 'language')
00848                 if lang is None and i == 0:
00849                     # for first code block tags are merged with method tags
00850                     lang = _tagval(methdecl, 'language')
00851                 if lang: lang = lang[0]
00852                 if not lang: lang = 'Any'
00853                 codes[lang] = codeblock['code']
00854 
00855         method = Method(name, 
00856                         parent = type, 
00857                         type = mtype,
00858                         rank = methdecl['rank'],
00859                         args = args,
00860                         expr = {},
00861                         code = codes,
00862                         tags = _tags(methdecl),
00863                         comment = _doc(methdecl))
00864         logging.debug("HddlReader._parseMeth: new method: %s", method)
00865 
00866                         
00867     def _parseConstant(self, decl, parent, included):
00868         ''' Parse constant declaration '''
00869 
00870         # check tag names
00871         self._checktags(decl, ['doc'])
00872 
00873         cname = decl['name']  # IDENTIFIER
00874         ctype = decl['type']  # IDENTIFIER
00875         cval = decl['value']  # const_expr
00876         
00877         # check type
00878         if ctype not in _intTypes:
00879             msg = "Constant has unexpected type '{0}', only integer types are supported now.".format(ctype)
00880             raise _error(self.location[-1], _lineno(decl), msg)
00881         
00882         # convert it back to string
00883         cval = decl['value_str']
00884         Constant(cname, cval, parent, included=included, comment = _doc(decl))
00885             
00886     def _parseEnum(self, decl, parent, included):
00887         ''' Parse enum declaration '''
00888         
00889         # check tag names
00890         self._checktags(decl, ['doc'])
00891 
00892         enum = Enum(decl['name'],
00893                     parent, 
00894                     base=decl.get('type', 'int32_t'), 
00895                     included=included, 
00896                     comment = _doc(decl))
00897 
00898         for cdecl in decl['constants']:
00899             Constant(cdecl['name'], cdecl['value_str'], enum, comment = _doc(cdecl))
00900 
00901     def _findInclude(self, inc):
00902         
00903         # look in every directory in include path
00904         for dir in self.inc_dir:            
00905             path = os.path.join(dir, inc)
00906             if  os.path.isfile(path):
00907                 return path
00908         
00909         # Not found
00910         return None
00911 
00912     def _initTypes(self, ns):
00913         """ Define few basic types in global namespace """
00914         
00915         tags = {'basic': 1, 'external': 2, 'value-type': 3}
00916         Type("char", size=1, align=1, package=ns, tags=tags)
00917         Type("int8_t", size=1, align=1, package=ns, tags=tags)
00918         Type("uint8_t", size=1, align=1, package=ns, tags=tags)
00919         Type("int16_t", size=2, align=2, package=ns, tags=tags)
00920         Type("uint16_t", size=2, align=2, package=ns, tags=tags)
00921         Type("int32_t", size=4, align=4, package=ns, tags=tags)
00922         Type("uint32_t", size=4, align=4, package=ns, tags=tags)
00923         Type("int64_t", size=8, align=8, package=ns, tags=tags)
00924         Type("uint64_t", size=8, align=8, package=ns, tags=tags)
00925         Type("float", size=4, align=4, package=ns, tags=tags)
00926         Type("double", size=8, align=8, package=ns, tags=tags)
00927 
00928         tags = {'basic': 1, 'external': 2, 'c++-name': 'const char*'}
00929         Type("string", size=0, align=0, package=ns, tags=tags)
00930 
00931     def _getTypeId(self, typedict):
00932         ''' Get type_id and version number from tags '''
00933 
00934         tags = [tag for tag in typedict['tags'] if tag['name'] == 'type_id']
00935         if len(tags) > 1:
00936             msg = "More than one type_id tags defined for type '{0}'".format(typedict['name'])
00937             raise _error(self.location[-1], _lineno(typedict), msg)
00938         if tags:
00939             tag = tags[0]
00940             args = tag['args']
00941             if args is None or len(args) != 2:
00942                 msg = "type_id tag requires exactly two arguments"
00943                 raise _error(self.location[-1], _lineno(tag), msg)
00944             
00945             if not isinstance(args[0], QID):
00946                 msg = "first argument to type_id() must be an identifier"
00947                 raise _error(self.location[-1], _lineno(tag), msg)
00948             if not isinstance(args[1], int):
00949                 msg = "second argument to type_id() must be a number"
00950                 raise _error(self.location[-1], _lineno(tag), msg)
00951             
00952             return str(args[0]), args[1]
00953             
00954         return None, None
00955 
00956      
00957     def _getIntTag(self, decl, tagname, default = None):
00958         ''' Get integer value from tags '''
00959 
00960         tags = [tag for tag in decl['tags'] if tag['name'] == tagname]
00961         if len(tags) > 1:
00962             msg = "More than one '{0}' tags defined in declaration of '{1}'".format(tagname, decl['name'])
00963             raise _error(self.location[-1], _lineno(decl), msg)
00964         if tags:
00965             tag = tags[0]
00966             args = tag['args']
00967             if args is None or len(args) != 1:
00968                 msg = "{0}() tag requires exactly one argument".format(tagname)
00969                 raise _error(self.location[-1], _lineno(tag), msg)
00970             
00971             if not isinstance(args[0], int):
00972                 msg = "argument to {0}() must be a number".format(tagname)
00973                 raise _error(self.location[-1], _lineno(tag), msg)
00974 
00975             return args[0]
00976             
00977         return default
00978 
00979 
00980     def _checktags(self, decl, allowed):
00981         ''' 
00982         check all tags against the list of allowed tags
00983         '''
00984         for tag in decl['tags']:
00985             if tag['name'] not in allowed:
00986                 msg = "Unexpected tag name: {0}".format(tag['name'])
00987                 raise _error(self.location[-1], _lineno(tag), msg)
00988 
00989     
00990     def _getConfigTypes(self, typedict, pkg):
00991         ''' Get values of config() tags as a list of config type objects '''
00992 
00993         cfgtypes = []
00994         tags = [tag for tag in typedict['tags'] if tag['name'] == 'config']
00995         for tag in tags:
00996             args = tag['args']
00997             if not args:
00998                 msg = "config() tag requires one or more arguments"
00999                 raise _error(self.location[-1], _lineno(tag), msg)
01000             for cfg in args:
01001                 if not isinstance(cfg, QID):
01002                     msg = "arguments to config() tag must be qualified identifiers"
01003                     raise _error(self.location[-1], _lineno(tag), msg)
01004                 cfgtype = pkg.lookup(str(cfg), Type)
01005                 if not cfgtype:
01006                     if not self.parseDevelTypes and self._isDevelType(str(cfg), pkg):
01007                         print >>sys.stderr, "Warning: %s type=%s, DEVEL type=%s in config list is being omitted" % (pkg, typedict['name'], cfg)
01008                         continue
01009                     msg = "Failed to resolve name of a config type '{0}'".format(cfg)
01010                     raise _error(self.location[-1], _lineno(tag), msg)
01011                 cfgtypes.append(cfgtype)
01012         return cfgtypes
01013      
01014      
01015 #
01016 #  In case someone decides to run this module
01017 #
01018 if __name__ == "__main__" :
01019 
01020     # In principle we can try to run test suite for this module,
01021     # have to think about it later. Right now just abort.
01022     sys.exit ( "Module is not supposed to be run as main module" )
01023 

Generated on 19 Dec 2016 for PSDMSoftware by  doxygen 1.4.7