A better version of regex 101
| 1 | import { useState } from "react"; |
| 2 | |
| 3 | const placeholder = `Enter your variables here like below. |
| 4 | group1Delim:\\n |
| 5 | \tvar1:\\w |
| 6 | \tvar2:\\d |
| 7 | \tgroup2Delim:\\s |
| 8 | \t\tvar3:\\w |
| 9 | \t\tvar4:\\d`; |
| 10 | |
| 11 | const buildToRegex = (build) => { |
| 12 | const re = buildToRegexRecursion(build, ""); |
| 13 | console.log(re); |
| 14 | return re; |
| 15 | }; |
| 16 | |
| 17 | const 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 | |
| 28 | const 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 | |
| 35 | const 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 | |
| 72 | const parseBuilderRegex = (numTabs) => { |
| 73 | return new RegExp( |
| 74 | `^(\\t{${numTabs}})(?<name>\\w*)(?<optional>\\??):(?<regex>.*)$`, |
| 75 | "gm" |
| 76 | ); |
| 77 | }; |
| 78 | |
| 79 | function 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 | |
| 142 | export default App; |