A Pike programozási nyelv

Fordítók

Előfeldolgozó

A Pike kód értelmezése előtt, a forrásfájl előfeldolgozására kerül sor. Az előfordító konvertálja a forráskódot karakterkódolásától függően a Pike belső reprezentációjának megfelelően, valamint elvégez néhány egyszerűbb normalizációt és ellenőrzi a konzisztenciát, valamint végrehajtja a fordítási direktívákban foglaltakat. A fordítási direktívák egy nagyon egyszerű programozási nyelv kulcsszavainak tekinthetőek, amely nyelv lehetővé teszi számunkra a kódgenerálást vagy a kódmanipulációt. A előfordító hívása a "cpp" kulcsszóval történik a Pike-ban.

Karakterlap eldöntése

Egy Pike forrásfájlban akár Unicode karakterek is írhatóak, tehát az előfordítónak az az első feladata, hogy megpróbálja eldönteni a forrásfájl karakter kódolását. Először a feldolgozandó fájl első két bájtját vizsgálja meg, majd dönt a táblázatnak megfelelően.

0. bájt 1. bájt Értelmezés
0 0 32 bites wide string.
0 >0 16 bites Unicode string.
>0 0 16 bites Unicode string fordított bájt elrendezésben.
0xfe 0xff 16 bites Unicode string.
0xff 0xfe 16 bites Unicode string fordított bájt elrendezésben.
0x7b 0x83 EBCDIC-US ("#c").
0x7b 0x40 EBCDIC-US ("# ").
0x7b 0x09 EBCDIC-US ("#\t").

Bármely más bájt kombinációra az előfordító iso-8859-1 karakter kódolásúnak fogja értelmezni a fájlt, amíg nem fodul elő a #charset direktíva.

A fájl méretének 2 vagy 4 többszörösének kell lennie bájtokban, hogy helyesen legyen dekódolva 32 bites vagy 16 bites wide stringeknek megfelelően.

Hibás program, amely EBCDIC karakter kódolású és nem #charset direktívával kezdődik.

Egy összezavaró megjegyzés: Lehetséges hogy a #charset direktíva EBCDIC kódolású, a fájl további része pedig a dírektívában szereplő karakterlapnak megfelelő.

Kód normalizáció

Az előfordító összevon minden egymást követő stringen kívüli white space karaktert, kivéve az új sor karaktereket, egyetlen szóközzé. A // jelek utáni a /**/ közötti részek, valamint a #! kezdődő sorok egyaránt eltávolításra kerülnek. A Pike tekintettel van az ANSI/DEC vezérlősorozatokra, ahogy a white space-ekre is. Támogatott formátumok [\040-\077]+[\100-\177] és

[\040-\077]*[\100-\177]. Ez azt jelenti, hogy lehetséges színemelést végrehajtani a forrásfájlban.

Az előfeldolgozó hét stringen kívüli egymáskövető < karaktert CVS ütközési hibaként fog értékelni és "CVS conflict detected" hibaüzenettel fog visszatérni.

A define-ok és a makrók

Makrók és konstansok definiálása az egyike a leggyakrabban használt előfeldolgozó által nyújtott szolgáltatásoknak. Lehetővé téve ezzel az absztrakciót a kód generálás szintjén, valamint a konstans értékek megváltoztatását.

#define DO_OVERSAMPLING Az #ifdef és az #ifndef fordítási direktívák segítségével tudunk blokkokat aktiválni vagy deaktiválni az adott definíció létezésétől függően.

#ifdef DO_OVERSAMPLING // This code is not always run. img->render(size*4)->shrink(4); #endif

Fontos tudni, hogy define-ok futási időben is megadhatóak a Pike számára. Parancssorból a DO_OVERSAMPLING beállítása a -DDO_OVERSAMPLING-gel történik a program nevének megadása előtt. Pl: pike -DDO_OVERSAMPLING my_program.pike.

Egy define-nak is adhatunk meg paramétert, ami a definíció minden előfordulásába behelyettesítődik a forrás fájlban.

#define CYCLES 20 void do_stuff() { for(int i; i < CYCLES; i++) do_other_stuff(); }

A define-oknak adhatunk paramétereket parancssorból is, a shell által megkövetelt idézőjelek között:

~% pike '-DTEXT="Hello world!"' -e 'write("%s\n", TEXT);'

Hello world!

A define kulcsszó segítségével makrók is definiálhatók. A makró egy argumentumokkal kiterjesztett szöveg, ami gyakran hasznos eszköznek bizonyul a kód áttekinthetősége szempontjából, valamint rövidebb kódokat eredményez.

#define VAR(X) id->:misc->variable[X] #define ROL(X,Y) (((X)<<(Y))&7+((X)>>(8-(Y)))) #define PLACEHOLDER(X) void X(mixed ... args) { \ error("Method " #X " is not implemented yet.\n"); } #define ERROR(X,Y ...) werror("MyClass" X "\n", Y) #define NEW_CONSTANTS(X) do{ int i=sizeof(all_constants()); \ X \ werror("Constant diff is %d\n", sizeof(all_constants())-i); \ }while(0) #define MY_FUNC(X,Y) void my##X##Y()
  1. Egy makrónak maximum 254 paramétere lehet.
  2. Bölcs döntés lehet extra zárójeleket elhelyezni az argumentumok körül, mivel egyszerű szöveg helyettesítést végzünk. Például, ha a DOUBLE(X) definíciója X*2, akkor a DOUBLE(2+3) eredménye 2+3*2 lesz, ami egy nehezen felderíthető szemantikai hibához vezet.
  3. A backslash (\) karakter segítségével a definíció több soron keresztül adható meg.
  4. A hash (#) karakter egy makró változó előtt stringé "konvertálja" őt.
  5. Lehetőség nyílik argumentum listákkal rendelkező makrók definiálására segítségével.
  6. A makrók gyakran formulák, ilyenkor illik kitenni a pontosvesszőt már a makróban, ez növeli a kód olvashatóságát.
  7. A Pike makrókat és define-okat általában nagybetűkkel írjuk.
  8. Ha egy makró több utasításból áll, akkor hasznos lehet egy közös blokkba elhelyezni őket, például: do {BODY} while(0). Amennyiben nem teszünk így, akkor a makrónk nagyon nehezen felderíthető hibához vezethet.
  9. A dupla hash (##) eredménye egy makró változó előtt: összefűzi a makróváltozót az előtte álló szöveggel.

Előfeldolgozási direktívák

Minden előfeldolgozási direktívának a sor elején kell lennie. A whitespacek még megengedettek a sor elején, de elképzelhető, hogy hibát vagy figyelmeztetést fognak generálni a jövőben.

#!

Minden #!-lel kezdődő sor megjegyzésként kerül értelmezésre, ezzel lehetővé téve az integrációt a shellben. Ajánlott, hogy a Pike alkalmazásokat a #! /usr/bin/env pike" sorral kezdjük a maximális platformok közötti kompatibilitás érdekében.

#<integer> / #line

A hash karakter utáni számmal tudjuk beállítani az előfeldolgozó sorszámlálóját, hogy honnan kezdje a feldolgozást. A Pike hibaüzenetei ezeket a számokat fogják használni. Opcionálisan megadható egy fájl neve a szám után, pl: #1 "/home/pike/program.pike.in" . Ebben az esetben a megadott fájlnév fog szerepelni az aktuális fájl helyett a hibaüzenetekben.

#""

Ha egy string literál #"-el kezdődik, akkor a literálon belüli újsorok a feldolgozás folyamán \n vezérlőkarakterre cserélődnek, ahelyett hogy "newline in string" hibát kapnánk. Ez a direktíva bárhol használható, ahol egy string használható.

#string

A #string direktíva az utána álló fájl tartalmát illeszti be stringként. Ez a direktíva bárhol használható, ahol egy string használható.

do_something(#string "the_file.wks");
#include

Az #include direktíva más fájl tartalmának beillesztését eredményezi a feldolgozás alatt álló fájlban a direktíva helyére. A fájlneveket idézőjelek között meg lehet adni egyaránt abszolút és relatív elérési úttal vagy <> jelek között. Idézőjeles megadásra példa: "constants.pike" vagy #include "../debug.h". Az include környzeti változó által hivatkozott könyvtárakból való beillesztésre példa: #include <profiling.h>.

#if

Az #if direktíva egyszerű kifejezéseket tud kiértékelni és ha a kifejezés értéke igaz, akkor a kód feldolgozása az #if után folytatódik. A blokk akkor ér véget, ha #endif, #else, #elseif vagy #elif blokkhoz értünk ugyanabban a mélységben.

Az #if direktíva tartalmazhat define-okat, egészeket, stringeket és valós konstansokat, ?:, ||, &&,~, ^, !, |,&,<, >, <=, >=,==,!=, +, -, *, /, <<,>> operátorokat és zárójeleket. A stingek indexelhetőek a [] index operátor segítségével. Végül rendelkezésünkre áll 3 speciális funkció az #if kifejezesekben: defined, efun és constant. A define igazat ad vissza, ha az adott szimbólum definiálva van. A két kifejezés ekvivalens:

#if defined(MY_DEF) #ifdef MY_DEF

Az efun, akkor ad vissza igaz értéket, ha az argumentum típusa efun. A constant akkor lesz igaz, ha az argumentum konstansként kiértékelhető.

#ifdef

#ifdef ugyanúgy működik, mint az #if, de az argumentumok kiértékelése helyett, csak ellenőrzi az első szimbólum definiált-e vagy makró-e.

#ifndef

Az #ifdef inverzeként működik, akkor aktiválja az utána következő blokkot, ha szimbóluma nincs definiálva.

#endif

Az #if, #ifdef, #ifndef, #else, #elseif or #elif direktívákkal kezdődő blokkok lezárására szolgál

#if DEBUG do_debug_stuff(); #endif /* DEBUG */
#else

Az aktuális blokkot választja el inverz aktivizáció esetére.

#ifdef FAST_ALGORITHM do_fast_algorithm(); #elif defined(EXPERIMENTAL_ALGORITHM) do_experimental_algorithm(); #else do_default_algorithm(); #endif
#elseif and #elif

Az #elseif és az #elif értelmezése az #if/#ifdef/#ifndef szerint értendő

#undefine és #undef

Az #undefine és az #undef direktívákkal argumentumként megadott szimbólumok definícióit tudjuk megszüntetni

// Strip debug #define werror(X ...) lambda(X){} #include "/home/someone/experimental/stuff.h" #undef werror
#error

Hibaüzenet dob a feldolgozás folyamán.

#ifdef __NT__ #error "This program can not run on MS Windows." #endif
#charset

Megmondja az előfeldolgozónak milyen karaktertábla szerint kódolt a fájl.

#pike

Megmondja a fordítónak, hogy melyik Pike verzió szerint kell feldolgozni a kódot

#pike 7.2
#pragma all_inline #pragma all_nomask #pragma strict_types #pragma save_parent and #pragma dont_save_parent

Fordítási tulajdonságok beállítására szolgáló direktívák.

#warning

Figyelmeztetést generál a feldolgozás folyamán.

#if !constant(Crypto.SHA1.hash) #warning SHA1 hash not available. #endif

Az előredefiniált define-ok

Namespace cpp::

__VERSION__

Ez a define az aktuális Pike verzió számát tartalmazza egy valós számként. Ha másik Pike verzió emulációja történik, akkor a definíció értéke automatikusan frissül.

Lásd még: __REAL_VERSION__

__REAL_VERSION__

Ez a define mindig az aktuális Pike verzió értékét tartalmazza egy valós számként.

Lásd még: __VERSION__

__MAJOR__

Ez a define az aktuális Pike verzió egész értékét tartalmazza. Ha másik Pike verzió emulációja történik, akkor a definíció értéke automatikusan frissül.

Lásd még: __REAL_MAJOR__

__REAL_MAJOR__

Ez a define mindig az aktuális Pike verzió egész értékét tartalmazza.

Lásd még: __MAJOR__

__MINOR__

Ez a define az aktuális Pike verzió tizedes vessző utáni értékét tartalmazza. Ha másik Pike verzió emulációja történik, akkor a definíció értéke automatikusan frissül.

Lásd még: __REAL_MINOR__

__REAL_MINOR__

Ez a define mindig az aktuális Pike verzió tizedes vessző utáni értékét tartalmazza.

Lásd még: __MINOR__

__BUILD__

Ez a konstans az aktuális Pike verzió realizációját tartalmazza. Ha egy másik Pike verzió emulációja történik, akkor ez a konstans érték változatlanul marad.

Lásd még: __REAL_MINOR__

__REAL_BUILD__

Ez a define mindig az aktuális Pike verzió realizációját tartalmazza.

Lásd még: __BUILD__

__LINE__

Ez a define a feldolgozandó fájl aktuális sorának a számát tartalmazza.

__FILE__

Ez a define a file elérési útját tartalmazza.

__DATE__

Ez a define a fordítás dátumát tartalmazza. pl: "May 31 2006".

__TIME__

Ez a define a fordítás idejét tartalmazza, pl: "13:36:25".

__PIKE__

Ez a define mindig igaz.

__AUTO_BIGNUM__

Ez a define akkor van definiálva, ha az automatikus nagy szám (bignum) konverzió engedélyezve van. Amikor ez engedélyezett, minden egész automatikusan nagy számmá (bignum ) konvertálódik, ha nagyobb értéket vesz fel mint, amit az egész reprezentáció megenged, csendben lassítja a működést a program elszállása helyett.

__NT__

Akkor létezik ez a define, ha Microsoft Windows operációs rendszeren fut a Pike, nem csak NT rendszerre vonatkozik a definicó.

__amigaos__

Akkor létezik ez a define, ha Amiga operációs rendszeren fut a Pike.

A fordító használata

A Pike nyelvhez fordító a nyelv (leírás végén említett) honlapján található. A fordító szabad forrású (felhasználása a GPL-nek, LGPL-nek, Mozilla Public License-nek megfelelően történhet), elsősorban Un*xokra fejlesztik, de a Windowsra fordított bináris, és a hozzá tartozó modulok is elérhetők.

A rendszer magja a Hilfe ún. Incremental Pike Frontend, melybe programsorokat írhatunk egymás után, és azt az interpreter azonnal végrehajtja. Természetesen lehetőség van előre megírt kód futtatására is, ekkor a kívánt forrásfile-lal kell paraméterezni, például a következőképpen:

pike forras.pike

Az esetleges további paraméterek átadódnak a végrehajtott programnak.

A Pike egyik előnye a modulokkal való bővíthetőség. A fordítókörnyezet számos modullal körítve érkezik, többek között a grafikus kezelőfelületű programok írását lehetővé tevő GTK-val, különböző képfeldolgozást és hálózati kommunikációt megvalósító modulcsomagokkal, valamint szálkezelést engedélyező modullal. E modulok más nyelveken is készülhetnek (például C, C++), és akár magunk is bővíthetjük a rendszert továbbiakkal.