Lichen

transresults.py

955:db49ed1bde3b
2021-11-09 Paul Boddie Avoid making general expressions immutable as arguments. tagged-address-values
     1 #!/usr/bin/env python     2      3 """     4 Translation result abstractions.     5      6 Copyright (C) 2016, 2017, 2018, 2021 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from common import first, InstructionSequence    23 from encoders import encode_instructions, encode_literal_constant, encode_path    24 from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, NameRef, \    25                     ResolvedNameRef, Result    26     27 # Classes representing intermediate translation results.    28     29 class ReturnRef:    30     31     "Indicates usage of a return statement."    32     33     pass    34     35 class Expression(Result):    36     37     "A general expression."    38     39     def __init__(self, s):    40         if isinstance(s, Result):    41             self.s = str(s)    42             self.expr = s    43         else:    44             self.s = s    45             self.expr = None    46     47     def discards_temporary(self, test=True):    48     49         """    50         Return a list of temporary names that can be discarded if 'test' is    51         specified as a true value (or omitted).    52         """    53     54         return self.expr and self.expr.discards_temporary(False)    55     56     def __str__(self):    57         return self.s    58     59     def __repr__(self):    60         return "Expression(%r)" % self.s    61     62     def as_arg(self):    63     64         "Return the expression without any mutable tag."    65     66         return self.s    67     68 class TrResolvedNameRef(ResolvedNameRef):    69     70     "A reference to a name in the translation."    71     72     def __init__(self, name, ref, expr=None, is_global=False, location=None):    73         ResolvedNameRef.__init__(self, name, ref, expr, is_global)    74         self.location = location    75     76         # For sources, any identified static origin will be constant and thus    77         # usable directly. For targets, no constant should be assigned and thus    78         # the alias (or any plain name) will be used.    79     80         self.static_ref = self.static()    81         origin = self.static_ref and self.get_origin()    82         self.static_name = origin and encode_path(origin)    83     84         # Determine whether a qualified name is involved.    85     86         t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1)    87         self.parent = len(t) > 1 and t[0] or None    88         self.attrname = t[-1] and encode_path(t[-1])    89     90     def access_location(self):    91         return self.location    92     93     def __str__(self):    94     95         "Return an output representation of the referenced name."    96     97         # Temporary names are output program locals.    98     99         if self.name.startswith("$t"):   100             if self.expr:   101                 return "%s = %s" % (encode_path(self.name), self.expr)   102             else:   103                 return encode_path(self.name)   104    105         # Assignments.   106    107         if self.expr:   108    109             # Eliminate assignments between constants.   110    111             if self.static_ref and self.expr.static():   112                 return ""   113    114             # Qualified names must be converted into parent-relative assignments.   115    116             elif self.parent:   117                 return "__store_via_object(&%s, %s, %s)" % (   118                     encode_path(self.parent), self.attrname, self.expr)   119    120             # All other assignments involve the names as they were given.   121    122             else:   123                 return "%s = %s" % (self.attrname, self.expr)   124    125         # Expressions.   126    127         elif self.static_name:   128             return "__ATTRVALUE(&%s)" % self.static_name   129    130         # Qualified names must be converted into parent-relative accesses.   131    132         elif self.parent:   133             return "__load_via_object(&%s, %s)" % (   134                 encode_path(self.parent), self.attrname)   135    136         # All other accesses involve the names as they were given.   137    138         else:   139             return "(%s)" % self.attrname   140    141     def as_arg(self):   142    143         "Return the expression without any mutable tag."   144    145         s = self.__str__()   146    147         # NOTE: This is a superficial test for internal attributes that relies   148         # NOTE: on such attributes being used directly and passed to native   149         # NOTE: code.   150    151         if self.attrname in ("__data__", "__size__"):   152             return s   153         else:   154             return "__TO_IMMUTABLE(%s)" % s   155    156 class TrConstantValueRef(ConstantValueRef):   157    158     "A constant value reference in the translation."   159    160     def __str__(self):   161         return encode_literal_constant(self.number)   162    163     def as_arg(self):   164         return self.__str__()   165    166 class TrLiteralSequenceRef(LiteralSequenceRef):   167    168     "A reference representing a sequence of values."   169    170     def __str__(self):   171         return str(self.node)   172    173     def as_arg(self):   174         return self.__str__()   175    176 class TrInstanceRef(InstanceRef):   177    178     "A reference representing instantiation of a class."   179    180     def __init__(self, ref, expr):   181    182         """   183         Initialise the reference with 'ref' indicating the nature of the   184         reference and 'expr' being an expression used to create the instance.   185         """   186    187         InstanceRef.__init__(self, ref)   188         self.expr = expr   189    190     def __str__(self):   191         return self.expr   192    193     def __repr__(self):   194         return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr)   195    196     def as_arg(self):   197         return self.__str__()   198    199 class AttrResult(Result, InstructionSequence):   200    201     "A translation result for an attribute access."   202    203     def __init__(self, instructions, refs, location, context_identity,   204                  context_identity_verified, accessor_test, accessor_stored):   205    206         InstructionSequence.__init__(self, instructions)   207         self.refs = refs   208         self.location = location   209         self.context_identity = context_identity   210         self.context_identity_verified = context_identity_verified   211         self.accessor_test = accessor_test   212         self.accessor_stored = accessor_stored   213    214     def references(self):   215         return self.refs   216    217     def access_location(self):   218         return self.location   219    220     def context(self):   221         return self.context_identity   222    223     def context_verified(self):   224         return self.context_identity_verified and self.context() or None   225    226     def tests_accessor(self):   227         return self.accessor_test   228    229     def stores_accessor(self):   230         return self.accessor_stored   231    232     def get_origin(self):   233         return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()   234    235     def has_kind(self, kinds):   236         if not self.refs:   237             return False   238         for ref in self.refs:   239             if ref.has_kind(kinds):   240                 return True   241         return False   242    243     def __nonzero__(self):   244         return bool(self.instructions)   245    246     def __str__(self):   247         return encode_instructions(self.instructions)   248    249     def __repr__(self):   250         return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % (   251                 self.instructions, self.refs, self.location,   252                 self.context_identity, self.context_identity_verified,   253                 self.accessor_test, self.accessor_stored)   254    255     def as_arg(self):   256         return self.__str__()   257    258 class AliasResult(NameRef, Result):   259    260     "An alias for other values."   261    262     def __init__(self, name_ref, refs, location):   263         NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name())   264         self.name_ref = name_ref   265         self.refs = refs   266         self.location = location   267    268     def references(self):   269         ref = self.name_ref.reference()   270         return self.refs or ref and [ref] or None   271    272     def reference(self):   273         refs = self.references()   274         return len(refs) == 1 and first(refs) or None   275    276     def access_location(self):   277         return self.location   278    279     def get_name(self):   280         ref = self.reference()   281         return ref and ref.get_name()   282    283     def get_origin(self):   284         ref = self.reference()   285         return ref and ref.get_origin()   286    287     def static(self):   288         ref = self.reference()   289         return ref and ref.static()   290    291     def final(self):   292         ref = self.reference()   293         return ref and ref.final()   294    295     def has_kind(self, kinds):   296         if not self.refs:   297             return self.name_ref.has_kind(kinds)   298    299         for ref in self.refs:   300             if ref.has_kind(kinds):   301                 return True   302    303         return False   304    305     def __str__(self):   306         return str(self.name_ref)   307    308     def __repr__(self):   309         return "AliasResult(%r, %r)" % (self.name_ref, self.refs)   310    311     def as_arg(self):   312         return self.__str__()   313    314 class InvocationResult(Result, InstructionSequence):   315    316     "A translation result for an invocation."   317    318     def __str__(self):   319         return encode_instructions(self.instructions)   320    321     def __repr__(self):   322         return "InvocationResult(%r)" % self.instructions   323    324     def as_arg(self):   325         return self.__str__()   326    327 class InstantiationResult(InvocationResult, TrInstanceRef):   328    329     "An instantiation result acting like an invocation result."   330    331     def __init__(self, ref, instructions):   332         InstanceRef.__init__(self, ref)   333         InvocationResult.__init__(self, instructions)   334    335     def __repr__(self):   336         return "InstantiationResult(%r, %r)" % (self.ref, self.instructions)   337    338 class PredefinedConstantRef(Result):   339    340     "A predefined constant reference."   341    342     def __init__(self, value, expr=None):   343         self.value = value   344         self.expr = expr   345    346     def __str__(self):   347    348         # Eliminate predefined constant assignments.   349    350         if self.expr:   351             return ""   352    353         # Generate the specific constants.   354    355         if self.value in ("False", "True"):   356             return encode_path("__builtins__.boolean.%s" % self.value)   357         elif self.value == "None":   358             return encode_path("__builtins__.none.%s" % self.value)   359         elif self.value == "NotImplemented":   360             return encode_path("__builtins__.notimplemented.%s" % self.value)   361         else:   362             return self.value   363    364     def __repr__(self):   365         return "PredefinedConstantRef(%r)" % self.value   366    367     def as_arg(self):   368         return self.__str__()   369    370 class LogicalResult(Result):   371    372     "A logical expression result."   373    374     def _convert(self, expr):   375    376         "Return 'expr' converted to a testable value."   377    378         if isinstance(expr, LogicalResult):   379             return expr.apply_test()   380         else:   381             return "__BOOL(%s)" % expr   382    383 class NegationResult(LogicalResult):   384    385     "A negation expression result."   386    387     def __init__(self, expr):   388         self.expr = expr   389    390     def apply_test(self):   391    392         "Return the result in a form suitable for direct testing."   393    394         expr = self._convert(self.expr)   395         return "(!%s)" % expr   396    397     def discards_temporary(self, test=True):   398    399         """   400         Negations should have discarded their operand's temporary names when   401         being instantiated.   402         """   403    404         return None   405    406     def __str__(self):   407         return "(%s ? %s : %s)" % (   408             self._convert(self.expr),   409             PredefinedConstantRef("False"),   410             PredefinedConstantRef("True"))   411    412     def __repr__(self):   413         return "NegationResult(%r)" % self.expr   414    415     def as_arg(self):   416         return self.__str__()   417    418 class LogicalOperationResult(LogicalResult):   419    420     "A logical operation result."   421    422     def __init__(self, exprs, conjunction):   423         self.exprs = exprs   424         self.conjunction = conjunction   425    426     def apply_test(self):   427    428         """   429         Return the result in a form suitable for direct testing.   430    431         Convert ... to ...   432    433         <a> and <b>   434         ((__BOOL(<a>)) && (__BOOL(<b>)))   435    436         <a> or <b>   437         ((__BOOL(<a>)) || (__BOOL(<b>)))   438         """   439    440         results = []   441         for expr in self.exprs:   442             results.append(self._convert(expr))   443    444         if self.conjunction:   445             return "(%s)" % " && ".join(results)   446         else:   447             return "(%s)" % " || ".join(results)   448    449     def discards_temporary(self, test=True):   450    451         """   452         Return a list of temporary names that can be discarded if 'test' is   453         specified as a true value (or omitted).   454         """   455    456         if not test:   457             return None   458    459         temps = ["__tmp_result"]   460    461         for expr in self.exprs:   462             t = expr.discards_temporary(test)   463             if t:   464                 temps += t   465    466         return temps   467    468     def __str__(self):   469    470         """   471         Convert ... to ...   472    473         <a> and <b>   474         (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b>   475    476         <a> or <b>   477         (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>   478         """   479    480         results = []   481         for expr in self.exprs[:-1]:   482             results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or ""))   483         results.append(str(self.exprs[-1]))   484    485         return "(%s)" % "".join(results)   486    487     def __repr__(self):   488         return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction)   489    490     def as_arg(self):   491         return self.__str__()   492    493 # vim: tabstop=4 expandtab shiftwidth=4