2019-09-11 19:49:45 +01:00
|
|
|
from collections import namedtuple
|
2019-09-27 15:53:34 +01:00
|
|
|
import re
|
2019-09-11 19:49:45 +01:00
|
|
|
|
|
|
|
|
from c_analyzer_common import info, util
|
|
|
|
|
from c_analyzer_common.util import classonly, _NTBase
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_vartype(vartype):
|
|
|
|
|
"""Return the canonical form for a variable type (or func signature)."""
|
|
|
|
|
# We allow empty strring through for semantic reasons.
|
|
|
|
|
if vartype is None:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# XXX finish!
|
|
|
|
|
# XXX Return (modifiers, type, pointer)?
|
|
|
|
|
return str(vartype)
|
|
|
|
|
|
|
|
|
|
|
2019-09-27 15:53:34 +01:00
|
|
|
def extract_storage(decl, *, isfunc=False):
|
|
|
|
|
"""Return (storage, vartype) based on the given declaration.
|
|
|
|
|
|
|
|
|
|
The default storage is "implicit" or "local".
|
|
|
|
|
"""
|
|
|
|
|
if decl == info.UNKNOWN:
|
|
|
|
|
return decl, decl
|
|
|
|
|
if decl.startswith('static '):
|
|
|
|
|
return 'static', decl
|
|
|
|
|
#return 'static', decl.partition(' ')[2].strip()
|
|
|
|
|
elif decl.startswith('extern '):
|
|
|
|
|
return 'extern', decl
|
|
|
|
|
#return 'extern', decl.partition(' ')[2].strip()
|
|
|
|
|
elif re.match('.*\b(static|extern)\b', decl):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
elif isfunc:
|
|
|
|
|
return 'local', decl
|
|
|
|
|
else:
|
|
|
|
|
return 'implicit', decl
|
|
|
|
|
|
|
|
|
|
|
2019-09-11 19:49:45 +01:00
|
|
|
class Variable(_NTBase,
|
2019-09-27 15:53:34 +01:00
|
|
|
namedtuple('Variable', 'id storage vartype')):
|
2019-09-11 19:49:45 +01:00
|
|
|
"""Information about a single variable declaration."""
|
|
|
|
|
|
|
|
|
|
__slots__ = ()
|
|
|
|
|
|
2019-09-27 15:53:34 +01:00
|
|
|
STORAGE = (
|
|
|
|
|
'static',
|
|
|
|
|
'extern',
|
|
|
|
|
'implicit',
|
|
|
|
|
'local',
|
|
|
|
|
)
|
2019-09-17 17:04:46 +01:00
|
|
|
|
2019-09-11 19:49:45 +01:00
|
|
|
@classonly
|
2019-09-27 15:53:34 +01:00
|
|
|
def from_parts(cls, filename, funcname, name, decl, storage=None):
|
|
|
|
|
if storage is None:
|
|
|
|
|
storage, decl = extract_storage(decl, isfunc=funcname)
|
2019-09-11 19:49:45 +01:00
|
|
|
id = info.ID(filename, funcname, name)
|
2019-09-27 15:53:34 +01:00
|
|
|
self = cls(id, storage, decl)
|
2019-09-11 19:49:45 +01:00
|
|
|
return self
|
|
|
|
|
|
2019-09-27 15:53:34 +01:00
|
|
|
def __new__(cls, id, storage, vartype):
|
2019-09-11 19:49:45 +01:00
|
|
|
self = super().__new__(
|
|
|
|
|
cls,
|
|
|
|
|
id=info.ID.from_raw(id),
|
2019-09-27 15:53:34 +01:00
|
|
|
storage=str(storage) if storage else None,
|
2019-09-11 19:49:45 +01:00
|
|
|
vartype=normalize_vartype(vartype) if vartype else None,
|
|
|
|
|
)
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
|
return hash(self.id)
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
|
return getattr(self.id, name)
|
|
|
|
|
|
|
|
|
|
def _validate_id(self):
|
|
|
|
|
if not self.id:
|
|
|
|
|
raise TypeError('missing id')
|
|
|
|
|
|
|
|
|
|
if not self.filename or self.filename == info.UNKNOWN:
|
|
|
|
|
raise TypeError(f'id missing filename ({self.id})')
|
|
|
|
|
|
|
|
|
|
if self.funcname and self.funcname == info.UNKNOWN:
|
|
|
|
|
raise TypeError(f'id missing funcname ({self.id})')
|
|
|
|
|
|
|
|
|
|
self.id.validate()
|
|
|
|
|
|
|
|
|
|
def validate(self):
|
|
|
|
|
"""Fail if the object is invalid (i.e. init with bad data)."""
|
|
|
|
|
self._validate_id()
|
|
|
|
|
|
2019-09-27 15:53:34 +01:00
|
|
|
if self.storage is None or self.storage == info.UNKNOWN:
|
|
|
|
|
raise TypeError('missing storage')
|
|
|
|
|
elif self.storage not in self.STORAGE:
|
|
|
|
|
raise ValueError(f'unsupported storage {self.storage:r}')
|
|
|
|
|
|
2019-09-11 19:49:45 +01:00
|
|
|
if self.vartype is None or self.vartype == info.UNKNOWN:
|
|
|
|
|
raise TypeError('missing vartype')
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def isglobal(self):
|
2019-09-27 15:53:34 +01:00
|
|
|
return self.storage != 'local'
|
2019-09-11 19:49:45 +01:00
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def isconst(self):
|
|
|
|
|
return 'const' in self.vartype.split()
|