def compose_entry_point(self, given_body): """ Generate an entry point function for the environment model. :param given_body: Body of the main function provided by a translator. :return: List of C statements of the generated function body. """ ep = Function(self.entry_name, "int {}(void)".format(self.entry_name)) ep.definition_file = self.entry_file body = ['/* EMG_ACTION {' + '"thread": 1, "type": "CONTROL_FUNCTION_BEGIN", "comment": "Entry point \'{0}\'", ' '"function": "{0}"'.format(self.entry_name) + '} */'] # Init external allocated pointers cnt = 0 functions = [] if len(self.__external_allocated.keys()) > 0: for file, ext_vars in ((f, v) for f, v in self.__external_allocated.items() if v): func = Function('emg_allocate_external_{}'.format(cnt), "void emg_allocate_external_{}(void)".format(cnt)) func.declaration_files.add(file) func.definition_file = file init = ["{} = {}();".format(var.name, 'external_allocated_data') for var in ext_vars] func.body = init self.add_function_definition(func) self.add_function_declaration(self.entry_file, func, extern=True) functions.append(func) cnt += 1 gl_init = Function('emg_initialize_external_data', 'void emg_initialize_external_data(void)') gl_init.declaration_files.add(self.entry_file) gl_init.definition_file = self.entry_file init_body = ['{}();'.format(func.name) for func in functions] gl_init.body = init_body self.add_function_definition(gl_init) body += [ '/* Initialize external data */', 'emg_initialize_external_data();' ] if self._conf.get("initialize requirements", True): body.append('ldv_initialize();') comment_data = {'action': 'scenarios'} body += [model_comment('ACTION_BEGIN', 'Begin Environment model scenarios', comment_data)] + given_body + \ [model_comment('ACTION_END', other=comment_data)] if self._conf.get("check final state", True): body.append('ldv_check_final_state();') body += ['return 0;', '/* EMG_ACTION {' + '"comment": "Exit entry point \'{0}\'", "type": "CONTROL_FUNCTION_END", "function": "{0}"'. format(self.entry_name) + '} */'] ep.body = body self.add_function_definition(ep) return body
def _replace_comment(self, match): arguments = match.groups() if arguments[0] == 'callback': name = arguments[1] cmnt = model_comment('callback', name, {'call': "{}();".format(name)}) return cmnt else: raise NotImplementedError("Replacement of {!r} comments is not implemented".format(arguments[0]))
def control_function_comment_end(function_name, name): """ Compose a comment at the end of a control function. :param function_name: Control function name. :param name: Process or Automaton name. :return: Model comment string. """ data = {'function': function_name} return model_comment('CONTROL_FUNCTION_END', "End of control function based on process {!r}".format(name), data)
def control_function_comment_begin(function_name, comment, identifier=None): """ Compose a comment at the beginning of a control function. :param function_name: Control function name. :param comment: Comment text. :param identifier: Thread identifier if necessary. :return: Model comment string. """ data = {'function': function_name} if isinstance(identifier, int): data['thread'] = identifier + 1 elif identifier is None: pass else: raise ValueError('Unsupported identifier type {}'.format(str(type(identifier).__name__))) return model_comment('CONTROL_FUNCTION_BEGIN', comment, data)
def action_model_comment(action, text, begin=None): """ Model comment for identifying an action. :param action: Action object. :param text: Action comment string. :param begin: True if this is a comment before the action and False otherwise. :return: Model comment string. """ type_comment = 'ACTION' if begin is True: type_comment += '_BEGIN' else: type_comment += '_END' data = {'name': str(action)} if action and action.trace_relevant and begin is True: data['relevant'] = True return model_comment(type_comment, text, data)
def __init__(self, logger, conf, source, cmodel, entry_fsa, model_fsa, event_fsa): """ Initialize new FSA translation object. During the initialization an enviornment model in form of finite state machines with process-like actions is translated to C code. Translation includes the following steps: each pair label-interface is translated in a separate variable, each action is translated in code blocks (aux functions can be additionally generated), for each automaton a control function is generated, control functions for event modeling are called in a specific entry point function and control functions for function modeling are called insted of modelled functions. This class has an abstract methods to provide ability to implement different translation. :param logger: Logger object. :param conf: Configuration properties dictionary. :param source: Source collection object. :param cmodel: CModel object. :param entry_fsa: An entry point Automaton object. :param model_fsa: List with Automaton objects which correspond to function models. :param event_fsa: List with Automaton objects for event modeling. """ self._cmodel = cmodel self._entry_fsa = entry_fsa self._model_fsa = model_fsa self._event_fsa = event_fsa self._conf = conf self._source = source self._logger = logger self._structures = sortedcontainers.SortedDict() self._control_functions = sortedcontainers.SortedDict() self._logger.info("Include extra header files if necessary") conf.setdefault('do not skip signals', False) # Get from unused interfaces for process in (a.process for a in self._model_fsa + self._event_fsa if len(a.process.headers) > 0): self._cmodel.add_headers(process.file, sorted(process.headers, key=len)) # Generates base code blocks self._logger.info("Start the preparation of actions code") for automaton in self._event_fsa + self._model_fsa + [self._entry_fsa]: self._logger.debug( "Generate code for instance {!r} of process {!r} of categorty {!r}" .format(str(automaton), automaton.process.name, automaton.process.category)) for action in automaton.process.actions.filter(include={Action}): self._compose_action(action, automaton) # Start generators of control functions for automaton in self._event_fsa + self._model_fsa + [self._entry_fsa]: self._compose_control_function(automaton) # Generate aspects with kernel models for automaton in self._model_fsa: aspect_code = [ model_comment( 'FUNCTION_MODEL', 'Perform the model code of the function {!r}'.format( automaton.process.name)) ] function_obj = self._source.get_source_function( automaton.process.name) params = [] for position, param in enumerate( function_obj.declaration.parameters): if isinstance(param, str): params.append(param) else: params.append('$arg{}'.format(str(position + 1))) if not params and function_obj.declaration.return_value == 'void': arguments = [] ret_expression = '' elif not params: arguments = [] ret_expression = 'return ' elif function_obj.declaration.return_value == 'void': arguments = params ret_expression = '' else: ret_expression = 'return ' arguments = params if arguments and '...' == arguments[-1]: arguments = arguments[:-1] invoke = '{}{}({});'.format(ret_expression, self._control_function(automaton).name, ', '.join(arguments)) aspect_code.append(invoke) self._cmodel.add_function_model(function_obj, aspect_code) # Generate entry point function self._entry_point() # Add types for pair in self._structures.values(): file, decl = pair self._cmodel.types.setdefault(file, list()) if decl not in self._cmodel.types[file]: self._cmodel.types[file].append(decl) return
def _compose_control_function(self, automaton): self._logger.info('Generate state-based control function for automaton {} based on process {} of category {}'. format(automaton.identifier, automaton.process.name, automaton.process.category)) # Get function prototype cf = self._control_function(automaton) cf.definition_file = automaton.process.file # Do process initialization model_flag = True if automaton not in self._model_fsa: model_flag = False v_code = ["/* Control function based on process '{}' generated for interface category '{}' */". format(automaton.process.name, automaton.process.category)] f_code = [] tab = 0 state_chains = self.__state_chains(automaton) if len(state_chains) == 0: f_code.append('/* Empty control function */') else: if len(state_chains) == 1: new_v_code, new_f_code = self.__state_chain_code(automaton, list(state_chains.values())[0]) v_code.extend(new_v_code) f_code.extend(['\t' * tab + stm for stm in new_f_code]) else: f_code.append('\t' * tab + 'switch ({}) '.format(self.__state_variable(automaton).name) + '{') tab += 1 for case in sorted(list(state_chains.keys())): f_code.append('\t' * tab + 'case {}: '.format(case) + '{') tab += 1 new_v_code, new_f_code = self.__state_chain_code(automaton, state_chains[case]) v_code.extend(new_v_code) f_code.extend(['\t' * tab + stm for stm in new_f_code]) f_code.append('\t' * tab + 'break;') tab -= 1 f_code.append('\t' * tab + '}') f_code.append('\t' * tab + 'default: ldv_assume(0);') tab -= 1 f_code.append('\t' * tab + '}') # Add comments comment_data = {'name': 'var_init'} # TODO: Reimplement this v_code = [model_comment('CONTROL_FUNCTION_INIT_BEGIN', 'Declare auxiliary variables.', comment_data)] + \ v_code + \ [model_comment('CONTROL_FUNCTION_INIT_END', 'Declare auxiliary variables.', comment_data)] v_code.insert(0, control_function_comment_begin(cf.name, automaton.process.comment, automaton.identifier)) f_code.append(control_function_comment_end(cf.name, automaton.process.category)) # Add loop for nested case cf.body.extend(v_code + f_code) self._cmodel.add_global_variable(self.__state_variable(automaton), automaton.process.file, extern=False, initialize=True) else: # Generate function body label_based_function(self._conf, self._source, automaton, cf, model_flag) # Add function to source code to print self._cmodel.add_function_definition(cf) if model_flag: for file in self._source.get_source_function(automaton.process.name).declaration_files: self._cmodel.add_function_declaration(file, cf, extern=True) else: for var in automaton.variables(): self._cmodel.add_global_variable(var, automaton.process.file, initialize=False) return
def label_based_function(conf, analysis, automaton, cf, model=True): v_code, f_code = list(), list() # Determine returning expression for reuse if not conf.get('direct control functions calls') and not model: ret_expression = 'return 0;' else: ret_expression = 'return;' if model: kfunction_obj = analysis.get_source_function(automaton.process.name) if kfunction_obj.declaration.return_value != 'void': ret_expression = None # Then add memory external allocation marks f_code.extend(initialize_automaton_variables(conf, automaton)) # Initialize variables # First add variables declarations for var in automaton.variables(only_used=True): scope = {automaton.process.file} if automaton.process.file else None v_code.append(var.declare(scope=scope) + ';') # After that assign explicit values for var in (v for v in automaton.variables(only_used=True) if v.value): f_code.append("{} = {};".format(var.name, var.value)) # Intialize repeat counters for behaviour in ( b for b in automaton.process.actions.behaviour() if isinstance(b, Behaviour) and isinstance( b.description, Subprocess) and isinstance(b.repeat, int)): var = __repeate_subprocess_var_name(automaton, behaviour) v_code.append("int {} = {};".format(var, behaviour.repeat)) main_v_code, main_f_code = __subprocess_code( automaton, automaton.process.actions.initial_action) v_code += main_v_code f_code += main_f_code + ["/* End of the process */"] if ret_expression: f_code.append(ret_expression) processed = set() for subp in automaton.process.actions.filter(include={Subprocess}): if subp.name not in processed: first_actual_state = subp.action sp_v_code, sp_f_code = __subprocess_code(automaton, first_actual_state) v_code.extend(sp_v_code) f_code.extend([ '', '/* Subprocess {} */'.format(subp.name), 'emg_{}_{}:'.format(str(subp.name), str(automaton)) ]) f_code.extend(sp_f_code) f_code.append("/* End of the subprocess '{}' */".format(subp.name)) if ret_expression: f_code.append(ret_expression) processed.add(subp.name) comment_data = {'name': 'aux_variables_declaration'} v_code = [model_comment('ACTION_BEGIN', 'Declare auxiliary variables.', comment_data)] + \ v_code + \ [model_comment('ACTION_END', other=comment_data)] if model: name = automaton.process.name v_code.insert( 0, control_function_comment_begin(cf.name, automaton.process.comment)) else: name = '{}({})'.format(automaton.process.name, automaton.process.category) v_code.insert( 0, control_function_comment_begin(cf.name, automaton.process.comment, automaton.identifier)) f_code.append(control_function_comment_end(cf.name, name)) cf.body.extend(v_code + f_code) return cf.name