Pegjs: Adicione a capacidade de rastrear a posição do nó

Criado em 13 ago. 2011  ·  15Comentários  ·  Fonte: pegjs/pegjs

Os analisadores gerados pelo PEG.js atualmente não rastreiam a posição (linha e coluna). Eu gostaria de adicionar esse recurso, pois seria bastante útil.

Uma maneira é adicionar as propriedades line e column a cada objeto retornado como resultado de correspondência:

start = "a" b:"b" { return [b.line, b.column]; } // Returns [1, 2] on the input "ab".

Outra forma é apenas disponibilizar variáveis ​​especiais line e column dentro de ações/predicados, referindo-se à posição do início da regra atual:

start = "a" "b" { return [line, column]; } // Returns [1, 1] on the input "ab".

A primeira maneira é mais flexível, mas essa flexibilidade pode não ser necessária na verdade. Não tenho certeza de qual caminho vou implementar ainda.

Ambas as maneiras prejudicariam o desempenho. Para evitar isso nos casos em que o rastreamento de posição não é necessário, o rastreamento deve ser ativado somente se a opção trackPosition com um valor verdadeiro for passada para PEG.buildParser ao gerar o analisador.

feature

Comentários muito úteis

@tomitrescak esse recurso aparentemente mudou algumas vezes no passado. Dê uma olhada no changelog para mais informações. tl;dr é que você precisa usar a função location() em sua gramática

Todos 15 comentários

Qualquer uma das opções será ótima. Para minhas necessidades, simplesmente linha, coluna ficará bem.

Também estou procurando uma solução para isso. Atualmente, eu hackeei algo rapidamente baseado em computeErrorPosition(), mas tem vários problemas.

Na superfície, a primeira abordagem parece mais intuitiva do que a segunda.

eu gosto mais da primeira abordagem, também.

Uma possível eficiência é ter name.position como uma função, para que possa ser calculada preguiçosamente. Isso poderia retornar uma matriz de 2 itens de linha e coluna. Apenas uma idéia para aqueles que não querem calcular a posição de cada item.

Em retrospectiva, parece melhor fornecer as posições dos caracteres inicial e final e, em seguida, fornecer uma função auxiliar na biblioteca que converte a posição em linha/coluna. Como no caso geral, você não precisa de linha/coluna, a menos que haja um erro, e depois de atingir um erro, normalmente você só precisa disso uma vez.

Atualmente, estou fazendo isso de uma maneira hackeada, abusando das variáveis startPos0 e pos , o que parece hackish, mas funciona por enquanto.

Até que haja uma correção oficial, estou incluindo esta função (uma cópia aproximada de computeErrorPosition ) no início de minhas gramáticas. Pelo menos me dará a posição _current_ (embora não a posição dos nós individuais):

  function computeCurrentPos() {
    /*
     * The first idea was to use |String.split| to break the input up to the
     * error position along newlines and derive the line and column from
     * there. However IE's |split| implementation is so broken that it was
     * enough to prevent it.
     */

    var line = 1;
    var column = 1;
    var seenCR = false;

    for (var i = 0; i < pos; i++) {
      var ch = input.charAt(i);
      if (ch === '\n') {
        if (!seenCR) { line++; }
        column = 1;
        seenCR = false;
      } else if (ch === '\r' | ch === '\u2028' || ch === '\u2029') {
        line++;
        column = 1;
        seenCR = true;
      } else {
        column++;
        seenCR = false;
      }
    }

    return { line: line, column: column, pos: pos };
  }

Esse problema foi corrigido por uma série de commits .

Agora você pode passar a opção trackLineAndColumn para a função PEG.buildParser :

var parser = PEG.buildParser(myGrammar, { trackLineAndColumn: true});

Definir trackLineAndColumn para true torna duas novas variáveis ​​visíveis nas ações e predicados — line e column . Para ações, essas variáveis ​​denotam a posição inicial da expressão da ação enquanto em predicados denotam a posição atual. O comportamento ligeiramente diferente é motivado pelo uso esperado.

O rastreamento de linha e coluna é opcional porque prejudica o desempenho (torna os analisadores cerca de 3-4x mais lentos). Talvez eu consiga otimizar isso um pouco no futuro (tentei fazê-lo funcionar primeiro).

Na descrição do problema, mencionei uma abordagem diferente para esse problema: adicionar as propriedades line e column a cada resultado de partida. Embora essa solução fosse mais limpa e preferida pelos usuários, percebi que não é possível definir propriedades em valores primitivos, como strings, número ou booleanos (que geralmente são retornados como resultados de correspondência). Retornar instâncias de objetos de encapsulamento (por exemplo String , Number ou Boolean ) seria uma solução possível, mas provavelmente levaria a bugs sutis nos analisadores criados pelos usuários, porque esses wrappers se comportam de maneira um pouco diferente dos primitivos. Assim, decidi implementar a proposta alternativa.

A página de teste online será atualizada para oferecer suporte a esse recurso?

@paulftw O editor online sempre usa a versão estável mais recente do PEG.js (atualmente 0.6.2). Vou atualizá-lo assim que lançar o PEG.js 0.7.0 (que incluirá esse recurso). A menos que algo inesperado aconteça, isso acontecerá na segunda quinzena de abril.

Observe que o código-fonte do site está disponível no GitHub , portanto, se você estiver impaciente, poderá executá-lo e modificá-lo você mesmo.

Acabei de perceber que todas as linhas e colunas são baseadas em 1, enquanto os arrays JS, bem como praticamente todas as linguagens e bibliotecas modernas, usam indexação baseada em 0.

Alguma chance de reverter essa decisão de design?

@paulftw Você pode dar um exemplo de um gerador de analisador que relata linhas e colunas como baseadas em 0?

A ideia por trás da minha decisão é que esses números provavelmente serão exibidos aos usuários (em mensagens de erro, como posições de nós, etc.), então a indexação baseada em 1 faz mais sentido do que a baseada em 0. Para processamento de máquina, a variável offset provavelmente será usada com mais frequência, e essa é baseada em 0.

Alguma chance de reverter essa decisão de design?

Sim, se eu me convencer :-)

Eu uso peg.js para destacar texto no editor Ace. Muito naturalmente, os números de linha Ace são baseados em 0.
Parece, no entanto, que em Bison os locais são baseados em 1.
http://git.savannah.gnu.org/cgit/bison.git/tree/src/location.c#n73

Se este for realmente o caso, então eu deveria parar de discutir.

Não tenho certeza que tipo de exemplo você quer ver.
Agora eu uso o seguinte código para encontrar um nome de símbolo:

captor
= "" { return new Position(linha - 1, coluna - 1); }

símbolo
= start:captor name:IDENT end:captor S* { return new Symbol(name, new Range(start, end)); }

@paulftw Eu dividi esse problema em um problema separado . Eu esperaria que os usuários adotassem o 0.7.0 e veria qual é o uso predominante para decidir.

Eu sei que isso está fechado há muito tempo, mas como posso fazer isso funcionar? Sou bem noob nisso. Compilei minha gramática com o sinalizador "trackLineAndColumn': true", mas os nós ainda não contêm linhas e colunas. O que mais é necessário?

Você menciona a seguir, eu só não sei como usá-lo está documentado em algum lugar, por favor?

Definir trackLineAndColumn como true torna duas novas variáveis ​​visíveis nas ações e predicados — linha e coluna. Para ações, essas variáveis ​​denotam a posição inicial da expressão da ação enquanto em predicados denotam a posição atual. O comportamento ligeiramente diferente é motivado pelo uso esperado.

@tomitrescak esse recurso aparentemente mudou algumas vezes no passado. Dê uma olhada no changelog para mais informações. tl;dr é que você precisa usar a função location() em sua gramática

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

Questões relacionadas

StoneCypher picture StoneCypher  ·  8Comentários

brettz9 picture brettz9  ·  8Comentários

futagoza picture futagoza  ·  13Comentários

gatsbyz picture gatsbyz  ·  3Comentários

audinue picture audinue  ·  13Comentários