- Quando é preciso comparar expressões regulares como conjuntos de strings, o antimirov calcula em uma única tela a relação de inclusão e equivalência entre duas expressões α e β, além de interseção e diferença
- A área de resultados mostra o complemento e as relações juntos, permitindo verificar operações como
~α,α < β,α = β,α & β,α ^ β,α - β - Ao inserir uma string separada
s, é possível validar imediatamente se cada expressão regular faz correspondência, coms ∈ αes ∈ β - A sintaxe oferece suporte a
., concatenação, alternativa|, repetição*·+·?·{n}·{m,n}, grupos de caracteres, grupos de caracteres negados, escape e escapes Unicode UTF-16 - Não oferece suporte a recursos que alteram o comportamento, como âncoras, asserções de comprimento zero, retroreferências, extração de subgrupos, busca/correspondência parcial e ignorar maiúsculas e minúsculas
Comparação de regex e operações de conjunto
- A área de entrada recebe duas expressões regulares α e β
- A saída mostra juntos o complemento e as operações de relação/conjunto das duas expressões regulares
~α,~β: complemento de cada expressão regularα < β,α = β,α > β: relação de inclusão e equivalência entre as duas expressões regularesα & β: interseçãoα ^ β: diferença simétricaα - β: diferença de conjuntos
- Ao inserir a string s, é possível verificar no formato
s ∈ α,s ∈ βse ela pertence a cada expressão regular - Também são exibidos o tamanho de cada expressão regular e o número de estados do DFA
- Na tela de exemplo,
|α| = 1,|β| = 1 - Na tela de exemplo,
dfa(α)edfa(β)têm 1 estado cada
- Na tela de exemplo,
Sintaxe de expressões regulares suportada
- Os operadores básicos tratam caractere único, concatenação, alternativa e repetição
.: corresponde a qualquer caractere únicoxy: concatenação que corresponde axseguido deyx|y: corresponde axouyx*: repetição de 0 ou mais vezes(xyz): agrupamento(): regex vazia que corresponde à string vazia
- Também há suporte a abreviações comuns de repetição
x+: repetição de 1 ou mais vezes, equivalente axx*x?: correspondência opcional, equivalente a(x|)x{n}: concatenaxn vezesx{m,n}: concatenaxde no mínimo m até no máximo n vezes
- Conjuntos de caracteres e escapes podem usar as seguintes formas
[a-z0-9]: corresponde a um único caractere do grupo[^a-z0-9]: corresponde a um único caractere que não está no grupo\\c: escape do caractere especialc\\u001a: corresponde ao caractere UTF-16 correspondente- Além disso, caracteres como
a,b,ccorrespondem a si mesmos
Recursos não suportados
- O antimirov é focado em tratar expressões regulares como alvo de operações de conjunto, por isso exclui os seguintes recursos
- Âncoras
^,$- Ainda assim,
^e$precisam ser escapados
- Ainda assim,
- Asserções de comprimento zero, por exemplo
(?=...),(?<=...) - Retrorreferências, por exemplo
\\1,\\2 - Extração de subgrupos
- Busca ou correspondência parcial
- Outros flags que alteram o comportamento, como ignorar maiúsculas e minúsculas
- Âncoras
- Mais informações estão em non/antimirov
1 comentários
Opiniões no Hacker News
Criei uma demonstração web parecida que mostra o processo de uma regex virando parser → NFA → DFA → DFA mínimo, e que também gera saída do DFA mínimo para LLVM IR/Javascript/WebAssembly
http://compiler.org/reason-re-nfa/src/index.html
Como referência, a derivada de Brzozowski, que pode ser usada como uma abordagem alternativa para correspondência de regex, também pode ser interessante: https://en.wikipedia.org/wiki/Brzozowski_derivative
Esta biblioteca pode ser usada para criar uma hierarquia de classes de strings e, com isso, aproveitar strings tipadas de forma mais ativa
Por exemplo, e-mails e URLs têm gramáticas especiais, e o espaço de valores deles é um subconjunto de todas as strings não vazias; strings não vazias são um subconjunto de todas as strings
Se o sistema de tipos souber que uma string de e-mail é um subtipo de string não vazia, pode considerar válido passar um endereço de e-mail para uma função que exige uma string não vazia
Esta biblioteca pode ser usada para verificar a definição e a hierarquia desses tipos de string, e a implementação da hierarquia varia conforme a linguagem, com subclassing, limites de traits etc.
O construtor
Addressnão é exportado, apenas o tipo; a validação é feita emfromString :: Text -> Maybe Address, retornandoNothingse o endereço for inválidoNão misture a validade nos dados; sinalize-a por um caminho separado e, quando precisar produzir saída, extraia de volta o valor encapsulado com
toText :: Address -> Texthttps://news.ycombinator.com/item?id=31092912
@A heurística simples de considerar errada qualquer regex que tente expressar um “endereço de e-mail válido” é bastante segura, mas acaba com toda a diversão
Regex é um bom exemplo de uma teoria matemática bem elegante e complexa empacotada em uma interface valiosa
Sinto algo parecido com álgebra linear
Até transformações de Möbius no plano complexo,
w=(az+b)/(cz+d), podem ser convertidas em álgebra linearBoas interfaces têm valor intrínseco, mas muita gente focada em resultados não reconhece bem isso
Esta página incrível calcula relações binárias entre pares de regex e mostra o DFA como grafo
É uma demonstração realmente impressionante de operações bem não triviais realizadas sobre regex
Ainda assim, achei que as âncoras
^e$não seriam problemaColei “regex filter numbers divisible by 3” e a página travou completamente: https://stackoverflow.com/q/10992279/41948
^(?:[0369]+|[147](?:[0369]*[147][0369]*[258])*(?:[0369]*[258]|[0369]*[147][0369]*[147])|[258](?:[0369]*[258][0369]*[147])*(?:[0369]*[147]|[0369]*[258][0369]*[258]))+$^([0369]|[147][0369]*[258]|(([258]|[147][0369]*[147])([0369]|[258][0369]*[147])*([147]|[258][0369]\*[258])))+$Fiquei curioso se existe uma expressão mais curta
Por exemplo, coisas como
(ab+c+)+,(abc){100},a.*quick brown fox jumps over the lazy dogEu queria ver a interseção entre URLs e endereços de e-mail sintaticamente válidos, mas só inserir a regex de URL abaixo já faz a página demorar demais para processar
[\-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([\-a-zA-Z0-9()@:%_+.~#?&//=]*)Fonte: https://stackoverflow.com/a/3809435/623763
(...){1,256}são muito pesadas, e o código em Scala JS acaba estourando o tempo ou matando o navegadorSe trocar isso por
(...)+, funciona pelo menos no meu ambiente. Expressões pequenas como(...){1,6}provavelmente não têm problemaFiquei surpreso com o fato de que as regexes geradas por união e interseção não são particularmente concisas, mas logo entendi
Por exemplo, a interseção de
"y.+"e".+z"pode ser escrita como a expressão muito simples"y.*z", e a página também confirma a equivalência. Mas a ferramenta produzyz([^z][^z]*z|z)*|y[^z](zz*[^z]|[^z])*zz*Deve haver uma razão para esse resultado, mas é bem possível que gerar a regex mínima por algum critério como número de caracteres seja muito mais difícil
".+z"fica maior e mais bagunçada depois de ser convertida para um autômato determinísticoJá usei esse conceito no passado para escrever a lógica de validação da configuração “IP RegEx filter”
O objetivo era permitir que o usuário configurasse filtros de IP por meio de expressões regulares. O pessoal de marketing não entendia CIDR e, por causa do Google Analytics, conhecia expressões regulares
Como definir uma expressão regular válida? A interseção com a expressão regular de “todos os endereços IPv4” não podia ser vazia e, ao mesmo tempo, também não podia ser igual à expressão regular de “todos os endereços IPv4”
Isso evitou muitas reclamações de que o filtro não fazia nada, mas não impediu a entrada de filtros incorretos em si
Isso também ajuda a lidar com o problema de filtros incorretos
Para ficar mais prático no mobile, seria bom desativar a sugestão automática no campo de entrada de expressões regulares
https://stackoverflow.com/questions/35513968/disable-autocor...
Testei a página com 2 expressões regulares para números divisíveis por 3 parecidas, e ela travou mesmo removendo o
^e o$das extremidadesRegex 1:
([0369]|([258]|[147][0369]*[147])([0369]|([147][0369]*[258]|[258][0369]*[147]))*([147]|[258][0369]*[258])|([147]|[258][0369]*[258])([0369]|([147][0369]*[258]|[258][0369]*[147]))*([258]|[147][0369]*[147]))*Regex 2:
([0369]|[258][0369]*[147]|(([147]|[258][0369]*[258])([0369]|[147][0369]*[258])*([258]|[147][0369]*[147])))*Tudo era parseado até imediatamente antes do último
*, mas, no momento em que eu acrescentava o*, a página inteira travavaSem o
*, ele gerava um validador válido que parseava trechos numéricos cuja soma dos dígitos é divisível por 3