Czy C ++ jest bezkontekstowy lub wrażliwy na kontekst?


405

Często słyszę twierdzenia, że ​​C ++ jest językiem kontekstowym. Weź następujący przykład:

a b(c);

Czy to jest definicja zmiennej lub deklaracja funkcji? To zależy od znaczenia symbolu c. Jeśli cjest zmienną , to a b(c);definiuje zmienną o nazwie btyp a. Jest bezpośrednio inicjowany c. Ale jeśli cjest typem , to a b(c);deklaruje funkcję o nazwie, bktóra bierze a ci zwraca an a.

Jeśli spojrzysz na definicję języków bezkontekstowych, po prostu powie ci, że wszystkie reguły gramatyczne muszą mieć lewe strony, które składają się z dokładnie jednego nieterminalnego symbolu. Gramatyki kontekstowe pozwalają natomiast na dowolne ciągi symboli końcowych i nieterminalnych po lewej stronie.

Przeglądając Dodatek A „The C ++ Programming Language”, nie mogłem znaleźć żadnej reguły gramatycznej, która miałaby cokolwiek innego oprócz jednego nieterminalnego symbolu po lewej stronie. Oznaczałoby to, że C ++ jest pozbawiony kontekstu. (Oczywiście każdy język bezkontekstowy jest także wrażliwy na kontekst w tym sensie, że języki bezkontekstowe stanowią podzbiór języków kontekstowych, ale nie o to chodzi).

Czy C ++ jest bezkontekstowy czy kontekstowy?


12
@CarlNorum Pokaż mi jedną regułę gramatyczną C ++, która nie składa się z jednego nieterminalnego symbolu po lewej stronie, a ja natychmiast ci uwierzę.
fredoverflow

9
IIUC zależy to nieco od tego, gdzie wytyczysz granicę wrażliwości na kontekst. Wydaje mi się, że widziałem ludzi, którzy twierdzą, że prawie wszystkie statycznie typowane języki programowania są kontekstowe, nie dlatego, że nie można zbudować dla nich praktycznego kompilatora za pomocą narzędzi parsujących CFG, ale dlatego, że takie implementacje „oszukują”, analizując niektóre nieprawidłowe programy i odrzucając je później, podczas sprawdzania typu. Więc jeśli uważasz, że źle napisane programy nie są w języku (w sensie CS, tj. Zestawie ciągów), parser powinien zaakceptować, więcej języków niż C ++ jest wrażliwych na kontekst.

6
@DeadMG: Nie, mylisz się. W formalnej teorii języka nie ma w ogóle „parsowania” ani „semantyki”, tylko „język”, który jest zbiorem ciągów.
jpalecek

27
Jak dotąd żadne odpowiedzi nie odnosiły się do twojej definicji „gramatyki bezkontekstowej”. Moim zdaniem poprawna odpowiedź na to pytanie albo przytacza treść załącznika A, która nie pasuje do twojej definicji, albo pokazuje, że twoja definicja jest niepoprawna lub niewystarczająca. Postawić na swoim!
Wyścigi lekkości na orbicie

8
Zobacz Czy gramatyka języka D jest naprawdę pozbawiona kontekstu? . Myślę, że wszyscy tutaj powinni przeczytać to pytanie i odpowiedzi!
Wyścigi lekkości na orbicie

Odpowiedzi:


341

Poniżej moja (obecna) ulubiona demonstracja, dlaczego parsowanie C ++ jest (prawdopodobnie) zakończone przez Turinga , ponieważ pokazuje program, który jest poprawny pod względem składniowym wtedy i tylko wtedy, gdy dana liczba całkowita jest liczbą pierwszą.

Twierdzę więc, że C ++ nie jest pozbawiony kontekstu ani wrażliwy na kontekst .

Jeśli zezwalasz na dowolne sekwencje symboli po obu stronach dowolnej produkcji, tworzysz gramatykę typu 0 („nieograniczoną”) w hierarchii Chomsky'ego , która jest potężniejsza niż gramatyka kontekstowa; nieograniczone gramatyki są kompletne w Turinga. Gramatyka kontekstowa (typ 1) pozwala na umieszczenie wielu symboli kontekstu po lewej stronie produkcji, ale ten sam kontekst musi pojawić się po prawej stronie produkcji (stąd nazwa „kontekstowa”). [1] Gramatyki kontekstowe są równoważne maszynom Turinga ograniczonym liniowo .

W przykładowym programie obliczenia podstawowe mogą być wykonywane przez ograniczoną liniowo maszynę Turinga, więc nie do końca dowodzi to równoważności Turinga, ale ważną częścią jest to, że parser musi wykonać obliczenia w celu przeprowadzenia analizy składniowej. Mogłoby to być dowolne obliczenie wyrażone jako instancja szablonu i istnieją wszelkie powody, by sądzić, że tworzenie instancji C ++ jest zakończone metodą Turinga. Zobacz na przykład artykuł Todda L. Veldhuizena z 2003 roku .

Niezależnie od tego, C ++ może być analizowany przez komputer, więc z pewnością może być analizowany przez maszynę Turinga. W związku z tym nieograniczona gramatyka może to rozpoznać. W rzeczywistości pisanie takiej gramatyki byłoby niepraktyczne, dlatego standard tego nie próbuje. (Patrz poniżej.)

Problem „dwuznaczności” niektórych wyrażeń dotyczy głównie czerwonego śledzia. Po pierwsze, dwuznaczność jest cechą konkretnej gramatyki, a nie języka. Nawet jeśli można udowodnić, że język nie ma jednoznacznej gramatyki, jeśli można go rozpoznać po gramatyce bezkontekstowej, jest on pozbawiony kontekstu. Podobnie, jeśli nie może być rozpoznany przez gramatykę bezkontekstową, ale może być rozpoznany przez gramatykę kontekstową, jest wrażliwy na kontekst. Dwuznaczność nie ma znaczenia.

Ale w każdym razie, tak jak wiersz 21 (tj. auto b = foo<IsPrime<234799>>::typen<1>();) W poniższym programie, wyrażenia wcale nie są dwuznaczne; są one po prostu przetwarzane w różny sposób w zależności od kontekstu. W najprostszym wyrażeniu problemu kategoria składniowa niektórych identyfikatorów zależy od tego, jak zostały zadeklarowane (na przykład typy i funkcje), co oznacza, że ​​język formalny musiałby rozpoznać fakt, że dwa ciągi o dowolnej długości w ten sam program jest identyczny (deklaracja i użycie). Można to modelować za pomocą gramatyki „kopiowania”, która jest gramatyką, która rozpoznaje dwie kolejne dokładne kopie tego samego słowa. Łatwo to udowodnić dzięki lemie pompującejże ten język nie jest pozbawiony kontekstu. Gramatyka kontekstowa dla tego języka jest możliwa, a odpowiedź na to pytanie zawiera gramatyka typu 0: /math/163830/context-sensitive-grammar-for-the- język kopii .

Gdyby ktoś spróbował napisać gramatykę kontekstową (lub nieograniczoną) w celu analizy C ++, całkiem możliwe, że wypełniłby wszechświat bazgrołami. Napisanie maszyny Turinga do analizy C ++ byłoby równie niemożliwym przedsięwzięciem. Nawet napisanie programu w C ++ jest trudne i, o ile wiem, żaden nie został sprawdzony. Dlatego standard nie zapewnia pełnej formalnej gramatyki i dlatego postanawia napisać niektóre zasady analizy w języku angielskim technicznym.

To, co wygląda na formalną gramatykę w standardzie C ++, nie jest pełną formalną definicją składni języka C ++. Nie jest to nawet pełna formalna definicja języka po wstępnym przetwarzaniu, którą łatwiej sformalizować. (Nie byłby to jednak język: język C ++ zdefiniowany w standardzie obejmuje preprocesor, a działanie preprocesora jest opisane algorytmicznie, ponieważ niezwykle trudno byłoby opisać go w jakimkolwiek formalizmie gramatycznym. To w tej sekcji normy, w której opisany jest rozkład leksykalny, w tym zasady, w których należy go stosować więcej niż jeden raz).

Różne gramatyki (dwie nakładające się gramatyki do analizy leksykalnej, jedna, która ma miejsce przed przetwarzaniem wstępnym, a druga, jeśli to konieczne, później, wraz z gramatyką „składniową”) są zebrane w załączniku A, z tą ważną uwagą (podkreślenie dodane):

To podsumowanie składni C ++ ma na celu pomóc w zrozumieniu. To nie jest dokładne określenie języka . W szczególności opisana tutaj gramatyka akceptuje nadzbiór prawidłowych konstrukcji C ++ . W celu odróżnienia wyrażeń od deklaracji należy zastosować reguły ujednoznacznienia (6.8, 7.1, 10.2). Ponadto należy zastosować kontrolę dostępu, niejednoznaczność i reguły typów, aby wyeliminować konstrukcyjnie poprawne, ale bezsensowne konstrukcje.

Wreszcie, oto obiecany program. Linia 21 jest poprawna pod względem składniowym wtedy i tylko wtedy, gdy N IsPrime<N>jest liczbą pierwszą. W przeciwnym razie typenjest liczbą całkowitą, a nie szablonem, dlatego typen<1>()jest analizowana jako (typen<1)>()niepoprawna pod względem składniowym, ponieważ ()nie jest wyrażeniem poprawnym pod względem składniowym.

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] Mówiąc bardziej technicznie, każda produkcja w gramatyce kontekstowej musi mieć postać:

αAβ → αγβ

gdzie Ajest nie-końcowy i α, βprawdopodobnie są pustymi sekwencjami symboli gramatycznych, i γjest niepustą sekwencją. (Symbole gramatyczne mogą być terminalami lub nie-terminalami).

Można to odczytać A → γtylko w kontekście [α, β]. W gramatyce bezkontekstowej (typ 2) αi βmusi być pusta.

Okazuje się, że można również ograniczyć gramatykę z ograniczeniem „monotonicznym”, gdzie każda produkcja musi mieć formę:

α → βgdzie |α| ≥ |β| > 0  ( |α|oznacza „długość α”)

Można udowodnić, że zestaw języków rozpoznawanych przez gramatyki monotoniczne jest dokładnie taki sam jak zestaw języków rozpoznawanych przez gramatyki kontekstowe i często zdarza się, że łatwiej jest oprzeć dowody na gramatyce monotonicznej. W związku z tym dość powszechne jest używanie słowa „kontekstowego” tak, jakby oznaczało to „monotoniczny”.


27
Jest więc nie tylko zależny od kontekstu, ale można uzależnić go od dowolnego kontekstu, który można wyrazić w szablonach, które są kompletne w Turinga.
Puppy

7
@mehrdad, OP mówi „język kontekstowy”, a nie gramatyka kontekstowa. Dwuznaczność jest cechą gramatyki, a nie języka. Język jest rzeczywiście zależny od kontekstu, ale nie dlatego, że konkretna gramatyka jest niejednoznaczna.
rici

2
Zauważ, że mój przykład nie jest dwuznaczny. Jest to jednoznaczne wyrażenie poprawnego programu. Jeśli zmienisz wartość w wierszu 21, może ona stać się źle sformułowana. Ale w żadnym przypadku nie jest to dwuznaczne.
rici

5
Mam jedną wątpliwość: jak widać, wynik oceny szablonu może zrobić różnicę między dobrze uformowanym a źle sformułowanym programem. Ocena szablonu jest kompletna. Czy więc prawidłowe ustalenie, czy napis jest w języku (C ++), nie wymagałoby kompletności turinga? Jak mówisz, język kontekstowy jest „tylko” „automatem ograniczonym liniowo”, co nie jest całkowitym AFAIK-em. A może twój argument wykorzystuje ograniczenia, jakie standard C ++ nakłada na pewne rzeczy, w tym na głębokość oceny szablonu?

4
@AntonGolov: Moja oryginalna wersja tego przykładu właśnie to zrobiła (możesz to zrobić, umieszczając 0w niej (), na przykład), ale myślę, że jest to bardziej interesujące w ten sposób, ponieważ pokazuje, że potrzebujesz instancji szablonu nawet, aby rozpoznać, czy ciąg jest poprawnym składniowo programem C ++. Jeśli obie gałęzie się kompilują, musiałbym ciężko pracować, aby zakwestionować argument, że różnica jest „semantyczna”. Co ciekawe, chociaż często mam trudności z określeniem „składniowy”, nikt nigdy nie zaproponował definicji „semantycznej” innej niż „rzeczy, które nie uważam za składniowe” :)
rici 30.01.2013

115

Po pierwsze, słusznie zauważyłeś, że w gramatyce na końcu standardu C ++ nie ma reguł kontekstowych, więc gramatyka jest pozbawiona kontekstu.

Jednak ta gramatyka nie opisuje dokładnie języka C ++, ponieważ produkuje programy inne niż C ++, takie jak

int m() { m++; }

lub

typedef static int int;

Język C ++ zdefiniowany jako „zestaw dobrze sformułowanych programów C ++” nie jest pozbawiony kontekstu (można wykazać, że powoduje to jedynie deklaracja zmiennych wymagających). Biorąc pod uwagę, że możesz teoretycznie pisać programy kompletne Turinga w szablonach i źle programować na podstawie ich wyników, nie jest on nawet zależny od kontekstu.

Teraz (nieświadomi) ludzie (zwykle nie teoretycy języków, ale projektanci parserów) zazwyczaj używają słowa „bez kontekstu” w niektórych z następujących znaczeń

  • dwuznaczny
  • nie można go przeanalizować z Bizonem
  • nie LL (k), LR (k), LALR (k) lub jakąkolwiek inną klasę językową zdefiniowaną przez parser

Gramatyka z tyłu standardu nie spełnia tych kategorii (tzn. Jest dwuznaczna, nie LL (k) ...), więc gramatyka C ++ jest dla nich „bezkontekstowa”. W pewnym sensie mają rację, że cholernie trudno jest stworzyć działający parser C ++.

Zauważ, że użyte tutaj właściwości są tylko słabo powiązane z językami bezkontekstowymi - dwuznaczność nie ma nic wspólnego z wrażliwością na kontekst (w rzeczywistości reguły kontekstowe zwykle pomagają ujednoznacznić produkcje), pozostałe dwa są jedynie podzbiorami kontekstu -bezpłatne języki. A parsowanie języków bezkontekstowych nie jest procesem liniowym (chociaż parsowanie deterministycznych jest).


7
ambiguity doesn't have anything to do with context-sensitivityTo też była moja intuicja, więc cieszę się, że ktoś (a) zgadza się i (b) wyjaśnia to tam, gdzie nie mogę. Uważam, że dyskwalifikuje to wszystkie argumenty, które są oparte a b(c);, i częściowo odpowiada pierwotnemu pytaniu, którego przesłanką były „często słyszane” twierdzenia o wrażliwości kontekstu wynikającej z niejednoznaczności ... szczególnie, jeśli chodzi o gramatykę , nawet w przypadku MVP.
Wyścigi lekkości na orbicie

6
@KonradRudolph: Standard mówi, że „istnieje wielkość zdefiniowana w implementacji, która określa limit całkowitej głębokości instancji rekurencyjnych, który może obejmować więcej niż jeden szablon. Wynik nieskończonej rekurencji w tworzeniu instancji jest nieokreślony”. (14.7.1p15) Rozumiem, że oznacza to, że implementacja nie jest wymagana do zrozumienia każdego ważnego programu c ++, a nie że programy o zbyt dużej głębokości rekurencji są nieprawidłowe. Jedyne oznaczone jako nieprawidłowe to te o nieskończonej głębokości rekurencji.
rici

3
@KonradRudolph: Nie zgadzam się z tym, że jest to „odniesienie ogólne”. Fakt, że przeczytałem ten dość złożony artykuł i nie rozumiem go w wystarczającym stopniu, aby wyjaśnić ten mały fakt, powinien wystarczyć do wykazania tego. To nie tak, że powiedziałeś coś w stylu „komputery zwykle używają energii elektrycznej” lub „bity mogą być prawdziwe lub fałszywe”.
Wyścigi lekkości na orbicie

3
Jeśli ten fakt jest tak powszechnie znany, pomyślałbym, że o wiele łatwiej byłoby znaleźć odniesienie do niego niż spierać się o to, czy należy go podać. Nie wspominając o konstruktywności.
Samuel Edwin Ward

5
O ile wiem, @Konrad pomylił się, gdy powiedział: „Wrażliwość kontekstowa jest równoważna Turingowi kompletnemu”. (przynajmniej tak było, jeśli oznaczał „Rekurencyjnie wyliczalny” przez „Turing zakończony”) i od tego czasu nie był w stanie rozpoznać tego błędu. Oto odniesienie do właściwej relacji włączenia zestawu zaangażowanej tutaj: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix

61

Tak. Poniższe wyrażenie ma inną kolejność operacji w zależności od kontekstu rozpoznanego typu :

Edycja: Gdy rzeczywista kolejność operacji jest różna, niezwykle trudno jest używać „zwykłego” kompilatora, który przed dekorowaniem analizuje nieudekowaną AST (informacje o typie propagacji). Inne wymienione kontekstowe rzeczy są „raczej łatwe” w porównaniu z tym (nie to, że ocena szablonu jest wcale łatwa).

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

Śledzony przez:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );

Dlaczego nie można rozwiązać tego problemu, tak jak w przypadku C, pamiętając, które definicje typów są objęte zakresem?
Blaisorblade,

1
@ Blaisorblade: Jednym ze sposobów, aby kompilator był „czysty”, jest rozdzielenie zadań na niezależne kroki w łańcuchu, takie jak utworzenie drzewa analizy z danych wejściowych, a następnie kroku analizującego typ. C ++ zmusza cię do: 1) scalenia tych kroków w jeden lub 2) parsowania dokumentu zgodnie z obiema / wszystkimi możliwymi interpretacjami i umożliwienia stopniom rozpoznawania typów zawężenia go do poprawnej interpretacji.
Sam Harwell

@ 280Z28: zgodził się, ale dotyczy to również C; Myślę, że dobra odpowiedź na to pytanie powinna pokazać, dlaczego C ++ jest gorszy od C. Teza doktora, którą tu link, robi to: stackoverflow.com/a/243447/53974
Blaisorblade

26

Aby odpowiedzieć na twoje pytanie, musisz rozróżnić dwa różne pytania.

  1. Sama składnia prawie każdego języka programowania jest pozbawiona kontekstu. Zazwyczaj podaje się go w postaci rozszerzonej postaci Backus-Naur lub gramatyki bezkontekstowej.

  2. Jednak nawet jeśli program jest zgodny z bezkontekstową gramatyką zdefiniowaną przez język programowania, niekoniecznie jest to poprawny program. Istnieje wiele bezkontekstowych właściwości, które program musi spełnić, aby być poprawnym programem. Np. Najprostszą taką właściwością jest zakres zmiennych.

Podsumowując, kwestia, czy C ++ jest pozbawiony kontekstu, zależy od zadanego pytania.


5
Warto zauważyć, że często trzeba ustawić poziom „samej składni” niżej, niż można się spodziewać, aby uzyskać CFG dla języka programowania. Weźmy na przykład C. Możesz pomyśleć, że reguła gramatyczna dla prostej deklaracji zmiennej w C byłaby VARDECL : TYPENAME IDENTIFIER, ale nie możesz tego mieć, ponieważ nie możesz odróżnić nazw typów od innych identyfikatorów na poziomie CF. Kolejny przykład: na poziomie CF nie można zdecydować, czy parsować a*bjako deklarację zmiennej ( bwskaźnika typu do a), czy jako mnożenie.
LaC

2
@LaC: Tak, dziękuję za zwrócenie na to uwagi! Nawiasem mówiąc, jestem pewien, że istnieje bardziej powszechnie używany termin techniczny na samą składnię . Czy ktoś ma poprawny termin?
Dan

4
@ Dan: chodzi tu o przybliżenie języka podanego przez gramatykę bezkontekstową. Oczywiście takie przybliżenie nie jest z definicji pozbawione kontekstu. W tym sensie często używa się „składni” podczas omawiania języków programowania.
reinierpost

13

Możesz rzucić okiem na The Design & Evolution of C ++ autorstwa Bjarne Stroustrup. W nim opisuje swoje problemy z próbą użycia yacc (lub podobnego) do parsowania wczesnej wersji C ++ i żałując, że nie użył rekurencyjnego zejścia.


Wow ... Dzięki. Zastanawiam się, czy naprawdę warto myśleć o użyciu czegoś potężniejszego niż CFG do parsowania jakiegokolwiek sztucznego języka.
Dervin Thunk,

Świetna książka do zrozumienia, dlaczego C ++. Polecam to i Lippman's Inside the C ++ Object Model, aby zrozumieć, jak działa C ++. Chociaż oba są nieco przestarzałe, nadal są dobrą lekturą.
Matt Price

„Meta-S” to kontekstowy silnik parsujący Quinna Tylera Jacksona. Nie użyłem tego, ale opowiada imponującą historię. Sprawdź jego komentarze w comp.compilers i zobacz rnaparse.com/MetaS%20defined.htm
Ira Baxter

@IraBaxter: Twój x-ref jest dzisiaj MIA - a solidne odniesienia do oprogramowania wydają się nieuchwytne (wyszukiwarka Google nie zapewnia żadnych dobrych potencjalnych klientów, zarówno z „site: rnaparse.com meta-s”, jak i „quinn jackson meta- s '; są fragmenty, ale meta-s.com prowadzi na przykład do nieinformacyjnej strony internetowej).
Jonathan Leffler,

@Jathanathan: już dawno zauważyłem twoją skargę. Nie wiem, dlaczego link jest zły, pomyślałem, że to dobrze, kiedy to napisałem. Quinn był bardzo aktywny w kompilatorach komputerowych. Google wydaje się być niestabilny, to wszystko, co mogę znaleźć: groups.google.com/group/comp.compilers/browse_thread/thread/... IIRC, podpisał prawa do MetaS do jakiegoś stroju na Hawajach w celu ponownej sprzedaży. Biorąc pod uwagę, jak było to dziwne ze względów technicznych, IMHO podpisuje wyrok śmierci. Brzmiało to jak bardzo sprytny schemat.
Ira Baxter,

12

Tak, C ++ jest wrażliwy na kontekst, bardzo wrażliwy na kontekst. Nie można zbudować drzewa składniowego, po prostu analizując plik za pomocą parsera bez kontekstu, ponieważ w niektórych przypadkach musisz znać symbol z wcześniejszej wiedzy (np. Budować tablicę symboli podczas analizowania).

Pierwszy przykład:

A*B;

Czy to jest wyrażenie mnożenia?

LUB

Czy to deklaracja Bzmiennej ma być wskaźnikiem typu A?

Jeśli A jest zmienną, to jest to wyrażenie, jeśli A jest typem, jest to deklaracja wskaźnika.

Drugi przykład:

A B(bar);

Czy jest to prototyp funkcji przyjmujący argument bartypu?

LUB

Czy to deklaruje zmienną Btypu Ai wywołuje konstruktor A ze barstałą jako inicjalizatorem?

Musisz ponownie wiedzieć, czy barjest to zmienna, czy typ z tabeli symboli.

Trzeci przykład:

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

Jest tak w przypadku, gdy budowanie tabeli symboli podczas analizowania nie pomaga, ponieważ deklaracja xiy występuje po definicji funkcji. Musisz więc najpierw przejrzeć definicję klasy i spojrzeć na definicje metod w drugim przebiegu, aby stwierdzić, że x * y jest wyrażeniem, a nie deklaracją wskaźnika ani nic takiego.


1
A B();jest deklaracją funkcji nawet w definicji funkcji. Poszukaj najbardziej dokuczliwej analizy ...
AProgrammer

„Nie można zbudować drzewa składni, po prostu analizując plik” FALSE. Zobacz moją odpowiedź.
Ira Baxter,

10

C ++ jest analizowany z analizatorem składni GLR. Oznacza to, że podczas analizowania kodu źródłowego analizator składni może napotykać dwuznaczność, ale powinien kontynuować i zdecydować, której reguły gramatycznej użyć później .

patrz również

Dlaczego C ++ nie może zostać przeanalizowany za pomocą analizatora składni LR (1)?


Pamiętaj, że gramatyka bezkontekstowa nie może opisać WSZYSTKICH reguł składni języka programowania. Na przykład gramatyka atrybutów służy do sprawdzania poprawności typu wyrażenia.

int x;
x = 9 + 1.0;

Nie można opisać poniższej reguły gramatyką bezkontekstową: Prawa strona zadania powinna być tego samego typu co lewa strona.


4
Większość parserów C ++ nie korzysta z technologii parsowania GLR. GCC nie. Niektórzy. Zobacz semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html dla takiego, który to robi.
Ira Baxter,

10

Mam wrażenie, że istnieje pewne zamieszanie między formalną definicją „wrażliwego na kontekst” a nieformalnym użyciem „wrażliwego na kontekst”. Ten pierwszy ma dobrze zdefiniowane znaczenie. Ten ostatni służy do powiedzenia „potrzebujesz kontekstu, aby przeanalizować dane wejściowe”.

Jest to również zadawane tutaj: Czułość kontekstu a niejednoznaczność .

Oto gramatyka bezkontekstowa:

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

Jest niejednoznaczny, więc aby przeanalizować dane wejściowe „x”, potrzebujesz jakiegoś kontekstu (lub żyj w niejednoznaczności lub wyślij „Ostrzeżenie: E8271 - Dane wejściowe są niejednoznaczne w wierszu 115”). Ale z pewnością nie jest to gramatyka kontekstowa.


W jaki sposób posiadanie wielu symboli po lewej stronie produkcji rozwiązuje ten problem? Nie sądzę, że ta odpowiedź jest odpowiedzią na pytanie.
user541686,

1
Moja odpowiedź jest odpowiedzią na pierwsze zdanie: „Często słyszę twierdzenia, że ​​C ++ jest językiem kontekstowym”. Jeśli twierdzenia te używają wyrażenia „kontekstowe” nieformalnie, nie ma problemu. Nie sądzę, że C ++ jest formalnie kontekstowy.
Omri Barel

Myślę, że C ++ formalnie jest wrażliwe na kontekst, ale mam problem z tym, że nie rozumiem, w jaki sposób gramatyka wrażliwa na kontekst odniosłaby większy sukces w analizowaniu C ++ niż CFG.
user541686,

6

Żaden język podobny do Algolu nie jest pozbawiony kontekstu, ponieważ mają reguły ograniczające wyrażenia i instrukcje, w których identyfikatory mogą pojawiać się na podstawie ich typu, oraz ponieważ nie ma ograniczenia liczby instrukcji, które mogą wystąpić między deklaracją a użyciem.

Zwykłym rozwiązaniem jest napisanie bez kontekstowego analizatora składni, który akceptuje nadzbiór prawidłowych programów i umieszcza części kontekstowe w trybie ad hoc „semantycznym” kodzie dołączonym do reguł.

C ++ znacznie wykracza poza to dzięki systemowi szablonów Turing-complete. Zobacz pytanie o przepełnieniu stosu 794015 .




5

Jest wrażliwy na kontekst, podobnie jak a b(c);dwie poprawne deklaracje parsowania i zmienna. Kiedy mówisz „If cis a type”, to właśnie tam jest kontekst i dokładnie opisałeś, jak C ++ jest na niego wrażliwy. Jeśli nie masz takiego kontekstu „What isc ?” nie można tego jednoznacznie przeanalizować.

Tutaj kontekst wyraża się w wyborze tokenów - parser odczytuje identyfikator jako token nazwy typu, jeśli nazywa typ. Jest to najprostsza rozdzielczość i pozwala uniknąć dużej złożoności bycia kontekstowym (w tym przypadku).

Edycja: Oczywiście jest więcej problemów z wrażliwością na kontekst, skupiłem się tylko na tym, który pokazałeś. Szablony są do tego szczególnie nieprzyjemne.


1
Również a<b<c>>dprawda? (Twój przykład jest właściwie klasykiem z C , gdzie jest to jedyna przeszkoda w byciu bezkontekstowym.)
Kerrek SB

To chyba kwestia leksykalna. Ale z pewnością należy do tej samej kategorii, tak.
Szczeniak

2
Pytający nie pyta, w jaki sposób jest bardziej wrażliwy na kontekst niż C, tylko po to, aby pokazać, że jest wrażliwy na kontekst.
Szczeniak

Więc ... czy C ++ jest bardziej wrażliwy na kontekst niż C?
Kerrek SB

2
@DeadMG Nie sądzę, że odpowiadasz na pytanie (nie sądzę, że ja też). Jak posiadanie terminali po lewej stronie produkcji rozwiązuje ten problem?
user541686,

5

Produkcje w standardzie C ++ są napisane bezkontekstowo, ale jak wszyscy wiemy, tak naprawdę nie określają dokładnie języka. Niektóre z tego, co większość ludzi uważa za niejednoznaczne w obecnym języku, można (jak sądzę) rozwiązać jednoznacznie za pomocą gramatyki zależnej od kontekstu.

Dla najbardziej oczywisty przykład, rozważmy Najbardziej dokuczliwy Parse: int f(X);. Jeśli Xjest wartością, to definiuje się ją fjako zmienną, która zostanie zainicjalizowana X. Jeśli Xjest typem, definiuje się go fjako funkcję przyjmującą pojedynczy parametr typuX .

Patrząc na to z gramatycznego punktu widzenia, moglibyśmy zobaczyć to w następujący sposób:

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]

Oczywiście, aby być całkowicie poprawnym, musielibyśmy dodać dodatkowe „rzeczy”, aby uwzględnić możliwość interweniowania deklaracji innych typów (tj. A i B powinny tak naprawdę być „deklaracjami zawierającymi deklarację X jako ...” lub coś w tej kolejności).

To jednak wciąż różni się od typowego CSG (a przynajmniej tego, co pamiętam). Zależy to od konstruowanej tablicy symboli - części, która konkretnie rozpoznaje Xjako typ lub wartość, nie tylko jakiś rodzaj poprzedzających go instrukcji, ale właściwy typ instrukcji dla odpowiedniego symbolu / identyfikatora.

Jako taki, musiałbym zrobić coś, żeby się upewnić, ale natychmiast zgaduję, że tak naprawdę nie kwalifikuje się to jako CSG, przynajmniej tak jak zwykle używa się tego terminu.


Produkcje (bezkontekstowe) wystarczająco dobrze definiują najbardziej dokuczliwą analizę, aby można ją było przeanalizować za pomocą bez kontekstowego silnika analizy. Opóźnia to problem decydowania o tym, która z wielu interpretacji jest ważna do momentu zakończenia analizy, ale tylko ułatwia inżynierię analizatora składni i rozpoznawania nazw, ponieważ są one modułowe, a nie splątane, jak w konwencjonalnych analizatorach C ++. Zobacz AST dla najbardziej irytującej analizy: stackoverflow.com/questions/17388771/...
Ira Baxter

5

Najprostszy przypadek gramatyki bezkontekstowej polega na analizie wyrażeń zawierających szablony.

a<b<c>()

Może to być parsowane jako jedno z nich

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

Lub

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

Oba AST można jednoznacznie ocenić, badając deklarację „a” - poprzedni AST, jeśli „a” jest wzorem, lub drugi, jeśli nie.


Wierzę, że C ++ 11 nakazuje tę drugą interpretację, a ty musisz dodać pareny, aby wyrazić zgodę na tę drugą.
Joseph Garvin

1
@JosephGarvin, no. C ++ nakazuje, że <musi być nawiasiem, jeśli może być (np. Podąża za identyfikatorem, który nazywa szablon). W C ++ 11 dodano wymóg, aby >i pierwszy znak >>interpretować jako nawiasy klamrowe, jeśli takie użycie jest prawdopodobne. Wpływa to na analizowanie a<b>c>gdzie ajest szablon, ale nie ma wpływu na a<b<c>.
rici

@aaron: jak to jest prostsze niż a();(co jest albo expr.callalbo expr.type.conv)?
rici

@rici: Ups, nie zdawałem sobie sprawy, że jest asymetryczny.
Joseph Garvin

5
Opisujesz dwuznaczność lub wrażliwość na kontekst?
corazza

4

Wykazano, że szablony C ++ są potężne Turinga. Chociaż nie jest to formalne odniesienie, oto miejsce, w którym należy spojrzeć w tym względzie:

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

Zaryzykuję (tak stary jak folkoriczny i zwięzły dowód CACM pokazujący, że ALGOL w latach 60-tych nie może być ponownie reprezentowany przez CFG) i powiem, że C ++ nie może zatem zostać poprawnie przeanalizowany tylko przez CFG. CFG, w połączeniu z różnymi mechanizmami TP w przejściu drzewa lub podczas wydarzeń redukujących - to inna historia. W ogólnym sensie, z powodu problemu zatrzymania, istnieje jakiś program C ++, którego nie można pokazać jako poprawnego / niepoprawnego, ale mimo to jest poprawny / niepoprawny.

{PS- Jako autor Meta-S (wspomniany przez kilka osób powyżej) - z całą pewnością mogę powiedzieć, że Thothic nie jest ani zlikwidowany, ani oprogramowanie nie jest dostępne za darmo. Być może sformułowałem tę wersję mojej odpowiedzi w taki sposób, że nie zostałem usunięty ani nie przegłosowałem do -3.}


3

C ++ nie jest pozbawiony kontekstu. Nauczyłem się go jakiś czas temu w wykładzie na temat kompilatorów. Szybkie wyszukiwanie dało ten link, w którym sekcja „Składnia lub semantyka” wyjaśnia, dlaczego C i C ++ nie są wolne od kontekstu:

Wikipedia Dyskusja: Gramatyka bezkontekstowa

Pozdrawiam,
Ovanes


2

Oczywiście, jeśli przyjmiesz pytanie dosłownie, prawie wszystkie języki z identyfikatorami są wrażliwe na kontekst.

Trzeba wiedzieć, czy identyfikator jest nazwą typu (nazwą klasy, nazwą wprowadzoną przez typedef, parametrem szablonu nazwy typu), nazwą szablonu lub inną nazwą, aby móc poprawnie wykorzystać część identyfikatora. Na przykład:

x = (name)(expression);

jest rzutowaniem if namejest nazwą typu i wywołaniem funkcji ifname jest nazwą funkcji. Innym przypadkiem jest tak zwana „najbardziej dokuczliwa analiza”, w której nie jest możliwe rozróżnienie definicji zmiennej i deklaracji funkcji (istnieje reguła mówiąca, że ​​jest to deklaracja funkcji).

Że trudność wprowadziła potrzebę typenamei templatez nazwami zależnymi. Reszta C ++ nie jest wrażliwa na kontekst, o ile mi wiadomo (tzn. Można dla niego napisać gramatykę bezkontekstową).


2

Meta-S "to kontekstowy silnik parsujący Quinna Tylera Jacksona. Nie korzystałem z niego, ale opowiada imponującą historię. Sprawdź jego komentarze w kompilatorach comp.i zobacz rnaparse.com/MetaS%20defined.htm - Ira Baxter, 25 lipca o 10:42

Prawidłowym linkiem jest analiza maszyn

Meta-S była własnością nieistniejącej firmy o nazwie Thothic. Mogę wysłać bezpłatną kopię Meta-S każdemu zainteresowanemu i wykorzystałem ją w badaniach parsowania rna. Pamiętaj, że „gramatyka pseudoknotów” zawarta w folderach przykładów została napisana przez nie-bioinformatyka, programistę urządzeń i zasadniczo nie działa. Moje gramatyki mają inne podejście i działają całkiem dobrze.


To jest naprawdę interesujące znalezisko.
Dervin Thunk,

0

Dużym problemem jest tutaj to, że terminy „bezkontekstowy” i „wrażliwy na kontekst” są trochę nieintuicyjne w informatyce. W przypadku C ++ czułość kontekstu przypomina dwuznaczność, ale niekoniecznie jest tak w ogólnym przypadku.

W C / ++ instrukcja if jest dozwolona tylko w treści funkcji. Wydaje się, że dzięki temu jest wrażliwe na kontekst, prawda? Więc nie. Gramatyki bezkontekstowe tak naprawdę nie potrzebują właściwości, w której można wyciągnąć wiersz kodu i ustalić, czy jest poprawny. To nie jest tak naprawdę to, co oznacza kontekst. To naprawdę tylko etykieta, która niejasno implikuje coś w rodzaju powiązania z tym, jak to brzmi.

Teraz, jeśli instrukcja w ciele funkcji jest analizowana w różny sposób w zależności od czegoś zdefiniowanego poza bezpośrednimi przodkami gramatycznymi (np. Czy identyfikator opisuje typ lub zmienną), tak jak w a * b;przypadku, to w rzeczywistości jest zależna od kontekstu. Nie ma tu faktycznej dwuznaczności; zostanie sparsowany jako deklaracja wskaźnika, jeśli ajest typem, a w przeciwnym razie pomnożenie.

Bycie wrażliwym na kontekst niekoniecznie oznacza „trudny do przeanalizowania”. C nie jest tak trudne, ponieważ niesławną a * b;„dwuznaczność” można rozwiązać za pomocą tabeli symboli zawierającej typedefs napotkane wcześniej. Nie wymaga żadnych dowolnych instancji szablonów (które okazały się być Turing Complete), aby rozwiązać ten przypadek, jak C ++ czasami. W rzeczywistości nie jest możliwe napisanie programu C, który nie skompiluje się w skończonym czasie, mimo że ma taką samą wrażliwość kontekstową jak C ++.

Python (i inne języki wrażliwe na spacje) jest również zależny od kontekstu, ponieważ wymaga on stanu w leksykach do generowania tokenów wcięcia i dedentowania, ale nie jest to trudniejsze do przeanalizowania niż typowa gramatyka LL-1. W rzeczywistości używa parsera-generatora, co jest częścią tego, dlaczego Python ma takie nieinformacyjne komunikaty o błędach składniowych. Należy również zauważyć, że nie ma takiej „dwuznaczności” jaka * b; w Pythonie , co stanowi dobry konkretny przykład języka kontekstowego bez „dwuznacznej” gramatyki (jak wspomniano w pierwszym akapicie).


-4

Ta odpowiedź mówi, że C ++ nie jest pozbawiony kontekstu ... istnieje implikacja (nie przez odpowiadającego), że nie można go przeanalizować, a odpowiedź oferuje trudny przykład kodu, który produkuje nieprawidłowy program C ++, jeśli pewna stała nie jest Liczba pierwsza.

Jak zauważyli inni, pytanie, czy język jest kontekstowy / wolny, różni się od tego samego pytania o konkretną gramatykę.

Aby postawić pytanie o możliwość analizowania w celu spoczynku, przedstawiam empiryczne dowody, że dla C ++ istnieją gramatyki bezkontekstowe, których można użyć do stworzenia AST dla bez kontekstowej analizy tekstu źródłowego, w rzeczywistości analizując go z istniejącym GLR -parserowe narzędzie oparte na jawnej gramatyce.

Tak, udaje się to „akceptując zbyt wiele”; nie wszystko, co akceptuje, to poprawny program w C ++, dlatego są uzupełniane dodatkowymi kontrolami (typami). I tak, moduł sprawdzania typu może napotykać problemy z obliczalnością. W praktyce narzędzia nie mają tego problemu; gdyby ludzie pisali takie programy, żaden z nich nie skompilowałby się. (Myślę, że standard faktycznie ogranicza ilość obliczeń, jakie można wykonać, rozkładając szablon, więc w rzeczywistości obliczenia są w rzeczywistości skończone, ale prawdopodobnie dość duże).

Jeśli masz na myśli to, czy program źródłowy należy do zestawu prawidłowych programów źródłowych C ++ , zgodzę się, że problem jest znacznie trudniejszy. Ale problemem nie jest parsowanie .

Narzędzie rozwiązuje ten problem, izolując parsowanie od sprawdzania typu analizowanego programu. (W przypadku wielu interpretacji przy braku kontekstu zapisuje an niejasności węzeł w drzewie parsowania z kilkoma możliwymi parsami; sprawdzanie typu decyduje, który jest poprawny i eliminuje nieprawidłowe poddrzewa). Możesz zobaczyć (częściowe) drzewo analizy w poniższym przykładzie; całe drzewo jest zbyt duże, aby zmieścić się w odpowiedzi SO. Zauważ, że otrzymujesz parsowanie bez względu na to, czy używana jest wartość 234797, czy 234799.

Udane jest uruchomienie narzędzia rozpoznawania nazw / typów narzędzi w AST z oryginalną wartością 234799. Przy wartości 234797 przelicznik nazw kończy się niepowodzeniem (zgodnie z oczekiwaniami) z komunikatem o błędzie „typen nie jest typem”. dlatego ta wersja nie jest prawidłowym programem w C ++.

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

Ustalenie, czy jest to deklaracja zmiennej, czy mnożenie, nie jest funkcją sprawdzania typu. Musiałem też wyszorować twoją odpowiedź na temat autopromocji ... znowu.
Szczeniak

@Puppy: możesz powiedzieć, co chcesz, ale tak działa to narzędzie. Usunięcie nazwy narzędzia prawdopodobnie spowoduje, że ludzie zapytają, jaka jest nazwa narzędzia.
Ira Baxter,

To, czy tak działa narzędzie, nie ma znaczenia, ponieważ pytanie nie wymaga działania narzędzia. Myślę też, że możemy spokojnie poczekać, aż tak się stanie.
Szczeniak
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.