Rumah > Artikel > pembangunan bahagian belakang > Pretty-Printing ialah Kompilasi
Wadler's A Prettier Printer ialah mutiara berfungsi klasik. Namun—sama ada disebabkan kemalasan Haskell atau kemalasan saya sendiri—saya bergelut untuk melaksanakan semula ideanya dalam bahasa lain (atau pun dalam Haskell, lima minit selepas membaca kertas itu). Syukurlah, Lindig menyedari masalah ini dan membawa api kepada orang ramai dalam Strictly Pretty. Namun itu pun tidak cukup membosankan bagi saya.
Tetapi selepas meluangkan sedikit masa memikirkan idea daripada kedua-dua kertas kerja, saya rasa saya akhirnya mendapat idea itu.
Seperti yang dicadangkan oleh tajuk, kami akan menganggap pencetakan cantik sebagai proses penyusunan (dan pelaksanaan) atur cara yang ditulis dalam "bahasa dokumen" abstrak. Seperti kebanyakan bahasa pengaturcaraan, bahasa dokumen ini—yang akan kami panggil
Dokumen—menampilkan ungkapan yang boleh digubah bersama; ini memudahkan manusia untuk berfikir. Kami akan menyusun ungkapan dalam Dokumen kepada arahan dalam sejenis bahasa himpunan (ASM). Arahan dalam ASM lebih mudah diubah menjadi rentetan.
Berikut ialah paparan skematik proses:
Sebagai contoh konkrit, katakan kita ingin mencetak cantik senarai bersarang:
['onions', ['carrots', 'celery'], 'turnips']
Berikut ialah program Doc untuk berbuat demikian:
group( '[' + nest( 4, br() + "'onions'" + ',' + br(' ') + group( '[' + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'") + br() + ']' ) + ',' + br(' ') + "'turnips'" ) + br() + ']' )
Kami akan bertemu kumpulan, sarang, dsb. sebentar lagi. Buat masa ini sudah cukup untuk mendapatkan gambaran umum untuk bahasa dokumen.
Program ini kemudiannya disusun (dengan set "parameter seni bina" tertentu) ke dalam arahan ASM:
TEXT '[' LINE 4 TEXT "'onions'" TEXT ',' LINE 4 TEXT '[' TEXT '' TEXT "'carrots'" TEXT ',' TEXT ' ' TEXT "'celery'" TEXT '' TEXT ']' TEXT ',' LINE 4 TEXT "'turnips'" LINE 0 TEXT ']'
yang kemudiannya ditafsirkan sebagai rentetan:
[ 'onions', ['carrots', 'celery'], 'turnips' ]
Satu ciri penting yang disebut di atas ialah pengkompil mempunyai "parameter seni bina" yang boleh dikonfigurasikan, iaitu lebar garis maksimum sasaran. Bergantung pada nilai parameter ini, pengkompil akan mengeluarkan arahan ASM yang berbeza untuk program Doc yang sama. Dalam contoh di atas kami menggunakan lebar sasaran 30. Jika kami menggunakan 20 sebaliknya, arahan pemasangan yang dipancarkan akan berbeza, seperti rentetan yang terhasil:
[ 'onions', [ 'carrots', 'celery' ], 'turnips' ]
Dan jika kami menggunakan 60, ia akan menjadi:
['onions', ['carrots', 'celery'], 'turnips']
Bahasa himpunan adalah mudah, jadi kami akan menanganinya terlebih dahulu. Kami akan menganggap arahan ASM sebagai mengawal peranti pencetakan yang sangat mudah yang hanya mampu melakukan dua perkara:
Oleh itu ASM hanya terdiri daripada dua arahan:
Atur cara ASM ditafsirkan sebagai rentetan dengan melaksanakan arahannya, satu demi satu. Sebagai contoh, mari kita kesan pelaksanaan program:
['onions', ['carrots', 'celery'], 'turnips']
Kami akan menggunakan > untuk menunjukkan arahan sedang dilaksanakan, dan paparkan output semasa di bawah. Aksara ^ akan menunjukkan lokasi semasa "kepala pencetak". Kami juga akan menggunakan _ aksara untuk menunjukkan ruang, kerana ini sebaliknya sukar untuk dijejaki.
Arahan TEKS pertama menyebabkan rentetan 'hello' dipancarkan:
group( '[' + nest( 4, br() + "'onions'" + ',' + br(' ') + group( '[' + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'") + br() + ']' ) + ',' + br(' ') + "'turnips'" ) + br() + ']' )
LINE 2 kemudian mara ke baris seterusnya dan mengendenkan kepala sebanyak 2 ruang:
TEXT '[' LINE 4 TEXT "'onions'" TEXT ',' LINE 4 TEXT '[' TEXT '' TEXT "'carrots'" TEXT ',' TEXT ' ' TEXT "'celery'" TEXT '' TEXT ']' TEXT ',' LINE 4 TEXT "'turnips'" LINE 0 TEXT ']'
Kemudian TEKS 'inden' menyebabkan 'inden' ditambahkan:
[ 'onions', ['carrots', 'celery'], 'turnips' ]
Diikuti oleh 'dunia', disebabkan oleh 'dunia' TEKS:
[ 'onions', [ 'carrots', 'celery' ], 'turnips' ]
LINE 0 memajukan pencetak ke baris seterusnya (dan tidak mengenden langsung):
['onions', ['carrots', 'celery'], 'turnips']
Dan akhirnya, TEKS 'selamat tinggal' mengeluarkan 'selamat tinggal':
TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye'
Kami akan mewakili arahan ASM sebagai "jenis jumlah":
Iaitu:
> TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello ^
Mentafsirkan senarai AsmInsts ke dalam rentetan yang diwakilinya hanyalah soal mengulang arahan dan "melakukan perkara yang betul":
TEXT 'hello' > LINE 2 TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello __ ^
Untuk arahan TEKS, jurubahasa menambahkan teks pada hasil; untuk arahan LINE, jurubahasa menambahkan baris baharu ('n') diikuti dengan ruang inden.
Kami boleh menguji tafsiran dengan arahan ASM daripada contoh di atas, diterjemahkan ke dalam Python:
TEXT 'hello' LINE 2 > TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello __indented ^
Kami suka ASM kerana ia mudah ditafsir. Tapi memang payah nak guna. Ini mendorong bahasa Doc yang lebih mesra manusia. Manakala program ASM ialah urutan daripada arahan, program Doc ialah komposisi daripada ungkapan. Ungkapan ini diringkaskan oleh tatabahasa berikut:
TEXT 'hello' LINE 2 TEXT 'indented' > TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello __indented world ^
Contohnya:
TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' > LINE 0 TEXT 'goodbye' == OUTPUT == hello __indented world ^
ialah ungkapan Doc, seperti:
TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' LINE 0 > TEXT 'goodbye' == OUTPUT == hello __indented world goodbye ^
Apakah yang diwakili oleh ini?
Jadi, contohnya:
AsmInst = str | int
mewakili rentetan:
def interpret(insts: list[AsmInst]) -> str: """Interpret the ASM instructions as a string.""" result = "" for inst in insts: match inst: case text if isinstance(text, str): result += inst case indent if isinstance(indent, int): result += f"\n{' ' * indent}" return result
Ungkapan kedua yang lebih kompleks:
['onions', ['carrots', 'celery'], 'turnips']
boleh mewakili sama ada:
group( '[' + nest( 4, br() + "'onions'" + ',' + br(' ') + group( '[' + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'") + br() + ']' ) + ',' + br(' ') + "'turnips'" ) + br() + ']' )
atau:
TEXT '[' LINE 4 TEXT "'onions'" TEXT ',' LINE 4 TEXT '[' TEXT '' TEXT "'carrots'" TEXT ',' TEXT ' ' TEXT "'celery'" TEXT '' TEXT ']' TEXT ',' LINE 4 TEXT "'turnips'" LINE 0 TEXT ']'
bergantung pada nilai "parameter seni bina" lebar garis maksimum sasaran yang diberikan kepada pengkompil. Jadi ungkapan br mungkin sama ada dianggap sebagai pemisah baris atau teks biasa, dalam hal ini nilai teksnya digunakan (atau '' jika tiada hujah teks disediakan).
Kami akan mewakili ungkapan Doc menggunakan kelas strs dan Python. Khususnya:
[ 'onions', ['carrots', 'celery'], 'turnips' ]
Bagaimana pula dengan DocExpr DocExpr? Kami akan mewakili mereka yang menggunakan kelas Concat tambahan:
[ 'onions', [ 'carrots', 'celery' ], 'turnips' ]
Kami mahu menyokong penggunaan untuk menggabungkan ungkapan, jadi kami perlu melaksanakan __add__ dan __radd__ pada setiap kelas varian. Menambah dua ungkapan Doc menggunakan hanya membina Concat daripada keduanya. Cukup mudah untuk melakukan ini secara manual, cth.:
['onions', ['carrots', 'celery'], 'turnips']
Tetapi kita boleh menyelamatkan diri kita sendiri menaip dengan menentukan penghias untuk melakukannya untuk kita:
TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye'
Kelas varian kini kelihatan seperti:
> TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello ^
Tugas kami sekarang ialah menulis pengkompil yang menterjemahkan ungkapan dalam bahasa Dokumen ke dalam arahan ASM yang setara, memandangkan beberapa lebar garis maksimum sasaran:
TEXT 'hello' > LINE 2 TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello __ ^
Walau bagaimanapun, ternyata lebih mudah untuk mula-mula "menurunkan" ungkapan Dokumen menjadi ungkapan dalam bahasa Perwakilan Perantaraan (IR), dan kemudian menyusun ungkapan IR ke dalam ASM. Memperkenalkan "lulus" tambahan ini menjadikan setiap langkah lebih jelas.
Jadi skema kami yang menerangkan proses pencetakan cantik adalah agak terlalu dipermudahkan. Ini gambar penuh:
Ekspresi IR menyerupai ungkapan Doc dalam banyak cara:
TEXT 'hello' LINE 2 > TEXT 'indented' TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello __indented ^
Perbezaan utama ialah kami tidak lagi mempunyai dan ungkapan nil: ini ditukar kepada senarai ungkapan IR dalam proses menurunkan. Sebenarnya, itu sahaja semua pas menurunkan:
TEXT 'hello' LINE 2 TEXT 'indented' > TEXT ' world' LINE 0 TEXT 'goodbye' == OUTPUT == hello __indented world ^
Kita perlu mentakrifkan IrExprs terlebih dahulu.
Ini sepatutnya kelihatan biasa:
TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' > LINE 0 TEXT 'goodbye' == OUTPUT == hello __indented world ^
Semua yang dilakukan yang lebih rendah ialah menggantikan tika Nil() dengan senarai kosong ([]), dan tika Concat(kereta, cdr) dengan menambahkan hasil merendahkan ekspresi kereta dan cdr. Perlakuan yang sama digunakan pada subungkapan dalam Nest dan Group. Ini tidak lebih daripada operasi "meratakan" rekursif.
TEXT 'hello' LINE 2 TEXT 'indented' TEXT ' world' LINE 0 > TEXT 'goodbye' == OUTPUT == hello __indented world goodbye ^
Menguji lebih rendah dengan salah satu contoh ungkapan Doc kami dari atas:
AsmInst = str | int
itulah yang kami jangkakan.
Sekarang ke langkah terakhir: susun. Fungsi ini mengubah ungkapan IR kepada arahan ASM, dengan mengambil kira lebar garis maksimum sasaran:
def interpret(insts: list[AsmInst]) -> str: """Interpret the ASM instructions as a string.""" result = "" for inst in insts: match inst: case text if isinstance(text, str): result += inst case indent if isinstance(indent, int): result += f"\n{' ' * indent}" return result
Berikut ialah idea kasar algoritma:
['onions', ['carrots', 'celery'], 'turnips']
proses ialah tempat keajaiban berlaku:
group( '[' + nest( 4, br() + "'onions'" + ',' + br(' ') + group( '[' + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'") + br() + ']' ) + ',' + br(' ') + "'turnips'" ) + br() + ']' )
Ringkasnya:
Bagaimana fit_flat berfungsi? Ia hanya melalui arahan dalam kumpulan, menganggap br sebagai teks, dan berhenti apabila sama ada:
TEXT '[' LINE 4 TEXT "'onions'" TEXT ',' LINE 4 TEXT '[' TEXT '' TEXT "'carrots'" TEXT ',' TEXT ' ' TEXT "'celery'" TEXT '' TEXT ']' TEXT ',' LINE 4 TEXT "'turnips'" LINE 0 TEXT ']'
Akhirnya kita boleh klik bahagian bersama-sama:
[ 'onions', ['carrots', 'celery'], 'turnips' ]
Satu-satunya bahagian antara muka pencetak cantik yang tinggal ialah pembina ungkapan dokumen:
[ 'onions', [ 'carrots', 'celery' ], 'turnips' ]
Jom cuba contoh dari pengenalan:
['onions', ['carrots', 'celery'], 'turnips']
Lihat sumber penuh di sini.
? Dalam Pembinaan ?
? Dalam Pembinaan ?
Atas ialah kandungan terperinci Pretty-Printing ialah Kompilasi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!