Apakah ada pemrograman (atau scripting) bahasa (atau bahasa tertentu domain) memiliki dua operator biner opl
dan opr
dari yang sama didahulukan dengan opl
yang kiri-asosiatif dan opr
yang 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?
language-design
parsing
Basile Starynkevitch
sumber
sumber
x <@ y @> z
dengan<@
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).if_then_else_
atau[1;2;3]
didefinisikan di perpustakaan?).Jawaban:
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 :
Jadi, dalam GHC , jika kita mendefinisikan
infixl
operator asosiasi kiri ( )<@
dan operator asosiasi kanan@>
pada tingkat prioritas yang sama - katakanlah 0 - maka evaluasix <@ y @> z
memberikan kesalahan(Bahkan, Anda juga dapat mendeklarasikan operator sebagai infix tetapi non-asosiatif, seperti
==
, jadi itux == y == z
adalah 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
yang, ketika dipanggil, ditulis
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 punyax <@ y @> z
parsing sebagaix <@ (y @> z)
; danx @> y <@ z
parsing 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
parsing sebagai
atau
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 mendapatkanSebagian 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).
sumber
graphviz
?)\ttfamily \Tree[.<@ [.<@ [.<@ a b ] [.@> c [.@> d [.@> e f ]]]] g ]
.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.
sumber