Kiat untuk membuat polyglots


48

Sebuah adalah sebuah program yang dapat dijalankan dalam 2 atau lebih bahasa pemrograman yang berbeda.

Apa tips umum yang Anda miliki untuk membuat polyglots, atau memilih bahasa yang mudah untuk menulis polyglots untuk tugas tertentu?

Silakan kirim kiat yang dapat diterapkan di sebagian besar situasi. Yaitu mereka seharusnya tidak hanya bekerja di polyglots dari dua bahasa tertentu. (Anda bisa mengirim jawaban untuk pertanyaan polyglot jika Anda memiliki tip yang terlalu spesifik.) Tetapi Anda dapat memperkenalkan fitur-fitur bahasa yang membuatnya mudah digunakan dengan banyak bahasa, atau mudah ditambahkan ke polyglot yang ada.

Silakan kirim satu tip per jawaban. Dan jangan ragu untuk menyarankan edit jika tip khusus bahasa juga berlaku untuk bahasa lain.

Jawaban:


25

Memanfaatkan simbol komentar

Cara sederhana untuk membuat polyglot dua bahasa adalah dengan membuat kode dibagi menjadi dua bagian sebagai berikut:

  1. Bagian pertama melakukan pekerjaan aktual dalam bahasa A, tidak berbahaya dalam bahasa B (tidak ada kesalahan), dan berakhir pada simbol komentar bahasa-A, yang menyembunyikan bagian kedua dari bahasa A.
  2. Bagian kedua melakukan pekerjaan aktual dalam bahasa B.

Jadi

  • Bahasa A melihat bagian pertama, yang melakukan pekerjaan, dan kemudian komentar.
  • Bahasa B melihat bagian pertama yang tidak berguna dan kemudian bagian kedua, yang melakukan pekerjaan.

Satu-satunya bagian yang sulit di sini adalah menemukan seperangkat pernyataan (bagian pertama) yang melakukan pekerjaan dalam bahasa A sementara tidak memberikan kesalahan dalam bahasa B. Beberapa saran untuk ini:

  • Sebagian besar bahasa berbasis tumpukan memungkinkan hanya menampilkan bagian atas tumpukan di akhir program (kadang-kadang ini bahkan default, seperti pada 05AB1E).
  • Beberapa bahasa mengabaikan pernyataan yang tidak ditentukan (misalnya Golfscript).

Contoh sederhana yang menggunakan pedoman ini dapat ditemukan di sini . Bahasa A dan B masing-masing adalah MATL dan 05AB1E .


24

Gunakan bahasa dua dimensi

Tidak seperti bahasa satu dimensi, yang umumnya mem-parsing seluruh kode sumber dan akan menghasilkan kesalahan sintaks atau efek runtime yang tidak diinginkan pada hal-hal yang tidak mereka mengerti (sehingga memaksa Anda untuk menyembunyikan kode bahasa lain dari mereka), bahasa dua dimensi cenderung hanya kode parse di jalur eksekusi, yang berarti bahwa seluruh sisa program diabaikan. Ada juga lebih banyak ruang untuk memisahkan jalur eksekusi dari satu sama lain dalam dua dimensi; Anda dapat mengirim penunjuk instruksi berputar ke arah yang tidak biasa, seperti ke bawah atau bahkan ke kiri (membungkus putaran ke sisi kanan program), untuk mendapatkannya dengan cepat. Teknik-teknik yang berguna dalam bahasa satu dimensi juga menggeneralisasi ke bahasa dua dimensi (misalnya Anda dapat melewatkan kode dengan;; di Befunge-98, selain hanya mengirim IP ke arah yang aneh), membuat ini sebagian besar hanya keuntungan yang ketat dibandingkan dengan solusi satu dimensi.

Sebagai bonus, beberapa bahasa dua dimensi memiliki titik masuk selain bagian kiri atas program, yang berarti bahwa Anda tidak perlu melakukan upaya apa pun untuk memisahkannya dari bahasa lain; mereka akan memisahkan diri dari grup secara alami.


20

Ketahui Trues dan Falses Anda

Setiap bahasa melihat "benar" dan "salah" dengan cara yang sedikit berbeda. Jika mereka memiliki sintaks yang sama, Anda dapat mengeksploitasi ini dengan menambahkan keputusan bahwa bahasa akan menangani secara berbeda.

Salah satu contoh dari penggunaan thread Trick or Treat '', string kosong. Dalam Lua, ini dievaluasi kebenarannya, tetapi salah dalam Python, jadi berikut ini:

print(''and'trick'or'treat')

..akan mencetak string yang berbeda di setiap bahasa.

Yang diperlukan hanyalah menemukan nilai seperti ini. Misalnya, Anda bisa menggunakan '0', yang dievaluasi falsedalam PHP tetapi truedalam Python.


17

Blokir dalam setidaknya satu bahasa

Berikut ini contoh yang berfungsi baik dalam Python dan C ++

#include <iostream> /*
""" */
int main() {
    std::cout << "Hello World!\n";
}

/* """
print("Hello World!")
# */

Luis Mendo memberikan solusi yang paling mudah bagi saya, yaitu menggunakan komentar.

Anda mencari satu bahasa yang memiliki blok komentar dan bahasa lain di mana sintaks reguler di yang pertama adalah mengomentari sintaks di yang kedua.

Yang lebih mudah adalah dua bahasa dengan gaya komentar blok yang berbeda yang secara sintaksis dapat dipertukarkan dengan benar, tetapi saya tidak bisa repot untuk mengeceknya.

Lihat di Python 3.5 dan C ++


2
Baris pertama seharusnya tidak memiliki titik koma.

Benar. Poin bagus
dexgecko

15

Memecah dan menaklukkan

Saat Anda menulis polyglot dalam sejumlah besar bahasa, Anda tidak akan serta merta dapat langsung memisahkan semua kontrol bahasa dari satu sama lain. Jadi, Anda perlu "true polyglot" beberapa bahasa untuk jangka waktu yang lama, memungkinkan kode yang sama dijalankan di masing-masing bahasa. Ada dua aturan utama yang perlu diingat saat Anda melakukan ini:

  • Aliran kontrol dalam dua bahasa apa pun harus sangat mirip, atau sangat berbeda . Mencoba menangani sejumlah besar aliran kontrol yang disisipkan adalah resep untuk menjadi bingung dan membuat program Anda sulit untuk dimodifikasi. Sebagai gantinya, Anda harus membatasi jumlah pekerjaan yang harus Anda lakukan dengan memastikan bahwa semua program yang ada di tempat yang sama ada untuk alasan yang sama dan dengan senang hati dapat dijalankan secara paralel selama Anda membutuhkannya. Sementara itu, jika suatu bahasa sangat berbeda dari yang lain, Anda ingin pelaksanaannya pindah ke lokasi yang sangat berbeda sesegera mungkin, sehingga Anda tidak perlu mencoba membuat kode Anda sesuai dengan dua model sintaksis yang berbeda sekaligus.

  • Cari peluang untuk memisahkan satu bahasa, atau sekelompok bahasa yang mirip, dari satu sama lain. Bekerja dari kelompok yang lebih besar ke kelompok yang lebih kecil. Setelah Anda memiliki sekelompok bahasa yang serupa semuanya pada titik tertentu dalam program, Anda harus membaginya di beberapa titik. Pada awal program, Anda mungkin ingin, katakanlah, ingin memisahkan bahasa yang digunakan #sebagai penanda komentar dari bahasa yang menggunakan beberapa penanda komentar lainnya. Kemudian, mungkin Anda memiliki titik di mana semua bahasa menggunakan f(x)sintaks untuk panggilan fungsi, memisahkan perintah dengan titik koma, dan memiliki kesamaan sintaksis yang serupa. Pada titik itu, Anda bisa menggunakan sesuatu yang jauh lebih spesifik-bahasa untuk membaginya, misalnya fakta bahwa Ruby dan Perl tidak memproses urutan pelarian dalam ''string, tetapi Python dan JavaScript melakukannya.

Secara umum, alur logis program Anda harus berakhir sebagai pohon, berulang kali dipecah menjadi kelompok bahasa yang lebih mirip satu sama lain. Ini menempatkan sebagian besar kesulitan dalam menulis polyglot tepat di awal, sebelum split pertama. Saat aliran kontrol berkembang semakin banyak, dan bahasa yang berjalan pada titik tertentu semakin mirip, tugas Anda menjadi lebih mudah karena Anda dapat menggunakan sintaksis yang lebih maju tanpa menyebabkan bahasa yang terlibat dengan sintaks-kesalahan.

Contoh yang baik adalah himpunan {JavaScript, Ruby, Perl, Python 3}; semua bahasa ini menerima panggilan fungsi dengan tanda kurung dan dapat memisahkan pernyataan dengan titik koma. Mereka semua juga mendukung evalpernyataan, yang secara efektif memungkinkan Anda melakukan kontrol aliran dengan cara yang portabel. (Perl adalah yang terbaik dari bahasa-bahasa ini untuk memisahkan diri dari grup lebih awal, karena ia memiliki sintaks yang berbeda untuk variabel dari yang lain.)


13

Sembunyikan kode di dalam string literal

Dalam sebagian besar bahasa, string literal sendiri tidak melakukan apa-apa, atau melakukan sesuatu yang dapat dengan mudah dibalik (seperti mendorong string ke stack). Sintaks literal string juga relatif tidak standar, terutama untuk sintaksis alternatif yang digunakan banyak bahasa untuk menangani string dengan baris baru yang disematkan; misalnya, Python memiliki """ ... """, Perl memiliki q( ... ), dan Lua [[ ... ]].

Ada dua kegunaan utama ini. Pertama adalah untuk memungkinkan Anda melakukan interleave bagian-bagian yang dimaksudkan untuk bahasa yang berbeda melalui memulai string di akhir bagian pertama satu bahasa dan melanjutkannya di awal bagian kedua: itu seharusnya cukup mudah untuk menghindari penutupan string yang tidak sengaja karena variasi dari pembatas string di antara berbagai bahasa. Yang lain adalah bahwa banyak pembatas string kebetulan bermakna sebagai perintah dalam bahasa lain (seringkali lebih dari penanda komentar), sehingga Anda dapat melakukan sesuatu seperti x = [[4] ], yang merupakan tugas tidak berbahaya dalam bahasa yang menggunakan notasi JSON untuk daftar, tetapi yang dimulai string dalam Lua (dan dengan demikian memungkinkan Anda untuk membagi kode Lua dari yang lain, mengingat bahwa itu secara efektif "melompat" ke yang berikutnya ]]).


13

Mengakhiri program

Anda dapat mengakhiri program secara tiba-tiba dalam satu bahasa sehingga akan mengabaikan kode dalam bahasa lain.

Jadi pada dasarnya format ini bisa digunakan

code_in_language1 end_program_in_language1 code_for_language2 end_program_in_language2 ...

di mana end_program_in_languageNperintah untuk mengakhiri program.

Misalnya, dalam jawaban saya dalam Apa yang akan Anda bawa untuk Thanksgiving? , Saya mengakhiri program di Dip, dan kemudian saya menulis kode untuk bahasa lain, V, sehingga penerjemah Dip akan mengabaikannya.

"turkey"e#"corn"??"gravy"p&Ssalad
"turkey"e#"corn"??"gravy"                 
                         p&            # print stack and exit program (Dip) 
                           Ssalad      # Now that the program ended in Dip,
                                       # I can write V code that would otherwise
                                       # have caused errors in Dip

Tetapi kemudian, tidak semua bahasa memiliki perintah yang dapat mengakhiri program begitu saja. Namun, jika bahasa seperti itu memiliki fitur, itu harus digunakan dengan bijak.

Seperti yang disarankan oleh @LuisMendo, Anda dapat membuat kesalahan (jika diizinkan) untuk mengakhiri program jika bahasa tersebut belum memiliki builtin "end program".


2
Bahkan jika bahasa tidak memiliki fungsi atau pernyataan untuk mengakhiri program, kesalahan biasanya akan terjadi
Luis Mendo

1
@LuisMendo: Setuju, meskipun perhatikan bahwa banyak masalah polyglotting secara khusus melarang exit-via-crashing karena itu membuat semuanya terlalu mudah. Namun, itu ide yang bagus untuk mengeksploitasinya ketika mereka tidak melakukannya.

1
Anda mungkin harus menyebutkan bahwa kode bagian kedua masih harus benar secara sintaksis dalam bahasa pertama atau bahasa yang paling praktis akan menimbulkan kesalahan.
MilkyWay90

13

Variabel atau kode di dalam string literal

Literal string yang dikutip ganda sebagian besar tidak berbahaya dalam banyak bahasa. Tetapi dalam beberapa bahasa mereka juga bisa berisi kode.

Di Bash, Anda dapat menggunakan `...`(itu tidak mengakhiri program):

"`echo Hello world! >/proc/$$/fd/1`"

Di Tcl, Anda dapat menggunakan [...]:

"[puts {hello world!};exit]"

Di PHP, Anda dapat menggunakan ${...}(ini menghasilkan kesalahan dalam Bash sehingga harus muncul setelah kode Bash):

"${die(print(Hello.chr(32).world.chr(33)))}";

Di Ruby, Anda dapat menggunakan #{...}:

"#{puts 'Hello world!';exit}"

Mungkin juga ada yang lain.

Tata bahasa ini tidak kompatibel. Itu berarti Anda dapat meletakkan semua kode bahasa-bahasa ini dalam satu string di lokasi yang tidak berbahaya. Dan itu hanya akan mengabaikan kode yang tidak dikenal dalam bahasa lain dan menafsirkannya sebagai konten string.

Dalam banyak kasus, Anda juga dapat dengan mudah berkomentar karakter kutipan ganda di sana dan membuat polyglot yang lebih tradisional.


12

Aliasing variabel

Ini mungkin salah satu trik paling penting (IMO) yang paling penting untuk digunakan, terutama karena dapat menjangkau banyak bahasa.

Contoh:

print=alert;print("Hello World!")

Ini akan bekerja tidak hanya dalam Javascript, tetapi juga Python, Ruby, dll. Lebih banyak contoh nanti ketika saya memikirkan beberapa yang lain. Tentu saja, saran komentar / suntingan pos dipersilakan.


5
Perhatikan bahwa ketika melakukan mis. JS / Python, biasanya lebih pendek dari alias alertke printdalam Python (hanya 3) karena sintaksis komentar JS //,, dapat dengan mudah dikerjakan menjadi program Python, sementara Python #tidak dapat dikerjakan ke dalam JS.
ETHproduk

11

#komentar berbasis

Tip ini adalah bagian dari simbol komentar Eksploit dan Blockquotes dalam setidaknya satu bahasa

Saat membuat polyglots dengan banyak bahasa, terutama bahasa yang siap-produksi dan bukan esolang, akan berguna untuk melihat bahasa-bahasa yang digunakan #dalam komentar blok atau single-line.

  • Ada banyak bahasa dengan sintaksis komentar blokir yang dimulai dengan #, dan ada banyak variasi dalam karakter yang mengikuti #.
  • Sebagian besar dari bahasa-bahasa ini juga memungkinkan satu #sebagai komentar garis, yang berarti bahwa sesuatu yang dapat memulai blok komentar dalam satu bahasa hanyalah komentar biasa di bahasa lain, sehingga mudah untuk dimasukkan.

Berikut daftar ringkasan singkat bahasa yang digunakan #dalam komentar blok (tidak lengkap):

Language            Start       End      Single-line #?     Notes
------------------------------------------------------------------------------------------
Agena               #/          /#             ✓
AutoIt              #cs         #ce
Brat                #*          *#             ✓
C                   #if 0       #endif                      Not actually a comment
CoffeeScript        ###         ###            ✓            Needs to be on separate line
Common Lisp         #|          |#
Julia               #=          =#             ✓
Lily                #[          ]#             ✓
Objeck              #~          ~#             ✓
Perl 6              #`{         }#             ✓            Any bracketing chars will do
Picolisp            #{          }#             ✓
Scheme              #|          |#

Untuk lebih banyak contoh, lihat Kode Rosetta .

Berikut ini contoh cepat dan mudah, sebagai demonstrasi:

#|
###
#`[

print("Julia")
#=

|#
(format t "Common Lisp")
#|

###
alert("CoffeeScript")
###

]#
say "Perl 6"
#`[

...

# ]# # ### # |# ; =#

Zephyr punya #- ... -#.
DLosc

11

Perbedaan operator aritmatika

Untuk bahasa yang serupa atau poliglot sederhana, kadang-kadang berguna untuk mencari perbedaan dalam bagaimana bahasa melakukan aritmatika. Ini karena sebagian besar (non-esoterik) bahasa memiliki operator aritmatika infiks dan aritmatika dapat menjadi cara cepat dan mudah untuk memperkenalkan perbedaan.

Sebagai contoh:

  • ^ bitwise XOR dalam beberapa bahasa dan eksponensial dalam bahasa lain
  • / adalah pembagian integer dalam beberapa bahasa dan divisi floating point dalam bahasa lain
    • Untuk bahasa divisi integer, -1/2ada -1dalam beberapa bahasa (bulat ke bawah) dan 0dalam bahasa lain (bulat ke nol)
  • -1%2ada -1dalam beberapa bahasa dan 1lainnya
  • --x adalah no-op dalam beberapa bahasa (negasi ganda) dan pra-pengurangan dalam bahasa lain
  • 1/0 memberikan tak terhingga dalam beberapa bahasa dan kesalahan dalam yang lain
  • 1<<64memberi 0 dalam beberapa bahasa (overflow) dan 36893488147419103232lainnya

3
Contoh sederhananya adalah x=1;["JS","Python"][--x], yang mengembalikan nama bahasa yang digunakan (antara JS dan Python).
ETHproduk

10

Gunakan Brainfuck

Hampir semua implementasi BF membuang karakter yang tidak +-<>[].,, yang kebetulan menguntungkan kita!

BF mungkin adalah salah satu bahasa yang paling mudah digunakan untuk menjadi polyglot karena fitur ini, selama Anda menulis bagian BF terlebih dahulu. Setelah kode BF Anda ditulis, itu hanya masalah pemodelan kode apa pun yang Anda miliki di sekitar struktur BF.

Berikut ini contoh yang sangat sederhana:

.+[.+]

Peningkatan dan charcode-output ini cukup banyak "selamanya" (tergantung pada pengaturan runtime). Sekarang jika Anda ingin menulis sepotong kode acak, katakanlah, di JS, Anda bisa melakukannya:

x=>"asdf".repeat(+x)[x*Math.random()*2+1|0]

Perhatikan bagaimana JS dicetak di sekitar BF.

Pastikan untuk mengetahui bahwa ini bekerja paling baik jika Anda benar-benar mulai dengan BF; lebih sulit untuk memulai dengan bahasa lain dan mencoba menggabungkan BF.


6
Untuk polyglots yang lebih besar di mana beberapa byte penghematan dari pengintegrasian BF tidak banyak membantu, saya akan menulis BF terakhir dan membungkus kode lain sebanyak yang []diperlukan.
Sp3000

6
Ini tidak hanya berlaku untuk brainfuck tetapi juga sejumlah besar bahasa yang mirip dengan brainfuck dan banyak tarpit Turing lainnya.
0

2
Yang pertama x=>mengubah sel, yang dalam hal ini tidak masalah, tetapi hanya ingin mengatakan
Roman Gräf

7

Gunakan bahasa yang sebagian besar karakternya tidak penting

Ini adalah generalisasi poin Mama Fun Roll tentang BF . Esolang yang mengabaikan sebagian besar karakter sangat berguna dalam polyglots. Juga berguna: esolang di mana sejumlah besar karakter dipertukarkan. Beberapa contoh:

  • Whitespace mengabaikan segala sesuatu yang bukan ruang, tab, atau baris baru.
  • Brain-Flak pada dasarnya mengabaikan semuanya selain itu ()[]{}<>. ( @terkadang menyebabkan kesalahan ketika penerjemah mencoba menguraikannya sebagai awal dari bendera debug.)
  • oOo CODE mengabaikan segalanya kecuali huruf. Selain itu, semua huruf kecil dapat dipertukarkan, seperti halnya semua huruf besar.
  • Wierd hanya membedakan antara karakter whitespace dan non-whitespace.
  • Di Wordy , beberapa karakter tanda baca diabaikan, dan semua huruf dipertukarkan.
  • Kedua disisipkan dan Kurung Neraka mengabaikan segala sesuatu kecuali tanda kurung.

Saya memperbaiki @kesalahan itu.
Wheat Wizard

Coba gabungkan Whitespace dengan Python
enedil

@enedil Anda tidak perlu memiliki tab dengan Python. Anda dapat menggunakanexec('''...\t\n\40''')
MilkyWay90

5

Waspadai Komentar Blok Bersarang

Terkadang beberapa bahasa akan menggunakan sintaksis yang sama untuk memblokir komentar, yang lebih sering daripada bukan pemecah kesepakatan untuk membuat polyglot dengan dua bahasa. Namun sangat jarang, salah satu bahasa akan memungkinkan komentar blok bersarang, yang dapat disalahgunakan untuk membuat jalur kode terpisah.

Sebagai contoh, pertimbangkan polyglot ini:

#[#[]#print("Lily")#]#echo"Nim"

Nim dan Lily keduanya menggunakan #[dan ]#untuk memulai dan mengakhiri komentar blokir, tetapi hanya Nim yang memungkinkan komentar blokir bersarang.

Lily menganggap yang kedua #[sebagai bagian dari komentar blok tunggal dan yang pertama ]#sebagai mengakhiri komentar blok. ( #Pernyataan cetak Lily berikut ini adalah komentar garis yang menyembunyikan kode Nim.)

Nim sebagai alternatif, melihat #[]#komentar blok sebagai bersarang (meskipun kosong) dan print("Lily")#sebagai komentar blok luar.


4

Tidak yakin apakah ini diperhitungkan, tetapi ...

Gunakan garis shebang untuk mengubah semuanya menjadi perlprogram yang valid

Menurut jawaban ini dan dokumentasi Perl, jika Anda meneruskan file apa pun yang dimulai dengan baris shebang perl, ia memanggil program yang sesuai untuk menjalankannya. Misalnya, ini

#!/usr/bin/python

for i in range(6):
    print i**2

dieksekusi oleh juru bahasa Python jika Anda menelepon perl filename.py.


3
Meskipun program dapat dipanggil dengan perl, itu tidak menjadi program Perl.
Paŭlo Ebermann

2
@ PaŭloEbermann Saya menyadari bahwa ini adalah batas, itulah sebabnya saya memulai jawaban saya dengan "tidak yakin apakah itu diperhitungkan". :) Tapi apa yang mendefinisikan Perl benar, jika tidak "apa yang tertulis dalam dokumentasi dan dikembalikan oleh implementasi referensi perl"? Kedengarannya seperti meme philosoraptor yang baik ...
Federico Poloni

1

4

Panggil fungsi yang tidak ada, lalu keluar saat mengevaluasi argumen mereka

Banyak bahasa pemrograman yang dapat menguraikan pengidentifikasi acak diikuti oleh sepasang tanda kurung dengan ekspresi di dalam:

identifier(1 + 1)

Terkadang, bentuk pengidentifikasi yang dimaksud mungkin diperbaiki, karena diperlukan untuk memberikan kode ke bahasa lain yang Anda gunakan. Itu mungkin pada awalnya tampaknya menimbulkan masalah, jika pengidentifikasi tidak sesuai dengan fungsi yang sebenarnya dimiliki bahasa tersebut.

Namun, banyak bahasa pemrograman akan mengevaluasi argumen fungsi sebelum memeriksa untuk melihat apakah fungsi itu sendiri benar-benar ada (misalnya Lua), dan karenanya Anda dapat menggunakan konstruksi semacam ini; yang Anda butuhkan adalah keluar dari program di suatu tempat di dalam argumen fungsi.

Berikut ini contohnya, dc / Lua polyglot:

c2pq(1 + #os.exit(print(3)))

c2pqadalah program dc untuk mencetak 2 dan keluar; Lua melihat ini sebagai nama fungsi, tetapi Lua dapat dicegah dari kesalahan dengan menempatkan perintah keluar dalam argumennya. Keuntungan besar konstruksi ini adalah bahwa tidak seperti tugas ( c2pq =), itu tidak secara otomatis tidak sesuai dengan bahasa di mana nama variabel dimulai dengan sigil; sintaks nama fungsi jauh lebih konsisten di semua bahasa daripada sintaksis nama variabel.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.