Pegjs: Capacidade de especificar a contagem de repetições (como em regexps)

Criado em 11 ago. 2011  ·  22Comentários  ·  Fonte: pegjs/pegjs

Seria útil se a gramática PEG.js permitisse que algo como expressões de intervalo de expressões regulares básicas POSIX fossem usadas. Por exemplo:

  • "a"\{1,7\}

corresponde a , aa , ..., aaaaaaa

  • "a"\{0,1\}

corresponde à string vazia e a

  • "a"\{,6\}

corresponde a uma string com até (inclusive) seis a 's

  • "a"\{6,\}

corresponde a uma sequência de seis ou mais a 's

  • "a"\{3\}

corresponde apenas aaa , sendo equivalente a "a"\{3,3\}

feature

Comentários muito úteis

Eu adoraria contagens de repetição também. Mas eu sugeriria uma sintaxe um pouco diferente. Pegasus é quase idêntico ao pegjs, apenas para C#. Veja aqui: https://github.com/otac0n/Pegasus/wiki/Syntax-Guide#expressions

E eles implementaram esse recurso usando isso: d<3> e<2,> f<1,5>

Todos 22 comentários

Eu não vou implementar esse recurso.

A principal razão é que não há espaço na gramática PEG.js para a sintaxe {m,n} — chaves já são tomadas para ações e eu não quero usar barras invertidas como você sugere (elas são feias e não compatíveis com regexps Perl que são as mais usadas agora e também fonte de outra sintaxe PEG.js) ou outros delimitadores (isso seria confuso).

Na minha experiência, esse tipo de repetição limitada ocorre principalmente nas partes "lexicais" da gramática (regras como color = "#" hexdigit hexdigit hexdigit hexdigit hexdigit hexdigit ) e não com tanta frequência. Eu acho que não há problema em usar apenas sequências de expressões e operadores de repetição existentes ( * , + , ? ) lá.

Eu reconsiderei e estou reabrindo esta questão. Parece que a capacidade de especificar um número arbitrário de repetições é muito desejada pelos usuários.

Eu gostaria de evitar a sintaxe {m,n} do tipo regexp porque { e } já são tomadas para ações e reutilizá-las criaria ambiguidade. Atualmente estou pensando em algo assim:

"foo" @ 1..10   // repeat 1 to 10 times
"foo" @ 1..     // repeat at least once
"foo" @ ..10    // repeat at most 10 times

A maior questão é qual deve ser o(s) caractere(s) de separação e como marcar intervalos.

Quanto ao caractere de separação, @ parece bom para mim. Eu estava considerando % e # , mas na minha cabeça o primeiro já está associado à interpolação de strings (por exemplo, em Python) e o segundo com comentários (em várias linguagens). Também estou pensando em pular completamente o separador:

"foo" 1..10   // repeat 1 to 10 times
"foo" 1..     // repeat at least once
"foo" ..10    // repeat at most 10 times

Quanto à marcação de intervalo, me inspirei em Ruby. Eu também estava pensando em - , mas parece muito com um sinal de menos. Por outro lado, : do tipo Python também me parece legal.

Não tenho certeza sobre intervalos semi-abertos. Talvez seja melhor marcá-los usando + e - assim:

"foo" @ 1+    // repeat at least once
"foo" @ 10-   // repeat at most 10 times

Alguma ideia ou comentário?

Muito legal que você planeja oferecer suporte a esse recurso!

Eu gosto da sua sugestão (padrão):
"foo" @ 1..10 // repete 1 a 10 vezes
"foo" @ 1.. // repete pelo menos uma vez
"foo" @ ..10 // repete no máximo 10 vezes

Eu não gosto da sintaxe +/- para intervalos semi-abertos, a sintaxe de ponto duplo é IMO muito mais intuitiva e legível.

A única coisa que eu pensei duas vezes foi usar "#" vs "@", porque IMO "#" naturalmente implica números/contagem, enquanto "@" implica naturalmente uma referência, então "#" pode ser um pouco mais intuitivo e legível (e talvez você possa usar o "@" no futuro para alguma coisa?). Mas isso é realmente um problema menor, e eu ficaria feliz com a sintaxe "@".

Saúde!

Apenas um comentário rápido: acho que @ e % são escolhas melhores do que # porque marcadores de sintaxe que não suportam a gramática PEG.js, especialmente aqueles que tentam adivinhar a sintaxe (por exemplo, o marcador de código do Stack Overflow), provavelmente interpretará # como o início de um comentário, fazendo com que ele seja mostrado - irritantemente - a partir desse ponto até o EOL na "cor do comentário". Esta não é uma preferência baseada na lógica e no raciocínio, é claro, mas no pragmatismo.

Que tal um caso especial para {num, num} ? O que significa repetição, já que { , num} e { num, } não são códigos js válidos, e {num, num} e { num } são inúteis.

Eles provavelmente não serão significativos, mesmo que a ação seja de outros idiomas.

Eu gosto dessas variantes entre as sugeridas (mas isso cabe a você, claro, escolher, já que você é o autor :)):

// why we need separator, anyway? for me it looks very cool and simple to understand
"foo" 1..10   // repeat 1 to 10 times
"foo" 1..     // repeat at least once
"foo" ..10    // repeat at most 10 times

ou

"foo"@1..10   // repeat 1 to 10 times
"foo"@1..     // repeat at least once
"foo"@..10    // repeat at most 10 times

mas o segundo é menos preferível

a ideia x..y / ..y / x.. parece muito legal, já que .. parece um operador consistente graças a ela.

+/- não estão ok para mim, pois confundem e se tornam os operadores adicionais acima do .. (e + já é usado)

Pensando nisso novamente. Estes funcionarão?

'foo'<1,5>
'foo'< ,3>
'foo'<2, >

já que < e > não são usados ​​atualmente pela gramática

:+1: de mim, isso parece bom.

claro, <,3> é equivalente a <0,3> , então podemos exigir apenas o número mínimo. Isso seria congruente com o que o ECMA fez para expressões regulares JavaScript.

Eu gosto do <,> . Mas eu também sugeriria o uso de <3> sendo o mesmo que <3,3> .

Eu concordo, a sintaxe <> deve mapear diretamente para o comportamento de {} no RegExp tanto quanto possível.

Se não me engano, não há necessidade de adicionar nenhum delimitador, a menos que você queira permitir nomes de variáveis ​​nos intervalos.

foo 1,2 fighter
bar ,3 tender
baz 4, lurhmann
qux 5 quux

são todos inequívocos.

@pygy , o problema de não usar um delimitador é que ele potencialmente sufoca a evolução da sintaxe da linguagem.

Por exemplo, se quiséssemos usar vírgula para outra coisa mais tarde, teríamos problemas com colisões de sintaxe em todo lugar. Restringi-lo dentro de colchetes <> reduz substancialmente a área de superfície de vírgulas e números.

Além disso, as pessoas estão acostumadas a usar o estilo {1,6} em RegExps de qualquer maneira.

Eu não gosto muito da sintaxe, mas eu quero esse recurso, e seria ótimo se uma expressão pudesse ser usada como um valor de intervalo.

Meu caso de uso: analisar literais nas respostas do servidor IMAP, que se parecem com {42}\r\n... , onde 42 é o número de caracteres após a nova linha que representa uma string (mostrada aqui como reticências). Como não há delimitador final para um literal IMAP, a contagem de caracteres é a única maneira de analisar essa resposta.

E as variáveis ​​nas restrições? Isso é muito útil para mensagens com cabeçalho, contendo seu comprimento. Por exemplo, a gramática

  = len:number message:.<len,len> .* {return message;}
number
  = n:[0-9] {return parseInt(n);}

deve analisar

4[__] -> ['[', '_', '_', ']']
4[___] -> ['[', '_', '_', '_']
4[_] -> Error: expected 4 chars, got 3

Isso é útil para muitos protocolos.

Pode ser usar essa sintaxe:
expression |min,max| , então colchetes angulares podem ser usados ​​para regras de modelo.

Você ainda está pensando em implementar isso?
Que tal algo semelhante aos intervalos da ABNF ?

exp *     // 0 or more times
exp 1*    // at least once
exp *10   // up to 10 times
exp 1*10  // 1 to 10 times

Olá. Eu tenho um formato de arquivo complexo para analisar. É metade binário, metade ASCII.

Aqui uma versão simplificada do problema:

KK4TesRandom ou KK10TestATestBRandom

A lógica:

<StringIndicator><StringLength><String><otherStuff>

O KK é o indicador para marcar uma string. Os seguintes dígitos (aqui 4 e 10 ) são o comprimento da string. Em seguida, a string em si (aqui Test e TestATestB ). A string não é terminada por nenhum padrão previsível. Eu basicamente tenho que usar as informações de comprimento. Eu diria que este é um padrão comum em formatos de arquivos binários, mas é possível analisar com a gramática atual?

Obrigada.

Eu implemento tal coisa no meu branch ranges-dynamic-boundary . A gramática ficará assim:

start = len:nx data:.|len| { return data; };
nx = n:$[0-9]+ { return parseInt(n, 10); };

@Mingun uau! Isso funciona como um encanto! Muito obrigado por sua implementação e pelo pequeno exemplo. Fiz alguns testes e funciona muito bem. Espero que o seu pull-request seja aceito pelo mestre.

Eu adoraria contagens de repetição também. Mas eu sugeriria uma sintaxe um pouco diferente. Pegasus é quase idêntico ao pegjs, apenas para C#. Veja aqui: https://github.com/otac0n/Pegasus/wiki/Syntax-Guide#expressions

E eles implementaram esse recurso usando isso: d<3> e<2,> f<1,5>

Quais são as soluções das pessoas para isso? Estou apenas começando em PEGjs agora, então talvez eu esteja tentando girar um parafuso com um martelo, mas estou apenas tentando combinar entre 1 e 6 dígitos :)

Estou usando minha própria implementação (consulte o #267 para sintaxe, a solução final suporta números, variáveis ​​e blocos de código como limites) e prepararei o PR em breve para o Peggy (rebranding do fork PEG.js que é mantido)

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

futagoza picture futagoza  ·  13Comentários

emmenko picture emmenko  ·  15Comentários

mreinstein picture mreinstein  ·  12Comentários

jiyinyiyong picture jiyinyiyong  ·  34Comentários

ceremcem picture ceremcem  ·  22Comentários