linguagem com dois operadores binários de mesma precedência, associativo à esquerda e associativo à direita

11

Existe alguma linguagem de programação (ou script) (ou alguma linguagem específica de domínio) com dois operadores binários ople oprde mesma precedência em oplser associativo à esquerda e associativo à oprdireita?

(Não consigo encontrar esse exemplo, mas estou tentando codificar algum analisador geral o suficiente para lidar com esse caso estranho)

Como as expressões do formato x opl y opr z ou x opr y opl z são analisadas? E, geralmente, com ainda mais operandos?

Basile Starynkevitch
fonte
4
Se dói, não faça isso.
CodesInChaos
1
No Haskell, você pode definir seus próprios operadores de infix com suas próprias precedências e, nesse caso, você receberá um erro; Se você tem x <@ y @> zcom <@ser deixado-associativa e @>estar certo associativo, GHC dá-lhe um "Precedência erro de análise": "Não se pode misturar ' <@' [infixl 0] e ' @>' [infixr 0] na mesma expressão infix" (onde eu definido esses operadores no nível 0, por exemplo).
Antal Spector-Zabusky,
@ AntalSpector-Zabusky: Essa seria uma ótima resposta!
Basile Starynkevitch 22/02
@ AntalSpector-Zabusky: O mesmo em Swift. Eu acho que você pode realmente definir os operadores, mas em uma expressão você deve usar todos os operadores associativos à esquerda ou todos os associativos à direita com a mesma precedência. Portanto, você pode usar x leftop y leftop z, ou x rightop y rightop z, mas não x leftop y rightop z.
gnasher729
@BasileStarynkevitch: Como você deseja! Desde que você mencionou "analisadores flexíveis", incluí algumas linguagens mais obscuras que possuem analisadores muito flexíveis (sempre desejadas if_then_else_ou [1;2;3]definidas em bibliotecas?).
Antal Spector-Zabusky

Respostas:

10

Aqui estão três idiomas que permitem definir seus próprios operadores, que fazem duas coisas e meia diferentes ! Haskell e Coq proíbem esse tipo de travessuras - mas de maneira diferente - enquanto a Agda permite esse tipo de mistura de associatividades.


Primeiro, em Haskell , você simplesmente não tem permissão para fazer isso. Você pode definir seus próprios operadores e dar a eles a precedência (de 0 a 9) e a associatividade de sua escolha. No entanto, o Relatório Haskell não permite que você misture associatividades :

Operadores não parênteses consecutivos com a mesma precedência devem ser associativos à esquerda ou à direita para evitar um erro de sintaxe. [Relatório Haskell 2010, cap. 3]

Portanto, no GHC , se definirmos um infixloperador associativo à esquerda ( ) <@e associativo à direita @>no mesmo nível de precedência - digamos 0 -, a avaliação x <@ y @> zfornecerá o erro

O erro de análise de precedência
    não pode misturar ' <@' [ infixl 0] e ' @>' [ infixr 0] na mesma expressão de infixo

(Na verdade, você também pode declarar que um operador é infixo, mas não associativo, como ==, portanto, isso x == y == zé um erro de sintaxe!)


Por outro lado, há o provador de idiomas / teoremas de tipo dependente Agda (que, reconhecidamente, é consideravelmente menos popular). A Agda possui algumas das sintaxes mais maleáveis ​​de qualquer idioma que eu conheço, suportando operadores mixfix : a biblioteca padrão contém a função

if_then_else_ : ∀ {a} {A : Set a} → Bool → A → A → A

que, quando chamado, está escrito

if b then t else f

com os argumentos preenchendo os sublinhados! Menciono isso porque isso significa que ele precisa oferecer suporte à análise incrivelmente flexível. Naturalmente, Agda também tem declarações fixity (embora seus níveis de precedência variam números naturais mais arbitrárias, e são tipicamente em 0-100), e Agda faz permitir que você misturar operadores da mesma precedência, mas diferentes fixities. No entanto, não consigo encontrar informações sobre isso na documentação, então tive que experimentar.

Vamos reutilizar o nosso <@e @>de cima. Nos dois casos simples, temos

  • x <@ y @> zanalisando como x <@ (y @> z); e
  • x @> y <@ zanalisando como (x @> y) <@ z.

Acho que o que a Agda faz é agrupar a linha em partes "associativas à esquerda" e "associativas à direita" e - a menos que eu esteja pensando em coisas erradas - o pedaço associativo à direita recebe "prioridade" ao agarrar os argumentos adjacentes. Então isso nos dá

a <@ b <@ c @> d @> e @> f <@ g

analisando como

(((a <@ b) <@ (c @> (d @> (e @> f)))) <@ g

ou

Analise a árvore de <code> (((a <@ b) <@ (c @> (d </code> @> (e @> f)))) <@ g

No entanto, apesar dos meus experimentos, adivinhei errado na primeira vez que escrevi isso, o que pode ser instrutivo :-)

(E a Agda, como Haskell, tem operadores não associativos, que fornecem erros de análise corretamente, portanto, seria possível que associatividades mistas resultem em um erro de análise também.)


Por fim, há a linguagem Coq , comprovadora de teoremas / de tipo dependente , que possui sintaxe ainda mais flexível que a Agda, porque suas extensões de sintaxe são realmente implementadas fornecendo especificações para as novas construções sintáticas e reescrevendo-as na linguagem principal (vagamente semelhante a macro , Eu suponho). No Coq, a sintaxe da lista [1; 2; 3]é uma importação opcional da biblioteca padrão. Novas sintaxes podem até vincular variáveis!

Mais uma vez, no Coq, podemos definir nossos próprios operadores de infix e fornecer a eles níveis de precedência (de 0 a 99, principalmente) e associatividades. No entanto, no Coq, cada nível de precedência pode ter apenas uma associatividade . Portanto, se definirmos <@como associativo à esquerda e tentarmos definir @>como associativo à direita no mesmo nível - digamos, 50 - obteremos

Erro: o nível 50 já foi declarado associativo à esquerda, enquanto agora é esperado que seja associativo à direita

A maioria dos operadores em Coq está em níveis divisíveis por 10; se eu tive problemas de associatividade (essas associatividades de nível são globais), geralmente apenas aumentamos o nível em um em qualquer direção (geralmente acima).

Antal Spector-Zabusky
fonte
2
(. Gee, o que é uma selecção ímpar de idiomas Você pode dizer que eu estudar programação teoria da linguagem :-P?)
Antal Spector-Zabusky
Muito obrigado pela sua resposta detalhada. BTW, como você desenhar a imagem (com qual ferramenta? graphviz?)
Basile Starynkevitch
BTW, por que "fixidez" em vez de "precedência"?
Basile Starynkevitch 22/02
@BasileStarynkevitch: Isso depende de onde você quer dizer. Se você quer dizer na seção Coq, que foi apenas um erro :-) (e que agora está corrigido!)
Antal Spector-Zabusky
1
@BasileStarynkevitch: Além disso, eu perdi sua pergunta sobre a foto! Eu desenhei isso com o pacote qtree do LaTeX e o renderizei no LaTeXit , um renderizador de trechos de LaTeX para Macs. O código fonte relevante era \ttfamily \Tree[.<@ [.<@ [.<@ a b ] [.@> c [.@> d [.@> e f ]]]] g ].
Antal Spector-Zabusky 8/16
2

Desde que foram popularizados por Douglas Crockford, os analisadores Pratt (ou analisadores de precedência de operador descendente) começaram a se tornar mais comuns. Esses analisadores funcionam a partir de uma tabela de precedência e associatividade do operador, em vez de incorporar as regras em uma gramática fixa; portanto, são úteis para idiomas que permitem aos usuários definir seus próprios operadores.

Eles possuem uma função de análise que funciona analisando primeiro o termo mais à esquerda de uma expressão e, em seguida, vinculando recursivamente novos operadores e termos da mão direita, desde que sejam vinculados adequadamente. Operadores associativos de esquerda vincularão termos do lado direito que têm precedência até e inclusive a mesma precedência, enquanto operadores associativos de direita vinculam apenas seu nível de precedência, mas não o incluem. Acredito que isso resulta na mesma árvore de análise que para a Agda, citada acima.

Jules
fonte