Dalam proses manakah kesalahan sintaksis terjadi? (tokenizing atau parsing)

23

Saya mencoba memahami kompilasi dan interpretasi, langkah demi langkah mencari gambaran total. Jadi saya mengajukan pertanyaan saat membaca http://www.cs.man.ac.uk/~pjj/farrell/comp3.html artikel ini

Ia mengatakan :

Tahap selanjutnya dari kompiler disebut Parser. Bagian kompiler ini memiliki pemahaman tentang tata bahasa bahasa. Ia bertanggung jawab untuk mengidentifikasi kesalahan sintaksis dan untuk menerjemahkan program bebas kesalahan ke dalam struktur data internal yang dapat ditafsirkan atau ditulis dalam bahasa lain.

Tapi saya tidak tahu bagaimana tokenizer dapat dengan benar tokenize aliran yang diberikan yang memiliki kesalahan sintaksis.

Ini harus macet di sana atau memberikan informasi yang salah kepada pengurai. Maksud saya bukankah tokenizing juga semacam penerjemah?

Jadi bagaimana itu hanya mengatasi baris kode yang rusak leksikal sementara tokenizing.

Ada contoh token di dalam tautan di atas di tajuk The Tokenizer .

Seperti yang saya mengerti bentuk token sepertinya, jika ada sesuatu yang salah dalam kode token juga akan rusak.

Bisakah Anda jelaskan kesalahpahaman saya?

FZE
sumber

Jawaban:

32

Tokenizer hanyalah optimasi parser. Sangat mungkin untuk mengimplementasikan parser tanpa tokenizer.

Tokenizer (atau lexer, atau pemindai) memotong input ke dalam daftar token. Beberapa bagian dari string (komentar, spasi) biasanya diabaikan. Setiap token memiliki tipe (arti dari string ini dalam bahasa) dan nilai (string yang membentuk token). Misalnya, cuplikan sumber PHP

$a + $b

dapat diwakili oleh token

Variable('$a'),
Plus('+'),
Variable('$b')

Token tidak mempertimbangkan apakah token dimungkinkan dalam konteks ini. Misalnya input

$a $b + +

akan dengan senang hati menghasilkan aliran token

Variable('$a'),
Variable('$b'),
Plus('+'),
Plus('+')

Ketika parser kemudian mengkonsumsi token-token ini, akan terlihat bahwa dua variabel tidak dapat saling mengikuti, dan begitu pula dua operator infiks. (Perhatikan bahwa bahasa lain memiliki sintaks yang berbeda di mana aliran token semacam itu mungkin legal, tetapi tidak dalam PHP).

Parser mungkin masih gagal pada tahap tokenizer. Misalnya, mungkin ada karakter ilegal:

$a × ½ — 3

Tokenizer PHP tidak akan dapat mencocokkan input ini dengan aturannya, dan akan menghasilkan kesalahan sebelum penguraian utama dimulai.

Lebih formal, tokenizer digunakan ketika masing-masing token dapat digambarkan sebagai bahasa biasa . Token kemudian dapat dicocokkan dengan sangat efisien, mungkin diimplementasikan sebagai DFA. Sebaliknya, tata bahasa utama biasanya bebas konteks dan membutuhkan algoritma penguraian yang lebih rumit dan kurang berkinerja seperti LALR.

amon
sumber
Jadi kita bisa berpikir tokenizer sebagai bagian dari parser dan kesalahan sintaksis dapat terjadi baik langkah tokenizing atau langkah parsing sesuai dengan bentuk kesalahan sintaksis. Terimakasih atas klarifikasinya.
FZE
4
@ FZE: Anda bisa berpikir seperti itu, tapi itu menyesatkan. Lexing bukan "hanya optimasi parser". Sebaliknya, lexing memetakan representasi fisik (beberapa urutan karakter) ke dalam representasi logis (token yang diwakili oleh karakter tersebut). Ini mengisolasi parser dari hal-hal kecil seperti bagaimana akhir baris diwakili, atau apakah Anda memutuskan untuk mewakili logis-dan sebagai andatau &&atau sesuatu yang lain. Ini (kebanyakan) terpisah dan berbeda dari parsing. Optimalisasi (jika ada) adalah efek samping yang hampir tidak disengaja.
Jerry Coffin
@JerryCoffin terima kasih atas penjelasan lebih lanjut, sekarang lebih masuk akal.
FZE
2
@ JerryCoffin, amon benar bahwa perbedaannya tidak mendasar. Anda dapat membuat tata bahasa BNF yang kohesif yang mencakup bagian "lexer" dan "parser". Kami biasanya mengelompokkan aturan menjadi level rendah (mis., Angka, operator tambahan) dan level tinggi (penjumlahan), tetapi tata bahasanya sendiri tidak membuat perbedaan seperti itu.
Paul Draper
1
@PaulDraper Tidak yakin jika memisahkan bahasa biasa karena fase pertama adalah pilihan yang tepat. Misalnya pasangan yang cocok (tidak biasa) mungkin diperlukan untuk menggambarkan string literal dalam beberapa bahasa, namun masih masuk akal untuk menangani mereka pada fase pertama. Menghindari / meminimalkan pelacakan-kembali sepertinya panduan yang lebih baik.
CodesInChaos
16

Anda biasanya mengharapkan sebagian besar kesalahan sintaksis berasal dari parser, bukan lexer.

Lexer akan menghasilkan kesalahan jika (dan sebagian besar hanya jika) ada sesuatu dalam input yang tidak dapat di token. Namun, dalam banyak bahasa, hampir semua urutan karakter dapat diubah menjadi token, jadi kesalahan di sini cukup tidak biasa.

Parser akan menghasilkan kesalahan jika input berisi token yang valid, tetapi token tersebut tidak diatur sehingga mereka membentuk pernyataan / ekspresi yang valid dalam bahasa target. Ini jauh lebih umum sebagai suatu peraturan.

Jerry Coffin
sumber
11

Tokenizer hanya membagi aliran karakter menjadi token. Dari tokenizer POV ini benar-benar valid:

1 * * 1

dan diterjemahkan menjadi sesuatu seperti: ["1", MULTIPLY, MULTIPLY, "1"] Hanya parser yang dapat menolak ekspresi seperti itu - ia tahu operator gandakan tidak dapat mengikuti operator gandakan lainnya. Misalnya dalam JavaScript ini menghasilkan:

Uncaught SyntaxError: Unexpected token *(…)

Ada kesalahan yang mungkin terdeteksi oleh tokenizer. Misalnya literal yang belum selesai tali: "abcatau nomor tidak valid: 0x0abcdefg. Meskipun demikian, mereka mungkin dilaporkan sebagai kesalahan sintaks:

Uncaught SyntaxError: Unexpected token ILLEGAL

Namun perlu dicatat bahwa token tidak dikenali dan dilaporkan sebagai ILLEGAL.

Banthar
sumber