from collections import namedtuple from ..common.info import ID, UNKNOWN from ..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) # XXX Variable.vartype -> decl (Declaration). class Variable(_NTBase, namedtuple('Variable', 'id storage vartype')): """Information about a single variable declaration.""" __slots__ = () STORAGE = ( 'static', 'extern', 'implicit', 'local', ) @classonly def from_parts(cls, filename, funcname, name, decl, storage=None): varid = ID(filename, funcname, name) if storage is None: self = cls.from_id(varid, decl) else: self = cls(varid, storage, decl) return self @classonly def from_id(cls, varid, decl): from ..parser.declarations import extract_storage storage = extract_storage(decl, infunc=varid.funcname) return cls(varid, storage, decl) def __new__(cls, id, storage, vartype): self = super().__new__( cls, id=ID.from_raw(id), storage=str(storage) if storage else None, 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 == UNKNOWN: raise TypeError(f'id missing filename ({self.id})') if self.funcname and self.funcname == 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() if self.storage is None or self.storage == UNKNOWN: raise TypeError('missing storage') elif self.storage not in self.STORAGE: raise ValueError(f'unsupported storage {self.storage:r}') if self.vartype is None or self.vartype == UNKNOWN: raise TypeError('missing vartype') @property def isglobal(self): return self.storage != 'local' @property def isconst(self): return 'const' in self.vartype.split()