irongit

A better version of regex 101

moonregex/src/App.jsx
142 lines4.0 KBJavaScript
1import { useState } from "react";
2
3const placeholder = `Enter your variables here like below.
4group1Delim:\\n
5\tvar1:\\w
6\tvar2:\\d
7\tgroup2Delim:\\s
8\t\tvar3:\\w
9\t\tvar4:\\d`;
10
11const buildToRegex = (build) => {
12 const re = buildToRegexRecursion(build, "");
13 console.log(re);
14 return re;
15};
16
17const buildToRegexRecursion = (build, re) => {
18 if (!build?.length) return re;
19 const val = build.shift();
20 if (val.name) re += `(?P<${val.name}>${val.regex}`;
21 else re += `(?:${val.regex}`;
22 if (val.children) re += buildToRegexRecursion(val.children, "");
23 re += `${val.optional})`;
24
25 return buildToRegexRecursion(build, re, val);
26};
27
28const parseBuilder = (str) => {
29 const r = parseBuilderRegex(0);
30 const m = r.exec(str);
31 const vars = parseBuilderRecursion([], m, 0);
32 return buildToRegex(vars);
33};
34
35const parseBuilderRecursion = (vars, pm, lvl) => {
36 if (!pm) return;
37 vars.push(pm.groups);
38 pm.input = pm.input.split(pm[0])[1];
39 if (!pm.input.trim()) {
40 return vars;
41 }
42
43 // get next on this lvl
44 const r = parseBuilderRegex(lvl);
45 const m = r.exec(pm.input);
46
47 // get between if there is a next on this lvl
48 if (m) {
49 const children = pm.input.split(m[0])[0];
50 const childR = parseBuilderRegex(lvl + 1);
51 const childM = childR.exec(children);
52 if (childM) {
53 pm.groups.children = [];
54 parseBuilderRecursion(pm.groups.children, childM, lvl + 1);
55 }
56 return parseBuilderRecursion(vars, m, lvl);
57 }
58 // gap between if there is not a next on this lvl
59 else {
60 const childR = parseBuilderRegex(lvl + 1);
61 const childM = childR.exec(pm.input);
62 if (childM) {
63 pm.groups.children = [];
64 parseBuilderRecursion(pm.groups.children, childM, lvl + 1);
65 }
66 return vars;
67 }
68
69 // return this level with recursion
70};
71
72const parseBuilderRegex = (numTabs) => {
73 return new RegExp(
74 `^(\\t{${numTabs}})(?<name>\\w*)(?<optional>\\??):(?<regex>.*)$`,
75 "gm"
76 );
77};
78
79function App() {
80 const [state, setState] = useState({
81 valid: true,
82 build: [],
83 });
84 const regexInput = document.querySelector(
85 "#regex-app > div > div.QtZzw > div > div.AUc0W > div.rjodX > div.cO83v > div.h9z_E.T886D > div > div > div:nth-child(1) > textarea"
86 );
87 const regexValue = document.querySelector(
88 "#regex-app > div > div.QtZzw > div > div.AUc0W > div.rjodX > div.cO83v > div.h9z_E.T886D > div > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code"
89 );
90
91 return (
92 <div>
93 <div className="w-full">
94 <div className="w-full text-lg text-center">Build Variables</div>
95 <textarea
96 rows="10"
97 className={
98 "w-full p-2 rounded-sm font-re101-input text-input" +
99 (state.valid ? "" : " text-red-500")
100 }
101 placeholder={placeholder}
102 onKeyDown={(e) => {
103 switch (e.key) {
104 case "Enter":
105 break;
106 case "Tab":
107 e.preventDefault();
108 const index = e.target.selectionStart;
109 e.target.value =
110 e.target.value.substring(0, index) +
111 "\t" +
112 e.target.value.substring(index);
113 e.target.selectionStart = e.target.selectionEnd = index + 1;
114 break;
115 }
116 }}
117 onChangeCapture={(e) => {
118 state.build = [];
119 setState({ ...state });
120 const parseBuild = parseBuilder(e.target.value);
121 if (!parseBuild) setState({ valid: false });
122 else setState({ valid: true });
123 //regexInput.focus();
124 const length = regexValue.textContent.length;
125 for (let i = 0; i < length; i++)
126 regexInput.dispatchEvent(
127 new KeyboardEvent("keydown", {
128 key: "Backspace",
129 code: "Backspace",
130 keyCode: 8,
131 })
132 );
133 regexInput.value = parseBuild;
134 setTimeout(() => e.target.focus(), 100);
135 }}
136 ></textarea>
137 </div>
138 </div>
139 );
140}
141
142export default App;