linguaggio con due operatori binari della stessa precedenza, associativo di sinistra e associativo di destra


11

Esiste un linguaggio di programmazione (o scripting) (o un linguaggio specifico di dominio) con due operatori binari ople oprdella stessa precedenza con l' oplessere associativo di sinistra ed opressere associativo di destra?

(Non riesco a trovare un esempio del genere, ma sto provando a programmare un parser abbastanza generale da gestire quel caso strano)

Come verrebbero analizzate le espressioni della forma x opl y opr z o x opr y opl z ? E più in generale con ancora più operandi?


4
Se fa male, non farlo.
CodesInChaos,

1
In Haskell, puoi definire i tuoi operatori infix con le loro precedenti e in questo caso ricevi un errore; se hai x <@ y @> zcon <@essere associativo di sinistra ed associativo di @>destra, GHC ti dà un "errore di analisi della precedenza": "impossibile mescolare ' <@' [infixl 0] e ' @>' [infixr 0] nella stessa espressione infix" (dove ho definito questi operatori a livello 0 per l'esempio).
Antal Spector-Zabusky,

@ AntalSpector-Zabusky: sarebbe un'ottima risposta!
Basile Starynkevitch il

@ AntalSpector-Zabusky: lo stesso in Swift. Penso che tu possa effettivamente definire gli operatori, ma in un'espressione devi usare tutti gli operatori associativi di sinistra o tutti quelli di destra con la stessa precedenza. Quindi puoi usare x leftop y leftop z o x rightop y rightop z, ma non x leftop y rightop z.
gnasher729,

@BasileStarynkevitch: Come desideri! Da quando hai citato "parser flessibili", ho incluso un paio di linguaggi più oscuri che hanno parser molto flessibili ( hai mai desiderato if_then_else_o [1;2;3]essere definito nelle librerie?).
Antal Spector-Zabusky,

Risposte:


10

Ecco tre lingue che ti consentono di definire i tuoi operatori, che fanno due cose e mezzo diverse ! Haskell e Coq entrambi non ammettono questo tipo di shenanigans - ma in modo diverso - mentre Agda consente questo tipo di mescolanza di associatività.


Innanzitutto, in Haskell , semplicemente non ti è permesso farlo. Puoi definire i tuoi operatori e dare loro la precedenza (da 0 a 9) e l'associatività di tua scelta. Tuttavia, il Rapporto Haskell non ti consente di mescolare associatività :

Gli operatori consecutivi non personalizzati con la stessa precedenza devono essere entrambi associativi di destra o di sinistra per evitare un errore di sintassi. [Rapporto Haskell 2010, cap. 3]

Quindi, in GHC , se definiamo un infixloperatore associativo di sinistra ( ) <@e un operatore associativo di destra @>allo stesso livello di precedenza - diciamo 0 - allora la valutazione x <@ y @> zdà l'errore

L'errore di analisi della precedenza
    non può mescolare ' <@' [ infixl 0] e ' @>' [ infixr 0] nella stessa espressione infix

(In effetti, puoi anche dichiarare che un operatore è infisso ma non associativo ==, quindi x == y == zè un errore di sintassi!)


D'altra parte, c'è il prover del linguaggio / teorema tipicamente dipendente Agda (che, è vero, è notevolmente meno mainstream). Agda ha alcune delle sintassi più malleabili di qualsiasi lingua che conosca, supportando gli operatori di mixfix : la libreria standard contiene la funzione

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

che, quando chiamato, è scritto

if b then t else f

con gli argomenti che riempiono i caratteri di sottolineatura! Ne parlo perché ciò significa che deve supportare l'analisi incredibilmente flessibile. Naturalmente, Agda ha anche dichiarazioni fissità (anche se i suoi livelli di precedenza spaziano numeri naturali sopra arbitrari, e sono in genere in 0-100), e Agda fa si permette di mescolare gli operatori con la stessa precedenza, ma diverse fissità. Tuttavia, non riesco a trovare informazioni al riguardo nella documentazione, quindi ho dovuto sperimentare.

Riutilizziamo il nostro <@e @>dall'alto. Nei due casi semplici, abbiamo

  • x <@ y @> zanalizzando come x <@ (y @> z); e
  • x @> y <@ zanalizzando come (x @> y) <@ z.

Penso che quello che Agda fa sia raggruppare la linea in blocchi "associativi di sinistra" e "associativi di destra" e - a meno che non stia pensando a cose sbagliate - il pezzo associativo di destra ottiene "priorità" nell'afferrare gli argomenti adiacenti. Quindi questo ci dà

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

analizzando come

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

o

Albero di analisi di <code> ((((a <@ b) <@ (c @> (d </code> @> (e @> f)))) <@ g

Tuttavia, nonostante i miei esperimenti, ho indovinato la prima volta che l'ho scritto, il che potrebbe essere istruttivo :-)

(E Agda, come Haskell, ha operatori non associativi, che danno correttamente errori di analisi, quindi sarebbe possibile che anche associatività miste producano un errore di analisi.)


Infine, c'è il linguaggio teorema-prover / tipicamente dipendente Coq , che ha una sintassi ancora più flessibile di Agda perché le sue estensioni di sintassi sono effettivamente implementate dando specifiche per i nuovi costrutti sintattici e poi riscrivendole nel linguaggio principale (vagamente macro-like , Credo). In Coq, la sintassi dell'elenco [1; 2; 3]è un'importazione opzionale dalla libreria standard. Le nuove sintassi possono anche associare le variabili!

Ancora una volta, in Coq, possiamo definire i nostri operatori infix e dare loro livelli di precedenza (da 0 a 99, per lo più) e associatività. Tuttavia, in Coq, ogni livello di precedenza può avere solo un'associatività . Quindi, se definiamo <@associativa di sinistra e quindi proviamo a definire @>associativa di destra allo stesso livello - diciamo 50 - otteniamo

Errore: il livello 50 è già dichiarato associativo di sinistra mentre ora si prevede che sia associativo di destra

La maggior parte degli operatori in Coq sono su livelli divisibili per 10; se ho avuto problemi di associatività (questi livelli di associatività sono globali), in genere ho semplicemente aumentato il livello di uno in entrambe le direzioni (di solito in alto).


2
(Accidenti, che strana selezione di lingue. Puoi dirmi che studio teoria dei linguaggi di programmazione? :-P)
Antal Spector-Zabusky

Grazie mille per la risposta dettagliata. A proposito, come hai disegnato l'immagine (con quale strumento graphviz??)
Basile Starynkevitch il

A proposito, perché "fissità" invece di "precedenza"?
Basile Starynkevitch,

@BasileStarynkevitch: dipende da dove intendi. Se vuoi dire nella sezione Coq, è stato solo un errore :-) (E uno che ora è stato risolto!)
Antal Spector-Zabusky

1
@BasileStarynkevitch: Inoltre, ho perso la tua domanda sull'immagine! L'ho disegnato con il pacchetto LaTeX qtree e l'ho reso in LaTeXit , un renderizzatore di frammenti LaTeX per Mac. Il codice sorgente rilevante era \ttfamily \Tree[.<@ [.<@ [.<@ a b ] [.@> c [.@> d [.@> e f ]]]] g ].
Antal Spector-Zabusky

2

Da quando sono stati resi popolari da Douglas Crockford, Pratt Parsers (o parser di precedenza operatore top-down) hanno iniziato a diventare più comuni. Questi parser funzionano da una tabella di precedenza e associatività degli operatori, anziché avere le regole integrate in una grammatica fissa, quindi sono utili per le lingue che consentono agli utenti di definire i propri operatori.

Hanno una funzione di analisi che funziona analizzando dapprima il termine più a sinistra di un'espressione, quindi vincolando ricorsivamente nuovi operatori e termini della mano destra finché si legano in modo appropriato. Gli operatori associativi di sinistra vincoleranno i termini della mano destra che hanno la precedenza e includendo la stessa precedenza, mentre gli operatori associativi di destra si legano solo al loro livello di precedenza, ma non lo includono. Credo che ciò si traduca nello stesso albero di analisi di Agda, citato sopra.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.