A PEARL programozási nyelv

Alprogramok, modulok



Programok felépítése

Modulok

Egy PEARL program egy vagy több modulból áll. A modulok egymástól független fordítási egységek.

Minden modul egy rendszerfüggő (SYSTEM) és/vagy egy kizárólag az adott feladat megoldásával foglalkozó részből (PROBLEM) áll.

Általánosan egy PEARL program a következő alakú:

MODULE
SYSTEM; ...
PROBLEM; ...
MODEND;

A rendszerfüggő részben (SYSTEM) írjuk le a számítógép, és az egyes speciális eszközök (szenzorok...), a standard perifériák (billentyűzet, monitor, nyomtató...) stb. közötti kapcsolatokat. A programozó nevet adhat ezeknek az eszközöknek, valamint még az egyes szignáloknak és megszakításoknak, és ezeken keresztül hivatkozhat ezekre, így a probléma megoldását tartalmazó része a programnak (PROBLEM) már rendszerfüggetlen lehet.

Az egyes objektumokat vagy a modul szintjén (eljárásokon és taszkokon kívül), vagy eljárásokban/taszkokban deklarálhatunk. A modul szintjén deklarált változók az egész modulban ismertek és elérhetőek, a modul taszkjain, eljárásain belül változtathatóak. Taszkon, vagy eljáráson belül deklarált változó csak ezen belül használható vagy változtatható.

Deklaráció és specifikáció

Új elemek bevezetése a programba deklarációval, vagy specifikációval lehetséges.

Deklaráció(DECLARE/DCL)

A deklaráció arra szolgál, hogy bevezessen egy új objektum objektumot névvel ellátva. Egy deklaráció kiértékelésekor a program helyet foglal a memóriában, és ezt a memóriaterületet a deklarációban megadott név segítségével érhetjük el. A modul, vagy egy taszk/eljárás szintjén mindent csak egyszer deklarálhatunk. Ha egy x azonosítót a modulban, és egy eljárásában vagy taszkban is deklarálunk, akkor a megfelelő taszkban/eljárásban csak a lokálisan deklarált változó érhető el.

Példa:

PROBLEM;
DECLARE x FLOAT; ! 1. deklaráció a modulban
DECLARE x FIXED; ! 2. deklaráció a modulban - hibás!
P: PROCEDURE;
DECLARE x FIXED; ! P eljáráson belüli deklaráció újra megengedett
...
x := 3; ! a lokális x-re vonatkozó értékadás
...
END; ! P
T: TASK;
...
x := 5;
...
END; ! T
...

Taszkokban és eljárásokban

nem deklarálhatók, vagy specifikálhatók.

A következő táblázatban részletesebben megtalálható, mit hol lehet deklarálni, illetve specifikálni.

deklaráció és specifikáció lehetséges
modulokban
taszkokban
eljárásokban
blokkokban
változó, konstans
x
x
x
x
címke
-
x
x
x
eljárás
x
x
x
-
taszk
x
-
-
-
blokk
-
x
x
x
SEMA, BOLT változó
x
-
-
-
adatállomás
x
-
-
-
típus
x
x
x
x

Specifikáció(SPECIFY/SPC) és azonosítás (SPC IDENT)

Specifikációval egy már deklarált változóra hivatkozhatunk. Használható pl. más modulokban már deklarált változókra való hivatkozásokhoz, de egyszerűen egy további nevet is megadhatunk vele már deklarált változókhoz.

A következő példában a FIXED típusú x változónak adunk még egy nevet, az xx-et amin keresztül elérhetjük.

PROBLEM;
...
DECLARE x FIXED;
...
SPECIFY xx FIXED IDENT (x);
...
xx := 7; ! az x változónak ad értéket
...

Általánosan az azonosítás alakja a következő:

SPECIFY aznosító típus IDENT(változó);

Az adott típusnak meg kell egyeznie az IDENT után megadott változó típusával.

Blokkstruktúra, élettartam

Blokkokat arra használunk, hogy taszkokat és eljárásokat strukturáltabbá tegyünk, illetve adott esetben az változók élettartamának és láthatóságának befolyásolására.

A PEARL-ben a blokkok a következő alakúak:

BEGIN
...
END [ azonosító ] ;

Az azonosító megadása opcionális.

Blokkok egymásba ágyazhatóak. A blokkba A BEGIN végrehajtásával lépünk be, majd a hozzátartozó END végrehajtásával (vagy esetleg ugrással, vagy exit utasítással) lépünk ki. Blokkokba az ugrás nem megengedett.

Blokkon belül nem deklarálhatunk eljárást.

Példa:

MODULE;
SYSTEM;
printer: STDPRINT;
PROBLEM;
SPC printer DATION ... ;
T: TASK;
DCL a FIXED;
BEGIN
...
DCL x FLOAT;
...
END;
...
END; ! T
...
MODEND;

A változók hatáskörére/láthatóságára a következő szabályok érvényesek:

Példa:


PROBLEM;
DCL x FIXED;
...
SPC xx FIXED IDENT (x);
T: TASK;
...
END; ! T
P: PROC;
DCL y FIXED;
x := 2;
BEGIN
DCL x FLOAT;
x :=3 ;
...
BEGIN
DCL x DUR;
...
END;
...
END;
y := x; ! y = 2
...
END; ! P
...
MODEND;

Az END után a blokkoknak adható azonosító, így akár több egymásba ágyazott blokkot is elhagyhatunk egyszerre az exit utasítás segítségével.

Modulok közötti hivatkozások

Ha a PEARL program több modulból áll, akkor szükség lehet rá, hogy egyes objektumokat több modulból is elérjünk. Ezt teszik lehetővé a globális objektumok. Ezeket az objektumokat GLOBAL attribútummal deklaráljuk abban a modulban, ahol azt szeretnénk, hogy a tényleges helyfoglalás történjen, és a GLOBAL attribútummal specifikáljuk azokban a modulokban, ahol még el szeretnénk érni.

Példa:

MODULE (a);
PROBLEM;
...
DCL x FIXED GLOBAL;
...
x := 2;
MODEND;

MODULE (b);
PROBLEM;
...
SPCx FIXED GLOBAL (a);
...
x := 3;
MODEND;


Minden a rendszerfüggő (SYSTEM) részben megadott adatállomást, megszakítást és szignált úgy kezel a PEARL mintha GLOBAL attribútummal adtuk volna meg őket. Emiatt a modulok problémamegoldást tartalmazó részében (PROBLEM) csak specifikálni kell őket. Abban a modulban ami a deklarációt tartalmazza a GLOBAL attribútum elhagyható specifikációnál, de minden más modulban ami használja ki kell írni.

A GLOBAL attribútum után zárójelben opcionálisan megadható annak a modulnak a neve, amelyikben a deklaráció történt.

GLOBAL [ (modul neve) ]

Specifikációnál az adott objektum minden attribútumát át kell venni a deklarációtól, kivéve prioritást, pontosságot, vagy hosszt.

Példa:

MODULE;
PROBLEM;
T: TASK PRIO 3 GLOBAL;
! task body
END; ! T
P: PROC (A(,) FIXED IDENT)GLOBAL;
! procedure body
END; ! P
...
MODEND;



MODULE;
PROBLEM;
SPC T TASK GLOBAL;
P ENTRY ((,) FIXED IDENT) GLOBAL;
INIT: TASK;
DCL TAB (10,20) FIXED;
...

CALL P (TAB);
...
ACTIVATE T;
END; ! INIT

...
MODEND;

Program végrehajtása

A PEARL program betöltése után a PEARL run time system automatikusan elindít minden taszkot, amelyik MAIN attribútummal van ellátva, a prioritásuk sorrendjében. Minden taszkot amihez MAIN attribútumot adunk meg ugyanabban a modulban kell deklarálni.

Példa:

MODULE (Main);
SYSTEM;
...
PROBLEM;
start: TASK MAIN;
! activating and scheduling of other tasks
END; start
measuring: TASK PRIO 1;
! task body
END; ! measuring
...
MODEND;

Betöltés után a start címkéjű taszk indul el először.

Eljárások

Eljárások(PROC) deklarációja és specifikációja

Az utasítássorozatot, ami végrehajtódik eljáráshívás esetén az eljárás deklarációjában adjuk meg, és ehhez definiálunk egy azonosítót.

Az eljárásban az utasítások olyan adatokat használhatnak:

Az eljárások specifikációjánál a paraméterlista megadása nem kötelező, de megadható, és emiatt az eljárás deklarációk megfelelő része egy az egyben átmásolható a specifikációhoz, és így biztosított a helyessége.

Az eljárások és függvények között az a különbség, hogy a függvényeknek van visszatérési értéke (RETURN). A visszatérési érték típusa meghatározza, hogy milyen típusú kell legyen a kiszámított érték típusa, amit a függvény visszaad.

Eljárás vagy függvény törzsének a végrehajtását általában a RETURN utasítás zárja le.

Függvények esetében a végrehajtás csak RETURN utasítással fejeződhet be, máshogy nem.

Eljárás végrehajtása a RETURN utasítással fejeződhet be, vagy ennek hiányában az eljárás törzsében az utolsó utasítás végrehajtásával.

Eljárások törzse változódeklarációkon kívül tartalmazhatja még további eljárások deklarációját is.

Eljáráshívásnál az eljárás formális paramétereit társítjuk az aktuális paraméterekkel; a paraméterátadás módjait részletesebben a eljárás hívással foglalkozó részben tárgyaljuk.

Modul szinten deklarált eljárások egyidejűleg több taszkban is használhatók.

Eljárás hívás(CALL)

Eljárások hívása a CALL kulcsszó segítségével, vagy egyszerűen csak az eljárás nevével lehetséges.

Példa:

SPC Output PROC (P FIXED, N FIXED) GLOBAL;
DCL (Pos, No) FIXED;
...
! Assignments to Pos and No
CALL Output (Pos, No);

A következő példában az Ari eljárás kiszámolja egy FLOAT típusú elemeket tartalmazó tömb elemeinek az átlagát, majd kiírja, az “Arith.Average” szöveggel együtt.

Ari: PROC (Array() FLOAT IDENTICAL) RETURNS (FLOAT);
DCL Sum FLOAT;
DCL (LowerBound, UpperBound) FIXED;
Sum := 0;
LowerBound := LWB Array;
UpperBound := UPB Array;
FOR i FROM LowerBound BY 1 TO UpperBound REPEAT
Sum := Sum + Array(i);
END; ! loop
RETURN (Sum/(UpperBound - LowerBound + 1));
END; ! Ari
DCL MeasuredValue(10) FLOAT;
... /* Acquisition of the measured values */
PUT Ari (MeasuredValue), ’Arith.Average’ TO Printer BY LIST;
...

Függvényhívások kiértékelésénél az aktuális paramétereket megfeleltetjük a függvény formális paramétereinek, a leírás sorrendjében, aztán a függvény törzse hajtódik végre. Ezután a program végrehajtása a függvényhívást követő kifejezésnél folytatódik.

Függvény- és eljáráshívás esetén is az aktuális paraméterek típusa meg kell, hogy egyezzen a megfelelő formális paraméterek típusával.

Paraméterátadás kétféleképpen történhet: ha a formális paraméter specifikációja az IDENTICAL vagy IDENT attribútummal történt akkor cím szerinti paraméterátadásról beszélünk, egyébként érték szerinti.

Érték szerinti paraméterátadás esetén, egy új, a formális paraméter típusával megegyező típusú objektum jön létre, ami a függvénynek egy lokális változója lesz. Aztán az aktuális paraméter értékeit hozzárendeli a megfelelő formális paraméterekhez, és emiatt a függvénytörzsben a formális paraméterek változásai nincsenek hatással az aktuális paraméterek értékeire. Ebben az esetben bármilyen kifejezés lehet a függvény aktuális paramétere.

Cím szerinti paraméterátadás esetén a formális paramétert azonosítjuk az aktuális paraméterrel, vagyis a függvény törzsében az aktuális paraméter által tárolt adatot a formális paraméteren keresztül érjük el. Ez azt jelenti, hogy ha a formális paraméter értékét megváltoztatjuk, akkor az aktuális paraméter értéke is változik. Emiatt csak változók lehetnek az aktuális paraméterek, kifejezések nem.

Példa:

PROBLEM;
P1: PROC (pi FIXED, pj FLOAT IDENT);
...
pi := 3;
pj := 5.0;
END; ! P1
P2: PROC ...;
DCL (i, j) FIXED, a(100) FLOAT;
...
i := 2 ;
a(i) := 2.5;
CALL P1 (i, a(i));
...
END; ! P2
...

P1 hívása után P2-ben az i értéke marad 2, de a(i) értéke 5.0.

Az aktuális paraméterek típusa

lehet.

Nincs explicit érték rendelve DATION, SEMA, BOLT, INTERRUPT vagy SIGNAL típusú objektumokhoz, ezeknél csak cím szerinti paraméterátadás lehetséges, azaz a formális paramétereket IDENTICAL attribútummal kell ellátni.

Referenciák eljárásokra(REF PROC)

Az eljárásokra való hivatkozás lehetősége az egyik első lépés az objektumorientált programozás felé. Ezeknek segítségével absztrakt adattípusok készíthetők.

Eljárásra vonatkozó referencia deklarációja tartalmazza a paraméterek, valamint a visszatérési érték típusát.

Eljárás hívása a referencia nevével, és utána az aktuális paraméterek felsorolásával történik. Paraméterek nélküli eljárások esetén CALL, vagy függvényeknél még a CONT használható.

Példák:

1.

DCL ProcPointer REF PROC (a FIXED, b FIXED, c FIXED IDENT);
add: PROC (a FIXED, b FIXED, c FIXED IDENT);
c := a + b;
END;
DCL (A, B, C) FIXED;
ProcPointer := add;
ProcPointer (A, B, C);

2.

DCL FuncPointer REF PROC RETURNS(CLOCK);
time: PROC RETURNS(CLOCK);
RETURN(NOW);
END;
DCL(A, B) CLOCK;
FuncPointer := time;
A := FuncPointer;
B := CONT FuncPointer;