'''
:Version: 30.05.2011
:Authors:
knopsa,
Alexander Weigl (re-documentation for sphinx)
'''
import sys
import tempfile
from os import path
from xml.etree import ElementTree as ET
_DEA = "DEA"
_NEA = "NEA"
_ENEA = "ENEA"
_EPSILON = "\u03B5"
_EMPTYSET = "\u2205"
_KLEENE_STAR = "\u002A"
_ALTERNATION = "\u002B"
_LPARENTHESIS = "\u0028"
_RPARENTHESIS = "\u0029"
_DEFAULT_FILE = tempfile.gettempdir() + "/trap.xml"
#
# Different implementations
# for Python 2 and Python 3 compatibility.
#
if sys.version_info[0] < 3:
# print("Trap doesn't support Python " +
# sys.version_info[0].__str__()+ ".X!")
_python_input = raw_input
else:
_python_input = input
[docs]def load(filename=None, alt_order=False):
"""Loads a model from the given path.
Parameter:
:param filename: the path of the source-file
:param alt_order: the order of the tulple in case of an automata.
If false then[Q, Sigma, delta, start, F]
else [Sigma, Q, delta, start, F]
The Return-value is a finite state machine or a regular expression.
Finite State Machine:
A finite state Machine is a list
[Q, Sigma, delta, start, F] or [Sigma, Q, delta, start, F] with
- Q is a set of states (set of int values)
- Sigma is a a alphabet (set of str values of length 1)
- delta is the transition function (dictionary)
- start is the start-state (int value)
- F is a set of accept states (set of int values)
Regular Expression:
The return value is a list [exp, Sigma] with
- exp is the regular Expression (str vaule)
- Sigma is a a alphabet (set of int values)
:return: a list of five elements, see above
"""
def read_alphabet(xml_EA):
"""Creates a set (Alphabet) from an ElementTree.Element"""
token_Set = set([])
for token in xml_EA.find("alphabet"):
token_Set.add(token.text)
return token_Set
def load_ea(xml_EA, alt_order):
"""Creates a finite state machine Machine from Element"""
#
# read states
#
is_DEA = xml_EA.attrib["type"] == _DEA
xml_states = xml_EA.find("states")
str_state_set = set([])
int_state_set = set([])
str_accepting_set = set([])
state_map = {}
for xml_state in xml_states:
str_state = xml_state.attrib["name"]
if str_state.isdigit() and (len(str_state) == 1 or str_state[0] != "0"):
state_map[str_state] = int(str_state)
int_state_set.add(state_map[str_state])
else:
str_state_set.add(str_state)
if xml_state.attrib["accepting"].lower() == "true":
str_accepting_set.add(str_state)
i = 0
for str_state in sorted(str_state_set):
while i in int_state_set:
i += 1
int_state_set.add(i)
state_map[str_state] = i
#
# create Accepting Set
#
int_accepting_set = set([])
for str_state in str_accepting_set:
int_accepting_set.add(state_map[str_state])
#
# read Transitons
#
delta = {}
xml_transitions = xml_EA.find("transitions")
for xml_trans in xml_transitions:
start = xml_trans.attrib["start"]
end = xml_trans.attrib["end"]
token = xml_trans.attrib["token"]
if is_DEA:
delta[state_map[start], token] = state_map[end]
elif (state_map[start], token) in delta:
delta[state_map[start], token].add(state_map[end])
else:
delta[state_map[start], token] = set([state_map[end]])
start_state = state_map[xml_EA.find("startState").attrib["name"]]
ea = [int_state_set, read_alphabet(xml_EA), delta, start_state, int_accepting_set]
if alt_order == True:
ea = _twist(ea)
#weigl: print(xml_EA.attrib["type"] + " loaded")
return ea
def load_regex(xml_regex):
"""Creates a regular Expression from an ElementTree.Element"""
str_exp = xml_regex.find("expression").text
sigma = read_alphabet(xml_regex)
print("reg. Expression" + " loaded")
return str_exp, sigma
try:
if filename == None:
if not path.isfile(_DEFAULT_FILE):
print("File does not exist")
return None
filename = _DEFAULT_FILE
tree = ET.parse(filename)
xml_EA = tree.find("automata")
xml_EA
if (xml_EA != None):
return load_ea(xml_EA, alt_order)
else:
xml_regex = tree.find("regularExpression")
if (xml_regex != None):
return load_regex(xml_regex)
else:
print("Illegal file format")
return None
except IOError as e:
print("'" + e.filename + "'" + ": " + e.strerror)
return None
except KeyError as e:
print(e.__str__() + " is not a State")
return None
[docs]def save_dea(dea, filename=None, alt_order=False):
"""Writes an DEA to the given file.
:param dea: an deterministic finite-state machines
:param filename: the path of the target-file
:param alt_order: the order of the DEA-tulple
false: [Q, Sigma, delta, start, F]
true: [Sigma, Q, delta, start, F]
:return: true, if the file was written successfully.
A deterministic finite-state Machine is a list
[Q, Sigma, delta, start, F] with
- Q is a set of states (set of int values)
- Sigma is a a alphabet (set of str values of length 1)
- delta is the transition function
(dictionary: state X token -> state)
- start is the start-state (int value)
- F is a set of accept states (set of int values)
"""
if alt_order == True:
dea = _twist(dea)
return _save_ea(filename, dea, _DEA)
[docs]def save_nea(nea, filename=None, alt_order=False):
"""Writes an NEA to the given file.
:param nea: an nondeterministic finite-state machines
:param filename: the path of the target-file
:param alt_order: the order of the NEA-tulple
false: [Q, Sigma, delta, start, F]
true: [Sigma, Q, delta, start, F]
:return: true iff the file was written successfully.
A nondeterministic finite-state machines with epsilon moves Machine is a
list [Q, Sigma, delta, start, F] with
- Q is a set of states (set of int values)
- Sigma is a a alphabet (set of str values of length 1)
- delta is the transition function
(dictionary: state X (token + epsilon) -> set of states)
- start is the start-state (int value)
- F is a set of accept states (set of int values)
"""
if alt_order == True:
nea = _twist(nea)
return _save_ea(filename, nea, _NEA)
[docs]def save_enea(enea, filename=None, alt_order=False):
"""Writes an ENEA to the given file.
:param enea: an nondeterministic finite-state machines with epsilon moves
:param filename: the path of the target-file
:param alt_order:
the order of the ENEA-tulple
false: [Q, Sigma, delta, start, F]
true: [Sigma, Q, delta, start, F]
:return:true, iff the file was written successfully.
A nondeterministic finite-state machines with epsilon moves is a
list [Q, Sigma, delta, start, F] with
- Q is a set of states (set of int values)
- Sigma is a a alphabet (set of str values of length 1)
- delta is the transition function
(dictionary: state X (token + epsilon) -> set of states)
- start is the start-state (int value)
- F is a set of accept states (set of int values)
"""
if alt_order == True:
enea = _twist(enea)
return _save_ea(filename, enea, _ENEA)
def _save_ea(filename, ea, type):
"""Writes an EA to a file."""
def add_transition(xml_transitions, from_state, token, to_state):
xml_trans = ET.SubElement(xml_transitions, "transition")
xml_trans.attrib["start"] = from_state.__str__()
xml_trans.attrib["token"] = token.__str__()
xml_trans.attrib["end"] = to_state.__str__()
type = type.upper()
if is_ea_consistent(ea, type) == False:
return False
[states, alphabet, delta, start_state, accepting_set] = ea
xml_model = ET.Element("model")
xml_automata = ET.SubElement(xml_model, "automata", {"type": type})
ET.SubElement(xml_automata, "startState", {"name": start_state.__str__()})
xml_states = ET.SubElement(xml_automata, "states")
for state in states:
xml_state = ET.SubElement(xml_states, "state")
xml_state.attrib["name"] = state.__str__()
xml_state.attrib["accepting"] = (state in accepting_set).__str__().lower()
xml_transitions = ET.SubElement(xml_automata, "transitions")
for q, t in delta:
if type == _DEA:
add_transition(xml_transitions, q, t, delta[q, t])
else:
for q2 in delta[q, t]:
add_transition(xml_transitions, q, t, q2)
xml_automata.append(_create_alphabet(alphabet))
tree = ET.ElementTree(xml_model)
return _write(tree, filename, type.upper())
[docs]def is_ea_consistent(ea, type):
""" Checks if the given ea is consistent.
:return:
false, iff the ea uses any state/token,
which is not a member of the state-set/ alphabet.
"""
[states, alphabet, delta, start_state, accepting_set] = ea
if not is_sigma_consistent(alphabet):
return False
for q in accepting_set:
if q not in states:
print(q.__str__() + " is not a State")
return False
if type not in (_DEA, _NEA, _ENEA):
print("Illegal type '" + type + "'")
return False
if start_state not in states:
print(start_state.__str__() + " is not a State")
return False
for q, t in delta:
if t not in alphabet and not ( t == _EPSILON and type == _ENEA):
print(t.__str__() + " is not a Token")
return False
if q not in states:
print(q.__str__() + " is not a State")
return False
if type == _DEA:
if delta[q, t] != None and delta[q, t] not in states:
print(delta[q, t].__str__() + " is not a State")
return False
else:
for q2 in delta[q, t]:
if q2 not in states:
print(q2.__str__() + " is not a State")
return False
[docs]def is_sigma_consistent(sigma):
for t in sigma:
if type(t) != str:
print("Illegal alphabet token")
return False
if len(t) != 1:
print("Illegal alphabet token '" + t + "'")
return False
if t in (_EPSILON, _EMPTYSET, _KLEENE_STAR, _ALTERNATION, _RPARENTHESIS, _LPARENTHESIS ):
print("Illegal special token '" + t + "'")
return False
return True
[docs]def save_regex(expression, sigma, file_name=None):
"""Writes an regular Expression to the given file.
:param expression: The regular Expression.
:param sigma: the Alphabet.
:param file_name: The name of the file.
:return: true, iff the file was written successfully.
"""
if not is_sigma_consistent(sigma):
return False
xml_model = ET.Element("model")
xml_regex = ET.SubElement(xml_model, "regularExpression")
xml_exp = ET.SubElement(xml_regex, "expression")
xml_exp.text = expression
xml_regex.append(_create_alphabet(sigma))
tree = ET.ElementTree(xml_model)
return _write(tree, file_name, "reg. Expression")
def _twist(ea):
[A, B, delta, q0, F] = ea
return [B, A, delta, q0, F]
def _write(tree, filename, type):
"""Writes a ElementTree to a file."""
message = type + " saved"
try:
if filename != None:
if path.isfile(filename):
print("File does already exist!")
choice = None
while choice == None:
print('Do you want to replace the existing file? "Yes"/"No"')
choice = _python_input().lower()
if choice != "yes" and choice != "no":
choice = None
if choice == "no":
print("Canceled")
return None
message = message + "(" + filename + ")"
else:
filename = _DEFAULT_FILE
tree.write(filename, encoding="UTF-8")
print(message)
return True
except IOError as e:
print("'" + e.filename + "'" + ": " + e.strerror)
return False
def _create_alphabet(sigma):
"""Creates a subtree from an alphabet."""
xml_alphabet = ET.Element("alphabet")
for t in sigma:
xml_token = ET.SubElement(xml_alphabet, "token")
xml_token.text = t.__str__()
return xml_alphabet