refactor and reuse Automatas. TODO : add test for automatas
This commit is contained in:
parent
184f8dd1fe
commit
8f38095bec
3 changed files with 166 additions and 318 deletions
|
|
@ -317,12 +317,13 @@ namespace tp {
|
|||
return *this;
|
||||
}
|
||||
|
||||
void append(Arg data) {
|
||||
tType& append(Arg data) {
|
||||
if (mLoad == mSize) {
|
||||
resizeBuffer(tResizePolicy(mSize));
|
||||
}
|
||||
new (&mBuff[mLoad]) tType(data);
|
||||
mLoad++;
|
||||
return mBuff[mLoad - 1];
|
||||
}
|
||||
|
||||
void append(const Buffer& in) {
|
||||
|
|
|
|||
|
|
@ -299,6 +299,14 @@ namespace tp {
|
|||
}
|
||||
}
|
||||
|
||||
void transferNodes(List in) {
|
||||
removeAll();
|
||||
for (auto node : in) {
|
||||
attach(node);
|
||||
}
|
||||
in.detachAll();
|
||||
}
|
||||
|
||||
public:
|
||||
template <class tArchiver>
|
||||
void archiveWrite(tArchiver& ar) const {
|
||||
|
|
|
|||
|
|
@ -4,78 +4,90 @@
|
|||
#include "Utils.hpp"
|
||||
#include "List.hpp"
|
||||
#include "Map.hpp"
|
||||
#include "Tree.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
// Non-Deterministic Finite-State Automata
|
||||
template <typename tAlphabetType, typename tStateType>
|
||||
class NFA {
|
||||
class FiniteStateAutomation {
|
||||
public:
|
||||
struct State;
|
||||
|
||||
public:
|
||||
struct Vertex;
|
||||
class Transition {
|
||||
friend FiniteStateAutomation;
|
||||
|
||||
struct Edge {
|
||||
Vertex* mVertex = nullptr;
|
||||
bool mConsumesSymbol = false;
|
||||
Range<tAlphabetType> mAcceptingRange;
|
||||
bool mAcceptsAll = false;
|
||||
bool mExclude = false;
|
||||
public:
|
||||
enum Type { ANY, EPSILON, SYMBOL };
|
||||
|
||||
bool isTransition(const tAlphabetType& symbol) {
|
||||
if (symbol == 0) return false;
|
||||
if (!mConsumesSymbol || mAcceptsAll) return true;
|
||||
bool const in_range = (symbol >= mAcceptingRange.mBegin && symbol <= mAcceptingRange.mEnd);
|
||||
return in_range != mExclude;
|
||||
public:
|
||||
Transition(Type type, State* state, tAlphabetType symbol = tAlphabetType()) {
|
||||
mState = state;
|
||||
mType = type;
|
||||
mSymbol = symbol;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isTransition(const tAlphabetType& symbol) const {
|
||||
return (mType == ANY || mType == EPSILON) || (mSymbol == symbol);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool doesConsumes(const tAlphabetType& symbol) const {
|
||||
return (mType == ANY || (mType == SYMBOL && mSymbol == symbol));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isEpsilon() const { return mType == EPSILON; }
|
||||
|
||||
private:
|
||||
State* mState = nullptr;
|
||||
Type mType;
|
||||
tAlphabetType mSymbol;
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
List<Edge> edges;
|
||||
tStateType state{};
|
||||
bool isTermination = false;
|
||||
ualni debug_idx = 0;
|
||||
ualni flag = 0;
|
||||
class State {
|
||||
friend FiniteStateAutomation;
|
||||
|
||||
State() = default;
|
||||
|
||||
public:
|
||||
void setValue(const tStateType& stateValue) { mStateVal = stateValue; }
|
||||
void setAcceptance(bool isAccepting) { mIsAccepting = isAccepting; }
|
||||
|
||||
private:
|
||||
Buffer<Transition> mTransitions;
|
||||
tStateType mStateVal = tStateType();
|
||||
bool mIsAccepting = false;
|
||||
};
|
||||
|
||||
public:
|
||||
List<Vertex> mVertices;
|
||||
Vertex* mStart = nullptr;
|
||||
Buffer<tAlphabetType> mAllSymbols;
|
||||
private:
|
||||
List<State> mStates;
|
||||
const State* mStartState = nullptr;
|
||||
|
||||
public:
|
||||
NFA() = default;
|
||||
FiniteStateAutomation() = default;
|
||||
|
||||
Vertex* addVertex(const tStateType& state, bool termination) {
|
||||
auto node = mVertices.newNode();
|
||||
node->data.isTermination = termination;
|
||||
node->data.state = state;
|
||||
node->data.debug_idx = mVertices.length() + 1;
|
||||
mVertices.pushBack(node);
|
||||
State* addState(const tStateType& state, bool accepting) {
|
||||
auto node = mStates.newNode();
|
||||
node->data.mIsAccepting = accepting;
|
||||
node->data.mState = state;
|
||||
mStates.pushBack(node);
|
||||
return &node->data;
|
||||
}
|
||||
|
||||
void
|
||||
addTransition(Vertex* from, Vertex* to, Range<tAlphabetType> range, bool consumes, bool accepts_all, bool exclude) {
|
||||
Edge edge;
|
||||
edge.mVertex = to;
|
||||
edge.mConsumesSymbol = consumes;
|
||||
edge.mAcceptingRange = range;
|
||||
edge.mExclude = exclude;
|
||||
edge.mAcceptsAll = accepts_all;
|
||||
from->edges.pushBack(edge);
|
||||
void addTransition(State* from, State* to, const tAlphabetType& symbol) {
|
||||
from->edges.pushBack(Transition(Transition::SYMBOL, to, symbol));
|
||||
}
|
||||
|
||||
void setStartVertex(Vertex* start) { mStart = start; }
|
||||
void addEpsilonTransition(State* from, State* to) { from->edges.pushBack(Transition(Transition::SYMBOL, to)); }
|
||||
|
||||
[[nodiscard]] Vertex* getStartVertex() const { return mStart; }
|
||||
void addAnyTransition(State* from, State* to) { from->edges.pushBack(Transition(Transition::ANY, to)); }
|
||||
|
||||
void setVertexState(Vertex* vertex, const tStateType& state, bool terminated) {
|
||||
vertex->state = state;
|
||||
vertex->isTermination = terminated;
|
||||
}
|
||||
void setStartVertex(State* start) { mStartState = start; }
|
||||
|
||||
[[nodiscard]] State* getStartState() const { return mStartState; }
|
||||
|
||||
[[nodiscard]] bool isValid() const {
|
||||
if (!mStart) {
|
||||
if (!mStartState) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -83,285 +95,112 @@ namespace tp {
|
|||
|
||||
// vertices that are reachable from initial set with no input consumption (E-transitions)
|
||||
// does not include initial set
|
||||
void closure(const List<Vertex*>& set, List<Vertex*>& closure, ualni unique_call_id) {
|
||||
List<Vertex*> marked;
|
||||
marked = set;
|
||||
void findClosureSet(const Buffer<State*>& from, Buffer<State*>& closureSet) const {
|
||||
Map<State*, bool> lookup;
|
||||
List<State*> workingSet;
|
||||
|
||||
while (marked.length()) {
|
||||
auto first = marked.first()->data;
|
||||
if (first->flag != unique_call_id) {
|
||||
first->flag = unique_call_id;
|
||||
closure.pushBack(first);
|
||||
for (auto item : from) {
|
||||
workingSet.pushBack(item.data());
|
||||
}
|
||||
for (auto edge : first->edges) {
|
||||
if (!edge.data().mConsumesSymbol && edge.data().mVertex->flag != unique_call_id) {
|
||||
marked.pushBack(edge.data().mVertex);
|
||||
|
||||
while (workingSet.length()) {
|
||||
auto first = workingSet.first()->data;
|
||||
closureSet.append(first);
|
||||
|
||||
for (auto edge : first->mTransitions) {
|
||||
if (!edge.data().isEpsilon()) continue;
|
||||
if (lookup.presents(edge.data().mState)) continue;
|
||||
workingSet.pushBack(edge.data().mState);
|
||||
}
|
||||
}
|
||||
marked.popFront();
|
||||
|
||||
workingSet.popFront();
|
||||
}
|
||||
}
|
||||
|
||||
// vertices that are reachable from initial set with symbol transition
|
||||
void move(const List<Vertex*>& set, List<Vertex*>& reachable, tAlphabetType symbol, ualni unique_call_id) {
|
||||
for (auto vertex : set) {
|
||||
for (auto edge : vertex->edges) {
|
||||
if (!edge.data().mConsumesSymbol) continue;
|
||||
bool transition = edge.data().isTransition(symbol);
|
||||
if (transition && edge.data().mVertex->flag != unique_call_id) {
|
||||
edge.data().mVertex->flag = unique_call_id;
|
||||
reachable.pushBack(edge.data().mVertex);
|
||||
void findMoveSet(const Buffer<State*>& from, Buffer<State*>& moveSet, tAlphabetType symbol) const {
|
||||
Map<State*, bool> lookup;
|
||||
|
||||
for (auto vertex : from) {
|
||||
for (auto edge : vertex.mTransitions) {
|
||||
if (edge.data().isepsilon()) continue;
|
||||
if (!edge.data().isTransition(symbol)) continue;
|
||||
if (lookup.presents(edge.data().mState)) continue;
|
||||
moveSet.append(edge.data().mState);
|
||||
lookup.put(edge.data().mState, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Deterministic Finite-State Automata
|
||||
template <typename tAlphabetType, typename tStateType>
|
||||
class DFA {
|
||||
public:
|
||||
struct Vertex {
|
||||
struct Edge {
|
||||
Vertex* vertex = nullptr;
|
||||
tAlphabetType transition_code = nullptr;
|
||||
};
|
||||
|
||||
List<Edge> edges;
|
||||
tStateType state{};
|
||||
bool termination = false;
|
||||
bool marked = false;
|
||||
};
|
||||
|
||||
List<Vertex> mVertices;
|
||||
Vertex* mStart = nullptr;
|
||||
const Vertex* mIter = nullptr;
|
||||
Buffer<tAlphabetType> mAlphabetRange;
|
||||
bool mTrapState = false;
|
||||
|
||||
typedef typename NFA<tAlphabetType, tStateType>::Vertex NState;
|
||||
|
||||
public:
|
||||
explicit DFA(NFA<tAlphabetType, tStateType>& nfa) {
|
||||
if (!nfa.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mAlphabetRange = nfa.mAllSymbols;
|
||||
|
||||
struct DStateKey {
|
||||
const List<NState*>* nStates;
|
||||
bool operator==(const DStateKey& in) const {
|
||||
if (nStates->length() != in.nStates->length()) {
|
||||
template <typename tAlphabetIterator>
|
||||
bool makeDeterministic(const tAlphabetIterator& allSymbols) {
|
||||
if (!isValid()) {
|
||||
return false;
|
||||
}
|
||||
// FIXME : make linear time
|
||||
for (auto state : *nStates) {
|
||||
bool found = false;
|
||||
for (auto in_state : *in.nStates) {
|
||||
if (state.data() == in_state.data()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ualni dStateHashFunc(DStateKey key) {
|
||||
alni out = 0;
|
||||
for (auto state : *key.nStates) {
|
||||
out += alni(state.data());
|
||||
}
|
||||
return out;
|
||||
};
|
||||
struct Group {
|
||||
Buffer<State*> states;
|
||||
AvlTree<Group*, tAlphabetType> transitions;
|
||||
State* newState = nullptr;
|
||||
bool accepting = false;
|
||||
tStateType stateVal = tStateType();
|
||||
};
|
||||
|
||||
// all NFA states that are reachable from initial DFA State for specific symbol in alphabet
|
||||
struct DState {
|
||||
struct DTransition {
|
||||
DState* state = nullptr;
|
||||
tAlphabetType accepting_code;
|
||||
struct GroupKey {
|
||||
const Group* group;
|
||||
};
|
||||
|
||||
List<NState*> nStates;
|
||||
List<DTransition> transitions;
|
||||
Buffer<Group> newStates = { {} };
|
||||
|
||||
Vertex* dVertex = nullptr; // relevant DFA vertex
|
||||
// 1) find new states
|
||||
Map<GroupKey, bool> lookup;
|
||||
List<Group*> workingSet;
|
||||
|
||||
ualni debug_idx = 0;
|
||||
};
|
||||
findClosureSet({ getStartState() }, newStates.first().states);
|
||||
|
||||
// includes closure of NFA start state by definition
|
||||
auto start_state = new DState();
|
||||
nfa.closure({ nfa.getStartVertex() }, start_state->nStates, ualni(start_state));
|
||||
workingSet.pushBack(&newStates.first());
|
||||
while (workingSet.length()) {
|
||||
auto group = workingSet.first();
|
||||
|
||||
Map<DStateKey, DState*, DefaultAllocator, DStateKey::dStateHashFunc, 256> dStates;
|
||||
for (auto symbol : allSymbols) {
|
||||
|
||||
dStates.put({ &start_state->nStates }, start_state);
|
||||
// calculate new possible state
|
||||
Group potentialGroup;
|
||||
findMoveSet(group.states, potentialGroup.states, symbol);
|
||||
if (!potentialGroup.states.size()) continue;
|
||||
|
||||
List<DState*> working_set = { start_state };
|
||||
|
||||
// while there is items to work with
|
||||
auto currentDState = working_set.first();
|
||||
while (currentDState) {
|
||||
|
||||
// check all possible transitions for any symbol
|
||||
for (auto symbol : mAlphabetRange) {
|
||||
|
||||
List<NState*> reachableNStates;
|
||||
|
||||
nfa.move(currentDState->data->nStates, reachableNStates, symbol, ualni(currentDState + symbol));
|
||||
nfa.closure(reachableNStates, reachableNStates, ualni(currentDState + symbol));
|
||||
|
||||
if (!reachableNStates.length()) {
|
||||
continue;
|
||||
// find existing or create group
|
||||
Group* targetGroup = nullptr;
|
||||
auto iter = lookup.presents({ &potentialGroup });
|
||||
if (iter) {
|
||||
targetGroup = lookup.getSlotVal(iter);
|
||||
} else {
|
||||
targetGroup = newStates.append({});
|
||||
lookup.put({ targetGroup }, {});
|
||||
}
|
||||
|
||||
DState* targetDState = nullptr;
|
||||
|
||||
// check if set of all reachable NFA states already forms existing DFA state
|
||||
auto idx = dStates.presents({ &reachableNStates });
|
||||
if (idx) {
|
||||
targetDState = dStates.getSlotVal(idx);
|
||||
// add transition
|
||||
group.transitions.insert(targetGroup, symbol);
|
||||
}
|
||||
|
||||
if (!targetDState) {
|
||||
// register new DFA state
|
||||
targetDState = new DState();
|
||||
|
||||
targetDState->debug_idx = dStates.size();
|
||||
|
||||
targetDState->nStates = reachableNStates;
|
||||
|
||||
// append to working stack
|
||||
working_set.pushBack(targetDState);
|
||||
dStates.put({ &targetDState->nStates }, targetDState);
|
||||
workingSet.popFront();
|
||||
}
|
||||
|
||||
// add transition to DFA state
|
||||
currentDState->data->transitions.pushBack({ targetDState, symbol });
|
||||
// 2) find new states termination values
|
||||
// ...
|
||||
|
||||
// 3) transfer
|
||||
mStates.removeAll();
|
||||
|
||||
for (auto group : newStates) {
|
||||
group.newState = addState();
|
||||
}
|
||||
|
||||
working_set.popFront();
|
||||
currentDState = working_set.first();
|
||||
}
|
||||
|
||||
// create own vertices
|
||||
for (auto node : dStates) {
|
||||
const tStateType* state = nullptr;
|
||||
ualni numFound = 0;
|
||||
for (auto iter : node->val->nStates) {
|
||||
if (iter->isTermination) {
|
||||
state = &iter->state;
|
||||
break;
|
||||
for (auto group : newStates) {
|
||||
for (auto transition : group.transitions) {
|
||||
addTransition(/* ... */);
|
||||
}
|
||||
}
|
||||
if (numFound != 1) {
|
||||
printf("Error constructing dfa - invalid number of termination states in final node.");
|
||||
exit(1);
|
||||
}
|
||||
node->val->dVertex = addVertex(*state);
|
||||
}
|
||||
|
||||
// connect all vertices
|
||||
for (auto node : dStates) {
|
||||
for (auto edge : node->val->transitions) {
|
||||
addTransition(node->val->dVertex, edge.data().state->dVertex, edge.data().accepting_code);
|
||||
}
|
||||
}
|
||||
|
||||
// set the starting vertex
|
||||
mStart = start_state->dVertex;
|
||||
|
||||
// cleanup
|
||||
for (auto node : dStates) {
|
||||
delete node->val;
|
||||
}
|
||||
|
||||
collapseEquivalentVertices();
|
||||
}
|
||||
|
||||
/*
|
||||
tStateType move(tAlphabetType symbol) {
|
||||
if (mTrapState || !mIter) {
|
||||
return tNoStateVal;
|
||||
}
|
||||
|
||||
for (auto edge : mIter->edges) {
|
||||
if (edge.data().transition_code == symbol) {
|
||||
mIter = edge.data().vertex;
|
||||
return mIter->termination_state;
|
||||
}
|
||||
}
|
||||
|
||||
mTrapState = true;
|
||||
return tNoStateVal;
|
||||
}
|
||||
|
||||
void start() {
|
||||
mIter = mStart;
|
||||
mTrapState = false;
|
||||
}
|
||||
*/
|
||||
|
||||
[[nodiscard]] uhalni nVertices() const { return (uhalni) mVertices.length(); }
|
||||
|
||||
[[nodiscard]] const Buffer<tAlphabetType>& getRange() const { return mAlphabetRange; }
|
||||
|
||||
public:
|
||||
[[nodiscard]] Range<tAlphabetType> getAlphabetRange() const {
|
||||
Range<tAlphabetType> out;
|
||||
for (auto vertex : mVertices) {
|
||||
vertex.data().marked = false;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
getAlphabetRangeUtil(mStart, &out, first);
|
||||
out.mEnd++;
|
||||
return out;
|
||||
}
|
||||
|
||||
void getAlphabetRangeUtil(Vertex* vert, Range<tAlphabetType>* out, bool& first) const {
|
||||
vert->marked = true;
|
||||
for (auto edge : vert->edges) {
|
||||
auto const code = edge.data().transition_code;
|
||||
|
||||
if (first) {
|
||||
*out = { code, code };
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (code < out->mBegin) {
|
||||
out->mBegin = code;
|
||||
}
|
||||
if (code > out->mEnd) {
|
||||
out->mEnd = code;
|
||||
}
|
||||
|
||||
if (!edge.data().vertex->marked) {
|
||||
getAlphabetRangeUtil(edge.data().vertex, out, first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collapseEquivalentVertices() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
Vertex* addVertex(const tStateType& state) {
|
||||
auto node = mVertices.addNodeBack();
|
||||
node->data.state = state;
|
||||
return &node->data;
|
||||
}
|
||||
|
||||
void addTransition(Vertex* from, Vertex* to, tAlphabetType transition_symbol) {
|
||||
from->edges.pushBack({ to, transition_symbol });
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue