Para o objetivo desse desafio, dizemos que um padrão regex corresponde a uma sequência se toda a sequência for correspondida pelo padrão, não apenas uma substring.
Dado dois padrões de expressões regulares A e B , dizemos que A é mais especializado que B, se cada string que corresponde a A também corresponde a B, mas não o contrário. Dizemos que A é equivalente a B se ambos os padrões corresponderem exatamente ao mesmo conjunto de cadeias. Se nenhum padrão é mais especializado que o outro, nem é equivalente, dizemos que A e B são incomparáveis .
Por exemplo, o padrão Hello, .*!
é mais especializado que .*, .*!
; os padrões (Hello|Goodbye), World!
e Hello, World!|Goodbye, World!
são equivalentes; e os padrões Hello, .*!
e .*, World!
são incomparáveis.
A relação "mais especializado que" define uma ordem parcial estrita no conjunto de padrões de expressões regulares. Em particular, para todos os padrões A e B , exatamente um dos seguintes é verdadeiro:
- A é mais especializado que B ( A < B ).
- B é mais especializado que A ( A > B ).
- A e B são equivalentes ( A = B ).
- A e B são incomparáveis ( A ∥ B ).
Quando A e B são incomparáveis, podemos distinguir ainda mais entre dois casos:
- A e B são separados ( A ∥ B ), o que significa que nenhuma string é correspondida por ambos.
- A e B estão cruzando ( A ≬ B ), o que significa que algumas cadeias são correspondidas por ambas.
Desafio
Escreva um programa ou uma função que pegue um par de padrões regex e os compare usando a ordem acima. Ou seja, se os dois padrões são A e B , o programa deve determinar se A < B , A > B ,
A = B ou A ∥ B .
× 92% Bônus Um bônus adicional é concedido se, quando os padrões forem incomparáveis, o programa determinar se eles estão se cruzando ou não.
Entrada e saída
O programa deve aceitar dois padrões de expressão regular, como seqüências de caracteres, no sabor definido abaixo. Você pode ler a entrada através do STDIN , a linha de comando , como argumentos de função ou um método equivalente . Você pode assumir que os padrões são válidos.
O programa deve produzir exatamente uma das quatro saídas distintas (ou cinco saídas distintas, se você optar pelo bônus acima), dependendo do resultado da comparação (as saídas exatas são com você.) Você pode gravar a saída em STDOUT , retorne-o como resultado da função ou use um método equivalente .
Sabor Regex
Você pode oferecer suporte a quaisquer recursos de regex que desejar, mas deve suportar os seguintes:
- Alternância com
|
. - Quantificação com
*
. - Agrupando com
(
e)
. - Combinando qualquer caractere (possivelmente excluindo novas linhas) com
.
. - (Opcional: × 80% de bônus) Correspondência de classes de caracteres simples e negadas com
[…]
e[^…]
, respectivamente. Você não precisa suportar nenhuma classe de caracteres predefinida (por exemplo[:digit:]
), mas deve suportar intervalos de caracteres. - Personagem escapando com
\
. Deve ser pelo menos possível especificar caracteres especiais (por exemplo|*().[^-]\
) e, de preferência, também caracteres especiais comuns em outros tipos (por exemplo{}
), mas o comportamento ao escapar de caracteres não especiais não é especificado. Em particular, você não precisa oferecer suporte a seqüências de escape especiais, como\n
para uma nova linha e similares. Uma implementação possível é simplesmente pegar o caractere após o\
literal.
Você pode assumir a existência de um caractere de entrada que não pode ser correspondido por nenhum literal (ou seja, só pode ser correspondido por .
classes de caracteres negadas.)
Regras adicionais
- Você pode usar as bibliotecas de expressões regulares ou a funcionalidade interna de expressões regulares apenas para fins de correspondência e substituição de cadeias.
- Você pode ignorar quaisquer problemas relacionados ao código do idioma, como regras de agrupamento.
- Para afirmar o óbvio: seu programa deve terminar. Ele deve ser executado em uma quantidade razoável de tempo, considerando padrões típicos (definitivamente não mais que uma hora, de preferência muito menos).
Pontuação
Isso é código-golfe. Sua pontuação é o produto do tamanho do código , em bytes, e qualquer um dos bônus . A pontuação mais baixa vence.
Casos de teste
O formato dos casos de teste é o seguinte:
<Test ID>
<Pattern A>
<Ordering>
<Pattern B>
<Test ID>
<Pattern A>
<Ordering>
<Pattern B>
...
Onde <Test ID>
é um identificador para o caso de teste <Pattern A>
e <Pattern B>
os padrões de regex e <Ordering>
a ordem entre eles, e é um dos seguintes:
<
:<Pattern A>
é mais especializado que<Pattern B>
.>
:<Pattern B>
é mais especializado que<Pattern A>
.=
: Os padrões são equivalentes.|
: Os padrões são incomparáveis e disjuntos.X
: Os padrões são incomparáveis e se cruzam.
O valor especial <empty pattern>
representa o padrão vazio.
A. Padrões básicos
B. Padrões complexos
C. Padrões básicos com classes de caracteres
D. Padrões complexos com classes de caracteres
Programa de teste
O seguinte snippet pode ser usado para comparar padrões de regex:
<style>#main {display: none;}#main[loaded] {display: inline;}.pattern_container {position: relative;}.pattern_underlay, .pattern {font: 12pt courier, monospace;overflow: hidden;white-space: pre;padding: 7px;box-sizing: border-box;}.pattern_underlay {background-color: #dddddd;color: #707070;border-radius: 4px;box-shadow: 0.5px 0.5px 2.5px #aaaaaa;}.pattern_underlay[error] {background-color: #ffccbb;}.pattern {position: absolute;left: 0px;top: 0px;background: none;border: none;width: 100%;height: 100%;resize: none;white-space: normal;}#ordering {min-width: 28pt;text-align: center;font-size: 16pt;}#status {padding: 5px;background-color: #fffdce;box-shadow: 1.5px 1.5px 3.5px #aaaaaa;font-size: 10pt;white-space: pre;display: none;}#status[error] {display: inline;background-color: #ffe8df;}#status[loading] {display: inline;}.inline_code {background-color: #dddddd;font: 12pt courier, monospace;padding: 2px;}.placeholder {visibility: hidden;}.error_text {background-color: #fffcb7};</style><span id="main"><table><tr><td><div class="pattern_container"><div class="pattern_underlay" id="pattern1_underlay"></div><textarea class="pattern" id="pattern1" oninput="compare()">Hello, .*!</textarea></div></td><td id="ordering"></td><td><div class="pattern_container"><div class="pattern_underlay" id="pattern2_underlay"></div><textarea class="pattern" id="pattern2" oninput="compare()">.*, .*!</textarea></div></td></tr></table><br></span><span id="status" loading>Loading...</span><script type='text/javascript'>var Module = {setStatus: function (status) {document.getElementById("status").innerHTML = status;if (status == "") {compare();document.getElementById("status").removeAttribute("loading");document.getElementById("main").setAttribute("loaded", 1);}}};function underlay_chars(str) {if (/^\n*$/.exec(str))return str.split("\n").map(function () { return '<span class="placeholder"> \n</span>'; });if (str.indexOf("\n") >= 0)str = str.replace(/\s*$/gm, function (m) { return m.replace(/[^\n]/g, "\0"); });return (str + "\n").split("").map(function (c) {if (c == "\0") return "·";else return '<span class="placeholder">' + c + '</span>';});}function underlay_str(str) {return underlay_chars(str).join("");}function str_to_array32(str) {a = [];for (c of str) {n = c.charCodeAt(0);a.push(n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, n >> 24);}a.push(0, 0, 0, 0);return a;}function compare() {try {for (e of ["pattern1_underlay", "pattern2_underlay", "status"])document.getElementById(e).removeAttribute("error");for (e of ["pattern1", "pattern2"])document.getElementById(e + "_underlay").innerHTML = underlay_str(document.getElementById(e).value);c = Module.ccall("regex_compare", "number", ["array", "array"], [str_to_array32(document.getElementById("pattern1").value),str_to_array32(document.getElementById("pattern2").value)]);if (c >= 0)document.getElementById("ordering").innerHTML = "∥≬<>="[c];else {i = Module.ccall("regex_error_index", "number", [], []);l = Module.ccall("regex_error_length", "number", [], []);e = document.getElementById("pattern" + -c + "_underlay");t = underlay_chars(document.getElementById("pattern" + -c).value);e.setAttribute("error", 1);e.innerHTML =t.slice(0, i).join("") +'<span class="error_text">' + t.slice(i, i + l).join("") + "</span>" +t.slice(i + l).join("");e.setAttribute("error", 1);throw "Pattern error: " + Module.ccall("regex_error", "string", [], []).replace(/`(.*?)'/g, '<span class="inline_code">$1</span>');}} catch (e) {document.getElementById("ordering").innerHTML = "??";document.getElementById("status").innerHTML = e;document.getElementById("status").setAttribute("error", 1);}}</script><script async type="text/javascript" src="https://gist.githack.com/anonymous/91f27d6746566c7b4e4c/raw/c563bf84a01c3a1c6e5f021369a3e730a2e74a1a/rpo.js"></script>