Pegjs: Mendesain ulang pelaporan kesalahan

Dibuat pada 14 Agu 2013  ·  7Komentar  ·  Sumber: pegjs/pegjs

Saya melihat tiga masalah dengan sistem pelaporan kesalahan saat ini di PEG.js:

  1. Tindakan melaporkan kesalahan dengan mengembalikan null
Reporting using `null` is inflexible (it doesn't allow to carry any information about the kind of error) and makes it impossible to return `null` as a regular value from actions, which would be useful sometimes (for example [in a JSON parser](https://github.com/dmajda/pegjs/blob/791034fad92c9cd7a9d1c71187df03441bbfd521/examples/json.pegjs#L45)).

Lihat juga #17.

  1. Tidak ada cara untuk menghasilkan kesalahan dengan pesan yang sepenuhnya dibuat khusus
For example, imagine a HTTP 1.1 parser that wants to report an error about missing `Host` header in a HTTP request with a message like "A HTTP 1.1 request must contain a Host header". Currently the only way to do this is to throw an exception with desired message manually. This is cumbersome and it does not interact well with the backtracking behavior (throwing an exception halts the parsing completely, even when it is possible to backtrack and try another alternatives).

  1. Properti expected dari SyntaxError sulit untuk diproses secara mekanis
The `expected` property of `SyntaxError` is either an array of expectations (each describing one alternative which the parser expected at the position of error) or `null` (meaning that the end of input was expected).

Setiap harapan diwakili oleh string. String ini dapat berarti literal yang diharapkan (diwakili menggunakan sintaks PEG.js — string yang dikutip), kelas karakter (diwakili menggunakan sintaks PEG.js — [...] ), atau karakter apa pun (diwakili oleh string "any" ). Untuk membedakan antara kasus-kasus ini, seseorang harus mengurai representasi string secara manual, yang membuat alat pembangunan berdasarkan PEG.js seperti pelengkap otomatis menjadi tidak perlu.

Saya berencana untuk mendesain ulang sistem pelaporan kesalahan untuk memperbaiki masalah ini. Sebelum saya menjelaskan perubahan yang saya pikirkan, diperlukan sedikit gambaran tentang cara kerja sistem saat ini.

Bagaimana Pelaporan Kesalahan Saat Ini Bekerja?

Saat parser PEG.js mencoba mencocokkan string literal, kelas karakter, atau . , dan gagal, ini menghasilkan _match failure_. Ini terdiri dari _position_ dan _expectation_ (deskripsi tentang apa yang coba dicocokkan oleh parser).

Setelah menghasilkan kegagalan kecocokan, parser mundur dan mungkin mencoba alternatif lain. Jika tidak ada yang berhasil dan tidak ada lagi yang bisa dicoba, parser melempar pengecualian SyntaxError . Saat mengatur propertinya (seperti posisi, ekspektasi, pesan kesalahan, dll.), parser hanya mempertimbangkan kegagalan pencocokan terjauh (yang memiliki posisi terbesar). Jika ada lebih banyak kegagalan seperti itu, harapan mereka digabungkan.

Situasinya sedikit lebih rumit jika aturan bernama digunakan, tetapi ini tidak relevan untuk diskusi ini.

Perubahan yang Diusulkan

Saya sedang memikirkan tentang perubahan berikut dalam sistem pelaporan kesalahan:

  1. Harapan di SyntaxError.expected tidak akan diwakili oleh string, tetapi oleh objek. Objek akan memiliki properti type (dengan nilai seperti "literal" , "class" , "any" , "eof" ) yang akan menentukan tipe ekspektasi. Untuk beberapa tipe ekspektasi, properti lain akan berisi detail (misalnya untuk ekspektasi "literal" dimana akan menjadi properti value yang berisi string yang diharapkan). Ini akan menyederhanakan pemrosesan mekanis dari ekspektasi.

Alternatif untuk properti type adalah dengan menggunakan classses. Tapi saya pikir properti type akan lebih mudah ditangani oleh pengguna.

  1. Tindakan akan diizinkan untuk mengembalikan null . Ini akan menjadi nilai reguler dan tidak akan menandakan kesalahan.
  2. Tindakan akan dapat memicu kegagalan pencocokan menggunakan fungsi error baru. Ini akan mengambil pesan kesalahan sebagai parameternya. Kegagalan yang dipicu oleh fungsi ini (disebut _kegagalan pencocokan khusus_) akan terdiri dari _posisi_ dan _pesan kesalahan_. Mereka tidak akan memiliki harapan apapun.

Fungsi error tidak akan mengganggu eksekusi tindakan, itu hanya akan menandainya sebagai gagal dan menyimpan pesan kesalahan. Kegagalan yang sebenarnya akan dihasilkan hanya setelah eksekusi tindakan selesai. Jika fungsi error dipanggil beberapa kali, pemanggilan terakhir akan menang (pesan kesalahannya akan digunakan).

Kegagalan pencocokan khusus akan ditangani seperti kegagalan pencocokan biasa, artinya kegagalan tersebut tidak akan menghentikan penguraian sepenuhnya dan membiarkan pengurai mundur dan mungkin mencoba alternatif lain. Namun ada satu perbedaan — ketika akhirnya melempar pengecualian SyntaxError , aturan kombinasi ekspektasi yang berlaku untuk kegagalan pencocokan reguler tidak akan berlaku untuk yang khusus. Jika dalam kumpulan kegagalan pencocokan dengan posisi terjauh setidaknya ada satu kesalahan khusus, itu hanya akan menimpa yang biasa sepenuhnya. Jika ada lebih banyak kegagalan kustom, yang diproduksi terakhir akan menang.

Pengecualian SyntaxError berdasarkan kegagalan pencocokan khusus akan berbeda dari pengecualian yang didasarkan pada kegagalan biasa. Properti message akan sama dengan pesan kesalahan kegagalan dan properti expected akan menjadi null .

Contoh

```
mulai = tanda:[+-]? angka:[0-9]+ {
var hasil = parseInt((tanda || "") + digits.join(""), 10);

 if (result % 2 == 0) {
   error("The number must be an odd integer.");
 }

 return result;

}
```

Pada input 2 , parser yang dihasilkan dari tata bahasa di atas akan menghasilkan SyntaxError dengan message disetel ke "The number must be an odd integer." dan expected disetel ke null

  1. Tindakan juga dapat memicu kegagalan pencocokan reguler menggunakan fungsi expected . Ini akan mengambil deskripsi nilai yang diharapkan sebagai parameter. Fungsi ini akan disediakan terutama sebagai kemudahan untuk situasi di mana seseorang tidak perlu membuat pesan kesalahan penuh dan secara otomatis membuat formulir "Diharapkan _X_ tetapi "2" ditemukan." cukup.

Pengecualian SyntaxError berdasarkan kegagalan pencocokan yang dihasilkan oleh fungsi expected akan serupa dengan pengecualian yang didasarkan pada kegagalan biasa.

Contoh

```
mulai = tanda:[+-]? angka:[0-9]+ {
var hasil = parseInt((tanda || "") + digits.join(""), 10);

 if (result % 2 == 0) {
   expected("odd integer");
 }

 return result;

}
```

Pada input 2 , parser yang dihasilkan dari tata bahasa di atas akan menghasilkan SyntaxError dengan message disetel ke "Expected odd integer but "2" found." dan expected disetel ke [ { type: "user", description: "odd integer" } ]

Langkah selanjutnya

Saya menyambut setiap catatan untuk perubahan yang diusulkan — harap tambahkan sebagai komentar. Saya berencana untuk mulai mengimplementasikan proposal (atau versi modifikasi berdasarkan umpan balik) segera.

feature

Komentar yang paling membantu

Jadi, belum ada fungsi "peringatan"?

Semua 7 komentar

Akan lebih baik jika beberapa pemanggilan fungsi kesalahan menyebabkan banyak kesalahan. Anda kemudian dapat melanjutkan penguraian sebanyak mungkin.

string = '"' value:(!(eol / '"') .)+ '"' { return value; }
       / '"' value:(!(eol / '"') .)+     { error('unterminated string constant'); return value; }

Saya juga merekomendasikan menambahkan dukungan untuk peringatan juga.

Akan lebih baik jika beberapa pemanggilan fungsi kesalahan menyebabkan banyak kesalahan. Anda kemudian dapat melanjutkan penguraian sebanyak mungkin.

Bisakah Anda menyebutkan beberapa kasus penggunaan di mana Anda membutuhkan ini? Tampaknya bagi saya bahwa contoh yang Anda berikan akan bekerja dengan proposal saya juga.

Saya sudah memiliki beberapa kasus penggunaan, tetapi saya tidak tahu seberapa representatifnya mereka, jadi saya ingin melihat lebih banyak lagi.

Saya juga merekomendasikan menambahkan dukungan untuk peringatan juga.

Saya juga sedang memikirkannya.

Masalah dengan banyak kesalahan dan peringatan adalah bahwa mereka akan membutuhkan antarmuka yang berbeda dari yang sederhana dan intuitif " parse mengembalikan beberapa nilai pada keberhasilan atau pengecualian pada kesalahan". Pengurai perlu melaporkan beberapa kesalahan pada penguraian yang gagal, ditambah peringatan pada penguraian yang berhasil dan tidak berhasil.

Jenis API apa yang menurut Anda paling tidak berguna di sini? Sekali lagi, saya punya beberapa ide, tetapi saya ingin melihat apa yang dipikirkan orang lain.

Bisakah Anda menyebutkan beberapa kasus penggunaan di mana Anda membutuhkan ini? Tampaknya bagi saya bahwa contoh yang Anda berikan akan bekerja dengan proposal saya juga.

Kasus penggunaan utama saya adalah untuk kesalahan penguraian yang tidak fatal. String yang tidak diakhiri, pengidentifikasi yang dimulai dengan angka, komentar bersarang, titik koma yang hilang, dll.

Umumnya, yang terbaik adalah membiarkan parser maju sejauh mungkin, sehingga sebanyak mungkin kesalahan dapat ditampilkan kepada pengguna. Jika pengurai bahkan bisa menyelesaikan penguraian, dan membiarkan langkah selanjutnya (misalnya kompilasi) melaporkan kesalahan juga, itu akan bagus.

Ini sekarang diimplementasikan . Skrip tools/impact melaporkan dampak kinerja berikut dari seluruh rangkaian komit:

Speed impact
------------
Before:     1144.21 kB/s
After:      999.89 kB/s
Difference: -12.62%

Size impact
-----------
Before:     863523 b
After:      1019968 b
Difference: 18.11%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)

Saya pikir penalti kecepatan 12,62% dan penalti ukuran 18,11% baik-baik saja untuk menyelesaikan serangkaian masalah yang sudah berlangsung lama.

Penutupan.

@dmajda : Itu berita bagus! Saya sangat senang null tidak lagi menandakan kegagalan.

Jadi, belum ada fungsi "peringatan"?

Topik fungsi peringatan dilacak di #325

Apakah halaman ini membantu?
0 / 5 - 0 peringkat