162306a36Sopenharmony_ci#!/usr/bin/env python3
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only
362306a36Sopenharmony_ci#
462306a36Sopenharmony_ci# Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
562306a36Sopenharmony_ci#
662306a36Sopenharmony_ci# Automata object: parse an automata in dot file digraph format into a python object
762306a36Sopenharmony_ci#
862306a36Sopenharmony_ci# For further information, see:
962306a36Sopenharmony_ci#   Documentation/trace/rv/deterministic_automata.rst
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ciimport ntpath
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciclass Automata:
1462306a36Sopenharmony_ci    """Automata class: Reads a dot file and part it as an automata.
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci    Attributes:
1762306a36Sopenharmony_ci        dot_file: A dot file with an state_automaton definition.
1862306a36Sopenharmony_ci    """
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci    invalid_state_str = "INVALID_STATE"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci    def __init__(self, file_path):
2362306a36Sopenharmony_ci        self.__dot_path = file_path
2462306a36Sopenharmony_ci        self.name = self.__get_model_name()
2562306a36Sopenharmony_ci        self.__dot_lines = self.__open_dot()
2662306a36Sopenharmony_ci        self.states, self.initial_state, self.final_states = self.__get_state_variables()
2762306a36Sopenharmony_ci        self.events = self.__get_event_variables()
2862306a36Sopenharmony_ci        self.function = self.__create_matrix()
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci    def __get_model_name(self):
3162306a36Sopenharmony_ci        basename = ntpath.basename(self.__dot_path)
3262306a36Sopenharmony_ci        if basename.endswith(".dot") == False:
3362306a36Sopenharmony_ci            print("not a dot file")
3462306a36Sopenharmony_ci            raise Exception("not a dot file: %s" % self.__dot_path)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci        model_name = basename[0:-4]
3762306a36Sopenharmony_ci        if model_name.__len__() == 0:
3862306a36Sopenharmony_ci            raise Exception("not a dot file: %s" % self.__dot_path)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci        return model_name
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci    def __open_dot(self):
4362306a36Sopenharmony_ci        cursor = 0
4462306a36Sopenharmony_ci        dot_lines = []
4562306a36Sopenharmony_ci        try:
4662306a36Sopenharmony_ci            dot_file = open(self.__dot_path)
4762306a36Sopenharmony_ci        except:
4862306a36Sopenharmony_ci            raise Exception("Cannot open the file: %s" % self.__dot_path)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci        dot_lines = dot_file.read().splitlines()
5162306a36Sopenharmony_ci        dot_file.close()
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci        # checking the first line:
5462306a36Sopenharmony_ci        line = dot_lines[cursor].split()
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci        if (line[0] != "digraph") and (line[1] != "state_automaton"):
5762306a36Sopenharmony_ci            raise Exception("Not a valid .dot format: %s" % self.__dot_path)
5862306a36Sopenharmony_ci        else:
5962306a36Sopenharmony_ci            cursor += 1
6062306a36Sopenharmony_ci        return dot_lines
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci    def __get_cursor_begin_states(self):
6362306a36Sopenharmony_ci        cursor = 0
6462306a36Sopenharmony_ci        while self.__dot_lines[cursor].split()[0] != "{node":
6562306a36Sopenharmony_ci            cursor += 1
6662306a36Sopenharmony_ci        return cursor
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci    def __get_cursor_begin_events(self):
6962306a36Sopenharmony_ci        cursor = 0
7062306a36Sopenharmony_ci        while self.__dot_lines[cursor].split()[0] != "{node":
7162306a36Sopenharmony_ci           cursor += 1
7262306a36Sopenharmony_ci        while self.__dot_lines[cursor].split()[0] == "{node":
7362306a36Sopenharmony_ci           cursor += 1
7462306a36Sopenharmony_ci        # skip initial state transition
7562306a36Sopenharmony_ci        cursor += 1
7662306a36Sopenharmony_ci        return cursor
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci    def __get_state_variables(self):
7962306a36Sopenharmony_ci        # wait for node declaration
8062306a36Sopenharmony_ci        states = []
8162306a36Sopenharmony_ci        final_states = []
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci        has_final_states = False
8462306a36Sopenharmony_ci        cursor = self.__get_cursor_begin_states()
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci        # process nodes
8762306a36Sopenharmony_ci        while self.__dot_lines[cursor].split()[0] == "{node":
8862306a36Sopenharmony_ci            line = self.__dot_lines[cursor].split()
8962306a36Sopenharmony_ci            raw_state = line[-1]
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci            #  "enabled_fired"}; -> enabled_fired
9262306a36Sopenharmony_ci            state = raw_state.replace('"', '').replace('};', '').replace(',','_')
9362306a36Sopenharmony_ci            if state[0:7] == "__init_":
9462306a36Sopenharmony_ci                initial_state = state[7:]
9562306a36Sopenharmony_ci            else:
9662306a36Sopenharmony_ci                states.append(state)
9762306a36Sopenharmony_ci                if self.__dot_lines[cursor].__contains__("doublecircle") == True:
9862306a36Sopenharmony_ci                    final_states.append(state)
9962306a36Sopenharmony_ci                    has_final_states = True
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci                if self.__dot_lines[cursor].__contains__("ellipse") == True:
10262306a36Sopenharmony_ci                    final_states.append(state)
10362306a36Sopenharmony_ci                    has_final_states = True
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci            cursor += 1
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci        states = sorted(set(states))
10862306a36Sopenharmony_ci        states.remove(initial_state)
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci        # Insert the initial state at the bein og the states
11162306a36Sopenharmony_ci        states.insert(0, initial_state)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci        if has_final_states == False:
11462306a36Sopenharmony_ci            final_states.append(initial_state)
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci        return states, initial_state, final_states
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci    def __get_event_variables(self):
11962306a36Sopenharmony_ci        # here we are at the begin of transitions, take a note, we will return later.
12062306a36Sopenharmony_ci        cursor = self.__get_cursor_begin_events()
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci        events = []
12362306a36Sopenharmony_ci        while self.__dot_lines[cursor][1] == '"':
12462306a36Sopenharmony_ci            # transitions have the format:
12562306a36Sopenharmony_ci            # "all_fired" -> "both_fired" [ label = "disable_irq" ];
12662306a36Sopenharmony_ci            #  ------------ event is here ------------^^^^^
12762306a36Sopenharmony_ci            if self.__dot_lines[cursor].split()[1] == "->":
12862306a36Sopenharmony_ci                line = self.__dot_lines[cursor].split()
12962306a36Sopenharmony_ci                event = line[-2].replace('"','')
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci                # when a transition has more than one lables, they are like this
13262306a36Sopenharmony_ci                # "local_irq_enable\nhw_local_irq_enable_n"
13362306a36Sopenharmony_ci                # so split them.
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci                event = event.replace("\\n", " ")
13662306a36Sopenharmony_ci                for i in event.split():
13762306a36Sopenharmony_ci                    events.append(i)
13862306a36Sopenharmony_ci            cursor += 1
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci        return sorted(set(events))
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci    def __create_matrix(self):
14362306a36Sopenharmony_ci        # transform the array into a dictionary
14462306a36Sopenharmony_ci        events = self.events
14562306a36Sopenharmony_ci        states = self.states
14662306a36Sopenharmony_ci        events_dict = {}
14762306a36Sopenharmony_ci        states_dict = {}
14862306a36Sopenharmony_ci        nr_event = 0
14962306a36Sopenharmony_ci        for event in events:
15062306a36Sopenharmony_ci            events_dict[event] = nr_event
15162306a36Sopenharmony_ci            nr_event += 1
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci        nr_state = 0
15462306a36Sopenharmony_ci        for state in states:
15562306a36Sopenharmony_ci            states_dict[state] = nr_state
15662306a36Sopenharmony_ci            nr_state += 1
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci        # declare the matrix....
15962306a36Sopenharmony_ci        matrix = [[ self.invalid_state_str for x in range(nr_event)] for y in range(nr_state)]
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci        # and we are back! Let's fill the matrix
16262306a36Sopenharmony_ci        cursor = self.__get_cursor_begin_events()
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci        while self.__dot_lines[cursor][1] == '"':
16562306a36Sopenharmony_ci            if self.__dot_lines[cursor].split()[1] == "->":
16662306a36Sopenharmony_ci                line = self.__dot_lines[cursor].split()
16762306a36Sopenharmony_ci                origin_state = line[0].replace('"','').replace(',','_')
16862306a36Sopenharmony_ci                dest_state = line[2].replace('"','').replace(',','_')
16962306a36Sopenharmony_ci                possible_events = line[-2].replace('"','').replace("\\n", " ")
17062306a36Sopenharmony_ci                for event in possible_events.split():
17162306a36Sopenharmony_ci                    matrix[states_dict[origin_state]][events_dict[event]] = dest_state
17262306a36Sopenharmony_ci            cursor += 1
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci        return matrix
175