xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine

src/main.cpp (4354B)


  1#include <iostream>
  2#include <fstream>
  3#include <string>
  4#include <vector>
  5#include <memory>
  6#include <regex>
  7#include <algorithm>
  8#include "lexer.h"
  9#include "parser.h"
 10#include "nfa.h"
 11#include "emitter.h"
 12
 13std::string trim(const std::string& str) {
 14    size_t first = str.find_first_not_of(" \t\r\n");
 15    if (first == std::string::npos) return "";
 16    size_t last = str.find_last_not_of(" \t\r\n");
 17    return str.substr(first, (last - first + 1));
 18}
 19
 20int main(int argc, char* argv[]) {
 21    bool isPII = false;
 22    int argOffset = 1;
 23    if (argc > 1 && std::string(argv[1]) == "--pii") {
 24        isPII = true;
 25        argOffset = 2;
 26    }
 27
 28    if (argc < argOffset + 1) {
 29        std::cerr << "Usage: " << argv[0] << " [--pii] <regex_file> [test_strings_file] [output_dir]" << std::endl;
 30        return 1;
 31    }
 32
 33    std::string regexFilename = argv[argOffset];
 34    std::string testFilename = (argc > argOffset + 1) ? argv[argOffset + 1] : "";
 35    std::string outputDir = (argc > argOffset + 2) ? argv[argOffset + 2] : "output";
 36
 37    if (testFilename == "-" || testFilename == "none") {
 38        testFilename = "";
 39    }
 40
 41    std::ifstream regexFile(regexFilename);
 42    if (!regexFile.is_open()) {
 43        std::cerr << "Failed to open regex file: " << regexFilename << std::endl;
 44        return 1;
 45    }
 46
 47    std::string line;
 48    int lineNum = 0;
 49    int regexIdx = 0;
 50    std::vector<std::unique_ptr<NFA>> nfas;
 51    std::vector<std::string> rawRegexes;
 52
 53    std::cout << "Starting XIIRegexBuilder pipeline..." << std::endl;
 54
 55    while (std::getline(regexFile, line)) {
 56        lineNum++;
 57        std::string trimmedLine = trim(line);
 58        if (trimmedLine.empty() || trimmedLine[0] == '#') continue;
 59
 60        std::cout << "Processing Regex [" << regexIdx << "]: " << trimmedLine << std::endl;
 61
 62        Lexer lexer(trimmedLine, lineNum);
 63        try {
 64            std::vector<Token> tokens = lexer.tokenize();
 65            Parser parser(tokens);
 66            auto ast = parser.parse();
 67            auto nfa = NFABuilder::build(ast.get(), regexIdx);
 68            if (nfa) {
 69                nfas.push_back(std::move(nfa));
 70                rawRegexes.push_back(trimmedLine);
 71                regexIdx++; // Increment only on success
 72            }
 73        } catch (const std::exception& e) {
 74            std::cerr << "Error parsing regex: " << e.what() << std::endl;
 75        }
 76    }
 77
 78    if (nfas.empty()) {
 79        std::cerr << "No valid regexes found. Exiting." << std::endl;
 80        return 1;
 81    }
 82
 83    std::vector<std::string> testStrings;
 84    std::vector<std::string> expectedMatches;
 85
 86    if (!testFilename.empty()) {
 87        std::ifstream testFile(testFilename);
 88        if (testFile.is_open()) {
 89            std::string tline;
 90            while (std::getline(testFile, tline)) {
 91                size_t last = tline.find_last_not_of("\r\n");
 92                std::string s = (last != std::string::npos) ? tline.substr(0, last + 1) : (tline.empty() ? "" : tline);
 93                
 94                // Skip comments and truly blank lines
 95                if (!s.empty() && s[0] == '#') continue;
 96                if (s.empty() && trim(tline).empty()) continue;
 97
 98                testStrings.push_back(s);
 99                
100                // Generate expected matches using std::regex
101                std::string mask = "";
102                for (const auto& re_str : rawRegexes) {
103                    bool match = false;
104                    try {
105                        std::regex re(re_str);
106                        match = std::regex_match(s, re);
107                    } catch (...) {}
108                    mask += (match ? "1" : "0");
109                }
110                std::reverse(mask.begin(), mask.end());
111                expectedMatches.push_back(mask);
112            }
113            std::cout << "Loaded " << testStrings.size() << " test strings and generated golden matches." << std::endl;
114        }
115    }
116
117    std::cout << "Generated " << nfas.size() << " NFAs. Emitting Verilog..." << std::endl;
118
119    try {
120        Emitter::emit(nfas, outputDir, testStrings, expectedMatches, isPII);
121        std::cout << "Pipeline complete. Verilog files emitted to '" << outputDir << "/'" << std::endl;
122    } catch (const std::exception& e) {
123        std::cerr << "Error during Verilog emission: " << e.what() << std::endl;
124        return 1;
125    }
126
127    return 0;
128}