langage avec deux opérateurs binaires de même priorité, associatif gauche et associatif droit

11

Existe-t-il un langage de programmation (ou de script) (ou un langage spécifique à un domaine) ayant deux opérateurs binaires oplet oprde même priorité d' oplêtre à gauche et oprà droite?

(Je ne trouve pas un tel exemple, mais j'essaie de coder un analyseur assez général pour gérer ce cas étrange)

Comment les expressions de la forme x opl y opr z ou x opr y opl z seraient-elles analysées? Et plus généralement avec encore plus d'opérandes?

Basile Starynkevitch
la source
4
Si ça fait mal, ne faites pas ça.
CodesInChaos
1
Dans Haskell, vous pouvez définir vos propres opérateurs d'infixe avec leurs propres priorités, et vous obtenez une erreur dans ce cas; si vous avez x <@ y @> zà <@être associatif à gauche et à @>être associatif à droite, GHC vous donne une "erreur d'analyse de priorité": "ne peut pas mélanger ' <@' [infixl 0] et ' @>' [infixr 0] dans la même expression d'infixe" (où j'ai défini ces opérateurs au niveau 0 pour l'exemple).
Antal Spector-Zabusky
@ AntalSpector-Zabusky: Ce serait une excellente réponse!
Basile Starynkevitch
@ AntalSpector-Zabusky: Même chose dans Swift. Je pense que vous pouvez réellement définir les opérateurs, mais dans une expression, vous devez utiliser tous les opérateurs associatifs de gauche ou tous les opérateurs associatifs de droite avec la même priorité. Vous pouvez donc utiliser x leftop y leftop z, ou x rightop y rightop z, mais pas x leftop y rightop z.
gnasher729
@BasileStarynkevitch: Comme vous le souhaitez! Puisque vous avez mentionné les "analyseurs flexibles", j'ai inclus quelques langages plus obscurs qui ont des analyseurs très flexibles (vous avez toujours voulu if_then_else_ou [1;2;3]être défini dans des bibliothèques?).
Antal Spector-Zabusky

Réponses:

10

Voici trois langages qui vous permettent de définir vos propres opérateurs, qui font deux choses et demie différentes ! Haskell et Coq interdisent tous les deux ces sortes de manigances - mais différemment - tandis qu'Agda permet ce genre de mélange d'associativités.


Tout d'abord, à Haskell , vous n'êtes tout simplement pas autorisé à le faire. Vous pouvez définir vos propres opérateurs et leur donner la priorité (de 0 à 9) et l'associativité de votre choix. Cependant, le rapport Haskell vous interdit de mélanger les associativités :

Les opérateurs consécutifs non entre parenthèses avec la même priorité doivent tous deux être associatifs à gauche ou à droite pour éviter une erreur de syntaxe. [Rapport Haskell 2010, chap. 3]

Donc, dans GHC , si nous définissons un infixlopérateur associatif gauche ( ) <@et un opérateur associatif droit @>au même niveau de priorité - disons 0 - alors l'évaluation x <@ y @> zdonne l'erreur

L'erreur d'analyse de priorité
    ne peut pas mélanger ' <@' [ infixl 0] et ' @>' [ infixr 0] dans la même expression d'infixe

(En fait, vous pouvez également déclarer un opérateur infixe mais non associatif, comme ==, c'est x == y == zdonc une erreur de syntaxe!)


D'un autre côté, il y a le prouveur de langage / théorème de type dépendant Agda (qui, il est vrai, est considérablement moins courant). Agda possède certaines des syntaxes les plus malléables de toutes les langues que je connais, prenant en charge les opérateurs mixfix : la bibliothèque standard contient la fonction

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

qui, une fois appelé, est écrit

if b then t else f

avec les arguments remplissant les traits de soulignement! Je mentionne cela parce que cela signifie qu'il doit prendre en charge une analyse incroyablement flexible. Bien entendu, Agda a aussi des déclarations de fixité (bien que ses niveaux de priorité vont nombres naturels plus arbitraires, et sont généralement en 0-100), et Agda ne vous permettra de mélanger les opérateurs de même priorité mais différents fixités. Je ne peux cependant pas trouver d'informations à ce sujet dans la documentation, j'ai donc dû expérimenter.

Réutilisons notre <@et @>d'en haut. Dans les deux cas simples, nous avons

  • x <@ y @> zanalyse en tant que x <@ (y @> z); et
  • x @> y <@ zanalyse en tant que (x @> y) <@ z.

Je pense que ce qu'Agda fait est de regrouper la ligne en morceaux "associatifs de gauche" et "associatifs de droite", et - à moins que je ne pense à des choses erronées - le morceau associatif de droite obtient la "priorité" en saisissant les arguments adjacents. Cela nous donne donc

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

analyse en tant que

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

ou

Arbre d'analyse de <code> ((((a <@ b) <@ (c @> (d </code> @> (e @> f)))) <@ g

Cependant, malgré mes expériences, je me suis trompé la première fois que j'ai écrit cela, ce qui pourrait être instructif :-)

(Et Agda, comme Haskell, a des opérateurs non associatifs, qui donnent correctement des erreurs d'analyse, il serait donc possible que les associativités mixtes entraînent également une erreur d'analyse.)


Enfin, il y a le langage Coq , prouveur de théorèmes / type dépendant , qui a une syntaxe encore plus flexible que Agda parce que ses extensions de syntaxe sont en fait implémentées en donnant des spécifications pour les nouvelles constructions syntaxiques, puis en les réécrivant dans le langage de base (vaguement de type macro , Je suppose). Dans Coq, la syntaxe de liste [1; 2; 3]est une importation facultative de la bibliothèque standard. De nouvelles syntaxes peuvent même lier des variables!

Encore une fois, dans Coq, nous pouvons définir nos propres opérateurs d'infixe et leur donner des niveaux de priorité (de 0 à 99, principalement) et des associativités. Cependant, dans Coq, chaque niveau de priorité ne peut avoir qu'une seule associativité . Donc, si nous définissons <@comme associative à gauche, puis essayons de définir @>comme associative à droite au même niveau - disons, 50 - nous obtenons

Erreur: le niveau 50 est déjà déclaré associatif de gauche alors qu'il devrait désormais être associatif de droite

La plupart des opérateurs de Coq sont à des niveaux divisibles par 10; si j'ai eu des problèmes d'associativité (ces associativités de niveau sont globales), j'ai généralement juste augmenté le niveau d'un dans les deux sens (généralement vers le haut).

Antal Spector-Zabusky
la source
2
(Gee, quelle étrange sélection de langages. Pouvez-vous dire que j'étudie la théorie des langages de programmation? :-P)
Antal Spector-Zabusky
Merci beaucoup pour votre réponse détaillée. BTW, comment avez-vous dessiné l'image (avec quel outil graphviz??)
Basile Starynkevitch
BTW, pourquoi "fixité" au lieu de "priorité"?
Basile Starynkevitch
@BasileStarynkevitch: Cela dépend de l'endroit où vous voulez dire. Si vous voulez dire dans la section Coq, c'était juste une erreur :-) (Et celle qui est maintenant corrigée!)
Antal Spector-Zabusky
1
@BasileStarynkevitch: Aussi, j'ai raté votre question sur la photo! J'ai dessiné cela avec le paquet LaTeX qtree et je l' ai rendu dans LaTeXit , un rendu d' extraits LaTeX pour Mac. Le code source pertinent était \ttfamily \Tree[.<@ [.<@ [.<@ a b ] [.@> c [.@> d [.@> e f ]]]] g ].
Antal Spector-Zabusky
2

Depuis qu'ils ont été popularisés par Douglas Crockford, les analyseurs Pratt (ou analyseurs Top-Down Operator Precedence) ont commencé à devenir plus courants. Ces analyseurs fonctionnent à partir d'un tableau de priorité et d'associativité des opérateurs, plutôt que d'avoir les règles intégrées dans une grammaire fixe, ils sont donc utiles pour les langages qui permettent aux utilisateurs de définir leurs propres opérateurs.

Ils ont une fonction d'analyse qui fonctionne en analysant d'abord le terme le plus à gauche d'une expression, puis en liant récursivement de nouveaux opérateurs et des termes de droite tant qu'ils se lient de manière appropriée. Les opérateurs associatifs de gauche lieront les termes de droite qui ont la priorité jusqu'à et y compris la même priorité, tandis que les opérateurs associatifs de droite se lient uniquement à leur niveau de priorité, mais ne l'incluent pas. Je crois que cela donne le même arbre d'analyse que pour Agda, cité ci-dessus.

Jules
la source