Lichen

transresults.py

934:2989aab1b4f7
5 months ago Paul Boddie Renamed the utf8string class to unicode, eliminating the unicode function. This means that the simple case of merely returning an object if it is already a Unicode object no longer occurs when using the unicode callable, but such behaviour might be better supported with more general customised instantiation functionality.
     1 #!/usr/bin/env python     2      3 """     4 Translation result abstractions.     5      6 Copyright (C) 2016, 2017, 2018 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,   182                  context_identity_verified, accessor_test, accessor_stored):   183    184         InstructionSequence.__init__(self, instructions)   185         self.refs = refs   186         self.location = location   187         self.context_identity = context_identity   188         self.context_identity_verified = context_identity_verified   189         self.accessor_test = accessor_test   190         self.accessor_stored = accessor_stored   191    192     def references(self):   193         return self.refs   194    195     def access_location(self):   196         return self.location   197    198     def context(self):   199         return self.context_identity   200    201     def context_verified(self):   202         return self.context_identity_verified and self.context() or None   203    204     def tests_accessor(self):   205         return self.accessor_test   206    207     def stores_accessor(self):   208         return self.accessor_stored   209    210     def get_origin(self):   211         return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()   212    213     def has_kind(self, kinds):   214         if not self.refs:   215             return False   216         for ref in self.refs:   217             if ref.has_kind(kinds):   218                 return True   219         return False   220    221     def __nonzero__(self):   222         return bool(self.instructions)   223    224     def __str__(self):   225         return encode_instructions(self.instructions)   226    227     def __repr__(self):   228         return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % (   229                 self.instructions, self.refs, self.location,   230                 self.context_identity, self.context_identity_verified,   231                 self.accessor_test, self.accessor_stored)   232    233 class AliasResult(NameRef, Result):   234    235     "An alias for other values."   236    237     def __init__(self, name_ref, refs, location):   238         NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name())   239         self.name_ref = name_ref   240         self.refs = refs   241         self.location = location   242    243     def references(self):   244         ref = self.name_ref.reference()   245         return self.refs or ref and [ref] or None   246    247     def reference(self):   248         refs = self.references()   249         return len(refs) == 1 and first(refs) or None   250    251     def access_location(self):   252         return self.location   253    254     def get_name(self):   255         ref = self.reference()   256         return ref and ref.get_name()   257    258     def get_origin(self):   259         ref = self.reference()   260         return ref and ref.get_origin()   261    262     def static(self):   263         ref = self.reference()   264         return ref and ref.static()   265    266     def final(self):   267         ref = self.reference()   268         return ref and ref.final()   269    270     def has_kind(self, kinds):   271         if not self.refs:   272             return self.name_ref.has_kind(kinds)   273    274         for ref in self.refs:   275             if ref.has_kind(kinds):   276                 return True   277    278         return False   279    280     def __str__(self):   281         return str(self.name_ref)   282    283     def __repr__(self):   284         return "AliasResult(%r, %r)" % (self.name_ref, self.refs)   285    286 class InvocationResult(Result, InstructionSequence):   287    288     "A translation result for an invocation."   289    290     def __str__(self):   291         return encode_instructions(self.instructions)   292    293     def __repr__(self):   294         return "InvocationResult(%r)" % self.instructions   295    296 class InstantiationResult(InvocationResult, TrInstanceRef):   297    298     "An instantiation result acting like an invocation result."   299    300     def __init__(self, ref, instructions):   301         InstanceRef.__init__(self, ref)   302         InvocationResult.__init__(self, instructions)   303    304     def __repr__(self):   305         return "InstantiationResult(%r, %r)" % (self.ref, self.instructions)   306    307 class PredefinedConstantRef(Result):   308    309     "A predefined constant reference."   310    311     def __init__(self, value, expr=None):   312         self.value = value   313         self.expr = expr   314    315     def __str__(self):   316    317         # Eliminate predefined constant assignments.   318    319         if self.expr:   320             return ""   321    322         # Generate the specific constants.   323    324         if self.value in ("False", "True"):   325             return encode_path("__builtins__.boolean.%s" % self.value)   326         elif self.value == "None":   327             return encode_path("__builtins__.none.%s" % self.value)   328         elif self.value == "NotImplemented":   329             return encode_path("__builtins__.notimplemented.%s" % self.value)   330         else:   331             return self.value   332    333     def __repr__(self):   334         return "PredefinedConstantRef(%r)" % self.value   335    336 class LogicalResult(Result):   337    338     "A logical expression result."   339    340     def _convert(self, expr):   341    342         "Return 'expr' converted to a testable value."   343    344         if isinstance(expr, LogicalResult):   345             return expr.apply_test()   346         else:   347             return "__BOOL(%s)" % expr   348    349 class NegationResult(LogicalResult):   350    351     "A negation expression result."   352    353     def __init__(self, expr):   354         self.expr = expr   355    356     def apply_test(self):   357    358         "Return the result in a form suitable for direct testing."   359    360         expr = self._convert(self.expr)   361         return "(!%s)" % expr   362    363     def discards_temporary(self, test=True):   364    365         """   366         Negations should have discarded their operand's temporary names when   367         being instantiated.   368         """   369    370         return None   371    372     def __str__(self):   373         return "(%s ? %s : %s)" % (   374             self._convert(self.expr),   375             PredefinedConstantRef("False"),   376             PredefinedConstantRef("True"))   377    378     def __repr__(self):   379         return "NegationResult(%r)" % self.expr   380    381 class LogicalOperationResult(LogicalResult):   382    383     "A logical operation result."   384    385     def __init__(self, exprs, conjunction):   386         self.exprs = exprs   387         self.conjunction = conjunction   388    389     def apply_test(self):   390    391         """   392         Return the result in a form suitable for direct testing.   393    394         Convert ... to ...   395    396         <a> and <b>   397         ((__BOOL(<a>)) && (__BOOL(<b>)))   398    399         <a> or <b>   400         ((__BOOL(<a>)) || (__BOOL(<b>)))   401         """   402    403         results = []   404         for expr in self.exprs:   405             results.append(self._convert(expr))   406    407         if self.conjunction:   408             return "(%s)" % " && ".join(results)   409         else:   410             return "(%s)" % " || ".join(results)   411    412     def discards_temporary(self, test=True):   413    414         """   415         Return a list of temporary names that can be discarded if 'test' is   416         specified as a true value (or omitted).   417         """   418    419         if not test:   420             return None   421    422         temps = ["__tmp_result"]   423    424         for expr in self.exprs:   425             t = expr.discards_temporary(test)   426             if t:   427                 temps += t   428    429         return temps   430    431     def __str__(self):   432    433         """   434         Convert ... to ...   435    436         <a> and <b>   437         (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b>   438    439         <a> or <b>   440         (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>   441         """   442    443         results = []   444         for expr in self.exprs[:-1]:   445             results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or ""))   446         results.append(str(self.exprs[-1]))   447    448         return "(%s)" % "".join(results)   449    450     def __repr__(self):   451         return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction)   452    453 # vim: tabstop=4 expandtab shiftwidth=4