bahasa dengan dua operator biner dengan prioritas yang sama, kiri-asosiatif & kanan-asosiatif

11

Apakah ada pemrograman (atau scripting) bahasa (atau bahasa tertentu domain) memiliki dua operator biner opldan oprdari yang sama didahulukan dengan oplyang kiri-asosiatif dan opryang benar-asosiatif?

(Saya tidak dapat menemukan contoh seperti itu, tetapi saya mencoba kode beberapa parser cukup umum untuk menangani kasus aneh)

Bagaimana ekspresi dalam bentuk x opl y opr z atau x opr y opl z diurai? Dan lebih umum lagi dengan lebih banyak operan?

Basile Starynkevitch
sumber
4
Jika sakit, jangan lakukan itu.
CodesInChaos
1
Di Haskell, Anda dapat mendefinisikan operator infiks Anda sendiri dengan prioritas mereka sendiri, dan Anda mendapatkan kesalahan dalam hal ini; jika Anda memiliki x <@ y @> zdengan <@asosiatif kiri dan asosiatif @>kanan, GHC memberi Anda "kesalahan penguraian Precedence": "tidak dapat mencampur ' <@' [infixl 0] dan ' @>' [infixr 0] dalam ekspresi infiks yang sama" (di mana saya mendefinisikan operator ini pada level 0 misalnya).
Antal Spector-Zabusky
@ AntalSpector-Zabusky: Itu akan menjadi jawaban yang bagus!
Basile Starynkevitch
@ AntalSpector-Zabusky: Sama dengan Swift. Saya pikir Anda benar-benar dapat mendefinisikan operator, tetapi dalam ekspresi Anda harus menggunakan semua operator asosiatif kiri atau semua asosiasi kanan dengan prioritas yang sama. Jadi Anda dapat menggunakan x leftop y leftop z, atau x rightop y rightop z, tetapi tidak x leftop y rightop z.
gnasher729
@ BasileStarynkevitch: Terserah Anda! Karena Anda menyebutkan "parser fleksibel", saya menyertakan beberapa bahasa yang lebih tidak jelas yang memiliki parser sangat fleksibel (pernah ingin if_then_else_atau [1;2;3]didefinisikan di perpustakaan?).
Antal Spector-Zabusky

Jawaban:

10

Berikut adalah tiga bahasa yang memungkinkan Anda menentukan operator Anda sendiri, yang melakukan dua setengah hal berbeda ! Haskell dan Coq sama-sama tidak mengizinkan shenanigans semacam ini - tetapi berbeda - sementara Agda memungkinkan pencampuran asosiasi ini.


Pertama, di Haskell , Anda tidak diizinkan melakukan ini. Anda dapat menentukan operator Anda sendiri dan memberi mereka prioritas (dari 0–9) dan asosiasi pilihan Anda. Namun, Laporan Haskell melarang Anda dari pencampuran asosiasi :

Operator tidak berurutan berturut-turut dengan prioritas yang sama harus keduanya asosiatif kiri atau kanan untuk menghindari kesalahan sintaksis. [Laporan Haskell 2010, Ch. 3]

Jadi, dalam GHC , jika kita mendefinisikan infixloperator asosiasi kiri ( ) <@dan operator asosiasi kanan @>pada tingkat prioritas yang sama - katakanlah 0 - maka evaluasi x <@ y @> zmemberikan kesalahan

Kesalahan penguraian yang didahului
    tidak dapat mencampur ' <@' [ infixl 0] dan ' @>' [ infixr 0] dalam ekspresi infiks yang sama

(Bahkan, Anda juga dapat mendeklarasikan operator sebagai infix tetapi non-asosiatif, seperti ==, jadi itu x == y == zadalah kesalahan sintaks!)


Di sisi lain, ada bahasa pengantar / teorema bertipe dependen Agda (yang, memang, dianggap kurang mainstream). Agda memiliki beberapa sintaks yang paling bisa ditiru dari bahasa apa pun yang saya tahu, mendukung operator mixfix : pustaka standar berisi fungsi

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

yang, ketika dipanggil, ditulis

if b then t else f

dengan argumen yang mengisi garis bawah! Saya menyebutkan ini karena ini berarti harus mendukung parsing yang sangat fleksibel. Tentu, Agda juga memiliki deklarasi ketetapan (meskipun tingkat diutamakan yang berkisar bilangan lebih sewenang-wenang, dan biasanya di 0-100), dan Agda tidak mengizinkan Anda untuk mencampur operator didahulukan sama tetapi jepit yang berbeda. Namun, saya tidak dapat menemukan informasi tentang ini di dokumentasi, jadi saya harus bereksperimen.

Mari kita gunakan kembali <@dan @>dari atas. Dalam dua kasus sederhana, kita punya

  • x <@ y @> zparsing sebagai x <@ (y @> z); dan
  • x @> y <@ zparsing sebagai (x @> y) <@ z.

Saya pikir apa yang dilakukan Agda adalah mengelompokkan garis menjadi potongan "asosiatif kiri" dan "asosiatif kanan", dan - kecuali jika saya memikirkan hal-hal yang salah - potongan asosiatif kanan mendapat "prioritas" dalam meraih argumen yang berdekatan. Jadi itu memberi kita

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

parsing sebagai

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

atau

Memilah pohon <code> (((a <@ b) <@ (c @> (d </code> @> (e @> f))))) <@ g

Namun, meskipun saya bereksperimen, saya salah menebak pertama kali saya menulis itu, yang mungkin bersifat instruktif :-)

(Dan Agda, seperti Haskell, memiliki operator non-asosiatif, yang dengan benar memberikan kesalahan parse, sehingga dimungkinkan untuk gabungan asosiatif untuk menghasilkan kesalahan parse juga.)


Akhirnya, ada teorema-prover / bahasa dependen-diketik Coq , yang memiliki bahkan sintaks yang lebih fleksibel daripada Agda karena ekstensi sintaks benar-benar diterapkan dengan memberikan spesifikasi untuk konstruksi sintaksis baru dan kemudian menulis ulang mereka ke dalam bahasa inti (samar-samar makro seperti , Saya seharusnya). Dalam Coq, sintaksis daftar [1; 2; 3]adalah impor opsional dari pustaka standar. Sintaks baru bahkan dapat mengikat variabel!

Sekali lagi, dalam Coq, kita dapat mendefinisikan operator infiks kita sendiri dan memberi mereka tingkat prioritas (dari 0–99, sebagian besar) dan asosiatifitas. Namun, dalam Coq, setiap tingkat diutamakan hanya dapat memiliki satu asosiatif . Jadi jika kita mendefinisikan <@sebagai asosiatif kiri dan kemudian mencoba mendefinisikan @>sebagai asosiatif kanan pada tingkat yang sama - katakan, 50 - kita mendapatkan

Kesalahan: Level 50 sudah dinyatakan asosiatif kiri sementara sekarang diharapkan asosiatif benar

Sebagian besar operator dalam Coq berada pada level yang dapat dibagi 10; jika saya mempunyai masalah asosiatif (level asosiasi ini bersifat global), saya pada umumnya hanya menabrak level tersebut satu per satu arah (biasanya naik).

Antal Spector-Zabusky
sumber
2
(Astaga, pilihan bahasa yang aneh. Bisakah Anda memberi tahu saya mempelajari teori bahasa pemrograman? :-P)
Antal Spector-Zabusky
Terima kasih banyak atas jawaban terperinci Anda. BTW, bagaimana Anda menggambar gambar (dengan alat apa graphviz?)
Basile Starynkevitch
BTW, mengapa "perbaikan" bukannya "diutamakan"?
Basile Starynkevitch
@ BasileStarynkevitch: Itu tergantung pada maksud Anda. Jika Anda maksud di bagian Coq, itu hanya kesalahan :-) (Dan yang sekarang sudah diperbaiki!)
Antal Spector-Zabusky
1
@ BasileStarynkevitch: Juga, saya melewatkan pertanyaan Anda tentang gambar! Saya menggambar itu dengan qtree paket LaTeX , dan menerjemahkannya di LaTeXit , renderer snipet LaTeX untuk Mac. Kode sumber yang relevan adalah \ttfamily \Tree[.<@ [.<@ [.<@ a b ] [.@> c [.@> d [.@> e f ]]]] g ].
Antal Spector-Zabusky
2

Sejak mereka dipopulerkan oleh Douglas Crockford, Pratt Parsers (atau parser Precedence Operator Top-Down) mulai menjadi lebih umum. Parser ini bekerja dari tabel prioritas operator dan asosiatif, daripada memiliki aturan yang dibangun ke dalam tata bahasa tetap, jadi mereka membantu untuk bahasa yang memungkinkan pengguna untuk menentukan operator mereka sendiri.

Mereka memiliki fungsi parse yang bekerja dengan mem-parsing istilah paling kiri dari sebuah ekspresi, kemudian secara rekursif mengikat operator baru dan istilah tangan kanan selama mereka mengikat dengan tepat. Operator asosiasi kiri akan mengikat istilah tangan kanan yang memiliki presedensi hingga dan termasuk prioritas yang sama, sementara operator asosiasi kanan hanya mengikat hingga tingkat prioritas mereka, tetapi tidak termasuk itu. Saya percaya ini menghasilkan pohon parse yang sama dengan Agda, dikutip di atas.

Jules
sumber