xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine
commit 1314233eebd8a0f48ee7c9fbc7bde5d03499ffbd
parent 67bde232f6840495b986c67fdc1cb629471e7803
Author: Achuthan TM <achuthantm05@gmail.com>
Date:   Fri, 27 Mar 2026 13:41:32 +0530

Week 2: Implement Glushkov NFA Builder (Part 1: Initial definitions)

Diffstat:
Asrc/nfa.cpp | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/nfa.h | 14++++++++++++++
2 files changed, 102 insertions(+), 0 deletions(-)

diff --git a/src/nfa.cpp b/src/nfa.cpp @@ -0,0 +1,88 @@ +#include "nfa.h" +#include <iostream> +#include <algorithm> + +// Start at -1 so the first ++globalStateCounter gives 0. +int NFABuilder::globalStateCounter = -1; + +void NFA::addState(int id, bool isAccept) { + states.emplace(id, NFAState(id, isAccept)); +} + +void NFA::addTransition(int fromId, unsigned char c, int toId) { + states.at(fromId).transitions[c].insert(toId); +} + +bool NFA::simulate(const std::string& input) const { + std::set<int> activeStates = {startStateId}; + + for (unsigned char c : input) { + std::set<int> nextStates; + for (int stateId : activeStates) { + auto& state = states.at(stateId); + if (state.transitions.count(c)) { + for (int nextId : state.transitions.at(c)) { + nextStates.insert(nextId); + } + } + } + activeStates = nextStates; + if (activeStates.empty()) return false; + } + + for (int stateId : activeStates) { + if (states.at(stateId).isAccept) return true; + } + return false; +} + +std::unique_ptr<NFA> NFABuilder::build(ASTNode* root, int regexIdx) { + if (!root) return nullptr; + + auto nfa = std::make_unique<NFA>(regexIdx); + + // 1. Linearization (assign positions to symbols) + int localPosCounter = 1; + std::map<int, unsigned char> posToChar; + std::set<int> dotPositions; + linearize(root, localPosCounter, posToChar, dotPositions); + int numPositions = localPosCounter - 1; + + // 2. Compute nullable, firstpos, lastpos + computeNullableFirstLast(root); + + // 3. Compute followpos + std::map<int, std::set<int>> followpos; + for (int i = 1; i <= numPositions; ++i) { + followpos[i] = std::set<int>(); + } + computeFollowpos(root, followpos); + + // 4. Build NFA states and transitions + // Glushkov states: 0 (initial) + 1..n + // We map these to global IDs. + // Invariant: after each build(), globalStateCounter equals the highest global ID used. + + int startGlobalId = ++globalStateCounter; + nfa->startStateId = startGlobalId; + std::map<int, int> localToGlobal; + localToGlobal[0] = startGlobalId; + nfa->addState(startGlobalId, root->nullable); + + for (int i = 1; i <= numPositions; ++i) { + localToGlobal[i] = ++globalStateCounter; + bool isAccept = (root->lastpos.find(i) != root->lastpos.end()); + nfa->addState(localToGlobal[i], isAccept); + } + + // Transitions from state 0 + for (int p : root->firstpos) { + if (dotPositions.find(p) != dotPositions.end()) { // DOT + for (int val = 32; val <= 126; ++val) { + nfa->addTransition(localToGlobal[0], static_cast<unsigned char>(val), localToGlobal[p]); + } + } else { + unsigned char c = posToChar[p]; + nfa->addTransition(localToGlobal[0], c, localToGlobal[p]); + } + } diff --git a/src/nfa.h b/src/nfa.h @@ -0,0 +1,14 @@ +#ifndef NFA_H +#define NFA_H + +#include <vector> +#include <set> +#include <map> +#include <memory> +#include "parser.h" + +struct NFAState { + int id; + bool isAccept; + // Map from character to set of destination state IDs + std::map<unsigned char, std::set<int>> transitions;