Esempio n. 1
0
 def nfa_maker(self):
     self.get_alph()
     self.my_nfa = NFA([])
     self.get_parts()
     states = self.exp_parts(self.regex, 0)
     self.my_nfa.states = states
     self.my_nfa.alphabet = list(self.alphabet)
Esempio n. 2
0
def test1():
    s0 = state(0)
    s1 = state(1)
    s0.outpaths = {'b': [s0, s1]}
    s1.outpaths = {'a': [s1], 'b': [s0]}
    n = NFA([s0, s1])
    n.start_states = [s0]
    n.accept_states = [s1]
    n.alphabet = ['a', 'b']
    n.nfa_to_dfa()
    n.my_dfa.print_dfa()
    """
Esempio n. 3
0
def degree_of_ambiguity(automaton: NFA, length: int) -> tuple:

    max_word: str = ""
    max_path_num: int = 0

    for word in create_permutations(automaton.alphabet, length):

        word_ambiguity: int = automaton.ambiguity(word)

        if max_path_num < word_ambiguity:
            max_word = word
            max_path_num = word_ambiguity

    return max_word, max_path_num
Esempio n. 4
0
 def setup(self):
     # NFA which matches "a", "aaa", or any string of 'a's where number of
     # 'a's is even and greater than zero
     self.nfa = NFA(**{
         'states': {'q0', 'q1', 'q2', 'q3', 'q4',
                    'q5', 'q6', 'q7', 'q8', 'q9'},
         'symbols': {'a'},
         'transitions': {
             'q0': {'a': {'q1', 'q8'}},
             'q1': {'a': {'q2'}, '': {'q6'}},
             'q2': {'a': {'q3'}},
             'q3': {'': {'q4'}},
             'q4': {'a': {'q5'}},
             'q5': {},
             'q6': {'a': {'q7'}},
             'q7': {},
             'q8': {'a': {'q9'}},
             'q9': {'a': {'q8'}}
         },
         'initial_state': 'q0',
         'final_states': {'q4', 'q6', 'q9'}
     })
Esempio n. 5
0
class TestNFA():

    def setup(self):
        # NFA which matches "a", "aaa", or any string of 'a's where number of
        # 'a's is even and greater than zero
        self.nfa = NFA(**{
            'states': {'q0', 'q1', 'q2', 'q3', 'q4',
                       'q5', 'q6', 'q7', 'q8', 'q9'},
            'symbols': {'a'},
            'transitions': {
                'q0': {'a': {'q1', 'q8'}},
                'q1': {'a': {'q2'}, '': {'q6'}},
                'q2': {'a': {'q3'}},
                'q3': {'': {'q4'}},
                'q4': {'a': {'q5'}},
                'q5': {},
                'q6': {'a': {'q7'}},
                'q7': {},
                'q8': {'a': {'q9'}},
                'q9': {'a': {'q8'}}
            },
            'initial_state': 'q0',
            'final_states': {'q4', 'q6', 'q9'}
        })

    def test_init_json(self):
        """should copy given JSON object into new NFA"""
        with open('tests/files/nfa.json', 'r') as nfa_file:
            nfa_json = json.load(nfa_file)
        new_nfa = NFA(**nfa_json)
        nose.assert_equal(new_nfa.states, set(nfa_json['states']))
        nose.assert_is_not(new_nfa.states, nfa_json['states'])
        nose.assert_equal(new_nfa.symbols, set(nfa_json['symbols']))
        nose.assert_is_not(new_nfa.symbols, nfa_json['symbols'])
        nose.assert_is_not(new_nfa.transitions, nfa_json['transitions'])
        for start_state, paths in new_nfa.transitions.items():
            nose.assert_is_not(paths, nfa_json['transitions'][start_state])
            for symbol, end_states in paths.items():
                nose.assert_equal(
                    end_states,
                    set(nfa_json['transitions'][start_state][symbol]))
        nose.assert_equal(new_nfa.initial_state, nfa_json['initial_state'])
        nose.assert_equal(new_nfa.final_states, set(nfa_json['final_states']))
        nose.assert_is_not(new_nfa.final_states, nfa_json['final_states'])

    def test_validate_automaton_missing_state(self):
        """should raise error if a state has no transitions defined"""
        with nose.assert_raises(automaton.MissingStateError):
            del self.nfa.transitions['q1']
            self.nfa.validate_automaton()

    def test_validate_automaton_invalid_symbol(self):
        """should raise error if a transition references an invalid symbol"""
        with nose.assert_raises(automaton.InvalidSymbolError):
            self.nfa.transitions['q1']['c'] = {'q2'}
            self.nfa.validate_automaton()

    def test_validate_automaton_invalid_state(self):
        """should raise error if a transition references an invalid state"""
        with nose.assert_raises(automaton.InvalidStateError):
            self.nfa.transitions['q1']['a'] = {'q10'}
            self.nfa.validate_automaton()

    def test_validate_automaton_invalid_initial_state(self):
        """should raise error if the initial state is invalid"""
        with nose.assert_raises(automaton.InvalidStateError):
            self.nfa.initial_state = 'q10'
            self.nfa.validate_automaton()

    def test_validate_automaton_invalid_final_state(self):
        """should raise error if the final state is invalid"""
        with nose.assert_raises(automaton.InvalidStateError):
            self.nfa.final_states = {'q10'}
            self.nfa.validate_automaton()

    def test_validate_input_valid(self):
        """should return correct stop states when valid NFA input is given"""
        nose.assert_equal(
            self.nfa.validate_input('aaaaaa'), {'q5', 'q7', 'q9'})

    def test_validate_input_empty_str(self):
        """should resolve any empty state transitions on the stop states"""
        nose.assert_equal(self.nfa.validate_input('aaa'), {'q4', 'q7', 'q8'})

    def test_validate_input_invalid_symbol(self):
        """should raise error if an invalid symbol is read"""
        with nose.assert_raises(automaton.InvalidSymbolError):
            self.nfa.validate_input('aab')

    def test_validate_input_nonfinal_state(self):
        """should raise error if the stop state is not a final state"""
        with nose.assert_raises(automaton.FinalStateError):
            self.nfa.validate_input('aaaaa')
Esempio n. 6
0
import pytest

from automata.language import generate_language, language_over_range
from automata.nfa import NFA

automaton: NFA = NFA(**pytest.automata_config)


def test_generate_language():
    assert generate_language(automaton, 1) == ["a"]


def test_language_over_range():
    assert language_over_range(automaton, 3) == [
        ["a"],
        ["aa"],
        ["aaa"],
    ]
Esempio n. 7
0
def generate_language(automaton: NFA, length: int) -> list:

    return [
        x for x in create_permutations(automaton.alphabet, length)
        if automaton.accepts(x) != (False, [])
    ]
Esempio n. 8
0
class Regex:
    def __init__(self, ex):
        new_ex = ex.replace(' ', '')
        self.regex = new_ex
        self.syms = {'+', '*', '(', ')'}

    def get_alph(self):
        self.alphabet = set()
        for char in self.regex:
            if char not in self.syms and char not in self.alphabet and char != 'ε':
                self.alphabet.add(char)

    def make_nfa_part(self, part, num):
        if '*' not in part:
            states = [state(i) for i in range(num, num + len(part) + 1)]
            for i in range(len(states) - 1):
                states[i].outpaths[part[i]] = [states[i + 1]]
            return states, num + len(part) + 1
        else:
            starred_letters_index = set()
            for i in range(len(part)):
                if part[i] == '*':
                    starred_letters_index.add(i - 1)

            states = [state(num)]
            num += 1
            i = 0
            while i < len(part):
                if i not in starred_letters_index:
                    new_state = state(num)
                    if part[i] in states[-1].outpaths:
                        states[-1].outpaths.append(new_state)
                    else:
                        states[-1].outpaths[part[i]] = [new_state]
                    states.append(new_state)
                    num += 1
                    i += 1
                else:
                    if part[i] in states[-1].outpaths:
                        states[-1].outpaths.append(states[-1])
                    else:
                        states[-1].outpaths[part[i]] = [states[-1]]
                    if i < len(part) - 2 and i + 2 in starred_letters_index:
                        new_state = state(num)
                        if 'ε' in states[
                                -1].outpaths:  #check not necessary unless 'ε' in alphabet?
                            states[-1].outpaths['ε'].append(new_state)
                        else:
                            states[-1].outpaths['ε'] = [new_state]
                        states.append(new_state)
                        num += 1
                    i += 2
            return states, num

    def get_parts(self):
        strs = []
        str = ''
        for i in range(len(self.regex)):
            if self.regex[i] not in self.syms:
                str += self.regex[i]
            elif self.regex[i] == '*':
                if self.regex[i - 1] not in self.syms:
                    str += self.regex[i]
            else:
                if len(str) != 0:
                    strs.append(str)
                    str = ''
        if len(str) != 0:
            strs.append(str)

        num = 0
        parts_dict = {}
        for str in strs:
            states, num = self.make_nfa_part(str, num)
            if str in parts_dict:
                parts_dict[str].append(states)
            else:
                parts_dict[str] = [states]

        self.parts_dict = parts_dict
        #print([(key, len(parts_dict[key])) for key in parts_dict])
        self.count_dict = {k: 0 for k in self.parts_dict}
        self.num = num

    def plus(self, nfa_parts, finalize=False):
        if len(nfa_parts) == 1:
            if finalize:
                self.my_nfa.start_states = [nfa_parts[0][0]]
                self.my_nfa.accept_states = [nfa_parts[0][-1]]
            return nfa_parts[0]
        else:
            new_state = state(self.num)
            self.num += 1
            new_state.outpaths = {'ε': [states[0] for states in nfa_parts]}
            new_state2 = state(self.num)
            self.num += 1
            new_states = [new_state]
            for states in nfa_parts:
                if 'ε' not in states[-1].outpaths:
                    states[-1].outpaths['ε'] = [new_state2]
                else:
                    states[-1].outpaths['ε'].append(new_state2)
                new_states += states
            new_states += [new_state2]
            if finalize:
                self.my_nfa.start_states = [new_states[0]]
                self.my_nfa.accept_states = [new_states[-1]]
            return new_states

    def kleene_star(self, states):
        if 'ε' not in states[-1].outpaths:
            states[-1].outpaths['ε'] = [states[0]]
        else:
            states[-1].outpaths['ε'].append(states[0])
        if 'ε' not in states[0].outpaths:
            states[0].outpaths['ε'] = [states[-1]]
        else:
            states[0].outpaths['ε'].append(states[-1])
        return states

    def concat(self, nfa_parts):
        if len(nfa_parts) == 1:
            return nfa_parts[0]

        for i in range(len(nfa_parts) - 1):
            if 'ε' not in nfa_parts[i][-1].outpaths:
                nfa_parts[i][-1].outpaths['ε'] = [nfa_parts[i + 1][0]
                                                  ]  #check for epsilon?
            else:
                nfa_parts[i][-1].outpaths['ε'].append(nfa_parts[i + 1][0])

        new_nfa_parts = []
        for part in nfa_parts:
            new_nfa_parts += part

        return new_nfa_parts

    def exp_parts(self, part, round):

        if part in self.parts_dict:
            idx = self.count_dict[part]
            self.count_dict[part] += 1
            if round == 0:
                self.my_nfa.start_states = [self.parts_dict[part][idx][0]]
                self.my_nfa.accept_states = [self.parts_dict[part][idx][-1]]
            return self.parts_dict[part][idx]

        elif '(' not in part:
            parts = part.split('+')
            lst = []
            for key in parts:
                idx = self.count_dict[key]
                self.count_dict[key] += 1
                lst.append(self.parts_dict[key][idx])
            if round == 0:
                return self.plus(lst, finalize=True)
            return self.plus(lst)

        else:
            pluses = []
            paren_count = 0
            for i in range(len(part)):
                if part[i] == '(':
                    paren_count += 1
                elif part[i] == ')':
                    paren_count -= 1
                elif paren_count == 0 and part[i] == '+':
                    pluses.append(i)

            prev = 0
            plus_parts = []
            for idx in pluses:
                plus_parts.append(part[prev:idx])
                prev = idx + 1
            plus_parts.append(part[prev:])

            if len(plus_parts) == 0:
                plus_parts = [part]

            #concat_parts = [[] for p in plus_parts]
            concat_parts = []
            for pp in plus_parts:
                if '(' not in pp:
                    concat_parts.append([(pp, False)])
                else:
                    paren_count = 0
                    pairs = []
                    str = ''
                    for i in range(len(pp)):
                        if pp[i] == '(':
                            if paren_count == 0:
                                if len(str) > 0:
                                    pairs.append((str, False), )
                                    str = ''
                                opening = i
                            else:
                                str += pp[i]
                            paren_count += 1
                        elif pp[i] == ')':
                            paren_count -= 1
                            if paren_count == 0:
                                if i < len(pp) - 1 and pp[i + 1] == '*':
                                    pairs.append((
                                        str,
                                        True,
                                    ), )
                                else:
                                    pairs.append((str, False), )
                                str = ''
                            else:
                                str += pp[i]
                        elif paren_count != 0 or (paren_count == 0 and pp[i] != '*') \
                        or (i > 0 and pp[i-1] != ')' and paren_count == 0 and pp[i] == '*'):
                            str += pp[i]

                    if len(str) != 0:
                        pairs.append((str, False), )

                    concat_parts.append(pairs)

            new_plus = []
            for cp in concat_parts:
                new_concat = []
                for str, klst in cp:
                    #print(str)
                    nfa_part = self.exp_parts(str, round + 1)
                    if klst:
                        nfa_part = self.kleene_star(nfa_part)
                    new_concat.append(nfa_part)
                new_plus.append(new_concat)

            final_plus = []
            for cc in new_plus:
                final_plus.append(self.concat(cc))

            if round == 0:
                return self.plus(final_plus, finalize=True)

            return self.plus(final_plus)

    def nfa_maker(self):
        self.get_alph()
        self.my_nfa = NFA([])
        self.get_parts()
        states = self.exp_parts(self.regex, 0)
        self.my_nfa.states = states
        self.my_nfa.alphabet = list(self.alphabet)

    def dfa_maker(self):
        self.nfa_maker()
        self.my_nfa.nfa_to_dfa()
        self.my_dfa = self.my_nfa.my_dfa
        self.my_dfa.alphabet = list(self.alphabet)
        self.my_dfa.minimize()

    def complement_maker(self, rand=True):
        self.dfa_maker()
        self.my_dfa.take_dfa_complement()
        self.complement = self.my_dfa.complement.all_regex(rand)
Esempio n. 9
0
    print(f"[--- language: {args.configFile}: ---]")

    for n, row in enumerate(data):

        print(
            f"length: {n+1:02}\t language: |{len(row):05}| : {row[:5]}{'...' if len(row) > 5 else ''}"
        )

    print(f"[--- duration: {duration:2.4f} sec ---]\n")


#
#
#  -------- __main__ -----------
#
if __name__ == "__main__":

    # collect terminal parameters
    args = parser.parse_args()

    # load JSON config
    with open(args.configFile) as json_file:
        config = json.load(json_file)

    # init automaton
    automaton: NFA = NFA(**config)

    # run tasks
    task__ambiguity_over_range(automaton, args.length)
    task__language_over_range(automaton, args.length)