Di bawah ini adalah demonstrasi favorit saya (saat ini) tentang mengapa parsing C ++ (mungkin) Turing-complete , karena menunjukkan program yang secara sintaksis benar jika dan hanya jika bilangan bulat yang diberikan adalah prima.
Jadi saya menyatakan bahwa C ++ tidak bebas konteks atau peka konteks .
Jika Anda mengizinkan urutan simbol sewenang-wenang di kedua sisi dari produksi apa pun, Anda menghasilkan tata bahasa Tipe-0 ("tidak dibatasi") dalam hierarki Chomsky , yang lebih kuat daripada tata bahasa yang peka konteks; tata bahasa tanpa batas adalah Turing-complete. Tata bahasa konteks-sensitif (Tipe-1) memungkinkan banyak simbol konteks di sisi kiri produksi, tetapi konteks yang sama harus muncul di sisi kanan produksi (maka nama "konteks-sensitif"). [1] Tata bahasa yang peka terhadap konteks setara dengan mesin Turing yang dibatasi linier .
Dalam contoh program, perhitungan utama dapat dilakukan oleh mesin Turing yang dibatasi linier, sehingga tidak cukup membuktikan kesetaraan Turing, tetapi bagian yang penting adalah bahwa parser perlu melakukan perhitungan untuk melakukan analisis sintaksis. Itu bisa saja perhitungan yang dapat diekspresikan sebagai contoh template dan ada alasan untuk percaya bahwa contoh template C ++ adalah Turing-complete. Lihat, misalnya, makalah Todd L. Veldhuizen tahun 2003 .
Apapun, C ++ dapat diurai oleh komputer, jadi tentu saja dapat diurai oleh mesin Turing. Akibatnya, tata bahasa yang tidak terbatas bisa mengenalinya. Sebenarnya menulis tata bahasa seperti itu tidak praktis, itulah sebabnya standar tidak mencoba melakukannya. (Lihat di bawah.)
Masalah dengan "ambiguitas" ekspresi tertentu sebagian besar adalah ikan haring merah. Untuk mulai dengan, ambiguitas adalah fitur tata bahasa tertentu, bukan bahasa. Bahkan jika suatu bahasa dapat terbukti tidak memiliki tata bahasa yang jelas, jika itu dapat dikenali oleh tata bahasa bebas konteks, itu bebas konteks. Demikian pula, jika tidak dapat dikenali oleh tata bahasa bebas konteks tetapi bisa dikenali oleh tata bahasa yang peka konteks, itu peka konteks. Ambiguitas tidak relevan.
Tetapi dalam hal apa pun, seperti baris 21 (yaitu auto b = foo<IsPrime<234799>>::typen<1>();
) dalam program di bawah ini, ekspresi tidak ambigu sama sekali; mereka hanya diuraikan secara berbeda tergantung pada konteks. Dalam ungkapan paling sederhana dari masalah ini, kategori sintaksis dari pengidentifikasi tertentu bergantung pada bagaimana mereka telah dinyatakan (misalnya, jenis dan fungsi), yang berarti bahwa bahasa formal harus mengenali fakta bahwa dua string panjang sewenang-wenang dalam program yang sama identik (deklarasi dan penggunaan). Ini dapat dimodelkan dengan tata bahasa "copy", yang merupakan tata bahasa yang mengakui dua salinan tepat berturut-turut dari kata yang sama. Sangat mudah untuk dibuktikan dengan lemma pemompaan bahwa bahasa ini tidak bebas konteks. Tata bahasa konteks-sensitif untuk bahasa ini dimungkinkan, dan tata bahasa Tipe-0 disediakan dalam jawaban untuk pertanyaan ini: https: // math .stackexchange.com / questions / 163830 / context-sensitive-grammar-for-the-copy-language .
Jika seseorang mencoba untuk menulis tata bahasa konteks-sensitif (atau tidak dibatasi) untuk mem-parsing C ++, itu akan sangat mungkin mengisi alam semesta dengan coretan. Menulis mesin Turing untuk mem-parsing C ++ akan menjadi usaha yang sama mustahilnya. Bahkan menulis program C ++ sulit, dan sejauh yang saya tahu tidak ada yang terbukti benar. Inilah sebabnya mengapa standar tidak berusaha untuk menyediakan tata bahasa formal yang lengkap, dan mengapa ia memilih untuk menulis beberapa aturan parsing dalam bahasa Inggris teknis.
Apa yang tampak seperti tata bahasa formal dalam standar C ++ bukanlah definisi formal lengkap sintaks dari bahasa C ++. Itu bahkan bukan definisi formal lengkap dari bahasa setelah preprocessing, yang mungkin lebih mudah untuk diformalkan. (Namun itu bukan bahasa: bahasa C ++ seperti yang didefinisikan oleh standar termasuk preprosesor, dan operasi preprosesor dijelaskan secara algoritmik karena akan sangat sulit untuk dijelaskan dalam formalisme tata bahasa apa pun. Ini ada di bagian itu standar di mana dekomposisi leksikal dijelaskan, termasuk aturan di mana ia harus diterapkan lebih dari satu kali.)
Berbagai tata bahasa (dua tata bahasa yang tumpang tindih untuk analisis leksikal, satu yang terjadi sebelum preprocessing dan yang lainnya, jika perlu, sesudahnya, ditambah tata bahasa "sintaksis") dikumpulkan dalam Lampiran A, dengan catatan penting ini (penekanan ditambahkan):
Ringkasan sintaksis C ++ ini dimaksudkan sebagai bantuan untuk pemahaman. Ini bukan pernyataan bahasa yang tepat . Secara khusus, tata bahasa yang dijelaskan di sini menerima superset dari konstruksi C ++ yang valid . Aturan disambiguasi (6.8, 7.1, 10.2) harus diterapkan untuk membedakan ekspresi dari deklarasi. Lebih lanjut, kontrol akses, ambiguitas, dan aturan jenis harus digunakan untuk menghilangkan konstruksi yang valid secara sintaksis tetapi tidak berarti.
Akhirnya, inilah program yang dijanjikan. Baris 21 secara sintaksis benar jika dan hanya jika N in IsPrime<N>
adalah prima. Jika tidak, typen
merupakan bilangan bulat, bukan templat, sehingga typen<1>()
diuraikan sebagai (typen<1)>()
yang secara sintaksis salah karena ()
bukan ekspresi yang valid secara sintaksis.
template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};
template<bool no, bool yes, int f, int p> struct IsPrimeHelper
: IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };
template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; };
template<typename A> struct foo;
template<>struct foo<answer<true>>{
template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
static const int typen = 0;
};
int main() {
auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
return 0;
}
[1] Untuk membuatnya lebih teknis, setiap produksi dalam tata bahasa konteks-sensitif harus dalam bentuk:
αAβ → αγβ
di mana A
non-terminal dan α
, β
mungkin urutan kosong dari simbol tata bahasa, dan γ
merupakan urutan tidak kosong. (Simbol tata bahasa dapat berupa terminal atau non-terminal).
Ini dapat dibaca karena A → γ
hanya dalam konteks [α, β]
. Dalam tata bahasa bebas konteks (Tipe 2), α
dan β
harus kosong.
Ternyata Anda juga dapat membatasi tata bahasa dengan batasan "monoton", di mana setiap produksi harus dalam bentuk:
α → β
dimana |α| ≥ |β| > 0
( |α|
berarti "panjang α
")
Mungkin untuk membuktikan bahwa rangkaian bahasa yang dikenali oleh tata bahasa monoton persis sama dengan rangkaian bahasa yang dikenali oleh tata bahasa konteks-sensitif, dan sering kali lebih mudah untuk mendasarkan bukti pada tata bahasa monoton. Akibatnya, cukup umum untuk melihat "konteks-sensitif" digunakan seolah-olah itu berarti "monoton".