Rumah > pembangunan bahagian belakang > Tutorial Python > Pretty-Printing ialah Kompilasi

Pretty-Printing ialah Kompilasi

DDD
Lepaskan: 2024-11-01 04:21:02
asal
598 orang telah melayarinya

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.

Gambaran keseluruhan

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:

Pretty-Printing is Compilation

Sebagai contoh konkrit, katakan kita ingin mencetak cantik senarai bersarang:

['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Berikut ialah program Doc untuk berbuat demikian:

group(
    '['
    + nest(
        4,
        br()
        + "'onions'"
        + ','
        + br(' ')
        + group(
            '['
            + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'")
            + br()
            + ']'
        )
        + ','
        + br(' ')
        + "'turnips'"
    )
    + br()
    + ']'
)
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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 ']'
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

yang kemudiannya ditafsirkan sebagai rentetan:

[
    'onions',
    ['carrots', 'celery'],
    'turnips'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dan jika kami menggunakan 60, ia akan menjadi:

['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Bahasa Himpunan Pencetak

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:

  1. Mengeluarkan rentetan teks.
  2. Memajukan ke baris seterusnya dan mengenden dengan jumlah tertentu.

Oleh itu ASM hanya terdiri daripada dua arahan:

  1. TEKS , yang mengeluarkan rentetan teks.
  2. LINE , yang memajukan pencetak ke baris seterusnya, dan kemudian inden mengikut ruang inden.

Atur cara ASM ditafsirkan sebagai rentetan dengan melaksanakan arahannya, satu demi satu. Sebagai contoh, mari kita kesan pelaksanaan program:

['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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()
    + ']'
)
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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 ']'
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kemudian TEKS 'inden' menyebabkan 'inden' ditambahkan:

[
    'onions',
    ['carrots', 'celery'],
    'turnips'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Diikuti oleh 'dunia', disebabkan oleh 'dunia' TEKS:

[
    'onions',
    [
        'carrots',
        'celery'
    ],
    'turnips'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

LINE 0 memajukan pencetak ke baris seterusnya (dan tidak mengenden langsung):

['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dan akhirnya, TEKS 'selamat tinggal' mengeluarkan 'selamat tinggal':

TEXT 'hello'
LINE 2
TEXT 'indented'
TEXT ' world'
LINE 0
TEXT 'goodbye'
Salin selepas log masuk
Salin selepas log masuk

Kami akan mewakili arahan ASM sebagai "jenis jumlah":

  • Arahan TEKS akan diwakili oleh Python strs.
  • Arahan LINE akan diwakili oleh ints.

Iaitu:

> TEXT 'hello'
  LINE 2
  TEXT 'indented'
  TEXT ' world'
  LINE 0
  TEXT 'goodbye'

== OUTPUT ==

hello
     ^
Salin selepas log masuk
Salin selepas log masuk

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
__
  ^
Salin selepas log masuk
Salin selepas log masuk

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
          ^
Salin selepas log masuk
Salin selepas log masuk

Bahasa Dokumen (Penggoda)

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
                ^
Salin selepas log masuk
Salin selepas log masuk

Contohnya:

  TEXT 'hello'
  LINE 2
  TEXT 'indented'
  TEXT ' world'
> LINE 0
  TEXT 'goodbye'

== OUTPUT ==

hello
__indented world

^
Salin selepas log masuk
Salin selepas log masuk

ialah ungkapan Doc, seperti:

  TEXT 'hello'
  LINE 2
  TEXT 'indented'
  TEXT ' world'
  LINE 0
> TEXT 'goodbye'

== OUTPUT ==

hello
__indented world
goodbye
       ^
Salin selepas log masuk
Salin selepas log masuk

Apakah yang diwakili oleh ini?

  • Bahasa Python str melambangkan dirinya sendiri.
  • br() ialah kemungkinan pemutusan baris.
  • nest(indent, doc) mencipta subungkapan "bersarang" yang diimbangi secara visual oleh ruang inden.
  • group(doc) mengehadkan subungkapan di mana semua br()s sama ada dianggap sebagai pemisah baris atau tidak.
  • menggabungkan ungkapan Doc.
  • nil bertindak sebagai ungkapan "kosong".

Jadi, contohnya:

AsmInst = str | int
Salin selepas log masuk
Salin selepas log masuk

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
Salin selepas log masuk
Salin selepas log masuk

Ungkapan kedua yang lebih kompleks:

['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

boleh mewakili sama ada:

group(
    '['
    + nest(
        4,
        br()
        + "'onions'"
        + ','
        + br(' ')
        + group(
            '['
            + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'")
            + br()
            + ']'
        )
        + ','
        + br(' ')
        + "'turnips'"
    )
    + br()
    + ']'
)
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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 ']'
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Bagaimana pula dengan DocExpr DocExpr? Kami akan mewakili mereka yang menggunakan kelas Concat tambahan:

[
    'onions',
    [
        'carrots',
        'celery'
    ],
    'turnips'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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'
Salin selepas log masuk
Salin selepas log masuk

Kelas varian kini kelihatan seperti:

> TEXT 'hello'
  LINE 2
  TEXT 'indented'
  TEXT ' world'
  LINE 0
  TEXT 'goodbye'

== OUTPUT ==

hello
     ^
Salin selepas log masuk
Salin selepas log masuk

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
__
  ^
Salin selepas log masuk
Salin selepas log masuk

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.

Perwakilan Perantaraan

Jadi skema kami yang menerangkan proses pencetakan cantik adalah agak terlalu dipermudahkan. Ini gambar penuh:

Pretty-Printing is Compilation

Ekspresi IR menyerupai ungkapan Doc dalam banyak cara:

  TEXT 'hello'
  LINE 2
> TEXT 'indented'
  TEXT ' world'
  LINE 0
  TEXT 'goodbye'

== OUTPUT ==

hello
__indented
          ^
Salin selepas log masuk
Salin selepas log masuk

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
                ^
Salin selepas log masuk
Salin selepas log masuk

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

^
Salin selepas log masuk
Salin selepas log masuk

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
       ^
Salin selepas log masuk
Salin selepas log masuk

Menguji lebih rendah dengan salah satu contoh ungkapan Doc kami dari atas:

AsmInst = str | int
Salin selepas log masuk
Salin selepas log masuk

itulah yang kami jangkakan.

Penyusun (Akhirnya)

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
Salin selepas log masuk
Salin selepas log masuk

Berikut ialah idea kasar algoritma:

  • Pengkompil mengekalkan beberapa maklumat "keadaan":
    • Kedudukan garisan semasa (mendatar).
    • Jumlah lekukan semasa.
    • Sama ada atau tidak brs harus dianggap sebagai pemisah baris atau dijadikan "rata".
  • Kami mengulangi ungkapan, mengeluarkan beberapa arahan ASM dan mengemas kini kedudukan baris dengan sewajarnya.
['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

proses ialah tempat keajaiban berlaku:

group(
    '['
    + nest(
        4,
        br()
        + "'onions'"
        + ','
        + br(' ')
        + group(
            '['
            + nest(4, br() + "'carrots'" + ',' + br(' ') + "'celery'")
            + br()
            + ']'
        )
        + ','
        + br(' ')
        + "'turnips'"
    )
    + br()
    + ']'
)
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ringkasnya:

  • Untuk ungkapan teks, kami mengeluarkan arahan TEKS dan memajukan kedudukan (pos) mengikut panjang teks.
  • ungkapan br dikendalikan bergantung pada nilai rata:
    • Jika rata adalah Benar, layan mereka sebagai teks.
    • Jika tidak, keluarkan arahan INDENT dengan tahap lekukan semasa dan tetapkan semula kedudukan kepada nilai ini.
  • Untuk ungkapan sarang, kami memproses semua subungkapan, tetapi dengan tahap lekukan semasa meningkat dengan nilai lekukan sarang.
  • Akhir sekali, untuk ungkapan kumpulan, kami mula-mula menyemak sama ada keseluruhan kumpulan boleh dipaparkan rata tanpa melebihi ruang baki. Ini menentukan nilai rata untuk semua subungkapan terkumpul, yang seterusnya menentukan sama ada brs dipaparkan sebagai pemisah baris (atau sebagai teks).

Bagaimana fit_flat berfungsi? Ia hanya melalui arahan dalam kumpulan, menganggap br sebagai teks, dan berhenti apabila sama ada:

  • Kami telah kehabisan ruang (lebar < 0), dalam hal ini, subungkapan terkumpul tidak boleh dipaparkan secara rata.
  • Kami telah memproses semua subungkapan, dalam hal ini kumpulan boleh dijadikan rata.
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 ']'
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Menyatukan semuanya

Akhirnya kita boleh klik bahagian bersama-sama:

[
    'onions',
    ['carrots', 'celery'],
    'turnips'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Satu-satunya bahagian antara muka pencetak cantik yang tinggal ialah pembina ungkapan dokumen:

[
    'onions',
    [
        'carrots',
        'celery'
    ],
    'turnips'
]
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jom cuba contoh dari pengenalan:

['onions', ['carrots', 'celery'], 'turnips']
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Lihat sumber penuh di sini.

Bagaimana untuk "Program" dalam Doc

? Dalam Pembinaan ?

  • Corak biasa.
  • Cara br, sarang dan kumpulan berinteraksi.

Loceng dan Wisel

? Dalam Pembinaan ?

  • Menambah kumpulan parameter lipatan pada paksa lipatan.

Atas ialah kandungan terperinci Pretty-Printing ialah Kompilasi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan