00001
00002
00003
00004
00005
00006
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
00023
00024 __version__ = "$Revision: 11679 $"
00025
00026
00027
00028
00029 import sys
00030 import os
00031 import os.path
00032 import logging
00033
00034
00035
00036
00037
00038
00039
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
00059
00060
00061
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
00071
00072
00073
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']
00131 lhs = decl['left']
00132 rhs = decl['right']
00133 if op is None:
00134
00135 return str(rhs)
00136 elif lhs is None:
00137 if op == '(':
00138 return '(' + _constExprToString(rhs) + ')'
00139 else:
00140
00141 return op + _constExprToString(rhs)
00142 else:
00143
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
00175
00176
00177
00178
00179
00180 class HddlReader ( object ) :
00181
00182
00183
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
00191
00192 self.processed = []
00193
00194
00195 self.location = []
00196
00197
00198 self.develTypes = []
00199
00200
00201
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
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
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
00248 data = open(file).read()
00249
00250
00251 self.location.append(file)
00252 self.processed.append((file, data))
00253
00254 parser = HddlYacc(debug=0)
00255
00256 try:
00257
00258
00259 tree = parser.parse(data, file)
00260
00261
00262 for include in tree['includes']:
00263 self._parseInclude(include, model)
00264
00265
00266
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
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
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
00302 if self._processed(path):
00303 logging.debug("HddlReader._parseInclude: file %s was already included", file)
00304 return
00305
00306
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
00314 model.use.append(dict(file=file, cpp_headers=headers))
00315
00316
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']
00332
00333
00334 self._checktags(pkgdict, ['external', 'cpp_name', 'doc'])
00335
00336
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
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
00351 pkg = model
00352
00353 for decl in pkgdict['declarations']:
00354
00355
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
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
00391 typename = typedict['name']
00392
00393
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
00406 xtc_type_id, xtc_version = self._getTypeId(typedict)
00407
00408
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
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
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
00446 for decl in typedict['declarations']:
00447
00448 if decl['decl'] == 'ctor':
00449
00450 self._parseCtor(decl, type)
00451
00452
00453 type.calcOffsets()
00454
00455
00456 def _parseH5Type(self, schemadict, pkg, included):
00457 """Method which parses definition of h5schema"""
00458
00459
00460 self._checktags(schemadict, ['version', 'embedded', 'default', 'external', 'doc'])
00461
00462
00463 schemaname = schemadict['name']
00464
00465
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
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
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
00500 self._checktags(edecl, ['doc'])
00501
00502 enum_name = edecl['name']
00503
00504
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
00511 constants = enum_type.constants()
00512
00513 for remap in edecl['remaps']:
00514
00515 psname = remap['from']
00516 h5name = remap['to']
00517
00518
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
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
00545 method = _tagval(dsdecl, 'method', [None])[0]
00546 if method: method = str(method)
00547
00548
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
00564 schema_version = self._getIntTag(dsdecl, 'schema_version', 0)
00565
00566
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
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
00587 self._checktags(adecl, ['external', 'method', 'vlen', 'doc'])
00588
00589
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
00602 method = _tagval(adecl, 'method', [None])[0]
00603 if method: method = str(method)
00604
00605
00606 rank = -1
00607 shape = None
00608 shapedecl = adecl['shape']
00609 if shapedecl is None:
00610
00611 pass
00612 elif isinstance(shapedecl, int):
00613
00614 rank = shapedecl
00615 elif isinstance(shapedecl, list):
00616
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
00624 schema_version = self._getIntTag(adecl, 'schema_version', 0)
00625
00626
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
00643 self._checktags(adecl, ['public', 'private', 'protected', 'shape_method', 'doc'])
00644
00645
00646 attrname = adecl['name']
00647
00648
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
00656 shape = None
00657 if adecl['shape'] is not None:
00658 shape = ','.join(adecl['shape'])
00659
00660
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
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
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
00692 bfoff = 0
00693 for bfdecl in (adecl['bitfields'] or []):
00694
00695
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
00728 self._checktags(ctordecl, ['auto', 'inline', 'force_definition', 'external', 'doc'])
00729
00730
00731
00732 attr_init = []
00733 init2dest = {}
00734 for initdecl in ctordecl['inits']:
00735
00736
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
00745 expr = _constExprToString(initdecl['expr'])
00746
00747 attr_init.append(CtorInit(dest, expr))
00748
00749
00750 init2dest[expr] = dest
00751
00752
00753
00754 args = []
00755 for argdecl in ctordecl['args']:
00756
00757
00758 self._checktags(argdecl, ['method', 'doc'])
00759
00760
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
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
00779
00780 dest = init2dest.get(argname)
00781
00782
00783 rank = argdecl['rank']
00784 if rank is None: rank = -1
00785
00786
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
00797
00798
00799
00800
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
00815 self._checktags(methdecl, ['inline', 'external', 'language', 'doc'])
00816
00817
00818 name = methdecl['name']
00819
00820
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
00845 self._checktags(codeblock, ['language', 'doc'])
00846
00847 lang = _tagval(codeblock, 'language')
00848 if lang is None and i == 0:
00849
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
00871 self._checktags(decl, ['doc'])
00872
00873 cname = decl['name']
00874 ctype = decl['type']
00875 cval = decl['value']
00876
00877
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
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
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
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
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
01017
01018 if __name__ == "__main__" :
01019
01020
01021
01022 sys.exit ( "Module is not supposed to be run as main module" )
01023