11736SN/A# Copyright (c) 2005 The Regents of The University of Michigan
21736SN/A# All rights reserved.
31736SN/A#
41736SN/A# Redistribution and use in source and binary forms, with or without
51736SN/A# modification, are permitted provided that the following conditions are
61736SN/A# met: redistributions of source code must retain the above copyright
71736SN/A# notice, this list of conditions and the following disclaimer;
81736SN/A# redistributions in binary form must reproduce the above copyright
91736SN/A# notice, this list of conditions and the following disclaimer in the
101736SN/A# documentation and/or other materials provided with the distribution;
111736SN/A# neither the name of the copyright holders nor the names of its
121736SN/A# contributors may be used to endorse or promote products derived from
131736SN/A# this software without specific prior written permission.
141736SN/A#
151736SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
161736SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
171736SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
181736SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
191736SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
201736SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
211736SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
221736SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
231736SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
241736SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
251736SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
262665SN/A#
272665SN/A# Authors: Nathan Binkert
281736SN/A
291530SN/A# The SmartDict class fixes a couple of issues with using the content
301530SN/A# of os.environ or similar dicts of strings as Python variables:
311530SN/A#
321530SN/A# 1) Undefined variables should return False rather than raising KeyError.
331530SN/A#
341530SN/A# 2) String values of 'False', '0', etc., should evaluate to False
351530SN/A#    (not just the empty string).
361530SN/A#
371530SN/A# #1 is solved by overriding __getitem__, and #2 is solved by using a
381530SN/A# proxy class for values and overriding __nonzero__ on the proxy.
391530SN/A# Everything else is just to (a) make proxies behave like normal
401530SN/A# values otherwise, (b) make sure any dict operation returns a proxy
411530SN/A# rather than a normal value, and (c) coerce values written to the
421530SN/A# dict to be strings.
431530SN/A
4413714Sandreas.sandberg@arm.comfrom __future__ import print_function
4513714Sandreas.sandberg@arm.comfrom __future__ import absolute_import
4613719Sandreas.sandberg@arm.comimport six
4713719Sandreas.sandberg@arm.comif six.PY3:
4813719Sandreas.sandberg@arm.com    long = int
491530SN/A
5013714Sandreas.sandberg@arm.comfrom .convert import *
5113714Sandreas.sandberg@arm.comfrom .attrdict import attrdict
521519SN/A
531606SN/Aclass Variable(str):
541606SN/A    """Intelligent proxy class for SmartDict.  Variable will use the
551606SN/A    various convert functions to attempt to convert values to useable
561606SN/A    types"""
571606SN/A    def __int__(self):
581606SN/A        return toInteger(str(self))
591606SN/A    def __long__(self):
601606SN/A        return toLong(str(self))
611606SN/A    def __float__(self):
621606SN/A        return toFloat(str(self))
6313697Sandreas.sandberg@arm.com    def __bool__(self):
641606SN/A        return toBool(str(self))
6513697Sandreas.sandberg@arm.com    # Python 2.7 uses __nonzero__ instead of __bool__
6613697Sandreas.sandberg@arm.com    __nonzero__ = __bool__
671606SN/A    def convert(self, other):
681606SN/A        t = type(other)
691606SN/A        if t == bool:
701606SN/A            return bool(self)
711606SN/A        if t == int:
721606SN/A            return int(self)
731606SN/A        if t == long:
741606SN/A            return long(self)
751606SN/A        if t == float:
761606SN/A            return float(self)
771606SN/A        return str(self)
781606SN/A    def __lt__(self, other):
791606SN/A        return self.convert(other) < other
801606SN/A    def __le__(self, other):
811606SN/A        return self.convert(other) <= other
821606SN/A    def __eq__(self, other):
831606SN/A        return self.convert(other) == other
841606SN/A    def __ne__(self, other):
851606SN/A        return self.convert(other) != other
861606SN/A    def __gt__(self, other):
871606SN/A        return self.convert(other) > other
881606SN/A    def __ge__(self, other):
891606SN/A        return self.convert(other) >= other
901606SN/A
911606SN/A    def __add__(self, other):
921606SN/A        return self.convert(other) + other
931606SN/A    def __sub__(self, other):
941606SN/A        return self.convert(other) - other
951606SN/A    def __mul__(self, other):
961606SN/A        return self.convert(other) * other
971606SN/A    def __div__(self, other):
981606SN/A        return self.convert(other) / other
991606SN/A    def __truediv__(self, other):
1001606SN/A        return self.convert(other) / other
1011606SN/A
1021606SN/A    def __radd__(self, other):
1031606SN/A        return other + self.convert(other)
1041606SN/A    def __rsub__(self, other):
1051606SN/A        return other - self.convert(other)
1061606SN/A    def __rmul__(self, other):
1071606SN/A        return other * self.convert(other)
1081606SN/A    def __rdiv__(self, other):
1091606SN/A        return other / self.convert(other)
1101606SN/A    def __rtruediv__(self, other):
1111606SN/A        return other / self.convert(other)
1121606SN/A
1131606SN/Aclass UndefinedVariable(object):
1141606SN/A    """Placeholder class to represent undefined variables.  Will
1151606SN/A    generally cause an exception whenever it is used, but evaluates to
1161606SN/A    zero for boolean truth testing such as in an if statement"""
11713697Sandreas.sandberg@arm.com    def __bool__(self):
1181606SN/A        return False
1191606SN/A
12013697Sandreas.sandberg@arm.com    # Python 2.7 uses __nonzero__ instead of __bool__
12113697Sandreas.sandberg@arm.com    __nonzero__ = __bool__
12213697Sandreas.sandberg@arm.com
1236997Snate@binkert.orgclass SmartDict(attrdict):
1241606SN/A    """Dictionary class that holds strings, but intelligently converts
1251606SN/A    those strings to other types depending on their usage"""
1261530SN/A
1271606SN/A    def __getitem__(self, key):
1281606SN/A        """returns a Variable proxy if the values exists in the database and
1291606SN/A        returns an UndefinedVariable otherwise"""
1301519SN/A
1311606SN/A        if key in self:
1321606SN/A            return Variable(dict.get(self, key))
1331606SN/A        else:
1341606SN/A            # Note that this does *not* change the contents of the dict,
1351606SN/A            # so that even after we call env['foo'] we still get a
1361606SN/A            # meaningful answer from "'foo' in env" (which
1371606SN/A            # calls dict.__contains__, which we do not override).
1381606SN/A            return UndefinedVariable()
1391519SN/A
1401519SN/A    def __setitem__(self, key, item):
1411606SN/A        """intercept the setting of any variable so that we always
1421606SN/A        store strings in the dict"""
1431519SN/A        dict.__setitem__(self, key, str(item))
1441519SN/A
1451519SN/A    def values(self):
14613709Sandreas.sandberg@arm.com        for value in dict.values(self):
1471606SN/A            yield Variable(value)
1481519SN/A
1491519SN/A    def items(self):
15013709Sandreas.sandberg@arm.com        for key,value in dict.items(self):
1511606SN/A            yield key, Variable(value)
1521519SN/A
1531530SN/A    def get(self, key, default='False'):
1541606SN/A        return Variable(dict.get(self, key, str(default)))
1551519SN/A
1561530SN/A    def setdefault(self, key, default='False'):
1571606SN/A        return Variable(dict.setdefault(self, key, str(default)))
1581519SN/A
1591606SN/A__all__ = [ 'SmartDict' ]
160