Geradores de parser também podem produzir bons erros.
(dalinaum.github.io)Tradução de um texto publicado em 2010 por Russ Cox, desenvolvedor da linguagem Go.
-
Havia se espalhado o mito de que geradores de sintaxe como o yacc não conseguiam produzir bons erros de sintaxe.
-
Mas esse problema já havia sido resolvido por Clinton Jeffery em 2003, e não é algo que exija escrever o parser manualmente.
-
Se você encontrar na tabela o conteúdo correspondente ao estado (bison) e ao token de entrada, poderá usar erros melhores do que um simples erro de sintaxe.
3 comentários
(A seguir, organizei o conteúdo que escrevi no servidor coreano de Discord do Rust.)
O maior problema deste texto é, então, se Go usa geradores de parser hoje em dia. Não usa. O parser principal de Go continua sendo escrito à mão. Não sei exatamente o motivo, mas é bem possível que, só porque rsc achava aceitável, os outros desenvolvedores de Go não tenham achado também.
A ideia do Jeffrey, em termos de compiladores, pode ser comparada a um peephole optimizer. Antes de emitir código de máquina, ele mantém um número fixo das instruções de código de máquina emitidas mais recentemente (daí esse nome, porque se observa essa janela por um pequeno “buraco” de inspeção, um peep hole) e, quando vê um certo padrão, substitui ali mesmo aquelas instruções por outras mais rápidas. Um peephole optimizer é uma ferramenta conveniente, que pode ser usada em conjunto com outros passes de otimização mais gerais, mas não é possível realizar com ele sozinho todos os tipos de otimização exigidos de um compilador. Da mesma forma, a abordagem de gerar erros a partir dos tokens recentes não consegue cobrir todos os erros de parsing. Além disso, como é uma abordagem independente que pode ser aplicada mesmo sem um gerador de parser, não é correto afirmar que a existência dessa abordagem faz desaparecer os problemas dos geradores de parser.
Pessoalmente, não acho que o conceito de gerador de parser seja ruim; o problema é a forma de pensar que os geradores de parser “impõem”. Quando se implementa um parser, seja por geração ou manualmente, é preciso considerar left recursion e right recursion, e não dá para codificar diretamente uma gramática “natural”. Mas, quando se escreve à mão, isso já é aceito de antemão; se, mesmo usando um gerador de parser, ainda for preciso escrever de acordo com um subconjunto de “gramática” preso a algoritmos de parsing específicos como LL/LR, então as vantagens do gerador ficam bastante reduzidas. (Algoritmos como GLR até dão um pouco mais de margem, mas têm limites.) Há razões para que a maioria das implementações de linguagens em nível de produção hoje não use geradores de parser ou, ocasionalmente, adote abordagens como PEG, que até são geradores de parser, mas de forma alguma aproveitam as vantagens típicas de um gerador geral.
Entendo o ponto de não usar geradores de parser por não querer ficar preso à gramática que eles impõem, mas ainda assim acho estranho afirmar que geradores de parser estão forçando uma gramática específica ao usuário.
Os geradores de parser surgiram para ajudar porque é difícil demais construir tabelas de parsing para gramáticas LR, que têm a vantagem do parsing em tempo linear; gerar parsers para gramáticas context-sensitive não determinísticas, ou parsers recursivos em que nem se sabe quando o parsing vai terminar, provavelmente nem é algo que esteja no foco dos pesquisadores/desenvolvedores de geradores de parser.
Por isso, acho que os geradores de parser não são programas que veneram cegamente e impõem gramáticas LR/LALR pouco intuitivas, mas sim ferramentas que cumprem bem seu papel ao resolver um problema muito difícil: o parsing em tempo linear e a construção das tabelas de parsing necessárias para isso.
Quase todas as linguagens que vemos no mundo real têm gramáticas LL(1), então é possível fazer parsing em tempo linear sem usar um parser LR. (Isso não significa que a gramática preferida para essas linguagens seja LL(1).) Portanto, dizer que é preciso usar um parser LR para obter parsing em tempo linear e que, como é difícil criar a tabela de parsing, é necessário um gerador, inverte a relação de causa e efeito. Além disso, o próprio processo de criar a tabela de parsing não é intrinsecamente complexo (o difícil é provar o algoritmo).