xiiregexbuilder
FPGA-Accelerated Regular Expression Matching Enginesrc/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}