Pegjs: Como preservar os separadores de espaço em branco?

Criado em 3 out. 2019  ·  6Comentários  ·  Fonte: pegjs/pegjs

Tipo de problema

  • Relatório de Bug: _no_
  • Solicitação de recurso: _no_
  • Pergunta: _sim_
  • Não é um problema: _não_

Pré-requisitos

  • Você pode reproduzir o problema?: _sim_
  • Você pesquisou os problemas do repositório?: _yes_
  • Você verificou os fóruns?: _sim_
  • Você realizou uma pesquisa na web (google, yahoo, etc)?: _yes_

Estou lutando para fazer o analisador PEG.js manter os espaços em branco originais da equação.

Comportamento atual: 2 * 5 + SUM(1, 2, 3)

[
   "2",
   "*",
   "5",
   "+",
   "SUM",
   "(",
   [
      "1",
      ",",
      "2",
      ",",
      "3"
   ],
   ")"
]

Comportamento desejado : 2 * 5 + SUM(1, 2, 3)

[
   "2",
   " ",
   "*",
   " ",
   "5",
   " ",
   "+",
   " ",
   "SUM",
   "(",
   [
      "1",
      ",",
      " ",
      "2",
      ",",
      " ",
      "3"
   ],
   ")"
]

Gramática para copiar: https://pastebin.com/zpwqT6Uw
Playground PEG.js https://pegjs.org/online

o que estou perdendo?

Comentários muito úteis

@marek-baranowski Outro ping suave :smiley_cat:

Além disso, escrevi um plugin PEG.js pegjs-syntactic-actions para facilitar a depuração de gramáticas e ver especificamente quais caracteres são capturados por qual regra independentemente das ações, que provavelmente é o seu problema aqui, conforme explicado por @StoneCypher.

O raciocínio deste plugin é: Eu acho que muitas vezes/às vezes é difícil entender o resultado global quando não é o que esperamos, porque resulta da combinação de muitas pequenas ações, e encontrar a ação que se comporta mal/estranhamente pode ser demorado. Com este plugin, vemos qual regra captura qual personagem e dá o nome da ação para agir.

Todos 6 comentários

@futagoza desculpe incomodá-lo, mas é a primeira vez que estou lidando com o PEG.js e esse problema é crítico para mim. Posso pedir-lhe uma pequena dica?

Cumprimentos,
Marek

Tentei analisar sua gramática (ontem e agora), mas como é muito difícil entendê-la (convenções de nomenclatura à parte, o formato, para ser honesto, está em todo lugar), demorei um pouco para encontrar uma solução:

  1. As regras que consomem espaço e dados o retornam (por exemplo const retorna [left_space, cnst, right_space] )
  2. Qualquer regra/ação que leve o resultado deve fazer algo assim: [].concat.apply([], con)

Mesmo assim, para ser honesto com você, isso parece uma solução hacky para mim. Você tem um link para uma especificação ou algo assim? Ajudaria saber quais regras posso e não posso alterar para obter o resultado desejado sem a solução hacky acima.

Se não, contanto que você esteja disposto a dedicar um tempo e arrumar a gramática e renomear algumas regras (para que seja mais fácil descobrir o que você quer), então tentarei com prazer outra tentativa 😉

@marek-baranowski - Desculpe, não vi isso até agora. Espero que isso ainda seja útil para você

Se você quiser manter os espaços, apenas trate-os como conteúdo combinável.

Não está muito claro exatamente o que você deseja para dois espaços. Você pode ter uma string de dois espaços ou uma matriz de duas strings de um espaço. Normalmente eu esperaria o primeiro, mas... tudo o que você faz é por personagem

Além disso ... por que você quer caracteres perdidos como esse, exceto para a chamada de função? O analisador deve resumir isso para você.

De qualquer forma

Isto é o que você pediu:

Document = Expression*

Whitespace
  = tx:[ \r\n]+ { return tx.join(''); }

Number
  = str:[0-9]+ { return str.join(''); }

Oper
  = '+'
  / '-'
  / '/'
  / '*'
  / ','

Label
  = l:[a-zA-Z]+ { return l.join(''); }

Parens 
  = '(' Whitespace? ex:Expression* Whitespace? ')' { return ex; }

Expression 
  = Number 
  / Oper
  / Whitespace
  / Label
  / Parens
  / [^()]+

image

A coisa é, eu não estou super convencido de que é realmente o que você quer. Por exemplo, você pode analisar os números e operadores e retornar uma forma de nó padronizada para cada um:

Document = Expression*

Whitespace
  = tx:[ \r\n]+ { return { 
    ast: 'whitespace', value: tx.join('') 
  }; }

Number
  = str:[0-9]+ { return {
    ast: 'number', value: parseInt(str,10)
  }; }

Oper
  = '+' { return { ast: 'oper', value: 'add' }}
  / '-' { return { ast: 'oper', value: 'subtract' }}
  / '/' { return { ast: 'oper', value: 'divide' }}
  / '*' { return { ast: 'oper', value: 'multiply' }}
  / ',' { return { ast: 'oper', value: 'sequence' }}

Label
  = l:[a-zA-Z]+ { return { 
    ast: 'label', value: l.join('') 
  }; }

Parens 
  = '(' Whitespace? ex:Expression* Whitespace? ')' { 
    return { ast: 'parens', value: ex 
  }; }

Expression 
  = Number 
  / Oper
  / Whitespace
  / Label
  / Parens
  / [^()]+

Agora você ainda tem seu espaço em branco, mas também tem uma árvore analisada adequada e não precisa escrever um analisador para analisar a saída do analisador, e também é fácil agora começar a adicionar recursos regularizados como números de linha e assim por diante

image

@marek-baranowski - gostaria de reduzir um pouco o tamanho deste rastreador de problemas

Se o acima é o que você precisa, você consideraria fechar este problema? Obrigado 😄

Se não for, por favor, deixe-me saber por que, e eu vou tentar novamente

@marek-baranowski Outro ping suave :smiley_cat:

Além disso, escrevi um plugin PEG.js pegjs-syntactic-actions para facilitar a depuração de gramáticas e ver especificamente quais caracteres são capturados por qual regra independentemente das ações, que provavelmente é o seu problema aqui, conforme explicado por @StoneCypher.

O raciocínio deste plugin é: Eu acho que muitas vezes/às vezes é difícil entender o resultado global quando não é o que esperamos, porque resulta da combinação de muitas pequenas ações, e encontrar a ação que se comporta mal/estranhamente pode ser demorado. Com este plugin, vemos qual regra captura qual personagem e dá o nome da ação para agir.

oh wow isso é muito legal na verdade

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