Lichen

transresults.py

821:ed27b57be1df
2018-01-15 Paul Boddie Introduced parameter-based attribute initialisation for broader testing.
     1 #!/usr/bin/env python     2      3 """     4 Translation result abstractions.     5      6 Copyright (C) 2016, 2017 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 class TrResolvedNameRef(ResolvedNameRef):    63     64     "A reference to a name in the translation."    65     66     def __init__(self, name, ref, expr=None, is_global=False, location=None):    67         ResolvedNameRef.__init__(self, name, ref, expr, is_global)    68         self.location = location    69     70     def access_location(self):    71         return self.location    72     73     def __str__(self):    74     75         "Return an output representation of the referenced name."    76     77         # Temporary names are output program locals.    78     79         if self.name.startswith("$t"):    80             if self.expr:    81                 return "%s = %s" % (encode_path(self.name), self.expr)    82             else:    83                 return encode_path(self.name)    84     85         # For sources, any identified static origin will be constant and thus    86         # usable directly. For targets, no constant should be assigned and thus    87         # the alias (or any plain name) will be used.    88     89         ref = self.static()    90         origin = ref and self.get_origin()    91         static_name = origin and encode_path(origin)    92     93         # Determine whether a qualified name is involved.    94     95         t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1)    96         parent = len(t) > 1 and t[0] or None    97         attrname = t[-1] and encode_path(t[-1])    98     99         # Assignments.   100    101         if self.expr:   102    103             # Eliminate assignments between constants.   104    105             if ref and self.expr.static():   106                 return ""   107    108             # Qualified names must be converted into parent-relative assignments.   109    110             elif parent:   111                 return "__store_via_object(&%s, %s, %s)" % (   112                     encode_path(parent), attrname, self.expr)   113    114             # All other assignments involve the names as they were given.   115    116             else:   117                 return "%s = %s" % (attrname, self.expr)   118    119         # Expressions.   120    121         elif static_name:   122             parent = ref.parent()   123             context = ref.has_kind("<function>") and encode_path(parent) or None   124             return "__ATTRVALUE(&%s)" % static_name   125    126         # Qualified names must be converted into parent-relative accesses.   127    128         elif parent:   129             return "__load_via_object(&%s, %s)" % (   130                 encode_path(parent), attrname)   131    132         # All other accesses involve the names as they were given.   133    134         else:   135             return "(%s)" % attrname   136    137 class TrConstantValueRef(ConstantValueRef):   138    139     "A constant value reference in the translation."   140    141     def __str__(self):   142    143         # NOTE: Should reference a common variable for the type name.   144    145         if self.ref.get_origin() == "__builtins__.int.int":   146             return "__INTVALUE(%s)" % self.value   147         else:   148             return encode_literal_constant(self.number)   149    150 class TrLiteralSequenceRef(LiteralSequenceRef):   151    152     "A reference representing a sequence of values."   153    154     def __str__(self):   155         return str(self.node)   156    157 class TrInstanceRef(InstanceRef):   158    159     "A reference representing instantiation of a class."   160    161     def __init__(self, ref, expr):   162    163         """   164         Initialise the reference with 'ref' indicating the nature of the   165         reference and 'expr' being an expression used to create the instance.   166         """   167    168         InstanceRef.__init__(self, ref)   169         self.expr = expr   170    171     def __str__(self):   172         return self.expr   173    174     def __repr__(self):   175         return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr)   176    177 class AttrResult(Result, InstructionSequence):   178    179     "A translation result for an attribute access."   180    181     def __init__(self, instructions, refs, location, context_identity, context_identity_verified):   182         InstructionSequence.__init__(self, instructions)   183         self.refs = refs   184         self.location = location   185         self.context_identity = context_identity   186         self.context_identity_verified = context_identity_verified   187    188     def references(self):   189         return self.refs   190    191     def access_location(self):   192         return self.location   193    194     def context(self):   195         return self.context_identity   196    197     def context_verified(self):   198         return self.context_identity_verified and self.context() or None   199    200     def get_origin(self):   201         return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()   202    203     def has_kind(self, kinds):   204         if not self.refs:   205             return False   206         for ref in self.refs:   207             if ref.has_kind(kinds):   208                 return True   209         return False   210    211     def __nonzero__(self):   212         return bool(self.instructions)   213    214     def __str__(self):   215         return encode_instructions(self.instructions)   216    217     def __repr__(self):   218         return "AttrResult(%r, %r, %r, %r)" % (self.instructions, self.refs, self.location, self.context_identity)   219    220 class AliasResult(NameRef, Result):   221    222     "An alias for other values."   223    224     def __init__(self, name_ref, refs, location):   225         NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name())   226         self.name_ref = name_ref   227         self.refs = refs   228         self.location = location   229    230     def references(self):   231         ref = self.name_ref.reference()   232         return self.refs or ref and [ref] or None   233    234     def reference(self):   235         refs = self.references()   236         return len(refs) == 1 and first(refs) or None   237    238     def access_location(self):   239         return self.location   240    241     def get_name(self):   242         ref = self.reference()   243         return ref and ref.get_name()   244    245     def get_origin(self):   246         ref = self.reference()   247         return ref and ref.get_origin()   248    249     def static(self):   250         ref = self.reference()   251         return ref and ref.static()   252    253     def final(self):   254         ref = self.reference()   255         return ref and ref.final()   256    257     def has_kind(self, kinds):   258         if not self.refs:   259             return self.name_ref.has_kind(kinds)   260    261         for ref in self.refs:   262             if ref.has_kind(kinds):   263                 return True   264    265         return False   266    267     def __str__(self):   268         return str(self.name_ref)   269    270     def __repr__(self):   271         return "AliasResult(%r, %r)" % (self.name_ref, self.refs)   272    273 class InvocationResult(Result, InstructionSequence):   274    275     "A translation result for an invocation."   276    277     def __str__(self):   278         return encode_instructions(self.instructions)   279    280     def __repr__(self):   281         return "InvocationResult(%r)" % self.instructions   282    283 class InstantiationResult(InvocationResult, TrInstanceRef):   284    285     "An instantiation result acting like an invocation result."   286    287     def __init__(self, ref, instructions):   288         InstanceRef.__init__(self, ref)   289         InvocationResult.__init__(self, instructions)   290    291     def __repr__(self):   292         return "InstantiationResult(%r, %r)" % (self.ref, self.instructions)   293    294 class PredefinedConstantRef(Result):   295    296     "A predefined constant reference."   297    298     def __init__(self, value, expr=None):   299         self.value = value   300         self.expr = expr   301    302     def __str__(self):   303    304         # Eliminate predefined constant assignments.   305    306         if self.expr:   307             return ""   308    309         # Generate the specific constants.   310    311         if self.value in ("False", "True"):   312             return encode_path("__builtins__.boolean.%s" % self.value)   313         elif self.value == "None":   314             return encode_path("__builtins__.none.%s" % self.value)   315         elif self.value == "NotImplemented":   316             return encode_path("__builtins__.notimplemented.%s" % self.value)   317         else:   318             return self.value   319    320     def __repr__(self):   321         return "PredefinedConstantRef(%r)" % self.value   322    323 class LogicalResult(Result):   324    325     "A logical expression result."   326    327     def _convert(self, expr):   328    329         "Return 'expr' converted to a testable value."   330    331         if isinstance(expr, LogicalResult):   332             return expr.apply_test()   333         else:   334             return "__BOOL(%s)" % expr   335    336 class NegationResult(LogicalResult):   337    338     "A negation expression result."   339    340     def __init__(self, expr):   341         self.expr = expr   342    343     def apply_test(self):   344    345         "Return the result in a form suitable for direct testing."   346    347         expr = self._convert(self.expr)   348         return "(!%s)" % expr   349    350     def discards_temporary(self, test=True):   351    352         """   353         Negations should have discarded their operand's temporary names when   354         being instantiated.   355         """   356    357         return None   358    359     def __str__(self):   360         return "(%s ? %s : %s)" % (   361             self._convert(self.expr),   362             PredefinedConstantRef("False"),   363             PredefinedConstantRef("True"))   364    365     def __repr__(self):   366         return "NegationResult(%r)" % self.expr   367    368 class LogicalOperationResult(LogicalResult):   369    370     "A logical operation result."   371    372     def __init__(self, exprs, conjunction):   373         self.exprs = exprs   374         self.conjunction = conjunction   375    376     def apply_test(self):   377    378         """   379         Return the result in a form suitable for direct testing.   380    381         Convert ... to ...   382    383         <a> and <b>   384         ((__BOOL(<a>)) && (__BOOL(<b>)))   385    386         <a> or <b>   387         ((__BOOL(<a>)) || (__BOOL(<b>)))   388         """   389    390         results = []   391         for expr in self.exprs:   392             results.append(self._convert(expr))   393    394         if self.conjunction:   395             return "(%s)" % " && ".join(results)   396         else:   397             return "(%s)" % " || ".join(results)   398    399     def discards_temporary(self, test=True):   400    401         """   402         Return a list of temporary names that can be discarded if 'test' is   403         specified as a true value (or omitted).   404         """   405    406         if not test:   407             return None   408    409         temps = ["__tmp_result"]   410    411         for expr in self.exprs:   412             t = expr.discards_temporary(test)   413             if t:   414                 temps += t   415    416         return temps   417    418     def __str__(self):   419    420         """   421         Convert ... to ...   422    423         <a> and <b>   424         (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b>   425    426         <a> or <b>   427         (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>   428         """   429    430         results = []   431         for expr in self.exprs[:-1]:   432             results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or ""))   433         results.append(str(self.exprs[-1]))   434    435         return "(%s)" % "".join(results)   436    437     def __repr__(self):   438         return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction)   439    440 # vim: tabstop=4 expandtab shiftwidth=4