Compare commits
3 commits
46aab39ee6
...
d7bab30809
Author | SHA1 | Date | |
---|---|---|---|
|
d7bab30809 | ||
|
440aedbf64 | ||
|
4a789f6b16 |
4 changed files with 152 additions and 32 deletions
31
nfa.py
31
nfa.py
|
@ -8,3 +8,34 @@ def copy_nfa(nfa):
|
||||||
transitions_copy[from_state] = nfa.transitions[from_state].copy()
|
transitions_copy[from_state] = nfa.transitions[from_state].copy()
|
||||||
|
|
||||||
return NFA(nfa.start, nfa.accept, transitions_copy)
|
return NFA(nfa.start, nfa.accept, transitions_copy)
|
||||||
|
|
||||||
|
def prettyprint(nfa):
|
||||||
|
def process_state(state):
|
||||||
|
nonlocal start, accept
|
||||||
|
|
||||||
|
t = ''
|
||||||
|
if state == start:
|
||||||
|
# Bold
|
||||||
|
t += '\x1b[1m'
|
||||||
|
if state in accept:
|
||||||
|
# Green
|
||||||
|
t += '\x1b[32m'
|
||||||
|
|
||||||
|
if t != '':
|
||||||
|
return t + str(state) + '\x1b[0m'
|
||||||
|
else:
|
||||||
|
return str(state)
|
||||||
|
|
||||||
|
start, accept, transitions = nfa
|
||||||
|
states = transitions.keys()
|
||||||
|
|
||||||
|
print('\t' + '\t'.join(map(process_state, states)))
|
||||||
|
for from_state in states:
|
||||||
|
t = []
|
||||||
|
for to_state in states:
|
||||||
|
if to_state in transitions[from_state]:
|
||||||
|
t.append(str(transitions[from_state][to_state]))
|
||||||
|
else:
|
||||||
|
t.append('\x1b[90m-\x1b[0m')
|
||||||
|
|
||||||
|
print(process_state(from_state) + '\t' + '\t'.join(t))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import enum
|
import enum
|
||||||
|
|
||||||
from regex import lit, concat, bar, star
|
from regex import lit, concat, bar, star
|
||||||
from nfa import NFA, copy_nfa
|
from nfa import NFA, copy_nfa, prettyprint
|
||||||
|
|
||||||
def remove_states(nfa):
|
def remove_states(nfa):
|
||||||
start, accept, transitions = nfa
|
start, accept, transitions = nfa
|
||||||
|
@ -109,37 +109,6 @@ def to_regex(nfa):
|
||||||
|
|
||||||
return processed.transitions[_.start][_.end]
|
return processed.transitions[_.start][_.end]
|
||||||
|
|
||||||
def prettyprint(nfa):
|
|
||||||
def process_state(state):
|
|
||||||
nonlocal start, accept
|
|
||||||
|
|
||||||
t = ''
|
|
||||||
if state == start:
|
|
||||||
# Bold
|
|
||||||
t += '\x1b[1m'
|
|
||||||
if state in accept:
|
|
||||||
# Green
|
|
||||||
t += '\x1b[32m'
|
|
||||||
|
|
||||||
if t != '':
|
|
||||||
return t + str(state) + '\x1b[0m'
|
|
||||||
else:
|
|
||||||
return str(state)
|
|
||||||
|
|
||||||
start, accept, transitions = nfa
|
|
||||||
states = transitions.keys()
|
|
||||||
|
|
||||||
print('\t' + '\t'.join(map(process_state, states)))
|
|
||||||
for from_state in states:
|
|
||||||
t = []
|
|
||||||
for to_state in states:
|
|
||||||
if to_state in transitions[from_state]:
|
|
||||||
t.append(str(transitions[from_state][to_state]))
|
|
||||||
else:
|
|
||||||
t.append('\x1b[90m-\x1b[0m')
|
|
||||||
|
|
||||||
print(process_state(from_state) + '\t' + '\t'.join(t))
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
nfa = NFA('start', ['0'], {
|
nfa = NFA('start', ['0'], {
|
||||||
'start': {'1': lit('i'), '2': lit('d')},
|
'start': {'1': lit('i'), '2': lit('d')},
|
||||||
|
|
105
regex_to_nfa.py
Normal file
105
regex_to_nfa.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
from regex import Literal, Concatenation, Alternation, Star, lit, concat, bar, star
|
||||||
|
from nfa import NFA, prettyprint
|
||||||
|
|
||||||
|
def to_nfa(regex):
|
||||||
|
def new_state():
|
||||||
|
nonlocal state_name_counter, transitions
|
||||||
|
|
||||||
|
state_name = state_name_counter
|
||||||
|
state_name_counter += 1
|
||||||
|
|
||||||
|
transitions[state_name] = {}
|
||||||
|
|
||||||
|
return state_name
|
||||||
|
|
||||||
|
def worker(node):
|
||||||
|
nonlocal transitions
|
||||||
|
|
||||||
|
if type(node) == Literal:
|
||||||
|
# text
|
||||||
|
# (start) ---------> (end)
|
||||||
|
start_state = new_state()
|
||||||
|
end_state = new_state()
|
||||||
|
|
||||||
|
transitions[start_state][end_state] = lit(node.text)
|
||||||
|
|
||||||
|
return (start_state, end_state)
|
||||||
|
|
||||||
|
elif type(node) == Concatenation:
|
||||||
|
# (start) → […] → […] → […]
|
||||||
|
start_state = new_state()
|
||||||
|
|
||||||
|
prev_state = start_state
|
||||||
|
for element in node.elements:
|
||||||
|
inner_start, inner_end = worker(element)
|
||||||
|
|
||||||
|
# (prev) → (inner_start) → […]
|
||||||
|
transitions[prev_state][inner_start] = lit('')
|
||||||
|
|
||||||
|
# Link next element straight to the inner end
|
||||||
|
# state
|
||||||
|
prev_state = inner_end
|
||||||
|
|
||||||
|
return (start_state, prev_state)
|
||||||
|
|
||||||
|
elif type(node) == Alternation:
|
||||||
|
# +-> […] --+
|
||||||
|
# | |
|
||||||
|
# (start) --+-> […] --+-> (end)
|
||||||
|
# | |
|
||||||
|
# +-> […] --+
|
||||||
|
start_state = new_state()
|
||||||
|
end_state = new_state()
|
||||||
|
|
||||||
|
for element in node.elements:
|
||||||
|
inner_start, inner_end = worker(element)
|
||||||
|
|
||||||
|
# (start) → (inner_start) → […]
|
||||||
|
transitions[start_state][inner_start] = lit('')
|
||||||
|
|
||||||
|
# […] → (inner_end) → (end)
|
||||||
|
transitions[inner_end][end_state] = lit('')
|
||||||
|
|
||||||
|
return (start_state, end_state)
|
||||||
|
|
||||||
|
elif type(node) == Star:
|
||||||
|
# +- […] <-+
|
||||||
|
# | |
|
||||||
|
# v |
|
||||||
|
# (start) --+--> (end)
|
||||||
|
start_state = new_state()
|
||||||
|
end_state = new_state()
|
||||||
|
|
||||||
|
inner_start, inner_end = worker(node.element)
|
||||||
|
|
||||||
|
# (start) → (inner_start) → […]
|
||||||
|
transitions[start_state][inner_start] = lit('')
|
||||||
|
|
||||||
|
# […] → (inner_end) → (start)
|
||||||
|
transitions[inner_end][start_state] = lit('')
|
||||||
|
|
||||||
|
# (start) → (end)
|
||||||
|
transitions[start_state][end_state] = lit('')
|
||||||
|
|
||||||
|
return (start_state, end_state)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError('node has to be Literal, Concatenation, Alternation, or Star')
|
||||||
|
|
||||||
|
state_name_counter = 0
|
||||||
|
transitions = {}
|
||||||
|
|
||||||
|
start_state, end_state = worker(regex)
|
||||||
|
|
||||||
|
return NFA(start_state, [end_state], transitions)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
regex = concat(lit('x'), star(bar(lit('a'), lit('b'))), lit('y'))
|
||||||
|
print(regex)
|
||||||
|
|
||||||
|
nfa = to_nfa(regex)
|
||||||
|
|
||||||
|
prettyprint(nfa)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
15
regex_to_regex.py
Normal file
15
regex_to_regex.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from regex import lit, concat, bar, star
|
||||||
|
from regex_to_nfa import to_nfa
|
||||||
|
from nfa_to_regex import to_regex
|
||||||
|
|
||||||
|
def main():
|
||||||
|
regex = concat(bar(lit('foo'), lit('bar')), bar(lit('baz'), lit('qux')))
|
||||||
|
print(regex)
|
||||||
|
|
||||||
|
nfa = to_nfa(regex)
|
||||||
|
regex_prime = to_regex(nfa)
|
||||||
|
|
||||||
|
print(regex_prime)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in a new issue