Commit e6cb7cd9 authored by Ing. Jan Trávníček's avatar Ing. Jan Trávníček

allow FTA from string parsing

parent aa44494b
......@@ -46,15 +46,35 @@ L0:
token.value += character;
token.raw += character;
return token;
} else if(character == '[') {
token.type = TokenType::LEFT_BRACKET;
token.value += character;
token.raw += character;
return token;
} else if(character == ']') {
token.type = TokenType::RIGHT_BRACKET;
token.value += character;
token.raw += character;
return token;
} else if(character == '-') {
token.type = TokenType::NONE;
token.value += character;
token.raw += character;
return token;
} else if(character == ',') {
token.type = TokenType::COMMA;
token.value += character;
token.raw += character;
return token;
} else if(character == '#') {
token.value += character;
token.raw += character;
goto L1;
} else if ( ( character >= '0' ) && ( character <= '9' ) ) {
token.type = TokenType::RANK;
token.value += character;
token.raw += character;
goto L2;
} else if(input.clear(), input.unget(), input >> ext::string ( "MISNFA" ) ) {
token.type = TokenType::MULTI_INITIAL_STATE_NFA;
token.value = "MISNFA";
......@@ -80,6 +100,16 @@ L0:
token.value = "DFA";
token.raw += "DFA";
return token;
} else if(input.clear(), input >> ext::string ( "DFTA" ) ) {
token.type = TokenType::DFTA;
token.value = "DFTA";
token.raw += "DFTA";
return token;
} else if(input.clear(), input >> ext::string ( "NFTA" ) ) {
token.type = TokenType::NFTA;
token.value = "NFTA";
token.raw += "NFTA";
return token;
} else {
putback(input, token);
token.raw = "";
......@@ -104,6 +134,21 @@ L1:
token.type = TokenType::ERROR;
return token;
}
L2:
character = input.get ( );
if ( input.eof ( ) ) {
return token;
} else if ( ( character >= '0' ) && ( character <= '9' ) ) {
token.value += character;
token.raw += character;
goto L2;
} else {
input.clear ( );
input.unget ( );
return token;
}
}
} /* namespace automaton */
......@@ -22,12 +22,18 @@ public:
MULTI_INITIAL_STATE_EPSILON_NFA,
NFA,
DFA,
NFTA,
DFTA,
OUT,
IN,
EPSILON,
SEPARATOR,
LEFT_BRACKET,
RIGHT_BRACKET,
NONE,
COMMA,
NEW_LINE,
RANK,
TEOF,
ERROR
};
......
/*
* DFTA.cpp
*
* Created on: Apr 13, 2020
* Author: Jan Travnicek
*/
#include "DFTA.h"
#include <automaton/Automaton.h>
#include <registration/StringRegistration.hpp>
namespace {
auto stringWrite = registration::StringWriterRegister < automaton::DFTA < > > ( );
auto stringReader = registration::StringReaderRegister < automaton::Automaton, automaton::DFTA < > > ( );
} /* namespace */
/*
* DFTA.h
*
* Created on: Apr 13, 2020
* Author: Jan Travnicek
*/
#ifndef _STRING_DFTA_H_
#define _STRING_DFTA_H_
#include <automaton/TA/DFTA.h>
#include <core/stringApi.hpp>
#include <automaton/AutomatonFromStringLexer.h>
#include <automaton/string/common/AutomatonFromStringParserCommon.h>
#include <automaton/string/common/AutomatonToStringComposerCommon.h>
namespace core {
template<class SymbolType, class StateType >
struct stringApi < automaton::DFTA < SymbolType, StateType > > {
static automaton::DFTA < SymbolType, StateType > parse ( std::istream & input );
static bool first ( std::istream & input );
static void compose ( std::ostream & output, const automaton::DFTA < SymbolType, StateType > & automaton );
private:
static void parseTransition(std::istream& input, ext::set<StateType>& states, const ext::vector < common::ranked_symbol < SymbolType > >& symbols, ext::set<StateType>& finalStates, ext::set<ext::tuple<ext::vector<StateType>, common::ranked_symbol < SymbolType >, StateType>>& transitionFunction);
static void composeTransitionsToState(std::ostream& output, const automaton::DFTA < SymbolType, StateType > & automaton, const StateType & to);
};
template<class SymbolType, class StateType >
automaton::DFTA < SymbolType, StateType > stringApi < automaton::DFTA < SymbolType, StateType > >::parse ( std::istream & input ) {
automaton::AutomatonFromStringLexer::Token token = automaton::AutomatonFromStringLexer::next(input);
while(token.type == automaton::AutomatonFromStringLexer::TokenType::NEW_LINE) {
token = automaton::AutomatonFromStringLexer::next(input);
}
if(token.type != automaton::AutomatonFromStringLexer::TokenType::DFTA)
throw exception::CommonException("Unrecognised DFTA token.");
ext::vector < common::ranked_symbol < SymbolType > > symbols;
token = automaton::AutomatonFromStringLexer::next(input);
while(token.type != automaton::AutomatonFromStringLexer::TokenType::NEW_LINE) {
automaton::AutomatonFromStringLexer::putback(input, token);
SymbolType symbol = core::stringApi < SymbolType >::parse ( input );
token = automaton::AutomatonFromStringLexer::next ( input );
if ( token.type != automaton::AutomatonFromStringLexer::TokenType::RANK )
throw exception::CommonException ( "Missing rank" );
unsigned rank = ext::from_string < unsigned > ( token.value );
symbols.push_back ( common::ranked_symbol < SymbolType > ( std::move ( symbol ), rank ) );
token = automaton::AutomatonFromStringLexer::next(input);
}
ext::set<StateType> finalStates;
ext::set<StateType> states;
ext::set<ext::tuple<ext::vector < StateType >, common::ranked_symbol < SymbolType >, StateType > > transitionFunction; // from state, on symbol, to state
while(token.type == automaton::AutomatonFromStringLexer::TokenType::NEW_LINE) {
token = automaton::AutomatonFromStringLexer::next(input);
if(token.type == automaton::AutomatonFromStringLexer::TokenType::TEOF)
break;
else if (token.type == automaton::AutomatonFromStringLexer::TokenType::NEW_LINE)
continue;
else
automaton::AutomatonFromStringLexer::putback(input, token);
parseTransition(input, states, symbols, finalStates, transitionFunction);
token = automaton::AutomatonFromStringLexer::next(input);
}
if(token.type != automaton::AutomatonFromStringLexer::TokenType::TEOF)
throw exception::CommonException("Extra data after the automaton.");
automaton::DFTA < > res { };
res.setInputAlphabet ( ext::set < common::ranked_symbol < SymbolType > > ( symbols.begin ( ), symbols.end ( ) ) );
res.setStates(states);
res.setFinalStates(finalStates);
for ( const ext::tuple < ext::vector < StateType >, common::ranked_symbol < SymbolType >, StateType > & transition : transitionFunction )
res.addTransition(std::get<1>(transition), std::get<0>(transition), std::get<2>(transition));
return res;
}
template<class SymbolType, class StateType >
void stringApi < automaton::DFTA < SymbolType, StateType > >::parseTransition(std::istream& input, ext::set<StateType>& states, const ext::vector < common::ranked_symbol < SymbolType > > & symbols, ext::set<StateType>& finalStates, ext::set<ext::tuple<ext::vector<StateType>, common::ranked_symbol < SymbolType >, StateType>>& transitionFunction) {
automaton::AutomatonFromStringLexer::Token token = automaton::AutomatonFromStringLexer::next(input);
typename ext::vector < common::ranked_symbol < SymbolType > >::const_iterator iter = symbols.begin();
ext::set<ext::tuple<ext::vector<StateType>, common::ranked_symbol < SymbolType >>> innerTransitionFunction;
while ( iter != symbols.end ( ) ) {
if(token.type != automaton::AutomatonFromStringLexer::TokenType::NONE) {
automaton::AutomatonFromStringLexer::putback(input, token);
do {
ext::vector < StateType > from = automaton::AutomatonFromStringParserCommon::parseList < StateType > ( input );
for ( const StateType & state : from ) {
states.insert ( state );
}
innerTransitionFunction.insert(ext::make_tuple(std::move ( from ), *iter));
token = automaton::AutomatonFromStringLexer::next(input);
} while ( token.type == automaton::AutomatonFromStringLexer::TokenType::COMMA );
} else {
token = automaton::AutomatonFromStringLexer::next(input);
}
++iter;
}
automaton::AutomatonFromStringLexer::putback(input, token);
StateType to = core::stringApi<StateType>::parse(input);
states.insert ( to );
bool initial = false;
bool final = false;
automaton::AutomatonFromStringParserCommon::initialFinalState(input, final, initial);
if ( initial )
throw exception::CommonException ( "DFTA automata do not have initial states." );
if ( final )
finalStates.insert ( to );
token = automaton::AutomatonFromStringLexer::next(input);
if ( token.type != automaton::AutomatonFromStringLexer::TokenType::NEW_LINE && token.type != automaton::AutomatonFromStringLexer::TokenType::TEOF )
throw exception::CommonException("Invalid line format");
automaton::AutomatonFromStringLexer::putback(input, token);
for ( ext::tuple < ext::vector < StateType >, common::ranked_symbol < SymbolType > > && innerTransition : ext::make_mover ( innerTransitionFunction ) ) {
transitionFunction.insert ( ext::make_tuple ( std::move ( std::get < 0 > ( innerTransition ) ), std::move ( std::get < 1 > ( innerTransition ) ), to ) );
}
}
template<class SymbolType, class StateType >
bool stringApi < automaton::DFTA < SymbolType, StateType > >::first ( std::istream & input ) {
return automaton::AutomatonFromStringLexer::peek ( input ).type == automaton::AutomatonFromStringLexer::TokenType::DFTA;
}
template<class SymbolType, class StateType >
void stringApi < automaton::DFTA < SymbolType, StateType > >::compose ( std::ostream & output, const automaton::DFTA < SymbolType, StateType > & automaton ) {
output << "DFTA";
for(const auto& symbol : automaton.getInputAlphabet()) {
output << " ";
core::stringApi < SymbolType >::compose(output, symbol.getSymbol ( ) );
output << " " << ext::to_string ( symbol.getRank ( ) );
}
output << std::endl;
for(const auto& state : automaton.getStates()) {
composeTransitionsToState(output, automaton, state);
output << ' ';
core::stringApi<StateType>::compose(output, state);
if(automaton.getFinalStates().find(state) != automaton.getFinalStates().end())
output << " >";
output << std::endl;
}
}
template < class SymbolType, class StateType >
void stringApi < automaton::DFTA < SymbolType, StateType > >::composeTransitionsToState(std::ostream& output, const automaton::DFTA < SymbolType, StateType > & automaton, const StateType & to) {
ext::map < ext::pair < common::ranked_symbol < SymbolType >, ext::vector < StateType > >, StateType > symbolTransitionsToState = automaton.getTransitionsToState(to);
auto toStateTransitionsIter = symbolTransitionsToState.begin ( );
for(const common::ranked_symbol < SymbolType > & inputSymbol : automaton.getInputAlphabet()) {
output << ' ';
bool first = true;
if ( toStateTransitionsIter == symbolTransitionsToState.end ( ) || toStateTransitionsIter->first.first != inputSymbol ) {
output << '-';
} else while ( toStateTransitionsIter != symbolTransitionsToState.end ( ) && toStateTransitionsIter->first.first == inputSymbol ) {
if ( ! first )
output << ", ";
first = false;
automaton::AutomatonToStringComposerCommon::composeList ( output, toStateTransitionsIter->first.second );
++ toStateTransitionsIter;
}
}
}
} /* namespace core */
#endif /* _STRING_DFTA_H_ */
/*
* NFTA.cpp
*
* Created on: Apr 13, 2020
* Author: Jan Travnicek
*/
#include "NFTA.h"
#include <automaton/Automaton.h>
#include <registration/StringRegistration.hpp>
namespace {
auto stringWrite = registration::StringWriterRegister < automaton::NFTA < > > ( );
auto stringReader = registration::StringReaderRegister < automaton::Automaton, automaton::NFTA < > > ( );
} /* namespace */
/*
* NFTA.h
*
* Created on: Apr 13, 2020
* Author: Jan Travnicek
*/
#ifndef _STRING_NFTA_H_
#define _STRING_NFTA_H_
#include <automaton/TA/NFTA.h>
#include <core/stringApi.hpp>
#include <automaton/AutomatonFromStringLexer.h>
#include <automaton/string/common/AutomatonFromStringParserCommon.h>
#include <automaton/string/common/AutomatonToStringComposerCommon.h>
namespace core {
template<class SymbolType, class StateType >
struct stringApi < automaton::NFTA < SymbolType, StateType > > {
static automaton::NFTA < SymbolType, StateType > parse ( std::istream & input );
static bool first ( std::istream & input );
static void compose ( std::ostream & output, const automaton::NFTA < SymbolType, StateType > & automaton );
private:
static void parseTransition(std::istream& input, ext::set<StateType>& states, const ext::vector < common::ranked_symbol < SymbolType > >& symbols, ext::set<StateType>& finalStates, ext::set<ext::tuple<ext::vector<StateType>, common::ranked_symbol < SymbolType >, StateType>>& transitionFunction);
static void composeTransitionsToState(std::ostream& output, const automaton::NFTA < SymbolType, StateType > & automaton, const StateType & to);
};
template<class SymbolType, class StateType >
automaton::NFTA < SymbolType, StateType > stringApi < automaton::NFTA < SymbolType, StateType > >::parse ( std::istream & input ) {
automaton::AutomatonFromStringLexer::Token token = automaton::AutomatonFromStringLexer::next(input);
while(token.type == automaton::AutomatonFromStringLexer::TokenType::NEW_LINE) {
token = automaton::AutomatonFromStringLexer::next(input);
}
if(token.type != automaton::AutomatonFromStringLexer::TokenType::NFTA)
throw exception::CommonException("Unrecognised NFTA token.");
ext::vector < common::ranked_symbol < SymbolType > > symbols;
token = automaton::AutomatonFromStringLexer::next(input);
while(token.type != automaton::AutomatonFromStringLexer::TokenType::NEW_LINE) {
automaton::AutomatonFromStringLexer::putback(input, token);
SymbolType symbol = core::stringApi < SymbolType >::parse ( input );
token = automaton::AutomatonFromStringLexer::next ( input );
if ( token.type != automaton::AutomatonFromStringLexer::TokenType::RANK )
throw exception::CommonException ( "Missing rank" );
unsigned rank = ext::from_string < unsigned > ( token.value );
symbols.push_back ( common::ranked_symbol < SymbolType > ( std::move ( symbol ), rank ) );
token = automaton::AutomatonFromStringLexer::next(input);
}
ext::set<StateType> finalStates;
ext::set<StateType> states;
ext::set<ext::tuple<ext::vector < StateType >, common::ranked_symbol < SymbolType >, StateType > > transitionFunction; // from state, on symbol, to state
while(token.type == automaton::AutomatonFromStringLexer::TokenType::NEW_LINE) {
token = automaton::AutomatonFromStringLexer::next(input);
if(token.type == automaton::AutomatonFromStringLexer::TokenType::TEOF)
break;
else if (token.type == automaton::AutomatonFromStringLexer::TokenType::NEW_LINE)
continue;
else
automaton::AutomatonFromStringLexer::putback(input, token);
parseTransition(input, states, symbols, finalStates, transitionFunction);
token = automaton::AutomatonFromStringLexer::next(input);
}
if(token.type != automaton::AutomatonFromStringLexer::TokenType::TEOF)
throw exception::CommonException("Extra data after the automaton.");
automaton::NFTA < > res { };
res.setInputAlphabet ( ext::set < common::ranked_symbol < SymbolType > > ( symbols.begin ( ), symbols.end ( ) ) );
res.setStates(states);
res.setFinalStates(finalStates);
for ( const ext::tuple < ext::vector < StateType >, common::ranked_symbol < SymbolType >, StateType > & transition : transitionFunction )
res.addTransition(std::get<1>(transition), std::get<0>(transition), std::get<2>(transition));
return res;
}
template<class SymbolType, class StateType >
void stringApi < automaton::NFTA < SymbolType, StateType > >::parseTransition(std::istream& input, ext::set<StateType>& states, const ext::vector < common::ranked_symbol < SymbolType > > & symbols, ext::set<StateType>& finalStates, ext::set<ext::tuple<ext::vector<StateType>, common::ranked_symbol < SymbolType >, StateType>>& transitionFunction) {
automaton::AutomatonFromStringLexer::Token token = automaton::AutomatonFromStringLexer::next(input);
typename ext::vector < common::ranked_symbol < SymbolType > >::const_iterator iter = symbols.begin();
ext::set<ext::tuple<ext::vector<StateType>, common::ranked_symbol < SymbolType >>> innerTransitionFunction;
while ( iter != symbols.end ( ) ) {
if(token.type != automaton::AutomatonFromStringLexer::TokenType::NONE) {
automaton::AutomatonFromStringLexer::putback(input, token);
do {
ext::vector < StateType > from = automaton::AutomatonFromStringParserCommon::parseList < StateType > ( input );
for ( const StateType & state : from ) {
states.insert ( state );
}
innerTransitionFunction.insert(ext::make_tuple(std::move ( from ), *iter));
token = automaton::AutomatonFromStringLexer::next(input);
} while ( token.type == automaton::AutomatonFromStringLexer::TokenType::COMMA );
} else {
token = automaton::AutomatonFromStringLexer::next(input);
}
++iter;
}
automaton::AutomatonFromStringLexer::putback(input, token);
StateType to = core::stringApi<StateType>::parse(input);
states.insert ( to );
bool initial = false;
bool final = false;
automaton::AutomatonFromStringParserCommon::initialFinalState(input, final, initial);
if ( initial )
throw exception::CommonException ( "NFTA automata do not have initial states." );
if ( final )
finalStates.insert ( to );
token = automaton::AutomatonFromStringLexer::next(input);
if ( token.type != automaton::AutomatonFromStringLexer::TokenType::NEW_LINE && token.type != automaton::AutomatonFromStringLexer::TokenType::TEOF )
throw exception::CommonException("Invalid line format");
automaton::AutomatonFromStringLexer::putback(input, token);
for ( ext::tuple < ext::vector < StateType >, common::ranked_symbol < SymbolType > > && innerTransition : ext::make_mover ( innerTransitionFunction ) ) {
transitionFunction.insert ( ext::make_tuple ( std::move ( std::get < 0 > ( innerTransition ) ), std::move ( std::get < 1 > ( innerTransition ) ), to ) );
}
}
template<class SymbolType, class StateType >
bool stringApi < automaton::NFTA < SymbolType, StateType > >::first ( std::istream & input ) {
return automaton::AutomatonFromStringLexer::peek ( input ).type == automaton::AutomatonFromStringLexer::TokenType::NFTA;
}
template<class SymbolType, class StateType >
void stringApi < automaton::NFTA < SymbolType, StateType > >::compose ( std::ostream & output, const automaton::NFTA < SymbolType, StateType > & automaton ) {
output << "NFTA";
for(const auto& symbol : automaton.getInputAlphabet()) {
output << " ";
core::stringApi < SymbolType >::compose(output, symbol.getSymbol ( ) );
output << " " << ext::to_string ( symbol.getRank ( ) );
}
output << std::endl;
for(const auto& state : automaton.getStates()) {
composeTransitionsToState(output, automaton, state);
output << ' ';
core::stringApi<StateType>::compose(output, state);
if(automaton.getFinalStates().find(state) != automaton.getFinalStates().end())
output << " >";
output << std::endl;
}
}
template < class SymbolType, class StateType >
void stringApi < automaton::NFTA < SymbolType, StateType > >::composeTransitionsToState(std::ostream& output, const automaton::NFTA < SymbolType, StateType > & automaton, const StateType & to) {
ext::multimap < ext::pair < common::ranked_symbol < SymbolType >, ext::vector < StateType > >, StateType > symbolTransitionsToState = automaton.getTransitionsToState(to);
auto toStateTransitionsIter = symbolTransitionsToState.begin ( );
for(const common::ranked_symbol < SymbolType > & inputSymbol : automaton.getInputAlphabet()) {
output << ' ';
bool first = true;
if ( toStateTransitionsIter == symbolTransitionsToState.end ( ) || toStateTransitionsIter->first.first != inputSymbol ) {
output << '-';
} else while ( toStateTransitionsIter != symbolTransitionsToState.end ( ) && toStateTransitionsIter->first.first == inputSymbol ) {
if ( ! first )
output << ", ";
first = false;
automaton::AutomatonToStringComposerCommon::composeList ( output, toStateTransitionsIter->first.second );
++ toStateTransitionsIter;
}
}
}
} /* namespace core */
#endif /* _STRING_NFTA_H_ */
......@@ -6,30 +6,29 @@
*/
#include "AutomatonFromStringParserCommon.h"
#include <automaton/AutomatonFromStringLexer.h>
namespace automaton {
void AutomatonFromStringParserCommon::initialFinalState(std::istream& input, bool& initial, bool& final) {
initial = false;
final = false;
void AutomatonFromStringParserCommon::initialFinalState(std::istream& input, bool& rightArrow, bool& leftArrow) {
rightArrow = false;
leftArrow = false;
AutomatonFromStringLexer::Token token = AutomatonFromStringLexer::next(input);
if(token.type == AutomatonFromStringLexer::TokenType::IN) {
initial = true;
rightArrow = true;
token = AutomatonFromStringLexer::next(input);
if(token.type == AutomatonFromStringLexer::TokenType::OUT) {
final = true;
leftArrow = true;
} else {
AutomatonFromStringLexer::putback(input, token);
}
} else if(token.type == AutomatonFromStringLexer::TokenType::OUT) {
final = true;
leftArrow = true;
token = AutomatonFromStringLexer::next(input);
if(token.type == AutomatonFromStringLexer::TokenType::IN) {
initial = true;
rightArrow = true;
} else {
AutomatonFromStringLexer::putback(input, token);
}
......
......@@ -8,14 +8,55 @@
#ifndef AUTOMATON_FROM_STRING_PARSER_COMMON_H_
#define AUTOMATON_FROM_STRING_PARSER_COMMON_H_
#include <exception>
#include <alib/istream>
#include <automaton/AutomatonFromStringLexer.h>
#include <core/stringApi.hpp>
namespace automaton {
struct AutomatonFromStringParserCommon {
static void initialFinalState(std::istream& input, bool& initial, bool& final);
static void initialFinalState(std::istream& input, bool& rightArrow, bool& leftArrow);
template < class Type >
static ext::vector < Type > parseList ( std::istream & input );
};
template < class Type >
ext::vector<Type> AutomatonFromStringParserCommon::parseList (std::istream& input) {
ext::vector<Type> res;
automaton::AutomatonFromStringLexer::Token token = automaton::AutomatonFromStringLexer::next(input);
if(token.type != automaton::AutomatonFromStringLexer::TokenType::LEFT_BRACKET) {
throw exception::CommonException("Expected LEFT_BRACKET token.");
}
token = automaton::AutomatonFromStringLexer::next(input);
if(token.type != automaton::AutomatonFromStringLexer::TokenType::RIGHT_BRACKET) {
automaton::AutomatonFromStringLexer::putback(input, token);
while(true) {
Type symbol = core::stringApi<Type>::parse(input);
res.push_back(symbol);
token = automaton::AutomatonFromStringLexer::next(input);
if(token.type == automaton::AutomatonFromStringLexer::TokenType::RIGHT_BRACKET) {
break;
}
if(token.type != automaton::AutomatonFromStringLexer::TokenType::COMMA) {
throw exception::CommonException("Expected RIGHT_BRACKET or COMMA token");
}
}
}
if(token.type != automaton::AutomatonFromStringLexer::TokenType::RIGHT_BRACKET) {
throw exception::CommonException("Expected RIGHT_BRACKET token");
}
return res;
}
} /* namespace automaton */
#endif /* AUTOMATON_FROM_STRING_PARSER_COMMON_H_ */
/*
* AutomatonToStringComposerCommon.h
*
* Created on: Sep 26, 2017
* Author: Jan Travnicek
*/
#ifndef AUTOMATON_TO_STRING_COMPOSER_COMMON_H_
#define AUTOMATON_TO_STRING_COMPOSER_COMMON_H_
#include <ostream>
#include <alib/vector>
#include <core/stringApi.hpp>
namespace automaton {
struct AutomatonToStringComposerCommon {
template < class Type >
static void composeList ( std::ostream & output, const ext::vector < Type > & list );
};
template < class Type >
void AutomatonToStringComposerCommon::composeList ( std::ostream & output, const ext::vector < Type > & list ) {
output << '[';
bool first = true;
for ( const Type & value : list ) {
if ( ! first )
output << ", ";
first = false;
core::stringApi < Type >::compose ( output, value );
}
output << ']';
}
} /* namespace automaton */
#endif /* AUTOMATON_TO_STRING_COMPOSER_COMMON_H_ */
DFTA a 2 a 1 a 0
- - [] q0
- [q0] - q1
[q1,q1],[q1,q0],[q0,q1],[q0,q0] - - q2 >
DFTA a 2 a 1 a 0
- - [] q0
- [q0] - q1
[q1,q1],[q1,q0],[q0,q1],[q0,q0] - - q2 >