Prompt dan skop
Reka bentuk penghurai kecil untuk ungkapan aritmetik dan pengisytiharan pemboleh ubah. Input mungkin mengandungi beberapa ralat sintaksis, namun penghurai mesti melaporkan kedudukan, meneruskan pernyataan seterusnya, dan menyediakan AST separa untuk IDE.
Ini menguji sempadan antara penghuraian leksikal (lexing), penghuraian sintaksis (parsing), pemulihan, dan struktur data. Bison mengesyorkan agar input dibuang sehingga ke titik penyelarasan dan diteruskan; IDE juga memerlukan nod ralat, julat yang stabil, dan pemulihan melangkaui ralat pertama.
Perkara yang dinilai oleh penemu duga
Calon harus mentakrifkan token dan tatabahasa sebelum memilih penurunan rekursif (recursive descent) atau LR, membezakan ralat leksikal daripada ralat sintaksis, memilih titik penyelarasan yang selamat, menyekat ralat bertingkat (cascading), mengekalkan AST separa, dan menguji pembatas bersarang, pemisah yang hilang, serta rentetan tanpa penamat.
Kerangka jawapan 30 saat
“Saya mengasingkan lexer, parser, dan diagnostik. Parser menjejaki kursor token dan julat sumber; selepas ralat, ia merekodkan token yang dijangka dan sebenar, melangkau ke koma bertitik, pembatas penutup, atau EOF, memasukkan ErrorNode, dan meneruskan. Pemulihan menyekat pendua sehingga beberapa token berjaya diproses. Nod AST mengekalkan julat yang hilang supaya pemanggil boleh memilih mod ketat atau toleran.”
Jawapan mendalam langkah demi langkah
Langkah 1: Takrifkan token dan tatabahasa
Lexer mengeluarkan jenis, teks, ofset, serta kedudukan baris dan lajur untuk pengecam, nombor, pengendali, pembatas, koma bertitik, dan aksara tidak sah. Tatabahasa menetapkan keutamaan (precedence) dan kesekutuan (associativity) supaya pemulihan tidak berselerak dalam fungsi ungkapan.
Langkah 2: Pilih struktur parser
Penurunan rekursif mudah dibaca untuk tatabahasa kecil; precedence climbing mengendalikan ungkapan. Tatabahasa yang lebih besar boleh menggunakan penjana dengan strategi ralat yang jelas. Walau apa pun, parser memerlukan lookahead, pemulihan token berbatas, dan julat sumber.
Langkah 3: Asingkan ralat leksikal dan sintaksis
Aksara tidak sah atau rentetan tanpa penamat ialah ralat leksikal: keluarkan token ralat dan teruskan pengimbasan. Operand, pembatas, atau koma bertitik yang hilang ialah ralat sintaksis yang dilaporkan oleh parser mengikut konteks. Jangan labelkan setiap masalah sebagai “Unexpected token”.
Langkah 4: Pilih titik penyelarasan
Titik peringkat pernyataan ialah koma bertitik, kurungan dakap penutup, atau EOF. Di dalam ungkapan, selaraskan pada tanda koma, tanda kurung penutup, atau sempadan pengendali. Melangkau mesti sentiasa memajukan kursor atau mencapai EOF, jika tidak ralat yang sama akan bergelung selama-lamanya.
Langkah 5: Bina AST separa
Kekalkan julat ralat pada nod. Wakili anak yang hilang dengan MissingNode dan rentang yang tidak dapat dipulihkan dengan ErrorNode yang mengandungi token asal. Pemformatan, penyerlahan, dan pelengkapan automatik mesti mengendalikan nod ini daripada menganggap pepohon adalah lengkap.
Langkah 6: Sekat diagnostik bertingkat
Satu pemulihan boleh mendedahkan banyak ralat luaran. Jejaki titik penyelarasan terakhir dan bilangan token yang berjaya; tunggu beberapa anjakan yang berjaya sebelum melaporkan diagnostik lain. Hadkan ralat bagi setiap fail supaya log dan UI kekal boleh digunakan.
Langkah 7: Sokong penghuraian bertambah (incremental parsing)
Apabila teks editor berubah, huraikan semula julat token yang terjejas dan konteks tatabahasa berdekatan sambil menggunakan semula sub-pepohon yang tidak berubah. Pastikan julat nod dan ID token stabil; batalkan cache mengikut sempadan induk dan keadaan parser, bukan hanya mengikut ofset aksara.
Langkah 8: Uji dan ukur
Uji input yang sah, satu ralat, banyak ralat, ralat bersarang, rentetan panjang, dan ungkapan yang sangat besar. Buat penegasan (assert) kedudukan, kiraan, nod ralat, dan penamatan. Ukur pengimbasan linear, kedalaman rekursi, memori, dan kos pemulihan kes terburuk pada input yang panjang.
Pertukaran kompromi dan sempadan
Gagal cepat (Fail fast) berbanding teruskan
Pengkompil kelompok (batch compilers) mungkin meneruskan selepas diagnostik untuk maklum balas yang lebih baik; pengesahan konfigurasi mungkin menolak selepas ralat pertama. Jadikan ini sebagai mod sambil berkongsi lexer, token, dan diagnostik untuk mengelakkan perbezaan tingkah laku.
Banyak berbanding sedikit titik penyelarasan
Lebih banyak titik mengehadkan skop ralat tetapi mungkin melangkau token yang boleh dipulihkan; lebih sedikit titik mengekalkan konteks tetapi meningkatkan ralat bertingkat. Takrifkan titik mengikut pernyataan dan pembatas, kemudian uji dengan korpus yang representatif.
Penurunan rekursif berbanding penjana
Penurunan rekursif memudahkan diagnostik tersuai; penjana sesuai untuk tatabahasa besar yang stabil. Pemulihan harus menjadi antara muka yang jelas dan bukannya bergantung pada tetapan lalai penjana yang tidak diuji.
Latih tubi kegagalan dan evolusi
Tanda kurung penutup hilang
Tambah pernyataan seterusnya dan sahkan penyelarasan pada koma bertitik atau EOF-nya, satu diagnostik pembatas yang hilang, dan pengekalan pernyataan kemudian.
Aksara tidak sah dan rentetan tanpa penamat
Sahkan bahawa lexer mengeluarkan token ralat dan mencapai penghujung baris atau rentetan tanpa parser tersekat pada satu aksara.
Lambakan ralat berturut-turut
Bekalkan input di mana setiap token tidak sah. Kursor mesti maju, kiraan ralat mesti dihadkan, dan CPU tidak boleh meningkat secara kuadratik.
Kesilapan lazim dan tindakan susulan
Kesilapan 1: Mengembalikan null pada ralat pertama
Tanya bagaimana IDE meneruskan penyerlahan dan pelengkapan; kembalikan ErrorNode atau MissingNode dengan julat sebagai gantinya.
Kesilapan 2: Tidak memajukan kursor semasa pemulihan
Minta hujah penamatan yang menolak kemungkinan bergelung pada token yang sama.
Kesilapan 3: Melaporkan setiap ralat dalam parser
Tanya sama ada rentetan tanpa penamat dan aksara tidak sah tergolong dalam diagnostik lexer atau parser.
Kesilapan 4: Mengabaikan penyekatan ralat bertingkat
Tanya mengapa satu koma bertitik yang hilang tidak sepatutnya mencetak sepuluh ralat yang berulang.
Kesilapan 5: Menyimpan cache penghuraian bertambah mengikut ofset sahaja
Tanya nod induk dan keadaan parser manakah yang menjadi tidak sah selepas memasukkan satu pembatas.
Tindakan susulan lanjutan dan jawapan rujukan
Mengapa mengekalkan nod ralat?
Pemformatan, pelengkapan, dan penyerlahan masih memerlukan struktur dan julat. Nod ralat membolehkan alatan hiliran mengendalikan input yang tidak lengkap secara eksplisit daripada mengalami ranap.
Bagaimanakah anda menjamin pemulihan akan ditamatkan?
Setiap pemulihan menggunakan satu token atau mencapai EOF, dengan had ralat dan rekursi maksimum.
Bagaimanakah anda mengesahkan kualiti diagnostik?
Gunakan korpus yang mengandungi beberapa ralat bebas dan tegaskan kedudukan, kiraan, AST kemudian, serta masa jalanan dan bukannya menguji ralat pertama sahaja.