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 c
jest zmienną , to a b(c);
definiuje zmienną o nazwie b
typ a
. Jest bezpośrednio inicjowany c
. Ale jeśli c
jest typem , to a b(c);
deklaruje funkcję o nazwie, b
która bierze a c
i 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?
źródło
Odpowiedzi:
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):
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 razietypen
jest liczbą całkowitą, a nie szablonem, dlategotypen<1>()
jest analizowana jako(typen<1)>()
niepoprawna pod względem składniowym, ponieważ()
nie jest wyrażeniem poprawnym pod względem składniowym.[1] Mówiąc bardziej technicznie, każda produkcja w gramatyce kontekstowej musi mieć postać:
αAβ → αγβ
gdzie
A
jest 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”.
źródło
0
w 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” :)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
lub
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ń
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).
źródło
ambiguity doesn't have anything to do with context-sensitivity
To 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ą opartea 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.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).
Śledzony przez:
źródło
Aby odpowiedzieć na twoje pytanie, musisz rozróżnić dwa różne pytania.
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.
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.
źródło
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*b
jako deklarację zmiennej (b
wskaźnika typu doa
), czy jako mnożenie.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.
źródło
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:
Czy to jest wyrażenie mnożenia?
LUB
Czy to deklaracja
B
zmiennej ma być wskaźnikiem typuA
?Jeśli A jest zmienną, to jest to wyrażenie, jeśli A jest typem, jest to deklaracja wskaźnika.
Drugi przykład:
Czy jest to prototyp funkcji przyjmujący argument
bar
typu?LUB
Czy to deklaruje zmienną
B
typuA
i wywołuje konstruktor A zebar
stałą jako inicjalizatorem?Musisz ponownie wiedzieć, czy
bar
jest to zmienna, czy typ z tabeli symboli.Trzeci przykład:
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.
źródło
A B();
jest deklaracją funkcji nawet w definicji funkcji. Poszukaj najbardziej dokuczliwej analizy ...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.
Nie można opisać poniższej reguły gramatyką bezkontekstową: Prawa strona zadania powinna być tego samego typu co lewa strona.
źródło
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:
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.
źródło
Ż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 .
źródło
Prawdziwe :)
J. Stanley Warford. Systemy komputerowe . Strony 341–346.
źródło
Czasami jest gorzej: co ludzie mają na myśli, gdy mówią, że C ++ ma „nierozstrzygalną gramatykę”?
źródło
Jest wrażliwy na kontekst, podobnie jak
a b(c);
dwie poprawne deklaracje parsowania i zmienna. Kiedy mówisz „Ifc
is 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.
źródło
a<b<c>>d
prawda? (Twój przykład jest właściwie klasykiem z C , gdzie jest to jedyna przeszkoda w byciu bezkontekstowym.)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śliX
jest wartością, to definiuje się jąf
jako zmienną, która zostanie zainicjalizowanaX
. JeśliX
jest typem, definiuje się gof
jako funkcję przyjmującą pojedynczy parametr typuX
.Patrząc na to z gramatycznego punktu widzenia, moglibyśmy zobaczyć to w następujący sposób:
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
X
jako 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.
źródło
Najprostszy przypadek gramatyki bezkontekstowej polega na analizie wyrażeń zawierających szablony.
Może to być parsowane jako jedno z nich
Lub
Oba AST można jednoznacznie ocenić, badając deklarację „a” - poprzedni AST, jeśli „a” jest wzorem, lub drugi, jeśli nie.
źródło
<
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 analizowaniea<b>c>
gdziea
jest szablon, ale nie ma wpływu naa<b<c>
.a();
(co jest alboexpr.call
alboexpr.type.conv
)?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.}
źródło
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
źródło
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:
jest rzutowaniem if
name
jest 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ę
typename
itemplate
z nazwami zależnymi. Reszta C ++ nie jest wrażliwa na kontekst, o ile mi wiadomo (tzn. Można dla niego napisać gramatykę bezkontekstową).źródło
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.
źródło
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ślia
jest 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ącejtypedef
s 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” jak
a * b;
w Pythonie , co stanowi dobry konkretny przykład języka kontekstowego bez „dwuznacznej” gramatyki (jak wspomniano w pierwszym akapicie).źródło
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 ++.
źródło