automata.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/env python3
  2. # SPDX-License-Identifier: GPL-2.0-only
  3. #
  4. # Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <[email protected]>
  5. #
  6. # Automata object: parse an automata in dot file digraph format into a python object
  7. #
  8. # For further information, see:
  9. # Documentation/trace/rv/deterministic_automata.rst
  10. import ntpath
  11. class Automata:
  12. """Automata class: Reads a dot file and part it as an automata.
  13. Attributes:
  14. dot_file: A dot file with an state_automaton definition.
  15. """
  16. invalid_state_str = "INVALID_STATE"
  17. def __init__(self, file_path):
  18. self.__dot_path = file_path
  19. self.name = self.__get_model_name()
  20. self.__dot_lines = self.__open_dot()
  21. self.states, self.initial_state, self.final_states = self.__get_state_variables()
  22. self.events = self.__get_event_variables()
  23. self.function = self.__create_matrix()
  24. def __get_model_name(self):
  25. basename = ntpath.basename(self.__dot_path)
  26. if basename.endswith(".dot") == False:
  27. print("not a dot file")
  28. raise Exception("not a dot file: %s" % self.__dot_path)
  29. model_name = basename[0:-4]
  30. if model_name.__len__() == 0:
  31. raise Exception("not a dot file: %s" % self.__dot_path)
  32. return model_name
  33. def __open_dot(self):
  34. cursor = 0
  35. dot_lines = []
  36. try:
  37. dot_file = open(self.__dot_path)
  38. except:
  39. raise Exception("Cannot open the file: %s" % self.__dot_path)
  40. dot_lines = dot_file.read().splitlines()
  41. dot_file.close()
  42. # checking the first line:
  43. line = dot_lines[cursor].split()
  44. if (line[0] != "digraph") and (line[1] != "state_automaton"):
  45. raise Exception("Not a valid .dot format: %s" % self.__dot_path)
  46. else:
  47. cursor += 1
  48. return dot_lines
  49. def __get_cursor_begin_states(self):
  50. cursor = 0
  51. while self.__dot_lines[cursor].split()[0] != "{node":
  52. cursor += 1
  53. return cursor
  54. def __get_cursor_begin_events(self):
  55. cursor = 0
  56. while self.__dot_lines[cursor].split()[0] != "{node":
  57. cursor += 1
  58. while self.__dot_lines[cursor].split()[0] == "{node":
  59. cursor += 1
  60. # skip initial state transition
  61. cursor += 1
  62. return cursor
  63. def __get_state_variables(self):
  64. # wait for node declaration
  65. states = []
  66. final_states = []
  67. has_final_states = False
  68. cursor = self.__get_cursor_begin_states()
  69. # process nodes
  70. while self.__dot_lines[cursor].split()[0] == "{node":
  71. line = self.__dot_lines[cursor].split()
  72. raw_state = line[-1]
  73. # "enabled_fired"}; -> enabled_fired
  74. state = raw_state.replace('"', '').replace('};', '').replace(',','_')
  75. if state[0:7] == "__init_":
  76. initial_state = state[7:]
  77. else:
  78. states.append(state)
  79. if self.__dot_lines[cursor].__contains__("doublecircle") == True:
  80. final_states.append(state)
  81. has_final_states = True
  82. if self.__dot_lines[cursor].__contains__("ellipse") == True:
  83. final_states.append(state)
  84. has_final_states = True
  85. cursor += 1
  86. states = sorted(set(states))
  87. states.remove(initial_state)
  88. # Insert the initial state at the bein og the states
  89. states.insert(0, initial_state)
  90. if has_final_states == False:
  91. final_states.append(initial_state)
  92. return states, initial_state, final_states
  93. def __get_event_variables(self):
  94. # here we are at the begin of transitions, take a note, we will return later.
  95. cursor = self.__get_cursor_begin_events()
  96. events = []
  97. while self.__dot_lines[cursor][1] == '"':
  98. # transitions have the format:
  99. # "all_fired" -> "both_fired" [ label = "disable_irq" ];
  100. # ------------ event is here ------------^^^^^
  101. if self.__dot_lines[cursor].split()[1] == "->":
  102. line = self.__dot_lines[cursor].split()
  103. event = line[-2].replace('"','')
  104. # when a transition has more than one lables, they are like this
  105. # "local_irq_enable\nhw_local_irq_enable_n"
  106. # so split them.
  107. event = event.replace("\\n", " ")
  108. for i in event.split():
  109. events.append(i)
  110. cursor += 1
  111. return sorted(set(events))
  112. def __create_matrix(self):
  113. # transform the array into a dictionary
  114. events = self.events
  115. states = self.states
  116. events_dict = {}
  117. states_dict = {}
  118. nr_event = 0
  119. for event in events:
  120. events_dict[event] = nr_event
  121. nr_event += 1
  122. nr_state = 0
  123. for state in states:
  124. states_dict[state] = nr_state
  125. nr_state += 1
  126. # declare the matrix....
  127. matrix = [[ self.invalid_state_str for x in range(nr_event)] for y in range(nr_state)]
  128. # and we are back! Let's fill the matrix
  129. cursor = self.__get_cursor_begin_events()
  130. while self.__dot_lines[cursor][1] == '"':
  131. if self.__dot_lines[cursor].split()[1] == "->":
  132. line = self.__dot_lines[cursor].split()
  133. origin_state = line[0].replace('"','').replace(',','_')
  134. dest_state = line[2].replace('"','').replace(',','_')
  135. possible_events = line[-2].replace('"','').replace("\\n", " ")
  136. for event in possible_events.split():
  137. matrix[states_dict[origin_state]][events_dict[event]] = dest_state
  138. cursor += 1
  139. return matrix