def _transform(self): from niveristand._decorators import rt_seq_mode_id real_obj = getattr(self._top_level_func, rt_seq_mode_id, None) if real_obj is None: raise TranslateError(_errormessages.invalid_top_level_func) src = inspect.getsource(real_obj) top_node = ast.parse(src) try: func_node = top_node.body[0] except TypeError: func_node = None if func_node is None or not isinstance(func_node, ast.FunctionDef): raise TranslateError(_errormessages.invalid_top_level_func) self._rtseq = rtseqapi.create_real_time_sequence() transform_resources = Resources(self._rtseq, str(self)) if self._rtseqpkg is not None: transform_resources.set_dependency_pkg(self._rtseqpkg) utils.generic_ast_node_transform(func_node, transform_resources) self._rtseqpkg = transform_resources.get_dependency_pkg() self._rtseqpkg.append(inspect.getmodule(real_obj)) self._update_parameters(transform_resources.get_parameters()) self._update_channel_refs(transform_resources.get_all_channel_refs()) self.save() rtsequtils.compile_rtseq(self._rtseq)
def call_transformer(node, resources): if rtprimitives.is_channel_ref_type(node.func.id): if utils.is_node_ast_str(node.args[0]): node_value = utils.get_value_from_str_node(node.args[0]) identifier = resources.get_channel_ref_rtseq_name_from_channel_name(node_value) else: raise errors.TranslateError(_errormessages.invalid_type_for_channel_ref) return identifier if rtprimitives.is_supported_data_type(node.func.id): if rtprimitives.is_scalar_type(node.func.id): return _transform_data_type_scalar(node) elif isinstance(node.args[0], ast.List): return _transform_datatype_non_scalar(node, resources) else: raise errors.TranslateError(_errormessages.init_var_invalid_type) if node.func.id in custom_action_symbols._custom_action_symbols: # Custom action symbols are basically transformers for functions that don't have # their own ast node. Invoke them here return custom_action_symbols._custom_action_symbols[node.func.id](node, resources) if node.func.id in symbols._symbols: # In case of a builtin expression get it out from symbols and add any arguments it may have func_name = symbols._symbols[node.func.id] else: # It only can be a RT sequence call, so treat it accordingly func_name = str(utils.generic_ast_node_transform(node.func, resources)) resources.add_referenced_sequence(func_name) node_str = func_name + "(" for arg in node.args: node_str += str(utils.generic_ast_node_transform(arg, resources)) node_str += " ," if not node.args: return node_str + ")" else: # remove space and comma return node_str[:-2] + ")"
def try_transformer(node, resources): _validate_restrictions(node) for stmt in node.body: utils.generic_ast_node_transform(stmt, resources) resources.set_current_block(resources.get_rtseq().Code.CleanUp) for stmt in node.finalbody: utils.generic_ast_node_transform(stmt, resources)
def for_transformer(node, resources): _validate_restrictions(node) parent_block = resources.get_current_block() var_name = utils.get_variable_name_from_node(node.iter) if resources.has_variable(var_name): collection_value = resources.get_variable_py_value(var_name) if not isinstance(collection_value, ArrayType): raise TranslateError(_errormessages.scalar_iterable_collection) collection = resources.get_variable_rtseq_name(var_name) iterator = node.target.id for_statement = rtseqapi.add_foreach_loop(parent_block, iterator, collection) # add the iterator as local variable, so that attribute_transformer returns the actual rtseq name of # the iterator resources.add_variable(iterator, 0, iterator) resources.add_variable(iterator + ".value", 0, iterator) elif utils.generic_ast_node_transform(node.iter, resources).startswith("range("): if len(node.iter.args) > 1: raise TranslateError(_errormessages.invalid_range_call) max_range = utils.generic_ast_node_transform(node.iter.args[0], resources) variable = node.target.id for_statement = rtseqapi.add_for_loop(parent_block, variable, max_range) else: raise TranslateError(_errormessages.invalid_iterable_collection) for statement in node.body: resources.set_current_block(for_statement.Body) utils.generic_ast_node_transform(statement, resources) resources.set_current_block(parent_block)
def while_transformer(node, resources): _validate_restrictions(node, resources) test_condition = utils.generic_ast_node_transform(node.test, resources) parent_block = resources.get_current_block() while_statement = rtseqapi.add_while(parent_block, test_condition) for statement in node.body: resources.set_current_block(while_statement.Body) utils.generic_ast_node_transform(statement, resources) resources.set_current_block(parent_block)
def assign_transformer(node, resources): node_value = None initial_channel_ref_declaration = False lhs = node.targets[0] rtseq_var_name = utils.generic_ast_node_transform(lhs, resources) # the left hand side can only be a variable name or a name with an attribute (var.value) if isinstance(lhs, ast.Name): variable_name = utils.get_variable_name_from_node(lhs) # if the variable already exists this kind of assignment is invalid, use var.value instead if resources.has_variable(variable_name): raise TranslateError(_errormessages.variable_reassignment) elif isinstance(lhs, ast.Attribute): # in case of var[0].value get rid of the [0] part and search in the dictionary for var stripped_rtseq_var_name = rtseq_var_name[:rtseq_var_name.find("[")] if rtseq_var_name.find("[") != -1 \ else rtseq_var_name variable_name = resources.get_variable_py_name(stripped_rtseq_var_name) else: raise TranslateError(_errormessages.variable_reassignment) rtseq = resources.get_rtseq() block = resources.get_current_block() if not resources.has_variable(variable_name): # new local variable node_value = utils.get_value_from_node(node.value, resources) if isinstance( node_value, (_datatypes.ChannelReference, _datatypes.VectorChannelReference)): initial_channel_ref_declaration = True channel_name = utils.get_channel_name(node.value.args[0]) rtseq_var_name = rtseqapi.to_channel_ref_name(variable_name) resources.add_channel_ref( variable_name, channel_name, rtseq_var_name, isinstance(node_value, _datatypes.ArrayType)) elif isinstance(node_value, _datatypes.DataType): rtseq_var_name = rtseqapi.add_local_variable( rtseq, variable_name, node_value) # add the local variable's accessor to the resources resources.add_variable(variable_name, node_value, rtseq_var_name) resources.add_variable(variable_name + ".value", node_value, rtseq_var_name) else: raise TranslateError(_errormessages.init_var_invalid_type) transformed_node_value = utils.generic_ast_node_transform( node.value, resources) rtseq_var_name = resources.get_variable_rtseq_name( variable_name) if not rtseq_var_name else rtseq_var_name if not initial_channel_ref_declaration: if isinstance(node_value, _datatypes.ArrayType): if transformed_node_value.count(',') > 0: value_list = transformed_node_value.split(',') for index, val in enumerate(value_list): rtseqapi.add_assignment( block, rtseq_var_name + "[" + str(index) + "]", val) else: rtseqapi.add_assignment(block, rtseq_var_name, transformed_node_value)
def binaryoperator_transformer(node, resources): operator = _operator(node.op.__class__.__name__) if operator == "unknown": raise VeristandNotImplementedError() if operator in ('<<', '>>'): # Validate only the right hand side, on the left it makes sense to have negative numbers. validations.raise_if_negative_binary_operator_operand( node.right, resources) left = utils.generic_ast_node_transform(node.left, resources) right = utils.generic_ast_node_transform(node.right, resources) return "((" + left + ") " + operator + " (" + right + "))"
def if_transformer(node, resources): _validate_restrictions(node) test_condition = utils.generic_ast_node_transform(node.test, resources) parent_block = resources.get_current_block() if_else_statement = rtseqapi.add_if_else(parent_block, test_condition) for statement in node.body: resources.set_current_block(if_else_statement.IfTrue) utils.generic_ast_node_transform(statement, resources) for statement in node.orelse: resources.set_current_block(if_else_statement.IfFalse) utils.generic_ast_node_transform(statement, resources) resources.set_current_block(parent_block)
def booloperator_transformer(node, resources): operator = _operator(node.op.__class__.__name__) if operator == "unknown": raise VeristandNotImplementedError() for value in node.values: validations.raise_if_invalid_bool_operand(value, resources) result = "( " + utils.generic_ast_node_transform(node.values[0], resources) + " " for o in node.values[1:]: op = utils.generic_ast_node_transform(o, resources) result += operator + " " + op + " " return result + ")"
def compareoperator_transformer(node, resources): left = utils.generic_ast_node_transform(node.left, resources) result = "((" + left + ") " if len(node.ops) > 1: raise TranslateError( _errormessages.cascaded_comparison_operators_not_allowed) operator = _operator(node.ops[0].__class__.__name__) if operator == "unknown": raise VeristandNotImplementedError() right = utils.generic_ast_node_transform(node.comparators[0], resources) result += operator + " (" + right + ")" result += ")" return result
def with_transformer(node, resources): mt_name = _validate_multitask(node) parent_block = resources.get_current_block() multi_task = rtseqapi.add_multi_task(parent_block) for task_def in [ func_def for func_def in node.body if isinstance(func_def, ast.FunctionDef) ]: _validate_task(task_def, mt_name) task_block = rtseqapi.add_task(multi_task, task_def.name) resources.set_current_block(task_block) for stmt in task_def.body: utils.generic_ast_node_transform(stmt, resources) resources.set_current_block(parent_block)
def return_transformer(node, resources): _validate_restrictions(node) rtseq = resources.get_rtseq() expression = utils.generic_ast_node_transform(node.value, resources) stripped_expression = expression[:expression.find("[")] if expression.find( "[") != -1 else expression if isinstance(node.value, (ast.Name, ast.Attribute, ast.Subscript)): src_var_name = utils.get_variable_name_from_node(node.value) if resources.has_variable( str(src_var_name )) and not resources.has_channel_ref(stripped_expression): rt_expression = expression return_default_value = resources.get_variable_py_value( src_var_name) # In case of "return var[0]" the default value is saved as a list, so make sure to not return list type # because those cannot be return variables. # The len check is there to not get index error in case of empty lists -> for that I don't know yet # what the solution is, so I will leave it like this (Arnold) if isinstance(return_default_value, ArrayType): if len(return_default_value.value): return_default_value = return_default_value[0] else: raise TranslateError(_errormessages.invalid_return_value) elif isinstance(node.value, (ast.Num, ast.Call)): return_default_value = utils.get_value_from_node(node.value, resources) if isinstance(return_default_value, ArrayType): raise TranslateError(_errormessages.invalid_return_type) rt_expression = expression else: raise TranslateError(_errormessages.invalid_return_value) var_name = rtseqapi.add_return_variable(rtseq, '__ret_var__', return_default_value) rtseqapi.add_assignment(resources.get_current_block(), var_name, rt_expression) return "return " + str(expression)
def _init_args(node, resources): # default values for now by_value = False def_value = DoubleValue(0) if type(node) is ast.Name: arg_name = utils.generic_ast_node_transform(node, resources) elif 'arg' in dir(ast) and type(node) is ast.arg: arg_name = node.arg return _param(arg_name, def_value, by_value)
def list_transformer(node, resources): list_string = "" if not node.elts: return list_string else: for element in node.elts: list_string += utils.generic_ast_node_transform( element, resources) + "," return list_string[:-1]
def exp_transformer(node, resources): if validations.check_if_looks_like_doc_block(node): exp = "" else: exp = utils.generic_ast_node_transform(node.value, resources) if bool(exp): # Custom actions generate their own expressions and return None # so only add an expression if something was returned rtseqapi.add_expression(resources.get_current_block(), exp) return exp
def functiondef_transformer(node, resources): if validations.check_if_looks_like_doc_block(node.body[0]): node.body = node.body[1:] _validate_restrictions(node) for param in node.args.args: p = _init_args(param, resources) resources.add_parameter(p.name, p.def_value, p.by_value) for decorator in [ dec for dec in node.decorator_list if type(dec) is ast.Call ]: # the NivsParam decorator is a class, so it gets used like this: # @NivsParam(param_name, DataType(default), BY_REF) # which in ast terms is treated as an ast.Call. So, we look for those and convert to args. p = _decorator_to_arg(decorator, resources) resources.update_parameter(p.name, p.def_value, p.by_value) # reparenting should happen in each transformer that contains nested blocks resources.set_current_block(resources.get_rtseq().Code.Main.Body) for instruction in node.body: utils.generic_ast_node_transform(instruction, resources) return ""
def attribute_transformer(node, resources): var_name = utils.get_variable_name_from_node(node) if resources.has_variable(var_name): if isinstance(node.value, ast.Subscript): return utils.generic_ast_node_transform(node.value, resources) else: return resources.get_variable_rtseq_name(var_name) try: # Try to get the value of the node in case it's a DataType(x).value style. node_value = utils.get_value_from_node(node.value, resources) return str(node_value) except errors.TranslateError: # If we get a TranslateError it's because it wasn't a DataType(x).value, so move on. pass built_exp = utils.generic_ast_node_transform(node.value, resources) + '.' + node.attr if built_exp in symbols._symbols: return symbols._symbols[built_exp] else: raise errors.TranslateError(_errormessages.unknown_identifier % var_name)
def custom_generate_error(node, resources): _validate_restrictions(node) try: error_code = eval( utils.generic_ast_node_transform(node.args[0], resources)) except NameError: raise TranslateError( _errormessages.invalid_error_code_for_generate_error) message = node.args[1].s action = ErrorAction[node.args[2].attr] realtimesequencedefinition.add_generate_error( resources.get_current_block(), error_code, message, action.value) return ""
def unaryoperator_transformer(node, resources): operator = _operator(node.op.__class__.__name__) if operator == "unknown": raise VeristandNotImplementedError() if operator == '!': if isinstance(node.operand, ast.UnaryOp): validations.raise_if_invalid_bool_operand(node.operand.operand, resources) else: validations.raise_if_invalid_bool_operand(node.operand, resources) if operator == '~': validations.raise_if_invalid_invert_operand(node.operand, resources) operand = utils.generic_ast_node_transform(node.operand, resources) return "(" + operator + "(" + operand + ")" + ")"
def custom_math_log(node, resources): if len(node.args) == 1: func = 'ln' else: func = 'log' node_str = func + "(" for arg in node.args: node_str += str(utils.generic_ast_node_transform(arg, resources)) node_str += " ," if not node.args: return node_str + ")" else: # remove space and comma return node_str[:-2] + ")"
def ifexp_transformer(node, resources): validations.raise_if_invalid_if_test(node.test) test = utils.generic_ast_node_transform(node.test, resources) body = utils.generic_ast_node_transform(node.body, resources) orelse = utils.generic_ast_node_transform(node.orelse, resources) return "(" + test + ") ? (" + body + ") : (" + orelse + ")"
def subscript_transformer(node, resources): variable_name = utils.get_variable_name_from_node(node) rtseq_var_name = resources.get_variable_rtseq_name(variable_name) index = utils.generic_ast_node_transform(node.slice, resources) return rtseq_var_name + "[" + index + "]"
def index_transformer(node, resources): index = utils.generic_ast_node_transform(node.value, resources) return index
def augassign_transformer(node, resources): bin_op = ast.BinOp(node.target, node.op, node.value) assign_node = ast.Assign([node.target], bin_op) return utils.generic_ast_node_transform(assign_node, resources)
def _transform_datatype_non_scalar(node, resources): data_value_str = utils.generic_ast_node_transform(node.args[0], resources) return data_value_str
def module_transformer(node, resources): rtseqfuncs = [rtf for rtf in ast.iter_child_nodes(node) if type(rtf) is ast.FunctionDef and _has_rtseq_decorator(rtf)] for rtseqfunc in rtseqfuncs: utils.generic_ast_node_transform(rtseqfunc, resources) return ""