Preprocesor
Z Wikipedii
Preprocesor to program interpretujący, którego zadaniem jest przetworzenie tekstu wejściowego w sposób określony za pomocą poleceń preprocesora przez programistę na tekst wyjściowy. Dopiero tak przetworzony tekst poddawany jest analizie składniowej i kompilacji. Wynikiem działania preprocesora jest więc tekst wyjściowy po przetworzeniu podlegający następnie kompilacji.
Preprocesor jest najczęściej zintegrowany z kompilatorem języka programowania.
Spis treści |
[edytuj] Preprocesor języków C i C++
Najbardziej znane języki, które wyposażone są w preprocesor „wbudowany w język”, to C i C++. Dyrektywy preprocesora mogą występować w ogólności w dowolnym miejscu programu, a rozróżnienie ich od tekstu kodu źródłowego w językach C i C++ dokonywane jest poprzez poprzedzenie dyrektywy znakiem hash ‘#’. Do najważniejszych dyrektyw należą:
- #include … - dyrektywa włączająca tekst innego pliku źródłowego w miejscu jej wystąpienia w pliku podlegającym aktualnie przetwarzaniu, przy czym możliwe jest zagłębione występowanie dyrektywy include,
- #define … - definiuje stałe i makroinstrukcje (pseudofunkcje)
- #undef … - usuwa definicje stałej lub makra
- #if … - dyrektywy kompilacji warunkowej
- #elif … - działa podobnie jak else if w języku C
- #endif … - oznacza koniec bloku kompilacji warunkowej
- #ifdef … - znaczy to samo co #if defined(…)
- #ifndef … - znaczy to samo co #if !defined(…)
- i inne.
Dyrektywy preprocesora C/C++ wykorzystuje się często do zabezpieczenia plików nagłówkowych przed wielokrotnym dołączaniem do tego samego projektu. Jeżeli treść pliku nagłówkowego nazwa.hpp
obejmie się instrukcjami:
#ifndef _NAZWA_HPP_ // (1) #define _NAZWA_HPP_ // (2) // ... treść właściwa ... #endif // (3)
to przy pierwszej próbie dołączenia pliku, kompilator najpierw sprawdzi, czy zdefiniowano stałą _NAZWA_HPP_ (może ona mieć dowolną nazwę, ten sposób jest jednak dobrym zwyczajem promowanym przez programistów) (1) - jeżeli nie, zostanie ona zdefiniowana (2) i do programu zostanie dołączona treść między (2) i (3), oznaczający koniec części dodawanej tylko przy spełnieniu warunku (1).
Uwaga! Niektórzy programiści preferują instrukcję:
- #pragma once
zapobiegającą ponownemu załączeniu treści całego pliku, w którym się owa instrukcja znajduje, jest to jednak sposób niezgodny z ISO C++ i nieobsługiwany przez część kompilatorów (kompilator nie zwróci błędu kodu - po prostu zignoruje tę instrukcję i przy próbie dołączenia pliku po raz drugi zwróci błąd wielokrotnej deklaracji/definicji tej samej zmiennej/stałej/typu/funkcji).
[edytuj] Preprocesor języka Clipper
W wersji 5.x języka Clipper został zaimplementowany preprocesor wzorowany na preprocesorze języka C. Znalazły się więc w nim takie dyrektywy jak: #define, #ifdef, ifndef, #include, #undef. Ale preprocesor ten zawiera również dwie dodatkowe dyrektywy: #command i #translate. Ich składania jest następująca: #command <szablon rozpoznawczy> => <szablon wynikowy> i identycznie dla dyrektywy translate.
Dyrektywy te umożliwiają definiowanie rozkazów i pseudorozkazów. Analizowany kod źródłowy jest sprawdzany pod kątem wystąpienia zdefiniowanych szablonów wg następującej kolejności:
1. #define,
2. #translate,
3. #command,
a po znalezieniu wzorca następuje podstawienie w jego miejsce tekstu utworzonego wg szablonu wynikowego i ponowne sprawdzenie, czy nie występuje kolejny szablon rozpoznawczy w utworzonym kodzie.
[edytuj] Preprocesor języka Pike
Również preprocesor języka Pike wzorowany jest na preprocesorze języka C. W zasadzie składnia dyrektyw preprocesora tego języka jest identyczna jak w C. Zasadnicze różnice to: brak plików nagłówkowych dołączanych dyrektywą include (choć sama dyrektywa istnieje i służy do dołączania plików z kodem źródłowym), oraz dodatkowe dyrektywy jak np. string dołączająca plik jako wartość tekstową.
[edytuj] Preprocesor języka PL/1
Najbardziej rozbudowanym zestawem instrukcji dysponuje preprocesor języka PL/1 (implementacje IBM w systemie OS). W tym przypadku znakiem wyróżniającym dyrektywę preprocesora jest znak procentu ‘%’, a semantyka poleceń preprocesora jest niemal zgodna z semantyką analogicznych instrukcji języka PL/1. Lista instrukcji tego preprocesora obejmuje większość analogicznych instrukcji samego języka, w tym takich jak:
- %DECLARE – deklaracje zmiennych preprocesora,
- %vv=ee – przypisania,
- %GOTO – skoku,
- %IF – warunkowa,
- %DO - grupująca
- %DO vv=ee TO … BY … - pętli,
- %PROCEDURE - procedury
- i inne,
oraz specyficzne dla preprocesora, jak
- %INCLUDE – włączająca,
- i inne.
Choć preprocesor tego języka, podobnie jak w C, był wbudowany w kompilator, dzięki pewnym sztuczkom mógł być stosowany jako preprocesor dla innych języków programowania, np. FORTRAN.
[edytuj] Preprocesor asemblera
Niektóre asemblery, np. makroasembler IBM, w ramach pakietu oprogramowania dostarczają programy ułatwiające pisanie kodów źródłowych w asemblerze. Przykładem jest program SALUT wchodzący w skład w.w. pakietu, który przekształcał instrukcje strukturalne na instrukcje asemblera. Działał on więc na zasadzie preprocesora lecz nie wbudowanego w program główny, tylko jako osobny program. Przed uruchomieniem procesu asemblacji należało uruchomić program SALUT, i dopiero plik wynikowy tego programu poddać procesowi asemblowania.
W programie tym instrukcje strukturalne zapisane są w pliku źródłowym i poprzedzone znakiem dolara '$'. Program dostarcza takich konstrukcji programowania strukturalnego jak instrukcja warunkowa $IF ... $ELSE ... $ENDIF, instrukcja pętli $DO w różnych wariantach, czy instrukcja przeszukiwania $SEARCH.
[edytuj] Preprocesor a dyrektywy kompilatora
Niektóre implementacje języków programowania (np: Pascal), choć nie posiadają wbudowanego preprocesora, to udostępniają dyrektywy kompilatora, które są rozpatrywane w trakcie kompilacji, a nie jak w przypadku preprocesora przed jej wykonaniem. Zwykle lista takich dyrektyw jest znacznie bardziej uboga i o znikomych możliwościach w porównaniu z preprocesorem.