1.1 --- a/translator.py Thu Nov 04 23:44:29 2021 +0100
1.2 +++ b/translator.py Tue Aug 29 01:46:23 2023 +0200
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Translate programs.
1.6
1.7 -Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2015, 2016, 2017, 2018, 2023 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -493,7 +493,7 @@
1.13 # Names and attributes are assigned the entire expression.
1.14
1.15 if isinstance(n, compiler.ast.AssName):
1.16 - name_ref = self.process_name_node(n, self.process_structure_node(expr))
1.17 + name_ref = self.process_name_node(n, expr, True)
1.18 self.statement(name_ref)
1.19
1.20 # Employ guards after assignments if required.
1.21 @@ -858,6 +858,9 @@
1.22 self.max_context_index = 0
1.23 self.accessor_index = 0
1.24 self.max_accessor_index = 0
1.25 + self.result_target = 0
1.26 + self.max_result_target = 0
1.27 + self.result_target_name = None
1.28
1.29 # Volatile locals for exception handling.
1.30
1.31 @@ -1280,6 +1283,19 @@
1.32 if stores_accessor:
1.33 self.record_temp("__tmp_values")
1.34
1.35 + # Employ result targets only in functions.
1.36 +
1.37 + if self.in_function:
1.38 + if self.result_target_name:
1.39 + result_target = self.result_target_name
1.40 + self.result_target_name = None
1.41 + else:
1.42 + result_target = "__tmp_results[%d]" % self.result_target
1.43 + self.record_temp("__tmp_results")
1.44 + self.next_result()
1.45 + else:
1.46 + result_target = "__NULL"
1.47 +
1.48 # Arguments are presented in a temporary frame array with any context
1.49 # always being the first argument. Where it would be unused, it may be
1.50 # set to null.
1.51 @@ -1292,7 +1308,10 @@
1.52 else:
1.53 context_arg = "__NULL"
1.54
1.55 - args = [context_arg]
1.56 + # Start with result target and context arguments for each invocation.
1.57 +
1.58 + args = [result_target, context_arg]
1.59 + argstart = 2
1.60
1.61 # Complete the array with null values, permitting tests for a complete
1.62 # set of arguments.
1.63 @@ -1323,6 +1342,13 @@
1.64 for i, arg in enumerate(n.args):
1.65 argexpr = self.process_structure_node(arg)
1.66
1.67 + # Convert any attributes indicating value replacement.
1.68 +
1.69 + if isinstance(argexpr, InvocationResult):
1.70 + argexprstr = "__to_arg(%s)" % argexpr
1.71 + else:
1.72 + argexprstr = str(argexpr)
1.73 +
1.74 # Store a keyword argument, either in the argument list or
1.75 # in a separate keyword argument list for subsequent lookup.
1.76
1.77 @@ -1337,12 +1363,12 @@
1.78 except ValueError:
1.79 raise TranslateError("Argument %s is not recognised." % arg.name,
1.80 self.get_namespace_path(), n)
1.81 - args[argnum+1] = str(argexpr)
1.82 + args[argnum+argstart] = argexprstr
1.83
1.84 # Otherwise, store the details in a separate collection.
1.85
1.86 else:
1.87 - kwargs.append(str(argexpr))
1.88 + kwargs.append(argexprstr)
1.89 kwcodes.append("{%s, %s}" % (
1.90 encode_ppos(arg.name), encode_pcode(arg.name)))
1.91
1.92 @@ -1351,7 +1377,7 @@
1.93
1.94 else:
1.95 try:
1.96 - args[i+1] = str(argexpr)
1.97 + args[i+argstart] = argexprstr
1.98 except IndexError:
1.99 raise TranslateError("Too many arguments specified.",
1.100 self.get_namespace_path(), n)
1.101 @@ -1375,8 +1401,8 @@
1.102
1.103 for i, (argname, default) in enumerate(function_defaults):
1.104 argnum = parameters.index(argname)
1.105 - if not args[argnum+1]:
1.106 - args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.107 + if not args[argnum+argstart]:
1.108 + args[argnum+argstart] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.109
1.110 elif known_parameters:
1.111
1.112 @@ -1387,7 +1413,7 @@
1.113 i = len(n.args)
1.114 pos = i - (num_parameters - num_defaults)
1.115 while i < num_parameters:
1.116 - args[i+1] = "__GETDEFAULT(%s.value, %d)" % (target_var, pos)
1.117 + args[i+argstart] = "__GETDEFAULT(%s.value, %d)" % (target_var, pos)
1.118 i += 1
1.119 pos += 1
1.120
1.121 @@ -1400,10 +1426,10 @@
1.122 # Encode the arguments.
1.123
1.124 # Where literal instantiation is occurring, add an argument indicating
1.125 - # the number of values. The context is excluded.
1.126 + # the number of values. The result target and context are excluded.
1.127
1.128 if literal_instantiation:
1.129 - argstr = "%d, %s" % (len(args) - 1, ", ".join(args[1:]))
1.130 + argstr = "%d, %s" % (len(args) - 2, ", ".join(args[2:]))
1.131 else:
1.132 argstr = ", ".join(args)
1.133
1.134 @@ -1492,6 +1518,13 @@
1.135 len(args), "__ARGS(%s)" % argstr))
1.136 return InvocationResult(stages)
1.137
1.138 + def next_result(self):
1.139 +
1.140 + "Allocate the next result target storage."
1.141 +
1.142 + self.result_target += 1
1.143 + self.max_result_target = max(self.result_target, self.max_result_target)
1.144 +
1.145 def next_target(self):
1.146
1.147 "Allocate the next function target storage."
1.148 @@ -1605,7 +1638,7 @@
1.149
1.150 return LogicalOperationResult(results, conjunction)
1.151
1.152 - def process_name_node(self, n, expr=None):
1.153 + def process_name_node(self, n, expr=None, process_expr=False):
1.154
1.155 "Process the given name node 'n' with the optional assignment 'expr'."
1.156
1.157 @@ -1672,6 +1705,19 @@
1.158 if expr and self.in_function and not is_global and self.in_try_except:
1.159 self.make_volatile(n.name)
1.160
1.161 + # Set the replacement target for local objects.
1.162 + # Note that this will not apply to all local objects, with only floats
1.163 + # supporting replaceable values.
1.164 +
1.165 + if expr and self.in_function and not is_global:
1.166 + self.result_target_name = encode_path(n.name)
1.167 +
1.168 + # Expression processing is deferred until after any result target has
1.169 + # been set.
1.170 +
1.171 + if process_expr:
1.172 + expr = self.process_structure_node(expr)
1.173 +
1.174 # Qualified names are used for resolved static references or for
1.175 # static namespace members. The reference should be configured to return
1.176 # such names.
1.177 @@ -1746,7 +1792,11 @@
1.178
1.179 "Process the given return node 'n'."
1.180
1.181 + if self.in_function:
1.182 + self.result_target_name = "__result"
1.183 +
1.184 expr = self.process_structure_node(n.value) or PredefinedConstantRef("None")
1.185 +
1.186 if self.in_try_finally or self.in_try_except:
1.187 self.writestmt("__Return(%s);" % expr)
1.188 else:
1.189 @@ -2145,6 +2195,7 @@
1.190 # Generate any self reference.
1.191
1.192 l = []
1.193 + l.append("__attr __result")
1.194
1.195 if self.is_method(name):
1.196 l.append("__attr self")
1.197 @@ -2178,6 +2229,9 @@
1.198
1.199 # Add temporary variable usage details.
1.200
1.201 + if self.uses_temp(name, "__tmp_results"):
1.202 + self.writeline("__attr __tmp_results[%d] = {0};" % self.max_result_target)
1.203 +
1.204 if self.uses_temp(name, "__tmp_private_context"):
1.205 self.writeline("__attr __tmp_private_context;")
1.206 if self.uses_temp(name, "__tmp_target_value"):