Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2"""The core module.""" 

3 

4from abc import ABC, abstractmethod 

5from typing import List, TypeVar, Generic, Set, Optional 

6 

7StateType = TypeVar('StateType') 

8SymbolType = TypeVar('SymbolType') 

9 

10 

11class Alphabet(Generic[SymbolType], ABC): 

12 """Abstract class to represent a finite alphabet.""" 

13 

14 @abstractmethod 

15 def get_symbol(self, index: int) -> SymbolType: 

16 """ 

17 Get the symbol of the alphabet, given the index. 

18 

19 :param index: the index. 

20 :return: the corresponding symbol. 

21 :raises ValueError: if there is not any symbol for that index. 

22 """ 

23 

24 @abstractmethod 

25 def get_symbol_index(self, symbol: SymbolType) -> int: 

26 """ 

27 Get the index of a symbol of the alphabet. 

28 

29 :param symbol: the symbol. 

30 :return: its index. 

31 :raises ValueError: if the symbol does not belong to the alphabet. 

32 """ 

33 

34 @property 

35 @abstractmethod 

36 def size(self) -> int: 

37 """ 

38 Get the size of the alphabet. 

39 

40 :return: the size of the alphabet. 

41 """ 

42 

43 def contains(self, symbol: SymbolType) -> bool: 

44 """ 

45 Check if a symbol is part of the alphabet. 

46 

47 :param symbol: the symbol. 

48 :return: True if the symbol if part of the alphabet, False otherwise. 

49 """ 

50 try: 

51 index = self.get_symbol_index(symbol) 

52 if index < 0 or index >= self.size: 

53 return False 

54 return symbol == self.get_symbol(index) 

55 except ValueError: 

56 return False 

57 

58 @abstractmethod 

59 def __iter__(self): 

60 """Iterate over the number of symbols.""" 

61 

62 def __len__(self): 

63 """Return the size of the alphabet.""" 

64 return self.size 

65 

66 def __eq__(self, other) -> bool: 

67 """Check that two alphabet are equal.""" 

68 return isinstance(other, Alphabet) and set(self) == set(other) 

69 

70 

71class FiniteAutomaton(Generic[StateType, SymbolType], ABC): 

72 """This is an interface for any finite state automaton (DFAs, NFAs...).""" 

73 

74 @property 

75 @abstractmethod 

76 def states(self) -> Set[StateType]: 

77 """ 

78 Get the set of states. 

79 

80 :return: the set of states of the automaton. 

81 """ 

82 

83 @property 

84 @abstractmethod 

85 def initial_states(self) -> Set[StateType]: 

86 """Get the set of initial states.""" 

87 

88 @property 

89 @abstractmethod 

90 def final_states(self) -> Set[StateType]: 

91 """Get the set of final states.""" 

92 

93 @abstractmethod 

94 def get_successors(self, state: StateType, symbol: SymbolType) -> Set[StateType]: 

95 """ 

96 Get the successors of the state in input when reading a symbol. 

97 

98 :param state: the state from which to compute the successors. 

99 :param symbol: the symbol of the transition. 

100 :return: the set of successor states. 

101 :raises ValueError: if the state does not belong to the automaton, or the symbol is not correct. 

102 """ 

103 

104 @property 

105 def size(self) -> int: 

106 """ 

107 Get the size of the automaton. 

108 

109 :return: the number of states of the automaton. 

110 """ 

111 return len(self.states) 

112 

113 def is_accepting(self, state: StateType) -> bool: 

114 """ 

115 Check whether a state is accepting. 

116 

117 :param state: the state of the automaton. 

118 :return: True if the state is accepting, false otherwise. 

119 :raises ValueError: if the state does not belong to the automaton. 

120 """ 

121 return state in self.final_states 

122 

123 def accepts(self, word: List[SymbolType]) -> bool: 

124 """ 

125 Check whether the automaton accepts the word. 

126 

127 :param word: the list of symbols. 

128 :return: True if the automaton accepts the word, False otherwise. 

129 """ 

130 current_states = self.initial_states 

131 for symbol in word: 

132 next_current_states = set() 

133 for state in current_states: 

134 next_current_states.update(self.get_successors(state, symbol)) 

135 current_states = next_current_states 

136 

137 return any(self.is_accepting(state) for state in current_states) 

138 

139 

140class DFA(Generic[StateType, SymbolType], FiniteAutomaton[StateType, SymbolType], ABC): 

141 """An interface for a deterministic finite state automaton.""" 

142 

143 @property 

144 @abstractmethod 

145 def initial_state(self) -> StateType: 

146 """Get the (unique) initial state.""" 

147 

148 @abstractmethod 

149 def get_successor(self, state: StateType, symbol: SymbolType) -> Optional[StateType]: 

150 """ 

151 Get the (unique) successor. 

152 

153 If not defined, return None. 

154 """ 

155 

156 @property 

157 def initial_states(self) -> Set[StateType]: 

158 """Get the set of initial states.""" 

159 return {self.initial_state} 

160 

161 def get_successors(self, state: StateType, symbol: SymbolType) -> Set[StateType]: 

162 """Get the successors.""" 

163 successor = self.get_successor(state, symbol) 

164 return {successor} if successor is not None else set() 

165 

166 

167# not used yet 

168class AutomataOperations(Generic[StateType, SymbolType], ABC): 

169 """An interface for automata operations.""" 

170 

171 @abstractmethod 

172 def determinize(self) -> DFA[StateType, SymbolType]: 

173 """Make the automaton deterministic.""" 

174 

175 @abstractmethod 

176 def minimize(self) -> FiniteAutomaton[StateType, SymbolType]: 

177 """Minimize the automaton."""