Source code for delphi.translators.for2py.genCode

import ast
import sys
import re
from . import For2PyError


[docs]class PrintState: def __init__(self, sep=None, add=None): self.sep = sep if sep is not None else "\n" self.add = add if add is not None else " "
[docs] def copy(self, sep=None, add=None): return PrintState( self.sep if sep is None else sep, self.add if add is None else add )
[docs]class genCode: def __init__(self, use_numpy=False, string_length=None, lambda_string="", ): self.lambda_string = lambda_string # Setting the flag below creates the lambda file using numpy # functions instead of the previous Python approach self.use_numpy = use_numpy self.numpy_math_map = { "acos": "arccos", "acosh": "arccosh", "asin": "arcsin", "asinh": "arcsinh", "atan": "arctan", "atanh": "arctanh", "ceil": "ceil", "cos": "cos", "cosh": "cosh", "exp": "exp", "floor": "floor", "hypot": "hypot", "isnan": "isnan", "log": "log", "log10": "log10", "sin": "sin", "sinh": "sinh", "sqrt": "sqrt", "tan": "tan", "tanh": "tanh", } self.current_function = None self.target_arr = None if string_length: self.string_length = string_length else: self.string_length = None self.process_lambda_node = { "ast.FunctionDef": self.process_function_definition, "ast.arguments": self.process_arguments, "ast.arg": self._process_arg, "ast.Load": self._process_load, "ast.Store": self._process_store, "ast.Index": self.process_index, "ast.Num": self._process_num, "ast.List": self.process_list_ast, "ast.Str": self._process_str, "ast.For": self.process_for, "ast.If": self.process_if, "ast.UnaryOp": self.process_unary_operation, "ast.BinOp": self.process_binary_operation, "ast.Add": self._process_add, "ast.Sub": self._process_subtract, "ast.Mult": self._process_multiply, "ast.Pow": self._process_power, "ast.Div": self._process_divide, "ast.USub": self._process_unary_subtract, "ast.Eq": self._process_equals_to, "ast.NotEq": self._process_not_equal_to, "ast.Not": self._process_not, "ast.LtE": self._process_less_than_or_equal_to, "ast.Lt": self._process_less_than, "ast.Gt": self._process_greater_than, "ast.GtE": self._process_greater_than_or_equal_to, "ast.And": self._process_and, "ast.Or": self._process_or, "ast.Expr": self.process_expression, "ast.Compare": self.process_compare, "ast.Subscript": self.process_subscript, "ast.Name": self.process_name, "ast.AnnAssign": self.process_annotated_assign, "ast.Assign": self.process_assign, "ast.Call": self.process_call, "ast.Import": self.process_import, "ast.alias": self._process_alias, "ast.Module": self.process_module, "ast.BoolOp": self.process_boolean_operation, "ast.Attribute": self.process_attribute, "ast.AST": self.process_ast, "ast.Tuple": self.process_tuple, "ast.NameConstant": self._process_name_constant, }
[docs] def generate_code(self, node, state, length=None): """ This function parses the ast node of the python file and generates python code relevant to the information in the ast. This is used as the statements inside the lambda functions. """ node_name = node.__repr__().split()[0][2:] if isinstance(node, list): for cur in node: self.lambda_string += f"{self.generate_code(cur,state)}" \ f"{state.sep}" elif self.process_lambda_node.get(node_name): self.lambda_string = self.process_lambda_node[node_name](node, state) else: sys.stderr.write( "No handler for {0} in genCode, value: {1}\n".format( node.__class__.__name__, str(node) ) ) return self.lambda_string
[docs] def process_function_definition(self, node, state): code_string = "def {0}({1}):{2}{3}".format( node.name, self.generate_code(node.args, state), state.sep + state.add, self.generate_code(node.body, state.copy(state.sep + state.add)), ) return code_string
[docs] def process_arguments(self, node, state): code_string = ", ".join([self.generate_code(arg, state) for arg in node.args]) return code_string
@staticmethod def _process_arg(node, *_): code_string = node.arg return code_string @staticmethod def _process_load(*_): sys.stderr.write("genCode found ast.Load, is there a bug?\n") @staticmethod def _process_store(*_): sys.stderr.write("genCode found ast.Store, is there a bug?\n")
[docs] def process_index(self, node, state): code_string = "[{0}]".format(self.generate_code(node.value, state)) return code_string
@staticmethod def _process_name_constant(node, _): code_string = str(node.value) return code_string @staticmethod def _process_num(node, *_): code_string = str(node.n) return code_string
[docs] def process_list_ast(self, node, state): elements = [self.generate_code(elmt, state) for elmt in node.elts] code_string = ( str(elements[0]) if len(elements) == 1 else "[{0}]".format(", ".join(elements)) ) return code_string
[docs] def process_tuple(self, node, state): elements = [self.generate_code(elmt, state) for elmt in node.elts] # This tuple handler is a very specific method # for handling an array declaration lambda. code_string = "[0] * (" if len(elements) == 1: code_string += str(elements[0]) else: low_bound = None # Calculate the size of each dimension for elem in elements: if low_bound is None: low_bound = elem else: code_string += f"{elem} - {low_bound}" low_bound = None code_string += ")" return code_string
@staticmethod def _process_str(node, *_): code_string = '"{0}"'.format(node.s) return code_string
[docs] def process_for(self, node, state): code_string = "for {0} in {1}:{2}{3}".format( self.generate_code(node.target, state), self.generate_code(node.iter, state), state.sep + state.add, self.generate_code(node.body, state.copy(state.sep + state.add)), ) return code_string
[docs] def process_if(self, node, state): code_string = "if ({0}):{1}{2}{3}else:{4}{5}".format( self.generate_code(node.test, state), state.sep + state.add, self.generate_code(node.body, state.copy(state.sep + state.add)), state.sep, state.sep + state.add, self.generate_code(node.orelse, state.copy(state.sep + state.add)), ) return code_string
[docs] def process_unary_operation(self, node, state): code_string = "{0}({1})".format( self.generate_code(node.op, state), self.generate_code(node.operand, state) ) return code_string
[docs] def process_binary_operation(self, node, state): code_string = "({0}{1}{2})".format( self.generate_code(node.left, state), self.generate_code(node.op, state), self.generate_code(node.right, state), ) return code_string
@staticmethod def _process_add(*_): code_string = "+" return code_string @staticmethod def _process_subtract(*_): code_string = "-" return code_string @staticmethod def _process_multiply(*_): code_string = "*" return code_string @staticmethod def _process_power(*_): code_string = "**" return code_string @staticmethod def _process_divide(*_): code_string = "/" return code_string @staticmethod def _process_unary_subtract(*_): code_string = "-" return code_string @staticmethod def _process_equals_to(*_): code_string = "==" return code_string @staticmethod def _process_not_equal_to(*_): code_string = "!=" return code_string @staticmethod def _process_not(*_): code_string = "not" return code_string @staticmethod def _process_less_than_or_equal_to(*_): code_string = "<=" return code_string @staticmethod def _process_less_than(*_): code_string = "<" return code_string @staticmethod def _process_greater_than(*_): code_string = ">" return code_string @staticmethod def _process_greater_than_or_equal_to(*_): code_string = ">=" return code_string @staticmethod def _process_and(*_): code_string = "and" return code_string @staticmethod def _process_or(*_): code_string = "or" return code_string
[docs] def process_expression(self, node, state): code_string = self.generate_code(node.value, state) return code_string
[docs] def process_compare(self, node, state): if len(node.ops) > 1: sys.stderr.write( "Fix Compare in genCode! Don't have an example of what this " "will look like\n" ) else: code_string = "({0} {1} {2})".format( self.generate_code(node.left, state), self.generate_code(node.ops[0], state), self.generate_code(node.comparators[0], state), ) return code_string
[docs] def process_subscript(self, node, state): # typical: # lambda_string = '{0}{1}'.format(genCode(node.value, state), genCode( # node.slice, # state)) code_string = self.generate_code(node.value, state) return code_string
[docs] def process_name(self, node, *_): code_string = node.id if self.use_numpy: if self.current_function in ["np.maximum", "np.minimum"] and not \ self.target_arr: self.target_arr = code_string return code_string
[docs] def process_annotated_assign(self, node, state): code_string = self.generate_code(node.value, state) return code_string
[docs] def process_assign(self, node, state): code_string = self.generate_code(node.value, state) return code_string
[docs] def process_call(self, node, state): if isinstance(node.func, ast.Attribute): function_node = node.func # If the python code has a strip function, call it's inner call # function if function_node.attr == "strip": # TODO Add code to handle possible arguments to strip as # well. Look at select_case/select04.f for reference string = self.generate_code(function_node.value, state) return f"{string}.strip()" if not isinstance(function_node.value, ast.Attribute): module = function_node.value.id elif isinstance(function_node.value.value, ast.Name): module = function_node.value.value.id elif isinstance(function_node.value.value, ast.Call): call = node.func.value.value module = call.func.value.id function_name = function_node.attr if module == "math" and \ self.use_numpy and \ function_name in self.numpy_math_map: module = "np" function_name = self.numpy_math_map[function_name] function_name = module + "." + function_name else: function_name = node.func.id if self.use_numpy: if function_name == "max": function_name = "np.maximum" elif function_name == "min": function_name = "np.minimum" if function_name is not "Array": # Check for setter and getter functions to differentiate between # array and string operations if ".set_" in function_name and len(node.args) > 1 and \ function_name.split('.')[1] != "set_substr": # This is an Array operation # Remove the first argument of <.set_> # function of array as it is not needed del node.args[0] code_string = "" for arg in node.args: code_string += self.generate_code(arg, state) elif ".get_" in function_name and \ function_name.split('.')[1] != "get_substr": # This is an Array operation code_string = function_name.replace(".get_", "[") for arg in node.args: code_string += self.generate_code(arg, state) code_string += "]" else: # This is a String operation module_parts = function_name.split('.') module = module_parts[-1] function = module_parts[0] if module == "__str__": code_string = function return code_string code_string = f"{function_name}(" if len(node.args) > 0: arg_list = [] arg_count = len(node.args) if arg_count == 1 and \ module == "set_": # This is a setter function for the string arg_string = self.generate_code(node.args[0], state) arg_string = f"{arg_string}[0:{self.string_length}]" code_string = f'{arg_string}.' \ f'ljust({self.string_length}, " ")' return code_string elif function_name == "String": # This is a String annotated assignment i.e. # String(10, "abcdef") arg_string = self.generate_code(node.args[1], state) arg_string = f"{arg_string}[0:{self.string_length}]" code_string = f'{arg_string}.' \ f'ljust({self.string_length}, " ")' return code_string elif module == "f_index": # This is the f_index function find_string = self.generate_code(node.args[0], state) if arg_count == 1: code_string = f"{function}.find({find_string})+1" else: code_string = f"{function}.rfind({find_string})+1" return code_string elif module == "get_substr": start_id = self.generate_code(node.args[0], state) end_id = self.generate_code(node.args[1], state) code_string = f"{function}[{start_id}-1:{end_id}]" return code_string elif module == "set_substr": start_id = self.generate_code(node.args[0], state) end_id = self.generate_code(node.args[1], state) source_string = self.generate_code(node.args[2], state) new_index = f"{end_id}-{start_id}+1" prefix = f"{function_name}[:{start_id}-1]" suffix = f"{function_name}[{end_id}:]" new_string = f"{source_string}[:{new_index}]" code_string = f"{prefix}+{new_string}+{suffix}" return code_string self.current_function = function_name for arg_index in range(arg_count): arg_string = self.generate_code(node.args[arg_index], state) arg_list.append(arg_string) # Change the max() and min() functions for numpy # implementation if self.use_numpy and self.target_arr: for index, arg_string in enumerate(arg_list): if self._is_number(arg_string): arg_list[index] = f"np.full_like(" \ f"{self.target_arr}, {arg_string})" code_string += ", ".join(arg_list) code_string += ")" self.current_function = None else: code_string = self.generate_code(node.args[1], state) return code_string
[docs] def process_import(self, node, state): code_string = "import {0}{1}".format( ", ".join([self.generate_code(name, state) for name in node.names]), state.sep ) return code_string
@staticmethod def _process_alias(node, *_): if node.asname is None: code_string = node.name else: code_string = "{0} as {1}".format(node.name, node.asname) return code_string
[docs] def process_module(self, node, state): code_string = self.generate_code(node.body, state) return code_string
[docs] def process_boolean_operation(self, node, state): bool_tests = [self.generate_code(node.values[i], state) for i in range( len(node.values))] bool_operation = self.generate_code(node.op, state) code_string = f" {bool_operation} ".join(bool_tests) return f"({code_string})"
[docs] def process_attribute(self, node, *_): # Code below will be kept until all tests pass and removed if they do # lambda_string = genCode(node.value, state) # This is a fix on `feature_save` branch to bypass the SAVE statement # feature where a SAVEd variable is referenced as # <function_name>.<variable_name>. So the code below only returns the # <variable_name> which is stored under `node.attr`. code_string = node.attr if self.use_numpy: if self.current_function in ["np.maximum", "np.minimum"] and not \ self.target_arr: self.target_arr = code_string return code_string
[docs] @staticmethod def process_ast(node, *_): sys.stderr.write( "No handler for AST.{0} in genCode, fields: {1}\n".format( node.__class__.__name__, node._fields ) )
@staticmethod def _is_number(s): try: float(s) return True except ValueError: return False