Compile Regexes (By Substitution)
Snails, 48 bytes
0 -&gt; )0(\0!(l.)(~
1 -&gt; )0(\1!(l.)(~
( -&gt; )0({{(
) -&gt; )0}}(~
| -&gt; )0}|{(
* -&gt; )0),(~
If we had to search for partial matches rather than matching only the full input, then it would be very easy. 0 would become \0, 1 would become \1, * would become ,, and the others would map to themselves. Instead there are a lot of shenanigans to prevent matches from starting somewhere other than the beginning or ending somewhere other than the end. !(l.) is an assertion that will fail if the beginning of the match is not at the beginning of the input. ~ matches a cell outside of the input, so it is added to all the characters that are allowed to be at the end of the regex. If there is another regex character following, it is canceled out by a numerical quantifier 0 which requires it to be matched 0 times, essentially commenting it out. To allow * (,) to work correctly despite the dummy out-of-bounds test being in the way, the language's bracket matching rules are heavily used. From the documentation:

Matching pairs of parentheses () or curly braces {} will behave as expected (like parentheses in regex), but it is also possible to leave out one half of a pair and have it inferred, according to the following rules. ) or } groups everything to the left until the nearest unclosed group opening instruction of the same type(( or { respectively), or the beginning of the pattern if none exists. It closes any unclosed opening instructions of the opposite type in the middle of this range. An otherwise unmatched ( or { is closed by the end of the pattern.

Clear as mud, right?