Apakah C ++ bebas konteks atau peka konteks?

405

Saya sering mendengar klaim bahwa C ++ adalah bahasa yang peka konteks. Ambil contoh berikut:

a b(c);

Apakah ini definisi variabel atau deklarasi fungsi? Itu tergantung pada makna simbol c. Jika cadalah variabel , maka a b(c);tentukan variabel bernama btipe a. Langsung diinisialisasi dengan c. Tetapi jika cadalah tipe , maka a b(c);mendeklarasikan fungsi bernama byang mengambil a cdan mengembalikan a.

Jika Anda mencari definisi bahasa bebas konteks, pada dasarnya ia akan memberi tahu Anda bahwa semua aturan tata bahasa harus memiliki sisi kiri yang terdiri dari tepat satu simbol non-terminal. Tata bahasa yang peka terhadap konteks, di sisi lain, memungkinkan deretan simbol terminal dan non-terminal yang sewenang-wenang di sisi kiri.

Menelusuri Lampiran A dari "Bahasa Pemrograman C ++", saya tidak dapat menemukan aturan tata bahasa tunggal yang memiliki hal lain selain simbol non-terminal tunggal di sisi kiri. Itu menyiratkan bahwa C ++ bebas konteks. (Tentu saja, setiap bahasa bebas konteks juga peka konteks dalam arti bahwa bahasa bebas konteks membentuk bagian dari bahasa peka konteks, tetapi bukan itu intinya.)

Jadi, apakah C ++ bebas konteks atau peka konteks?

fredoverflow
sumber
12
@CarlNorum Tolong tunjukkan saya aturan tata bahasa tunggal C ++ yang tidak terdiri dari simbol non-terminal tunggal di sisi kiri dan saya akan segera percaya Anda.
fredoverflow
9
IIUC itu tergantung sedikit pada di mana Anda menggambar garis untuk sensitivitas konteks. Saya pikir saya telah melihat orang berpendapat bahwa hampir semua bahasa pemrograman yang diketik secara statis adalah peka konteks, bukan karena Anda tidak dapat membangun kompiler praktis untuk mereka dengan alat parsing CFG, tetapi karena implementasi seperti itu "curang" dengan menguraikan beberapa program yang tidak valid dan hanya menolaknya nanti, saat memeriksa jenis. Jadi, jika Anda menganggap program yang diketik dengan buruk tidak dalam bahasa (dalam pengertian CS, yaitu seperangkat string) yang harus diterima oleh parser, lebih banyak bahasa daripada C ++ yang peka terhadap konteks.
6
@DeadMG: Tidak, Anda salah. Tidak ada "parsing" atau "semantik" dalam teori bahasa formal sama sekali, hanya "bahasa" yang merupakan serangkaian string.
jpalecek
27
Sejauh ini tidak ada jawaban yang membahas definisi Anda tentang "tata bahasa bebas konteks". Menurut saya, jawaban yang benar untuk pertanyaan ini mengutip produksi dalam lampiran A yang tidak sesuai dengan definisi Anda, atau menunjukkan bahwa definisi Anda salah atau tidak cukup. Tetap ditempatmu!
Lightness Races dalam Orbit
8
Lihat Apakah tata bahasa D benar-benar bebas konteks? . Sebenarnya, saya pikir semua orang di sini harus membaca pertanyaan itu dan jawabannya!
Lightness Races dalam Orbit

Jawaban:

341

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, typenmerupakan 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 Anon-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".

rici
sumber
27
Jadi tidak hanya itu peka konteks, tetapi dapat dibuat bergantung pada konteks apa pun yang dapat Anda ungkapkan dalam templat, yang Turing-complete.
Puppy
7
@mehrdad, OP mengatakan "bahasa konteks-sensitif", bukan tata bahasa konteks-sensitif. Ambiguitas adalah fitur tata bahasa, bukan bahasa. Bahasanya memang peka konteks, tetapi bukan karena tata bahasa tertentu untuk ambigu.
rici
2
Perhatikan bahwa contoh saya tidak ambigu. Ini adalah ekspresi yang jelas dari program yang valid. Jika Anda mengubah nilai di baris 21, itu bisa menjadi buruk. Tapi dalam kedua kasus itu tidak ambigu.
rici
5
Saya punya satu keraguan: Seperti yang Anda tunjukkan, hasil evaluasi templat dapat membuat perbedaan antara program yang baik dan yang buruk. Evaluasi template selesai-turing. Jadi tidak akan dengan benar menentukan apakah suatu string ada dalam bahasa (C ++) memerlukan turing-kelengkapan? Seperti yang Anda katakan, bahasa konteks-sensitif adalah "hanya" a "linear bounded automaton", yang bukan AFAIK turing-complete. Atau apakah argumen Anda menggunakan batasan yang diberikan oleh standar C ++ pada beberapa hal termasuk kedalaman evaluasi templat?
4
@AntonGolov: Versi asli saya dari contoh itu melakukan hal itu (Anda dapat mencapainya dengan memasukkan ke 0dalam (), untuk yang sederhana), tapi saya pikir ini lebih menarik dengan cara ini, karena itu menunjukkan bahwa Anda memerlukan contoh template bahkan untuk mengenali jika string adalah program C ++ yang benar secara sintaksis. Jika kedua cabang dikompilasi, maka saya harus bekerja lebih keras untuk membantah argumen bahwa perbedaannya adalah "semantik". Anehnya, walaupun saya sering ditantang untuk mendefinisikan "sintaksis", tidak ada yang pernah menawarkan definisi "semantik" selain "hal-hal yang saya pikir tidak sintaksis" :)
rici
115

Pertama, Anda benar diamati tidak ada konteks aturan sensitif dalam tata bahasa pada akhir C ++ standar, sehingga tata bahasa adalah bebas konteks.

Namun, tata bahasa itu tidak secara tepat menggambarkan bahasa C ++, karena ia menghasilkan program non-C ++ seperti

int m() { m++; }

atau

typedef static int int;

Bahasa C ++ yang didefinisikan sebagai "himpunan program C ++ yang terbentuk dengan baik" tidak bebas konteks (dimungkinkan untuk menunjukkan bahwa hanya menuntut variabel yang akan dideklarasikan membuatnya demikian). Mengingat Anda secara teoritis dapat menulis program Turing-selesai dalam template dan membuat program salah berdasarkan hasil mereka, itu bahkan tidak peka konteks.

Sekarang, orang-orang (bodoh) (biasanya bukan ahli teori bahasa, tetapi perancang parser) biasanya menggunakan "tidak bebas konteks" dalam beberapa arti berikut

  • ambigu
  • tidak dapat diuraikan dengan Bison
  • bukan LL (k), LR (k), LALR (k) atau kelas bahasa parser apa pun yang mereka pilih

Tata bahasa di belakang standar tidak memenuhi kategori-kategori ini (yaitu ambigu, bukan LL (k) ...) sehingga tata bahasa C ++ "tidak bebas konteks" untuk mereka. Dan dalam arti tertentu, mereka benar sangat sulit untuk menghasilkan parser C ++ yang berfungsi.

Perhatikan bahwa properti yang digunakan di sini hanya terhubung dengan lemah ke bahasa bebas konteks - ambiguitas tidak ada hubungannya dengan sensitivitas konteks (pada kenyataannya, aturan konteks sensitif biasanya membantu menyamarkan produksi), dua lainnya hanyalah subset konteks -gratis bahasa. Dan parsing bahasa bebas konteks bukanlah proses linear (meskipun parsing deterministik adalah).

jpalecek
sumber
7
ambiguity doesn't have anything to do with context-sensitivityIni juga intuisi saya, jadi saya senang melihat seseorang (a) setuju, dan (b) menjelaskannya di tempat saya tidak bisa. Saya percaya itu mendiskualifikasi semua argumen yang didasarkan pada a b(c);, dan sebagian memuaskan pertanyaan asli yang premisnya "sering terdengar" klaim sensitivitas konteks karena ambiguitas ... terutama ketika untuk tata bahasa sebenarnya tidak ada ambiguitas bahkan di MVP.
Lightness Races di Orbit
6
@KonradRudolph: Yang dikatakan standar adalah "Ada kuantitas yang ditentukan implementasi yang menentukan batas pada kedalaman total instantiasi rekursif, yang dapat melibatkan lebih dari satu templat. Hasil rekursi tak terbatas dalam instantiasi tidak ditentukan." (14.7.1p15) Saya menafsirkan bahwa itu berarti bahwa suatu implementasi tidak diperlukan untuk memahami setiap program c ++ yang valid, bukan bahwa program dengan kedalaman rekursi yang terlalu besar tidak valid. Satu-satunya yang ditandai sebagai tidak valid adalah mereka yang memiliki kedalaman rekursi tak terbatas.
rici
3
@KonradRudolph: Saya membantah bahwa itu "referensi umum". Fakta bahwa saya membaca artikel yang agak rumit dan tidak memahaminya secara memadai untuk menjelaskan fakta kecil ini sudah cukup untuk menunjukkan hal itu. Bukannya Anda mengatakan sesuatu seperti "komputer biasanya menggunakan listrik", atau "bit bisa benar atau salah".
Lightness Races di Orbit
3
Jika fakta ini benar-benar diketahui secara luas, saya pikir akan jauh lebih mudah untuk menemukan referensi daripada berdebat panjang lebar tentang apakah harus disediakan atau tidak. Belum lagi konstruktif.
Samuel Edwin Ward
5
Sejauh yang saya tahu, @Konrad keliru ketika dia berkata "Konteks sensitif sama dengan Turing lengkap." (setidaknya, dia adalah jika dia menunjukkan "recursively enumerable" oleh "Turing complete"), dan sejak itu tidak dapat mengenali kesalahan ini. Berikut ini adalah referensi untuk hubungan inklusi set yang tepat yang terlibat di sini: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix
61

Iya. Ungkapan berikut memiliki urutan operasi yang berbeda tergantung pada jenis konteks yang diselesaikan :

Sunting: Ketika urutan operasi aktual bervariasi, itu membuatnya sangat sulit untuk menggunakan kompiler "biasa" yang mem-parsing ke AST yang tidak didekorasi sebelum menghiasnya (menyebarkan informasi jenis). Hal-hal sensitif konteks lainnya yang disebutkan adalah "agak mudah" dibandingkan dengan ini (bukan berarti evaluasi templat sama sekali mudah).

#if FIRST_MEANING
   template<bool B>
   class foo
   { };
#else
   static const int foo = 0;
   static const int bar = 15;
#endif

Diikuti oleh:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );
Sam Harwell
sumber
Mengapa masalah itu tidak bisa diselesaikan seperti untuk C, dengan mengingat definisi tipe apa yang ada dalam ruang lingkup?
Blaisorblade
1
@ Blaisorblade: Salah satu cara untuk membuat kompiler "bersih" adalah dengan memisahkan tugas menjadi langkah-langkah independen dalam rantai, seperti membuat pohon parse dari input diikuti oleh langkah yang melakukan analisis tipe. C ++ memaksa Anda untuk 1) menggabungkan langkah-langkah ini menjadi satu atau 2) mengurai dokumen sesuai dengan kedua / semua interpretasi yang mungkin, dan memungkinkan tahapan resolusi jenis untuk mempersempit ke interpretasi yang benar.
Sam Harwell
@ 280Z28: setuju, tapi begitu juga untuk C; Saya pikir jawaban yang baik untuk pertanyaan ini harus menunjukkan mengapa C ++ lebih buruk daripada C. Tesis PhD yang ditautkan di sini melakukan itu: stackoverflow.com/a/243447/53974
Blaisorblade
26

Untuk menjawab pertanyaan Anda, Anda perlu membedakan dua pertanyaan yang berbeda.

  1. Sintaks belaka dari hampir setiap bahasa pemrograman bebas konteks. Biasanya, ini diberikan sebagai bentuk diperpanjang Backus-Naur atau gramar bebas konteks.

  2. Namun, bahkan jika suatu program sesuai dengan gramar bebas konteks yang ditentukan oleh bahasa pemrograman, itu tidak selalu merupakan program yang valid . Ada banyak poperties bebas-konteks yang harus dipenuhi oleh sebuah program agar menjadi program yang valid. Misalnya, properti yang paling sederhana adalah ruang lingkup variabel.

Untuk menyimpulkan, apakah C ++ bebas konteks tergantung pada pertanyaan yang Anda ajukan.

Dan
sumber
5
Sangat menarik untuk dicatat bahwa Anda sering harus menempatkan tingkat "sintaks belaka" lebih rendah dari yang Anda harapkan, untuk mendapatkan CFG untuk bahasa pemrograman Anda. Ambil C, misalnya. Anda mungkin berpikir bahwa aturan tata bahasa untuk deklarasi variabel sederhana dalam C adalah VARDECL : TYPENAME IDENTIFIER, tetapi Anda tidak bisa memilikinya, karena Anda tidak bisa membedakan nama tipe dari pengidentifikasi lain di level CF. Contoh lain: pada level CF, Anda tidak dapat memutuskan apakah akan mem a*b- parsing sebagai deklarasi variabel ( bdari penunjuk tipe ke a) atau sebagai perkalian.
LaC
2
@ LaC: Ya, terima kasih telah menunjukkan ini! Omong-omong, saya yakin ada istilah teknis yang lebih umum digunakan untuk sintaks belaka . Apakah ada istilah yang benar?
Dan
4
@Dan: apa yang Anda bicarakan adalah perkiraan bahasa yang diberikan oleh beberapa tata bahasa bebas konteks. Tentu saja perkiraan seperti itu bebas coontext menurut definisi. Ini adalah arti di mana "sintaks" sering digunakan ketika membahas bahasa pemrograman.
reinierpost
13

Anda mungkin ingin melihat Desain & Evolusi C ++ , oleh Bjarne Stroustrup. Di dalamnya ia menggambarkan masalahnya mencoba menggunakan yacc (atau serupa) untuk mem-parsing versi awal C ++, dan berharap ia menggunakan keturunan rekursif sebagai gantinya.

Matthias Weiler
sumber
Wow terima kasih. Saya ingin tahu apakah benar-benar masuk akal untuk berpikir tentang menggunakan sesuatu yang lebih kuat daripada CFG untuk mengurai bahasa buatan.
Dervin Thunk
Buku bagus untuk memahami mengapa C ++. Saya merekomendasikan itu dan Lippman Di dalam C ++ Object Model untuk memahami bagaimana C ++ bekerja. Meskipun keduanya sedikit tanggal, mereka masih bisa dibaca.
Harga Matt
"Meta-S" adalah mesin parsing konteks-sensitif oleh Quinn Tyler Jackson. Saya belum menggunakannya, tapi dia menceritakan kisah yang mengesankan. Periksa komentarnya di comp.compiler, dan lihat rnaparse.com/MetaS%20defined.htm
Ira Baxter
@IraBaxter: x-ref Anda MIA hari ini - dan referensi yang kuat ke perangkat lunak tampaknya sulit dipahami (pencarian Google tidak memberikan petunjuk yang baik, baik dengan 'situs: rnaparse.com meta-s' atau 'quinn jackson meta- s '; ada sedikit demi sedikit , tetapi meta-s.com mengarah ke situs web yang tidak informatif, misalnya).
Jonathan Leffler
@ Jonathan: sudah lama, perhatikan saja keluhan Anda. Entah mengapa tautannya buruk, saya pikir itu bagus ketika saya menulisnya. Quinn dulu cukup aktif di comp.compiler. Google tampaknya semakin tidak stabil, hanya ini yang bisa saya temukan: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, ia menandatangani hak untuk MetaS untuk beberapa pakaian di Hawaii untuk dipasarkan ulang. Mengingat betapa anehnya ini, IMHO ini menandatangani surat kematiannya. Kedengarannya seperti skema yang sangat cerdas.
Ira Baxter
12

Ya C ++ sensitif terhadap konteks, sangat sensitif terhadap konteks. Anda tidak dapat membangun pohon sintaks dengan hanya mengurai file menggunakan pengurai konteks bebas karena dalam beberapa kasus Anda perlu mengetahui simbol dari pengetahuan sebelumnya untuk memutuskan (mis. Membangun tabel simbol saat mengurai).

Contoh pertama:

A*B;

Apakah ini ekspresi multiplikasi?

ATAU

Apakah ini deklarasi Bvariabel menjadi pointer tipe A?

Jika A adalah variabel, maka itu ekspresi, jika A adalah tipe, itu adalah deklarasi pointer.

Contoh kedua:

A B(bar);

Apakah ini prototipe fungsi yang mengambil argumen bartipe?

ATAU

Apakah ini menyatakan variabel Btipe Adan memanggil konstruktor A dengan barkonstanta sebagai penginisialisasi?

Anda perlu tahu lagi apakah baritu variabel atau tipe dari tabel simbol.

Contoh ketiga:

class Foo
{
public:
    void fn(){x*y;}
    int x, y;
};

Ini adalah kasus ketika membangun tabel simbol sementara parsing tidak membantu karena deklarasi x dan y muncul setelah definisi fungsi. Jadi, Anda perlu memindai definisi kelas terlebih dahulu, dan melihat definisi metode dalam pass kedua, untuk mengatakan x * y adalah ekspresi, dan bukan deklarasi pointer atau apa pun.

Calmarius
sumber
1
A B();adalah deklarasi fungsi bahkan dalam definisi fungsi. Carilah parse paling menjengkelkan ...
Pemrogram
"Anda tidak dapat membangun pohon sintaks dengan hanya menguraikan file" FALSE. Lihat jawaban saya.
Ira Baxter
10

C ++ diuraikan dengan parser GLR. Itu berarti selama parsing kode sumber, parser mungkin menghadapi ambiguitas tetapi harus melanjutkan dan memutuskan aturan tata bahasa mana yang akan digunakan nanti .

lihat juga,

Mengapa C ++ tidak dapat diuraikan dengan parser LR (1)?


Ingat bahwa tata bahasa bebas konteks tidak dapat menggambarkan SEMUA aturan sintaksis bahasa pemrograman. Misalnya, Tata bahasa atribut digunakan untuk memeriksa validitas jenis ekspresi.

int x;
x = 9 + 1.0;

Anda tidak bisa mendeskripsikan aturan berikut dengan tata bahasa bebas konteks: Sisi Kanan dari tugas harus dari jenis yang sama dari sisi Kiri.

AraK
sumber
4
Kebanyakan parser C ++ tidak menggunakan teknologi parsing GLR. GCC tidak. Beberapa melakukannya. Lihat semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html untuk yang melakukannya.
Ira Baxter
10

Saya punya perasaan bahwa ada beberapa kebingungan antara definisi formal "sensitif terhadap konteks" dan penggunaan "sensitif konteks" secara informal. Yang pertama memiliki makna yang jelas. Yang terakhir digunakan untuk mengatakan "Anda perlu konteks untuk menguraikan input".

Ini juga ditanyakan di sini: Konteks-sensitivitas vs Ambiguitas .

Berikut ini adalah tata bahasa bebas konteks:

<a> ::= <b> | <c>
<b> ::= "x"
<c> ::= "x"

Ini ambigu, jadi untuk mengurai input "x" Anda memerlukan beberapa konteks (atau hidup dengan ambiguitas, atau memancarkan "Peringatan: E8271 - Input ambigu di baris 115"). Tapi itu jelas bukan tata bahasa konteks-sensitif.

Omri Barel
sumber
Bagaimana cara memiliki banyak simbol di sisi kiri produksi menyelesaikan masalah ini? Saya kira jawaban ini tidak menjawab pertanyaan.
user541686
1
Jawaban saya adalah sebagai tanggapan terhadap kalimat pertama: "Saya sering mendengar klaim bahwa C ++ adalah bahasa yang peka konteks." Jika klaim tersebut menggunakan ungkapan "sensitif terhadap konteks" secara tidak resmi, maka tidak ada masalah. Saya tidak berpikir bahwa C ++ secara formal sensitif terhadap konteks.
Omri Barel
Saya pikir C ++ adalah resmi konteks-sensitif, tetapi masalah Saya mengalami adalah bahwa saya tidak mengerti bagaimana tata bahasa konteks-sensitif akan memiliki lebih sukses di parsing C ++ dari CFG akan.
user541686
6

Tidak ada bahasa seperti Algol yang bebas konteks, karena mereka memiliki aturan yang membatasi ekspresi dan pernyataan yang dapat muncul pengidentifikasi berdasarkan jenisnya, dan karena tidak ada batasan jumlah pernyataan yang dapat terjadi antara deklarasi dan penggunaan.

Solusi yang biasa adalah dengan menulis parser bebas konteks yang benar-benar menerima superset dari program yang valid dan menempatkan bagian peka konteks dalam kode "semantik" ad hoc yang dilampirkan pada aturan.

C ++ melampaui ini, berkat sistem template Turing-complete. Lihat Pertanyaan Stack Overflow 794015 .

James Jones
sumber
5

Benar :)

J. Stanley Warford. Sistem komputer . Halaman 341-346.

anno
sumber
5

Itu peka konteks, seperti a b(c);memiliki dua parses- deklarasi dan variabel yang valid. Ketika Anda mengatakan "If cis a type", itu konteks, di sana, dan Anda telah menggambarkan dengan tepat bagaimana C ++ sensitif terhadapnya. Jika Anda tidak memiliki konteks "Apa ituc ?" Anda tidak dapat menguraikan ini dengan jelas.

Di sini, konteksnya dinyatakan dalam pilihan token - parser membaca pengidentifikasi sebagai token nama ketik jika nama jenis. Ini adalah resolusi paling sederhana, dan menghindari banyak kerumitan menjadi peka konteks (dalam hal ini).

Sunting: Ada, tentu saja, lebih banyak masalah sensitivitas konteks, saya hanya berfokus pada yang Anda tunjukkan. Template sangat tidak menyenangkan untuk ini.

Anak anjing
sumber
1
Juga a<b<c>>dkan? (Contoh Anda sebenarnya adalah karya klasik dari C , di mana itu adalah satu - satunya penghalang untuk bebas konteks.)
Kerrek SB
Itu lebih merupakan masalah lexing, saya pikir. Tetapi tentu saja dalam kategori yang sama, ya.
Puppy
2
Penanya tidak bertanya bagaimana itu lebih sensitif terhadap konteks daripada C, hanya untuk menunjukkan bahwa itu sensitif terhadap konteks.
Puppy
Jadi .. apakah C ++ lebih sensitif terhadap konteks daripada C?
Kerrek SB
2
@DeadMG Saya tidak berpikir Anda menjawab pertanyaan (saya pikir saya juga tidak). Bagaimana cara memiliki terminal di sisi kiri produksi menyelesaikan masalah ini?
user541686
5

Produksi dalam standar C ++ ditulis bebas konteks, tetapi seperti yang kita semua tahu tidak benar-benar mendefinisikan bahasa secara tepat. Beberapa dari apa yang kebanyakan orang lihat sebagai ambiguitas dalam bahasa saat ini dapat (saya percaya) diselesaikan dengan jelas dengan tata bahasa yang peka konteks.

Untuk contoh yang paling jelas, mari kita mempertimbangkan paling menjengkelkan Parse: int f(X);. Jika Xadalah nilai, maka ini mendefinisikan fsebagai variabel yang akan diinisialisasi dengan X. Jika Xadalah tipe, itu didefinisikan fsebagai fungsi yang mengambil parameter tipe tunggal X.

Melihat itu dari sudut pandang tata bahasa, kita bisa melihatnya seperti ini:

A variable_decl ::= <type> <identifier> '(' initializer ')' ';'

B function_decl ::= <type> <identifier> '(' param_decl ')' ';'

A ::= [declaration of X as value]
B ::= [declaration of X as type]

Tentu saja, untuk menjadi sepenuhnya benar kita perlu menambahkan beberapa "barang" tambahan untuk menjelaskan kemungkinan campur tangan deklarasi jenis lain (yaitu, A dan B keduanya harus benar-benar menjadi "deklarasi termasuk deklarasi X sebagai ..." , atau sesuatu pada urutan itu).

Ini masih agak berbeda dari CSG pada umumnya (atau setidaknya yang saya ingat dari mereka). Ini tergantung pada tabel simbol yang sedang dibangun - bagian yang secara khusus mengenaliX sebagai jenis atau nilai, bukan hanya beberapa jenis pernyataan sebelumnya, tetapi jenis pernyataan yang benar untuk simbol / pengidentifikasi yang tepat.

Karena itu, saya harus melakukan beberapa upaya untuk memastikan, tetapi tebakan langsung saya adalah bahwa ini tidak benar-benar memenuhi syarat sebagai CSG, setidaknya seperti istilah yang biasanya digunakan.

Jerry Coffin
sumber
Produksi (bebas konteks) mendefinisikan parse yang paling menjengkelkan dengan cukup baik sehingga dapat diurai oleh mesin parsing bebas konteks. Itu menunda masalah memutuskan interpretasi berganda mana yang valid sampai setelah parse selesai, tetapi itu hanya membuat teknik parser dan nama resolver lebih mudah, karena mereka modular daripada kusut seperti pada parser C ++ konvensional. Lihat AST untuk sebagian besar parsing yang menjengkelkan: stackoverflow.com/questions/17388771/…
Ira Baxter
5

Kasus paling sederhana dari tata bahasa non-konteks bebas melibatkan parsing ekspresi yang melibatkan template.

a<b<c>()

Ini dapat diuraikan sebagai salah satu

template
   |
   a < expr > ()
        |
        <
      /   \
     b     c

Atau

 expr
   |
   <
 /   \
a   template
     |
     b < expr > ()
          |
          c

Kedua AST hanya dapat disatukan dengan memeriksa deklarasi 'a' - AST sebelumnya jika 'a' adalah templat, atau yang kedua jika tidak.

Harun
sumber
Saya percaya C ++ 11 mengamanatkan interpretasi yang terakhir, dan Anda harus menambahkan parens untuk memilih yang pertama.
Joseph Garvin
1
@ JosephephGarvin, tidak. Mandat C ++ yang <harus menjadi braket jika bisa (mis., Mengikuti pengenal yang menamai templat). C ++ 11 menambahkan persyaratan bahwa >dan karakter pertama >>ditafsirkan sebagai kurung dekat jika penggunaan itu masuk akal. Ini memengaruhi penguraian di a<b>c>mana atemplat tetapi tidak berpengaruh a<b<c>.
rici
@ Harun: bagaimana itu lebih sederhana dari a();(yang merupakan salah satu expr.callatau expr.type.conv)
rici
@rici: Ups, saya tidak menyadari itu asimetris.
Joseph Garvin
5
Apakah Anda menggambarkan ambiguitas , atau sensitivitas konteks?
corazza
4

Templat C ++ telah terbukti Turing Powerfull. Meskipun bukan referensi formal, berikut adalah tempat untuk melihat dalam hal ini:

http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html

Saya akan berani menebak (setua bukti CACM folkoric dan singkat menunjukkan bahwa ALGOL di tahun 60-an tidak dapat diwakili oleh CFG) dan mengatakan bahwa C ++ karena itu tidak dapat dengan benar diurai hanya oleh CFG. CFG, dalam hubungannya dengan berbagai mekanisme TP baik dalam melewati pohon atau selama acara pengurangan - ini adalah cerita lain. Secara umum, karena Masalah Pemutusan, ada beberapa program C ++ yang tidak dapat ditunjukkan benar / salah tetapi tetap benar / salah.

{PS- Sebagai penulis Meta-S (disebutkan oleh beberapa orang di atas) - Saya dapat dengan yakin mengatakan bahwa Thothic tidak mati, juga tidak ada perangkat lunak yang tersedia secara gratis. Mungkin saya telah menuliskan versi tanggapan saya ini sehingga saya tidak bisa dihapus atau memilih ke -3.}

Quinn Tyler Jackson
sumber
3

C ++ tidak bebas konteks. Saya mempelajarinya beberapa waktu lalu di kompiler kuliah. Pencarian cepat memberi tautan ini, di mana bagian "Sintaks atau semantik" menjelaskan mengapa C dan C ++ tidak bebas konteks:

Wikipedia Talk: Tata bahasa bebas konteks

Salam,
Ovan

kapal selam
sumber
2

Jelas, jika Anda menjawab pertanyaan itu kata demi kata, hampir semua bahasa dengan pengidentifikasi peka terhadap konteks.

Orang perlu tahu apakah pengenal adalah nama jenis (nama kelas, nama yang diperkenalkan oleh typedef, parameter templat nama samaran), nama templat atau nama lain untuk dapat dengan benar beberapa penggunaan pengidentifikasi. Contohnya:

x = (name)(expression);

adalah cast if nameadalah nama jenis dan panggilan fungsi ifname adalah nama fungsi. Kasus lain adalah apa yang disebut "parse paling menjengkelkan" di mana tidak mungkin untuk membedakan definisi variabel dan deklarasi fungsi (ada aturan yang mengatakan itu adalah deklarasi fungsi).

Kesulitan itu telah memperkenalkan kebutuhan typenamedan templatedengan nama-nama yang tergantung. Sisa dari C ++ tidak sensitif konteks sejauh yang saya tahu (yaitu mungkin untuk menulis tata bahasa bebas konteks untuk itu).

Pemrogram
sumber
2

Meta-S "adalah mesin parsing konteks-sensitif oleh Quinn Tyler Jackson. Saya belum menggunakannya, tapi dia menceritakan kisah yang mengesankan. Lihatlah komentarnya di comp.compiler, dan lihat rnaparse.com/MetaS%20defined.htm - Ira Baxter 25 Jul pukul 10:42

Tautan yang benar adalah parsing enigines

Meta-S adalah milik perusahaan mati bernama Thothic. Saya dapat mengirim salinan gratis Meta-S kepada siapa pun yang tertarik dan saya telah menggunakannya dalam penelitian parsing. Harap perhatikan "tata bahasa pseudoknot" yang termasuk dalam folder contoh ditulis oleh non-bioinformatika, programmer amature dan pada dasarnya tidak berfungsi. Tata bahasa saya mengambil pendekatan yang berbeda dan bekerja dengan baik.

sth
sumber
Ini sebenarnya merupakan penemuan yang menarik.
Dervin Thunk
0

Masalah besar di sini adalah bahwa istilah "bebas konteks" dan "peka konteks" sedikit tidak intuitif dalam ilmu komputer. Untuk C ++, sensitivitas konteks terlihat sangat mirip dengan ambiguitas, tetapi itu tidak selalu benar dalam kasus umum.

Dalam C / ++, pernyataan if hanya dibolehkan di dalam tubuh fungsi. Itu tampaknya membuatnya sensitif terhadap konteks, bukan? Ya tidak. Tata bahasa bebas konteks sebenarnya tidak membutuhkan properti tempat Anda dapat mencabut beberapa baris kode dan menentukan apakah itu valid. Sebenarnya bukan itu arti bebas konteks. Ini benar-benar hanya label yang secara tidak langsung menyiratkan sesuatu yang terkait dengan seperti apa suaranya.

Sekarang, jika pernyataan di dalam fungsi tubuh diuraikan secara berbeda tergantung pada sesuatu yang didefinisikan di luar leluhur gramatikal langsung (misalnya apakah pengidentifikasi menggambarkan suatu jenis atau variabel), seperti dalam a * b;kasus, maka, pada kenyataannya, konteks-sensitif. Tidak ada ambiguitas aktual di sini; itu akan diurai sebagai deklarasi pointer jika amerupakan tipe dan perkalian sebaliknya.

Menjadi peka konteks tidak selalu berarti "sulit untuk diuraikan". C sebenarnya tidak terlalu sulit karena a * b;"ambiguitas" yang terkenal itu dapat diselesaikan dengan tabel simbol yang mengandung typedefs yang ditemukan sebelumnya. Itu tidak memerlukan instantiasi template sewenang-wenang (yang telah terbukti Turing Lengkap) untuk menyelesaikan kasus seperti C ++ pada kesempatan tertentu. Sebenarnya tidak mungkin untuk menulis program C yang tidak akan dikompilasi dalam jumlah waktu yang terbatas walaupun ia memiliki sensitivitas konteks yang sama seperti yang dilakukan oleh C ++.

Python (dan bahasa spasi-spasi sensitif lainnya) juga tergantung pada konteks, karena ia membutuhkan keadaan dalam lexer untuk menghasilkan token indent dan dedent, tetapi itu tidak membuat lebih sulit untuk diuraikan daripada tata bahasa LL-1 yang khas. Ini sebenarnya menggunakan parser-generator, yang merupakan bagian dari mengapa Python memiliki pesan kesalahan sintaksis yang tidak informatif. Penting juga untuk dicatat di sini bahwa tidak ada "ambiguitas" seperti a * b;masalah dalam Python, memberikan contoh konkret yang baik dari bahasa konteks-sensitif tanpa tata bahasa "ambigu" (seperti yang disebutkan dalam paragraf pertama).

Beefster
sumber
-4

Jawaban ini mengatakan C ++ tidak bebas konteks ... ada implikasi (bukan oleh penjawab) yang tidak dapat diuraikan, dan jawabannya menawarkan contoh kode yang sulit yang menghasilkan program C ++ yang tidak valid jika konstanta tertentu bukan bilangan prima.

Seperti yang telah diamati orang lain, pertanyaan tentang apakah bahasa itu peka konteks / bebas berbeda dari pertanyaan yang sama tentang tata bahasa tertentu.

Untuk mengatur pertanyaan tentang parseabilitas untuk beristirahat, saya menawarkan bukti empiris bahwa ada tata bahasa bebas konteks untuk C ++, yang dapat digunakan untuk menghasilkan AST untuk parse bebas konteks dari teks sumber dengan sebenarnya menguraikannya dengan GLR yang ada alat berbasis -parser yang didorong oleh tata bahasa eksplisit.

Ya, itu berhasil dengan "menerima terlalu banyak"; tidak semua yang diterimanya adalah program C ++ yang valid, oleh karena itu diikuti dengan pemeriksaan tambahan (pemeriksaan jenis). Dan ya, pemeriksa tipe mungkin mengalami masalah komputasi. Dalam praktiknya alat tidak memiliki masalah ini; jika orang menulis program seperti itu, tidak ada yang mau dikompilasi. (Saya pikir standar sebenarnya membatasi jumlah perhitungan yang dapat Anda lakukan dengan membuka kerangka, jadi sebenarnya perhitungannya sebenarnya terbatas tetapi mungkin cukup besar).

Jika yang Anda maksud adalah, tentukan apakah program sumber adalah anggota dari set program sumber C ++ yang valid , maka saya akan setuju bahwa masalahnya jauh lebih sulit. Tapi itu tidak parsing itu masalahnya.

Alat ini mengatasi masalah ini dengan mengisolasi parsing dari jenis-memeriksa program parsing. (Di mana ada beberapa interpretasi dengan tidak adanya konteks, ia mencatat simpul ambiguitas di pohon parse dengan beberapa kemungkinan parse; pengecekan tipe memutuskan mana yang benar dan menghilangkan sub pohon yang tidak valid). Anda dapat melihat pohon parse (sebagian) dalam contoh di bawah ini; keseluruhan pohon terlalu besar untuk dapat dimasukkan dalam jawaban SO. Catatan Anda mendapatkan pohon parse apakah nilai 234797 atau 234799 digunakan.

Menjalankan nama / jenis penyelesai alat di atas AST dengan nilai asli 234799 berhasil. Dengan nilai 234797 pemecah nama gagal (seperti yang diharapkan) dengan pesan kesalahan, "typen bukan tipe." dan dengan demikian versi itu bukan program C ++ yang valid.

967 tree nodes in tree.
15 ambiguity nodes in tree.
(translation_unit@Cpp~GCC5=2#6b11a20^0 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
 (declaration_seq@Cpp~GCC5=1021#6b06640^1#6b11a20:1 {10} Line 1 Column 1 File C:/temp/prime_with_templates.cpp
  (pp_declaration_seq@Cpp~GCC5=1022#6b049a0^1#6b06640:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b04980^1#6b049a0:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b04960^1#6b04980:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2082#6afbde0^1#6b04960:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter@Cpp~GCC5=2085#6afbd80^1#6afbde0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6afbd40^1#6afbd80:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6afb880^1#6afbd40:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6afb840^1#6afb880:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6afb7e0^1#6afb840:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1138#6afb7a0^1#6afb7e0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6afb7e0
   |   | )decl_specifier#6afb840
   |   |)basic_decl_specifier_seq#6afb880
   |   |(ptr_declarator@Cpp~GCC5=1417#6afbc40^1#6afbd40:2 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6afbba0^1#6afbc40:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6afbb80^1#6afbba0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6afbaa0^1#6afbb80:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6afb9c0^1#6afbaa0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afb780^1#6afb9c0:1[`V'] Line 1 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6afb9c0
   |   |   )id_expression#6afbaa0
   |   |  )declarator_id#6afbb80
   |   | )noptr_declarator#6afbba0
   |   |)ptr_declarator#6afbc40
   |   )parameter_declaration#6afbd40
   |  )template_parameter#6afbd80
   | )template_parameter_list#6afbde0
   | (declaration@Cpp~GCC5=1033#6b04940^1#6b04960:2 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |  (block_declaration@Cpp~GCC5=1050#6b04920^1#6b04940:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   (simple_declaration@Cpp~GCC5=1060#6b04900^1#6b04920:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b048e0^1#6b04900:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b048c0^1#6b048e0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |  (type_specifier@Cpp~GCC5=1110#6b048a0^1#6b048c0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   (class_specifier@Cpp~GCC5=1761#6b04880^1#6b048a0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   |(class_head@Cpp~GCC5=1763#6afb980^1#6b04880:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   | (class_key@Cpp~GCC5=1791#6afbca0^1#6afb980:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp)class_key
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afbcc0^1#6afb980:2[`answer'] Line 1 Column 25 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | (optional_base_clause@Cpp~GCC5=1872#6afba60^1#6afb980:3 Line 1 Column 32 File C:/temp/prime_with_templates.cpp)optional_base_clause
   |   |   |)class_head#6afb980
   |   |   |(member_specification@Cpp~GCC5=1794#6b042e0^1#6b04880:2 {2} Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b04060^1#6b042e0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04040^1#6b04060:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04020^1#6b04040:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1673#6afbec0^1#6b04020:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6afbfe0^1#6afbec0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6afbf80^1#6afbfe0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6afbf60^1#6afbf80:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6afbea0^1#6afbf60:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6afbb40^1#6afbea0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6afbc80^1#6afbb40:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6afbc20^1#6afbc80:1[`answer'] Line 1 Column 34 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |  )unqualified_id#6afbc80
   |   |   |   |   | )id_expression#6afbb40
   |   |   |   |   |)declarator_id#6afbea0
   |   |   |   |   )noptr_declarator#6afbf60
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1559#6afbd00^1#6afbf80:2 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(pp_parameter_declaration_list@Cpp~GCC5=1570#6afb940^1#6afbd00:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pp_parameter_declaration_seq@Cpp~GCC5=1574#6afb800^1#6afb940:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (parameter_declaration@Cpp~GCC5=1610#6afb9a0^1#6afb800:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6afbf40^1#6afb9a0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(decl_specifier@Cpp~GCC5=1073#6afbfa0^1#6afbf40:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6afbfc0^1#6afbfa0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (simple_type_specifier@Cpp~GCC5=1140#6afb860^1#6afbfc0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   |   | )trailing_type_specifier#6afbfc0
   |   |   |   |   |   |)decl_specifier#6afbfa0
   |   |   |   |   |   )basic_decl_specifier_seq#6afbf40
   |   |   |   |   |  )parameter_declaration#6afb9a0
   |   |   |   |   | )pp_parameter_declaration_seq#6afb800
   |   |   |   |   |)pp_parameter_declaration_list#6afb940
   |   |   |   |   )parameter_declaration_clause#6afbd00
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6afbce0^1#6afbf80:3 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6afbf80
   |   |   |   | )ptr_declarator#6afbfe0
   |   |   |   |)function_head#6afbec0
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04000^1#6b04020:2 Line 1 Column 46 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=888#6afbee0^1#6b04000:1 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)compound_statement
   |   |   |   |)function_body#6b04000
   |   |   |   )function_definition#6b04020
   |   |   |  )member_declaration#6b04040
   |   |   | )member_declaration_or_access_specifier#6b04060
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b042c0^1#6b042e0:2 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04820^1#6b042c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04280^1#6b04820:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1674#6b04220^1#6b04280:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b040e0^1#6b04220:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (decl_specifier@Cpp~GCC5=1073#6b040c0^1#6b040e0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b040a0^1#6b040c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(simple_type_specifier@Cpp~GCC5=1138#6b04080^1#6b040a0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   )trailing_type_specifier#6b040a0
   |   |   |   |  )decl_specifier#6b040c0
   |   |   |   | )basic_decl_specifier_seq#6b040e0
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6b04200^1#6b04220:2 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6b041e0^1#6b04200:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6b041a0^1#6b041e0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6b04180^1#6b041a0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b04160^1#6b04180:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=320#6b04140^1#6b04160:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (operator_function_id@Cpp~GCC5=2027#6b04120^1#6b04140:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(operator@Cpp~GCC5=2070#6b04100^1#6b04120:1 Line 1 Column 62 File C:/temp/prime_with_templates.cpp)operator
   |   |   |   |   |   )operator_function_id#6b04120
   |   |   |   |   |  )unqualified_id#6b04140
   |   |   |   |   | )id_expression#6b04160
   |   |   |   |   |)declarator_id#6b04180
   |   |   |   |   )noptr_declarator#6b041a0
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1558#6afba40^1#6b041e0:2 Line 1 Column 65 File C:/temp/prime_with_templates.cpp)parameter_declaration_clause
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6b041c0^1#6b041e0:3 Line 1 Column 66 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6b041e0
   |   |   |   | )ptr_declarator#6b04200
   |   |   |   |)function_head#6b04220
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04300^1#6b04280:2 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=889#6b04760^1#6b04300:1 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (pp_statement_seq@Cpp~GCC5=894#6b04780^1#6b04760:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (statement@Cpp~GCC5=857#6b04440^1#6b04780:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(jump_statement@Cpp~GCC5=1011#6afba80^1#6b04440:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pm_expression@Cpp~GCC5=551#6b04380^1#6afba80:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (cast_expression@Cpp~GCC5=543#6b04360^1#6b04380:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (unary_expression@Cpp~GCC5=465#6b04340^1#6b04360:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(primary_expression@Cpp~GCC5=307#6b04320^1#6b04340:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b042a0^1#6b04320:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04260^1#6b042a0:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04240^1#6b04260:1[`V'] Line 1 Column 74 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |   |  )unqualified_id#6b04260
   |   |   |   |   |   | )id_expression#6b042a0
   |   |   |   |   |   |)primary_expression#6b04320
   |   |   |   |   |   )unary_expression#6b04340
   |   |   |   |   |  )cast_expression#6b04360
   |   |   |   |   | )pm_expression#6b04380
   |   |   |   |   |)jump_statement#6afba80
   |   |   |   |   )statement#6b04440
   |   |   |   |  )pp_statement_seq#6b04780
   |   |   |   | )compound_statement#6b04760
   |   |   |   |)function_body#6b04300
   |   |   |   )function_definition#6b04280
   |   |   |  )member_declaration#6b04820
   |   |   | )member_declaration_or_access_specifier#6b042c0
   |   |   |)member_specification#6b042e0
   |   |   )class_specifier#6b04880
   |   |  )type_specifier#6b048a0
   |   | )decl_specifier#6b048c0
   |   |)basic_decl_specifier_seq#6b048e0
   |   )simple_declaration#6b04900
   |  )block_declaration#6b04920
   | )declaration#6b04940
   |)template_declaration#6b04960
   )declaration#6b04980
  )pp_declaration_seq#6b049a0
  (pp_declaration_seq@Cpp~GCC5=1022#6b06620^1#6b06640:2 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b06600^1#6b06620:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b065e0^1#6b06600:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2083#6b05460^1#6b065e0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter_list@Cpp~GCC5=2083#6b05140^1#6b05460:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   (template_parameter_list@Cpp~GCC5=2083#6b04ee0^1#6b05140:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(template_parameter_list@Cpp~GCC5=2082#6b04cc0^1#6b04ee0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (template_parameter@Cpp~GCC5=2085#6b04ca0^1#6b04cc0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (parameter_declaration@Cpp~GCC5=1611#6b04c80^1#6b04ca0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04a40^1#6b04c80:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |(decl_specifier@Cpp~GCC5=1073#6b04a20^1#6b04a40:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6b04a00^1#6b04a20:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |  (simple_type_specifier@Cpp~GCC5=1138#6b049e0^1#6b04a00:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   | )trailing_type_specifier#6b04a00
   |   |   |)decl_specifier#6b04a20
   |   |   )basic_decl_specifier_seq#6b04a40
   |   |   (ptr_declarator@Cpp~GCC5=1417#6b04c40^1#6b04c80:2 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(noptr_declarator@Cpp~GCC5=1421#6b04be0^1#6b04c40:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (declarator_id@Cpp~GCC5=1487#6b04bc0^1#6b04be0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |  (id_expression@Cpp~GCC5=317#6b04b60^1#6b04bc0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   (unqualified_id@Cpp~GCC5=319#6b04ac0^1#6b04b60:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   |(IDENTIFIER@Cpp~GCC5=3368#6b049c0^1#6b04ac0:1[`no'] Line 3 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   )unqualified_id#6b04ac0
   |   |   |  )id_expression#6b04b60
   |   |   | )declarator_id#6b04bc0
   |   |   |)noptr_declarator#6b04be0
   |   |   )ptr_declarator#6b04c40
   |   |  )parameter_declaration#6b04c80
   |   | )template_parameter#6b04ca0
   |   |)template_parameter_list#6b04cc0
   |   |(template_parameter@Cpp~GCC5=2085#6b04ec0^1#6b04ee0:2 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   | (parameter_declaration@Cpp~GCC5=1611#6b04ea0^1#6b04ec0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |  (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04b40^1#6b04ea0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   (decl_specifier@Cpp~GCC5=1073#6b04ba0^1#6b04b40:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   |(trailing_type_specifier@Cpp~GCC5=1118#6b04c60^1#6b04ba0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   | (simple_type_specifier@Cpp~GCC5=1138#6b04580^1#6b04c60:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |)trailing_type_specifier#6b04c60
   |   |   )decl_specifier#6b04ba0
   |   |  )basic_decl_specifier_seq#6b04b40
   |   |  (ptr_declarator@Cpp~GCC5=1417#6b04e60^1#6b04ea0:2 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   (noptr_declarator@Cpp~GCC5=1421#6b04e40^1#6b04e60:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |(declarator_id@Cpp~GCC5=1487#6b04de0^1#6b04e40:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   | (id_expression@Cpp~GCC5=317#6b04d80^1#6b04de0:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04ce0^1#6b04d80:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04560^1#6b04ce0:1[`yes'] Line 3 Column 24 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |  )unqualified_id#6b04ce0
   |   |   | )id_expression#6b04d80
   |   |   |)declarator_id#6b04de0
   |   |   )noptr_declarator#6b04e40
   |   |  )ptr_declarator#6b04e60
   |   | )parameter_declaration#6b04ea0
   |   |)template_parameter#6b04ec0
   |   )template_parameter_list#6b04ee0
   |   (template_parameter@Cpp~GCC5=2085#6b05120^1#6b05140:2 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |(parameter_declaration@Cpp~GCC5=1611#6b05100^1#6b05120:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04d20^1#6b05100:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |  (decl_specifier@Cpp~GCC5=1073#6b04dc0^1#6b04d20:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b04e80^1#6b04dc0:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   |(simple_type_specifier@Cpp~GCC5=1140#6b046e0^1#6b04e80:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   )trailing_type_specifier#6b04e80
   |   |  )decl_specifier#6b04dc0
   |   | )basic_decl_specifier_seq#6b04d20
   |   | (ptr_declarator@Cpp~GCC5=1417#6b05080^1#6b05100:2 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |  (noptr_declarator@Cpp~GCC5=1421#6b05020^1#6b05080:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   (declarator_id@Cpp~GCC5=1487#6b05000^1#6b05020:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |(id_expression@Cpp~GCC5=317#6b04fa0^1#6b05000:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   | (unqualified_id@Cpp~GCC5=319#6b04f00^1#6b04fa0:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |  (IDENTIFIER@Cpp~GCC5=3368#6b046c0^1#6b04f00:1[`f'] Line 3 Column 33 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | )unqualified_id#6b04f00
   |   |   |)id_expression#6b04fa0
   |   |   )declarator_id#6b05000
   |   |  )noptr_declarator#6b05020
   |   | )ptr_declarator#6b05080
   |   |)parameter_declaration#6b05100
   |   )template_parameter#6b05120
   |  )template_parameter_list#6b05140
   |  (template_parameter@Cpp~GCC5=2085#6b05440^1#6b05460:2 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6b05420^1#6b05440:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b05160^1#6b05420:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b04fe0^1#6b05160:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6b050e0^1#6b04fe0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1140#6b050c0^1#6b050e0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6b050e0
   |   | )decl_specifier#6b04fe0
   |   |)basic_decl_specifier_seq#6b05160
   |   |(ptr_declarator@Cpp~GCC5=1417#6b053e0^1#6b05420:2 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6b053c0^1#6b053e0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6b05360^1#6b053c0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6b05280^1#6b05360:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6b051a0^1#6b05280:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6b046a0^1#6b051a0:1[`p'] Line 3 Column 40 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6b051a0
   |   |   )id_expression#6b05280
   |   |  )declarator_id#6b05360
   |   | )noptr_declarator#6b053c0
   |   |)ptr_declarator#6b053e0
   |   )parameter_declaration#6b05420
   |  )template_parameter#6b05440
   | )template_parameter_list#6b05460
Ira Baxter
sumber
Menentukan apakah itu deklarasi variabel atau perkalian bukan fitur pemeriksaan tipe. Juga saya harus menggosok jawaban Anda tentang hal-hal promosi diri ... lagi.
Anak anjing
@Puppy: Anda bisa mengatakan apa yang Anda suka, tapi begitulah cara kerjanya. Menghapus nama alat mungkin hanya akan membuat orang bertanya apa nama alat itu.
Ira Baxter
Apakah itu cara alat bekerja atau tidak tidak relevan, karena pertanyaannya tidak menanyakan cara kerja alat tersebut. Juga, saya pikir kita bisa dengan aman menunggu itu benar-benar terjadi.
Anak anjing