Article View: pl.comp.lang.c
Article #268083Re: Ciemna strona C++
From: Marcin ‘Qrczak’ Kowalczyk
Date: Fri, 14 Dec 2007 01:41
Date: Fri, 14 Dec 2007 01:41
102 lines
7055 bytes
7055 bytes
Dnia 14-12-2007, Pt o godzinie 01:49 +0100, Adam Karpierz pisze:
> Jako "nosnik" starczy zwykly stos /w scope pamieta sie tylko
> gdzie wrocic a szuka sie przy jednoczesnej redukcji
> typedefow od konca/.
To jest trochę inne podejście (też dobre): uproszczona analiza
kontekstowa tylko dla potrzeb rozpoznawania typedefów, której wyniki
służą tylko lekserowi. Potem się to robi od nowa z uwzględnieniem
wszystkich konstrukcji.
Bo można też wykonywać początkowe etapy dalszej analizy równolegle
z lekserem: budować wszelkie informacje o symbolach widocznych w różnych
zakresach. Do dokończenia po parsowaniu, a nie do zrobienia od początku.
> Zgadza sie. Tyle ze niekoniecznie do pokreconej struktury tylko
> normalnie w drzewie jako node typedefa
> (node typu: typedef z subnode1: nazwa i subnode2: "rozczesanym" typem).
Zupełnie nie o to mi chodziło. Problemem nie jest parsowanie samych
definicji ze słowem kluczowym 'typedef', tylko rozróżnianie
identyfikatorów oznaczających typy od pozostałych w innych produkcjach.
Wszystkie gramatyki C, o których słyszałem, mają osobny leksem na
identyfikator zwykły i osobny na identyfikator wprowadzony przez
typedef. Bez tego gramatyka byłaby niejednoznaczna i nie wiem, czy
ujednoznacznianie byłoby praktyczne.
Jeśli mamy coś takiego:
a(*b)(c(d));
to jeśli a był wprowadzone przez typedef, to jest to deklaracja
wskaźnika b na funkcję zwracającą a, której parametr jest typu c
(nazwa d nie ma tu znaczenia jako nazwa parametru w typie funkcji).
Jeśli jednak a nie jest wprowadzone przez typedef, to jest to aplikacja
funkcji a do wartości odczytanej spod wskaźnika b, gdzie następnie
wynik jest zaaplikowany do aplikacji c do d. To jest zupełnie inne
drzewko, tzn. może i strukturalnie wygląda podobnie, ale węzły mają
zupełnie inną interpretację.
Inne są ciągi leksemów dopuszczone w analogicznych miejscach w jednym
i w drugim przypadku. Na przykład po deklaracji może być "=", "{"
i inicjalizator tablicy bądź struktury (w C99 z dodatkowymi ficzerami
w stylu jawnych nazw pól), a jeśli po wyrażeniu jest "=", to jest to
operator przypisania, po którym jest wyrażenie, które nie może mieć
formy czegoś w klamrach.
Zatem gdyby tworzyć zunifikowane drzewko bez wiedzy o tym, które
identyfikatory zostały wprowadzone przez typedef, to tych wszystkich
wariantów nie można by rozróżniać już w trakcie parsowania, tylko trzeba
zapamiętać, że było coś w klamrach, i dopiero potem się okaże, czy to ma
być analizowane jak inicjalizator, czy jak wyrażenie.
Podobnie problematyczna jest składnia rzutowania, która zaczyna się od
zupełnie niewinnego nawiasu okrągłego. (a)(b)(c) to może być aplikacja
a do b, a wynik jeszcze zaaplikowany do c, albo rzutowanie aplikacji b
do c, na typ a, albo rzutowanie c na typ b, a potem jeszcze na typ a.
A co dopiero, jeśli w nawiasach będą kombinacje typów funkcyjnych,
tablicowych i wskaźnikowych, gdzie nie wiadomo, co jest typem, a co
wyrażeniem.
Na domiar złego przy rzutowaniu mamy typ bez identyfikatora znajdującego
się w tym miejscu, w którym zazwyczaj przy wystąpieniu typu jest jakiś
identyfikator. To znaczy deklaracja zmiennej albo typedefu może wyglądać
np. tak:
int (*(*a)(int *b))[10];
gdzie definiowanym identyfikatorem jest a, ale przy rzutowaniu mamy
w tym miejscu dziurę:
(int (*(*)(int *b))[10])x
co może być trudno parsować jako rozszerzone wyrażenie, gdyby zamiast
int był identyfikator, o którym nie wiemy, czy jest typedefem.
Nie wiem, jak mogłoby wyglądać zunifikowane drzewko przy gramatyce
nierozróżniającej identyfikatorów typedefów od pozostałych. Może
wcale nie przypominałoby drzewka, może na przykład nie mogłoby zrobić
wiele więcej niż zbilansowanie nawiasów różnych rodzajów i zebranie
ciągów wyrażenio-typo-deklaratoro-inicjalizatorów między nawiasami.
Ludzie wolą jednak sprzęgnąć lekser z dalszymi fazami. Parsowanie
bezkontekstowe ze zunifikowanym drzewkiem jest co prawda teoretycznie
możliwe (o ile nie mamy narzucone, które aspekty składni musimy
rozpoznać już na etapie parsowania, a które możemy zostawić na potem),
ale być może jest całkiem niepraktyczne.
Dlatego twierdzę, że else jest łatwiejsze, bo else można ujednoznacznić
samą zmianą gramatyki, w dodatku całkiem łatwą. Język z takim else
jak w C jest wciąż bezkontekstowy - jedyny kłopot jest tej natury,
że najprostsza gramatyka bezkontekstowa jest większa niż najprostsza
gramatyka z niejednoznacznością. Jest większa i zawiera irytujące
prawie-duplikaty, dlatego literatura woli używać tej drugiej gramatyki,
gdzie trzeba uważać na sposób potraktowania niejednoznaczności.
W C++ równolegle z analizą leksykalną oprócz śledzenia typedefów trzeba
sprawdzać typy, budować hierarchię klas, rozpoznawać miejsca wstawienia
niejawnych konwersji, rozwiązywać przeciążanie funkcji, dedukować
parametry szablonów z typów argumentów funkcji szablonowej, uwzględniać
Koenig lookup, liczyć sizeofy typów, wykonywać arytmetykę na stałych -
połowa kompilatora musi być napisana w taki sposób, żeby można było
wykonywać ją przyrostowo na tym, co właśnie sparsowaliśmy, nie znając
jeszcze pełnego drzewa modułu wychodzącego z parsera.
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
Technical Details
Message-ID:
<1197596465.19934.36.camel@qrnik>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!newsfeed.pionier.net.pl!news.internetia.pl!newsfeed.tpinternet.pl!nemesis.news.tpi.pl!atlantis.news.tpi.pl!news.tpi.pl!not-for-mailReferences:
<fghgeo$spf$1@nemesis.news.tpi.pl> <fghsb4$kpq$1@news.dialog.net.pl> <fgo4c7$mtu$1@news.vectranet.pl> <20071106073039.433f4934@Vorago> <fgqn9m$74j$2@news.vectranet.pl> <618565031.20071107113041@pkierski.net> <fgt6sh$jov$1@news.vectranet.pl> <fgv318$hl7$1@bozon2.softax.pl> <fgv82j$htn$1@news.vectranet.pl> <fjncjm$ov6$1@inews.gazeta.pl> <fjpc4l$6ei$2@news.vectranet.pl> <fjrerb$8su$1@bozon2.softax.pl> <fjri46$h22$1@inews.gazeta.pl> <1197561477.3981.11.camel@qrnik> <fjs9m7$qi$2@news.vectranet.pl> <fjsdb1$eeg$1@inews.gazeta.pl> <1197591962.23942.28.camel@qrnik> <fjsjva$6ba$1@inews.gazeta.pl>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!newsfeed.pionier.net.pl!news.internetia.pl!newsfeed.tpinternet.pl!nemesis.news.tpi.pl!atlantis.news.tpi.pl!news.tpi.pl!not-for-mail From: Marcin ‘Qrczak’ Kowalczyk <qrczak@knm.org.pl> Newsgroups: pl.comp.lang.c Subject: Re: Ciemna strona C++ Date: Fri, 14 Dec 2007 02:41:05 +0100 Organization: tp.internet - http://www.tpi.pl/ Lines: 131 Message-ID: <1197596465.19934.36.camel@qrnik> References: <fghgeo$spf$1@nemesis.news.tpi.pl> <fghsb4$kpq$1@news.dialog.net.pl> <fgo4c7$mtu$1@news.vectranet.pl> <20071106073039.433f4934@Vorago> <fgqn9m$74j$2@news.vectranet.pl> <618565031.20071107113041@pkierski.net> <fgt6sh$jov$1@news.vectranet.pl> <fgv318$hl7$1@bozon2.softax.pl> <fgv82j$htn$1@news.vectranet.pl> <fjncjm$ov6$1@inews.gazeta.pl> <fjpc4l$6ei$2@news.vectranet.pl> <fjrerb$8su$1@bozon2.softax.pl> <fjri46$h22$1@inews.gazeta.pl> <1197561477.3981.11.camel@qrnik> <fjs9m7$qi$2@news.vectranet.pl> <fjsdb1$eeg$1@inews.gazeta.pl> <1197591962.23942.28.camel@qrnik> <fjsjva$6ba$1@inews.gazeta.pl> NNTP-Posting-Host: hjd150.internetdsl.tpnet.pl Mime-Version: 1.0 X-Trace: nemesis.news.tpi.pl 1197596818 13645 79.187.237.150 (14 Dec 2007 01:46:58 GMT) X-Complaints-To: usenet@tpi.pl NNTP-Posting-Date: Fri, 14 Dec 2007 01:46:58 +0000 (UTC) In-Reply-To: <fjsjva$6ba$1@inews.gazeta.pl> X-Mailer: Evolution 2.12.1 PLD Linux Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit