Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Pretty-Printing ialah Kompilasi

Pretty-Printing ialah Kompilasi

DDD
DDDasal
2024-11-01 04:21:02511semak imbas

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']

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 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']

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":

  • 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
     ^

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
          ^

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
                ^

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?

  • 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

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.

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
          ^

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.

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

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']

proses ialah tempat keajaiban berlaku:

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

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 ']'

Menyatukan semuanya

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.

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!

Kenyataan:
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