refactor and reuse Automatas. TODO : add test for automatas

This commit is contained in:
IlyaShurupov 2024-02-06 17:18:19 +03:00 committed by Ilya Shurupov
parent 184f8dd1fe
commit 8f38095bec
3 changed files with 166 additions and 318 deletions

View file

@ -317,12 +317,13 @@ namespace tp {
return *this; return *this;
} }
void append(Arg data) { tType& append(Arg data) {
if (mLoad == mSize) { if (mLoad == mSize) {
resizeBuffer(tResizePolicy(mSize)); resizeBuffer(tResizePolicy(mSize));
} }
new (&mBuff[mLoad]) tType(data); new (&mBuff[mLoad]) tType(data);
mLoad++; mLoad++;
return mBuff[mLoad - 1];
} }
void append(const Buffer& in) { void append(const Buffer& in) {

View file

@ -299,6 +299,14 @@ namespace tp {
} }
} }
void transferNodes(List in) {
removeAll();
for (auto node : in) {
attach(node);
}
in.detachAll();
}
public: public:
template <class tArchiver> template <class tArchiver>
void archiveWrite(tArchiver& ar) const { void archiveWrite(tArchiver& ar) const {

View file

@ -4,78 +4,90 @@
#include "Utils.hpp" #include "Utils.hpp"
#include "List.hpp" #include "List.hpp"
#include "Map.hpp" #include "Map.hpp"
#include "Tree.hpp"
namespace tp { namespace tp {
// Non-Deterministic Finite-State Automata // Non-Deterministic Finite-State Automata
template <typename tAlphabetType, typename tStateType> template <typename tAlphabetType, typename tStateType>
class NFA { class FiniteStateAutomation {
public:
struct State;
public: public:
struct Vertex; class Transition {
friend FiniteStateAutomation;
struct Edge { public:
Vertex* mVertex = nullptr; enum Type { ANY, EPSILON, SYMBOL };
bool mConsumesSymbol = false;
Range<tAlphabetType> mAcceptingRange;
bool mAcceptsAll = false;
bool mExclude = false;
bool isTransition(const tAlphabetType& symbol) { public:
if (symbol == 0) return false; Transition(Type type, State* state, tAlphabetType symbol = tAlphabetType()) {
if (!mConsumesSymbol || mAcceptsAll) return true; mState = state;
bool const in_range = (symbol >= mAcceptingRange.mBegin && symbol <= mAcceptingRange.mEnd); mType = type;
return in_range != mExclude; 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 { class State {
List<Edge> edges; friend FiniteStateAutomation;
tStateType state{};
bool isTermination = false; State() = default;
ualni debug_idx = 0;
ualni flag = 0; 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: private:
List<Vertex> mVertices; List<State> mStates;
Vertex* mStart = nullptr; const State* mStartState = nullptr;
Buffer<tAlphabetType> mAllSymbols;
public: public:
NFA() = default; FiniteStateAutomation() = default;
Vertex* addVertex(const tStateType& state, bool termination) { State* addState(const tStateType& state, bool accepting) {
auto node = mVertices.newNode(); auto node = mStates.newNode();
node->data.isTermination = termination; node->data.mIsAccepting = accepting;
node->data.state = state; node->data.mState = state;
node->data.debug_idx = mVertices.length() + 1; mStates.pushBack(node);
mVertices.pushBack(node);
return &node->data; return &node->data;
} }
void void addTransition(State* from, State* to, const tAlphabetType& symbol) {
addTransition(Vertex* from, Vertex* to, Range<tAlphabetType> range, bool consumes, bool accepts_all, bool exclude) { from->edges.pushBack(Transition(Transition::SYMBOL, to, symbol));
Edge edge;
edge.mVertex = to;
edge.mConsumesSymbol = consumes;
edge.mAcceptingRange = range;
edge.mExclude = exclude;
edge.mAcceptsAll = accepts_all;
from->edges.pushBack(edge);
} }
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) { void setStartVertex(State* start) { mStartState = start; }
vertex->state = state;
vertex->isTermination = terminated; [[nodiscard]] State* getStartState() const { return mStartState; }
}
[[nodiscard]] bool isValid() const { [[nodiscard]] bool isValid() const {
if (!mStart) { if (!mStartState) {
return false; return false;
} }
return true; return true;
@ -83,285 +95,112 @@ namespace tp {
// vertices that are reachable from initial set with no input consumption (E-transitions) // vertices that are reachable from initial set with no input consumption (E-transitions)
// does not include initial set // does not include initial set
void closure(const List<Vertex*>& set, List<Vertex*>& closure, ualni unique_call_id) { void findClosureSet(const Buffer<State*>& from, Buffer<State*>& closureSet) const {
List<Vertex*> marked; Map<State*, bool> lookup;
marked = set; List<State*> workingSet;
while (marked.length()) { for (auto item : from) {
auto first = marked.first()->data; workingSet.pushBack(item.data());
if (first->flag != unique_call_id) { }
first->flag = unique_call_id;
closure.pushBack(first); 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);
} }
for (auto edge : first->edges) {
if (!edge.data().mConsumesSymbol && edge.data().mVertex->flag != unique_call_id) { workingSet.popFront();
marked.pushBack(edge.data().mVertex);
}
}
marked.popFront();
} }
} }
// vertices that are reachable from initial set with symbol transition // 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) { void findMoveSet(const Buffer<State*>& from, Buffer<State*>& moveSet, tAlphabetType symbol) const {
for (auto vertex : set) { Map<State*, bool> lookup;
for (auto edge : vertex->edges) {
if (!edge.data().mConsumesSymbol) continue; for (auto vertex : from) {
bool transition = edge.data().isTransition(symbol); for (auto edge : vertex.mTransitions) {
if (transition && edge.data().mVertex->flag != unique_call_id) { if (edge.data().isepsilon()) continue;
edge.data().mVertex->flag = unique_call_id; if (!edge.data().isTransition(symbol)) continue;
reachable.pushBack(edge.data().mVertex); if (lookup.presents(edge.data().mState)) continue;
moveSet.append(edge.data().mState);
lookup.put(edge.data().mState, {});
}
}
}
template <typename tAlphabetIterator>
bool makeDeterministic(const tAlphabetIterator& allSymbols) {
if (!isValid()) {
return false;
}
struct Group {
Buffer<State*> states;
AvlTree<Group*, tAlphabetType> transitions;
State* newState = nullptr;
bool accepting = false;
tStateType stateVal = tStateType();
};
struct GroupKey {
const Group* group;
};
Buffer<Group> newStates = { {} };
// 1) find new states
Map<GroupKey, bool> lookup;
List<Group*> workingSet;
findClosureSet({ getStartState() }, newStates.first().states);
workingSet.pushBack(&newStates.first());
while (workingSet.length()) {
auto group = workingSet.first();
for (auto symbol : allSymbols) {
// calculate new possible state
Group potentialGroup;
findMoveSet(group.states, potentialGroup.states, symbol);
if (!potentialGroup.states.size()) 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 }, {});
} }
// add transition
group.transitions.insert(targetGroup, symbol);
}
workingSet.popFront();
}
// 2) find new states termination values
// ...
// 3) transfer
mStates.removeAll();
for (auto group : newStates) {
group.newState = addState();
}
for (auto group : newStates) {
for (auto transition : group.transitions) {
addTransition(/* ... */);
} }
} }
} }
}; };
}
// 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()) {
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;
};
};
// 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;
};
List<NState*> nStates;
List<DTransition> transitions;
Vertex* dVertex = nullptr; // relevant DFA vertex
ualni debug_idx = 0;
};
// includes closure of NFA start state by definition
auto start_state = new DState();
nfa.closure({ nfa.getStartVertex() }, start_state->nStates, ualni(start_state));
Map<DStateKey, DState*, DefaultAllocator, DStateKey::dStateHashFunc, 256> dStates;
dStates.put({ &start_state->nStates }, start_state);
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;
}
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);
}
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);
}
// add transition to DFA state
currentDState->data->transitions.pushBack({ targetDState, symbol });
}
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;
}
}
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 });
}
};
}