# HG changeset patch # User Paul Boddie # Date 1532254488 -7200 # Node ID 21f92740b225b7af58c9be7739bb83d5dec0d6c3 # Parent 638e28765ea5a805a303e179ff89e2343994c3e6# Parent 8f8361de472ae4b511cd393f7a8e6fe400eddca0 Merged changes from the default branch. diff -r 638e28765ea5 -r 21f92740b225 encoders.py --- a/encoders.py Thu Jul 12 18:53:58 2018 +0200 +++ b/encoders.py Sun Jul 22 12:14:48 2018 +0200 @@ -259,6 +259,14 @@ "__load_static_ignore", "__load_static_replace", "__load_static_test", "", ) +accessor_values = ( + "", + ) + +accessor_ops = ( + "", "", + ) + context_values = ( "", ) @@ -278,7 +286,7 @@ "", "", "", "", "" ) -def encode_access_instruction(instruction, subs, context_index): +def encode_access_instruction(instruction, subs, accessor_index, context_index): """ Encode the 'instruction' - a sequence starting with an operation and @@ -288,6 +296,9 @@ The 'subs' parameter defines a mapping of substitutions for special values used in instructions. + The 'accessor_index' parameter defines the position in local accessor + storage for the referenced accessor or affected by an accessor operation. + The 'context_index' parameter defines the position in local context storage for the referenced context or affected by a context operation. @@ -304,7 +315,7 @@ if args: converting_op = op for arg in args: - s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, context_index) + s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index) substituted.update(_substituted) a.append(s) converting_op = None @@ -326,6 +337,11 @@ elif op in static_ops: a[-1] = "&%s" % a[-1] + # Add accessor storage information to certain operations. + + if op in accessor_ops: + a.insert(0, accessor_index) + # Add context storage information to certain operations. if op in context_ops: @@ -365,19 +381,19 @@ return "%s%s" % (op, argstr), substituted -def encode_access_instruction_arg(arg, subs, op, context_index): +def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index): """ Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the - operation to which the argument belongs, and 'context_index' to indicate any - affected context storage. + operation to which the argument belongs, and with 'accessor_index' and + 'context_index' indicating any affected accessor and context storage. Return a tuple containing the encoded form of 'arg' along with a collection of any substituted values. """ if isinstance(arg, tuple): - encoded, substituted = encode_access_instruction(arg, subs, context_index) + encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index) return attribute_to_reference(op, arg[0], encoded, substituted) # Special values only need replacing, not encoding. @@ -386,7 +402,7 @@ # Handle values modified by storage details. - if arg in context_values: + if arg in accessor_values or arg in context_values: encoded = "%s(%s)" % (subs.get(arg), context_index) else: encoded = subs.get(arg) diff -r 638e28765ea5 -r 21f92740b225 lib/__builtins__/tuple.py --- a/lib/__builtins__/tuple.py Thu Jul 12 18:53:58 2018 +0200 +++ b/lib/__builtins__/tuple.py Sun Jul 22 12:14:48 2018 +0200 @@ -3,7 +3,7 @@ """ Tuple objects. -Copyright (C) 2015, 2016, 2017 Paul Boddie +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -22,7 +22,8 @@ from __builtins__.iteration.iterator import itemiterator from __builtins__.sequence import hashable, sequence from native import tuple_init, \ - list_element, list_len, list_setsize, list_setelement + list_element, list_len, list_setsize, list_setelement, \ + isinstance as _isinstance class tuple(sequence, hashable): @@ -73,7 +74,13 @@ return list_len(self.__data__) - def __add__(self, other): pass + def __add__(self, other): + + "Add this tuple to 'other'." + + if not _isinstance(other, tuple): + raise TypeError + return tuple(tuplepair(self, other)) def __str__(self): @@ -110,4 +117,28 @@ raise TypeError +class tuplepair: + + "A combination of tuples." + + def __init__(self, a, b): + self.a = a + self.b = b + + def __len__(self): + + "Return the combined length of the tuples." + + return len(self.a) + len(self.b) + + def __getitem__(self, index): + + "Return the value from 'index' in the combined tuple." + + asize = len(self.a) + if index < asize: + return self.a.__get_single_item__(index) + else: + return self.b.__get_single_item__(index - asize) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 638e28765ea5 -r 21f92740b225 templates/ops.h --- a/templates/ops.h Thu Jul 12 18:53:58 2018 +0200 +++ b/templates/ops.h Sun Jul 22 12:14:48 2018 +0200 @@ -107,10 +107,11 @@ __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]); __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]); +#define __get_accessor(__TARGET) (__tmp_values[__TARGET]) #define __get_context(__TARGET) (__tmp_contexts[__TARGET]) #define __set_context(__TARGET, __ATTR) (__tmp_contexts[__TARGET] = (__ATTR)) #define __set_private_context(__ATTR) (__tmp_private_context = (__ATTR)) -#define __set_accessor(__ATTR) (__tmp_value = (__ATTR)) +#define __set_accessor(__TARGET, __ATTR) (__tmp_values[__TARGET] = (__ATTR)) #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR)) /* Context testing for invocations. */ diff -r 638e28765ea5 -r 21f92740b225 tests/methods_rebound.py --- a/tests/methods_rebound.py Thu Jul 12 18:53:58 2018 +0200 +++ b/tests/methods_rebound.py Sun Jul 22 12:14:48 2018 +0200 @@ -22,17 +22,30 @@ e = E() +# Normal method access and invocation. + print c.f.__name__ # f print c.f() # <__main__.C instance> # 123 + +# Access and call assigned bound method. + print d.f.__name__ # wrapper print d.f() # <__main__.C instance> # 123 +# Access and call assigned function. + print e.f.__name__ # fn print e.f() # 456 + +# Access details of assigned method. + print e.g.__name__ # f +# Attempt to call method belonging to another class via an incompatible +# instance. In Python, this would be an unbound method call attempt. + try: print e.g() except TypeError: diff -r 638e28765ea5 -r 21f92740b225 tests/nested_calls.py --- a/tests/nested_calls.py Thu Jul 12 18:53:58 2018 +0200 +++ b/tests/nested_calls.py Sun Jul 22 12:14:48 2018 +0200 @@ -13,3 +13,31 @@ c = C(3) print c.length() # 6 + +# Explicit function for addition purposes. + +def combine(x, y, z): + return x + y + z + +class Tree: + def __init__(self, item, left=None, right=None): + self.item = item + self.left = left + self.right = right + + def count(self): + if self.left and self.right: + # Test calls in parameter lists needing separate temporary storage. + return combine(self.item, self.left.count(), self.right.count()) + else: + return self.item + +tree = \ + Tree(10000, + Tree(2000, + Tree(300), + Tree(40) + ), + Tree(5)) + +print tree.count() # 12345 diff -r 638e28765ea5 -r 21f92740b225 tests/tuple.py --- a/tests/tuple.py Thu Jul 12 18:53:58 2018 +0200 +++ b/tests/tuple.py Sun Jul 22 12:14:48 2018 +0200 @@ -31,3 +31,19 @@ a, b, c = l except ValueError, exc: print "a, b, c = l: failed with length", exc.value + +# Add tuples together. + +m = ("five", 6, 7, 8) +n = t + m +print n # (1, 2, 3, "four", "five", 6, 7, 8) + +# Add to list, really testing tuple iteration. + +o = l + m +print o # [1, 2, 3, "four", "five", 6, 7, 8] + +try: + print t + l # should raise an exception +except TypeError: + print "t + l: failed due to tuple-list addition" diff -r 638e28765ea5 -r 21f92740b225 translator.py --- a/translator.py Thu Jul 12 18:53:58 2018 +0200 +++ b/translator.py Sun Jul 22 12:14:48 2018 +0200 @@ -115,6 +115,7 @@ self.namespaces = [] self.in_conditional = False + self.in_parameter_list = False # Exception raising adjustments. @@ -317,6 +318,8 @@ self.max_function_target = 0 self.context_index = 0 self.max_context_index = 0 + self.accessor_index = 0 + self.max_accessor_index = 0 self.start_module() self.process_structure(node) self.end_module() @@ -554,11 +557,13 @@ # The context set or retrieved will be that used by any enclosing # invocation. + accessor_index = self.accessor_index context_index = self.context_index context_identity = None context_identity_verified = False final_identity = None accessor_test = False + accessor_stored = False # Obtain encoded versions of each instruction, accumulating temporary # variables. @@ -568,7 +573,9 @@ # Intercept a special instruction identifying the context. if instruction[0] in ("", ""): - context_identity, _substituted = encode_access_instruction_arg(instruction[1], subs, instruction[0], context_index) + context_identity, _substituted = \ + encode_access_instruction_arg(instruction[1], subs, instruction[0], + accessor_index, context_index) context_identity_verified = instruction[0] == "" continue @@ -585,10 +592,16 @@ instruction = ("__to_error", instruction) accessor_test = True + # Intercept accessor storage. + + elif instruction[0] == "": + accessor_stored = True + # Collect the encoded instruction, noting any temporary variables # required by it. - encoded, _substituted = encode_access_instruction(instruction, subs, context_index) + encoded, _substituted = encode_access_instruction(instruction, subs, + accessor_index, context_index) output.append(encoded) substituted.update(_substituted) @@ -604,7 +617,9 @@ refs = set([self.importer.identify(final_identity)]) del self.attrs[0] - return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test) + return AttrResult(output, refs, location, + context_identity, context_identity_verified, + accessor_test, accessor_stored) def init_substitutions(self): @@ -619,21 +634,22 @@ # Substitutions used by instructions. "" : "__tmp_private_context", - "" : "__tmp_value", "" : "__tmp_target_value", # Mappings to be replaced by those given below. + "" : "__tmp_values", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_private_context", - "" : "__tmp_value", + "" : "__tmp_values", "" : "__tmp_target_value", } self.op_subs = { + "" : "__get_accessor", "" : "__get_context", "" : "__test_context_revert", "" : "__test_context_static", @@ -840,6 +856,8 @@ self.max_function_target = 0 self.context_index = 0 self.max_context_index = 0 + self.accessor_index = 0 + self.max_accessor_index = 0 # Volatile locals for exception handling. @@ -1082,6 +1100,7 @@ # With such operations present, the expression cannot be eliminated. tests_accessor = have_access_context and expr.tests_accessor() + stores_accessor = have_access_context and expr.stores_accessor() # Parameter details and parameter list dimensions. @@ -1258,6 +1277,9 @@ if need_context_stored: self.record_temp("__tmp_contexts") + if stores_accessor: + self.record_temp("__tmp_values") + # Arguments are presented in a temporary frame array with any context # always being the first argument. Where it would be unused, it may be # set to null. @@ -1284,6 +1306,7 @@ function_target = self.function_target context_index = self.context_index + accessor_index = self.accessor_index if need_target_stored: self.next_target() @@ -1291,6 +1314,12 @@ if need_context_stored: self.next_context() + if stores_accessor: + self.next_accessor() + + in_parameter_list = self.in_parameter_list + self.in_parameter_list = True + for i, arg in enumerate(n.args): argexpr = self.process_structure_node(arg) @@ -1329,8 +1358,12 @@ # Reference the current target again. - self.function_target = function_target - self.context_index = context_index + self.in_parameter_list = in_parameter_list + + if not self.in_parameter_list: + self.function_target = function_target + self.context_index = context_index + self.accessor_index = accessor_index # Defaults are added to the frame where arguments are missing. @@ -1473,6 +1506,13 @@ self.context_index += 1 self.max_context_index = max(self.context_index, self.max_context_index) + def next_accessor(self): + + "Allocate the next accessor value storage." + + self.accessor_index += 1 + self.max_accessor_index = max(self.accessor_index, self.max_accessor_index) + def always_callable(self, refs): "Determine whether all 'refs' are callable." @@ -1527,8 +1567,9 @@ name = self.get_lambda_name() function_name = self.get_object_path(name) - - defaults = self.process_function_defaults(n, name, function_name, "__tmp_value") + instance_name = "__get_accessor(%d)" % self.accessor_index + + defaults = self.process_function_defaults(n, name, function_name, instance_name) # Without defaults, produce an attribute referring to the function. @@ -1539,11 +1580,16 @@ # copy. else: - self.record_temp("__tmp_value") - return make_expression("(__tmp_value = __ATTRVALUE(__COPY(&%s, sizeof(%s))), %s, __tmp_value)" % ( + self.record_temp("__tmp_values") + return make_expression("""\ +(__set_accessor(%d, __ATTRVALUE(__COPY(&%s, sizeof(%s)))), + %s, + __get_accessor(%d))""" % ( + self.accessor_index, encode_path(function_name), encode_symbol("obj", function_name), - ", ".join(defaults))) + ", ".join(defaults), + self.accessor_index)) def process_logical_node(self, n): @@ -2119,24 +2165,21 @@ "Write temporary storage employed by 'name'." - # Provide space for the given number of targets. - - targets = self.max_function_target + # Provide space for the recorded number of temporary variables. if self.uses_temp(name, "__tmp_targets"): - self.writeline("__attr __tmp_targets[%d];" % targets) - - index = self.max_context_index + self.writeline("__attr __tmp_targets[%d];" % self.max_function_target) if self.uses_temp(name, "__tmp_contexts"): - self.writeline("__attr __tmp_contexts[%d];" % index) + self.writeline("__attr __tmp_contexts[%d];" % self.max_context_index) + + if self.uses_temp(name, "__tmp_values"): + self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index) # Add temporary variable usage details. if self.uses_temp(name, "__tmp_private_context"): self.writeline("__attr __tmp_private_context;") - if self.uses_temp(name, "__tmp_value"): - self.writeline("__attr __tmp_value;") if self.uses_temp(name, "__tmp_target_value"): self.writeline("__attr __tmp_target_value;") if self.uses_temp(name, "__tmp_result"): diff -r 638e28765ea5 -r 21f92740b225 transresults.py --- a/transresults.py Thu Jul 12 18:53:58 2018 +0200 +++ b/transresults.py Sun Jul 22 12:14:48 2018 +0200 @@ -3,7 +3,7 @@ """ Translation result abstractions. -Copyright (C) 2016, 2017 Paul Boddie +Copyright (C) 2016, 2017, 2018 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -179,13 +179,15 @@ "A translation result for an attribute access." def __init__(self, instructions, refs, location, context_identity, - context_identity_verified, accessor_test): + context_identity_verified, accessor_test, accessor_stored): + InstructionSequence.__init__(self, instructions) self.refs = refs self.location = location self.context_identity = context_identity self.context_identity_verified = context_identity_verified self.accessor_test = accessor_test + self.accessor_stored = accessor_stored def references(self): return self.refs @@ -202,6 +204,9 @@ def tests_accessor(self): return self.accessor_test + def stores_accessor(self): + return self.accessor_stored + def get_origin(self): return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() @@ -220,8 +225,10 @@ return encode_instructions(self.instructions) def __repr__(self): - return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs, - self.location, self.context_identity, self.accessor_test) + return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % ( + self.instructions, self.refs, self.location, + self.context_identity, self.context_identity_verified, + self.accessor_test, self.accessor_stored) class AliasResult(NameRef, Result):