From 8f38095bec3498bb8e98b0312fa1f268cdc6b89b Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Tue, 6 Feb 2024 17:18:19 +0300 Subject: [PATCH] refactor and reuse Automatas. TODO : add test for automatas --- Containers/public/Buffer.hpp | 3 +- Containers/public/List.hpp | 8 + Language/public/Automata.hpp | 473 ++++++++++++----------------------- 3 files changed, 166 insertions(+), 318 deletions(-) diff --git a/Containers/public/Buffer.hpp b/Containers/public/Buffer.hpp index be21103..441283e 100644 --- a/Containers/public/Buffer.hpp +++ b/Containers/public/Buffer.hpp @@ -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) { diff --git a/Containers/public/List.hpp b/Containers/public/List.hpp index eba0d95..dbcfd44 100644 --- a/Containers/public/List.hpp +++ b/Containers/public/List.hpp @@ -299,6 +299,14 @@ namespace tp { } } + void transferNodes(List in) { + removeAll(); + for (auto node : in) { + attach(node); + } + in.detachAll(); + } + public: template void archiveWrite(tArchiver& ar) const { diff --git a/Language/public/Automata.hpp b/Language/public/Automata.hpp index 1b742a0..7914c0a 100644 --- a/Language/public/Automata.hpp +++ b/Language/public/Automata.hpp @@ -4,78 +4,90 @@ #include "Utils.hpp" #include "List.hpp" #include "Map.hpp" +#include "Tree.hpp" namespace tp { // Non-Deterministic Finite-State Automata template - class NFA { + class FiniteStateAutomation { + public: + struct State; public: - struct Vertex; + class Transition { + friend FiniteStateAutomation; - struct Edge { - Vertex* mVertex = nullptr; - bool mConsumesSymbol = false; - Range 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 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 mTransitions; + tStateType mStateVal = tStateType(); + bool mIsAccepting = false; }; - public: - List mVertices; - Vertex* mStart = nullptr; - Buffer mAllSymbols; + private: + List 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 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& set, List& closure, ualni unique_call_id) { - List marked; - marked = set; + void findClosureSet(const Buffer& from, Buffer& closureSet) const { + Map lookup; + List 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()); + } + + 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) { - marked.pushBack(edge.data().mVertex); - } - } - marked.popFront(); + + workingSet.popFront(); } } // vertices that are reachable from initial set with symbol transition - void move(const List& set, List& 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& from, Buffer& moveSet, tAlphabetType symbol) const { + Map 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, {}); + } + } + } + + template + bool makeDeterministic(const tAlphabetIterator& allSymbols) { + if (!isValid()) { + return false; + } + + struct Group { + Buffer states; + AvlTree transitions; + State* newState = nullptr; + bool accepting = false; + tStateType stateVal = tStateType(); + }; + + struct GroupKey { + const Group* group; + }; + + Buffer newStates = { {} }; + + // 1) find new states + Map lookup; + List 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 - class DFA { - public: - struct Vertex { - struct Edge { - Vertex* vertex = nullptr; - tAlphabetType transition_code = nullptr; - }; - - List edges; - tStateType state{}; - bool termination = false; - bool marked = false; - }; - - List mVertices; - Vertex* mStart = nullptr; - const Vertex* mIter = nullptr; - Buffer mAlphabetRange; - bool mTrapState = false; - - typedef typename NFA::Vertex NState; - - public: - explicit DFA(NFA& nfa) { - if (!nfa.isValid()) { - return; - } - - mAlphabetRange = nfa.mAllSymbols; - - struct DStateKey { - const List* 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 nStates; - List 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 dStates; - - dStates.put({ &start_state->nStates }, start_state); - - List 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 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& getRange() const { return mAlphabetRange; } - - public: - [[nodiscard]] Range getAlphabetRange() const { - Range 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* 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 }); - } - }; -} +} \ No newline at end of file