Oberon-2

Eljárások

4.1. Eljárás deklarációk

Egy eljárásfejbõl és egy eljárástörzsobõl áll. A fej a szignatúrát (eljárás azonosítója, formális paraméterek, visszatérési érték) tartalmazza, míg a törzs a deklarációkat és az utasításokat. Az eljárás azonosítóját meg kell ismétleni az eljárás deklarációjának végén. ( END ) Kétféle eljárás van: a normál eljárás és a függvényeljárás. Az eljárásban deklarált objektumok (változó, konstans, típus, eljárás) lokálisak! Tetszõleges szinten egymásba lehet ágyazni eljárásokat, az eljárásban meghívhatjuk rekurzívan saját magát is. Az eljárásban a lokális objektumokon és a formális paramétereken kívül látszanak még azok az objektumok, amelyek az eljárás környezetében lett deklarálva, feltéve hogy nem takarják el azonos nevû lokális objektumok.

ProcedureDeclaration = ProcedureHeading ";" ProcedureBody ident

ProcedureHeading = PROCEDURE [Receiver] IdentDef [FormalParameters]

ProcedureBody = DeclarationSequence [BEGIN StatementSequence] END

DeclarationSequence = {CONST {ConstantDeclaration ";"} | TYPE {TypeDeclaration ";"} | VAR {VariableDeclaration ";"}} {ProcedureDeclaration ";" | ForwardDeclaration ";"}

ForwardDeclaration = PRROCEDURE "^" [Receiver] IdentDef [FormalParameters]

Az elõre deklarálás (forward declaration) segítségével hivatkozhatunk elõbb egy eljárásra, minthogy definiáltuk volna. (A definiálásnak azért még ugyanebben a láthatósági blokkban meg kell történnie.)

Formális paraméterek

A formális paraméter az aktuális paraméterre utal. A megfeleltetés az eljárás hívásakor történik. Az érték szerint átadott paraméterek deklarációjánál nem kell a VAR kulcsszó, míg a cím szerintinél igen. Egy függvény típusú eljárásnak mindenképpen kell paraméter listája (akár az üres () lista). A formális paraméterek lokálisak az eljárásban, mintha az eljárás hívásakor deklaráltuk volna oket.
FormalParameters = "(" [FPSection {";" FPSection}] ")" [":" Qualident]

FPSection = [VAR] ident {"," ident} ":" Type

Mindegyik formális paraméter típusát meg kell határozni a paraméterlistában. A cím szerinti paramétereknél a típusnak bázis típusának kell lennie az aktuális paraméter típusának. Érték szerinti paraméternél az értékadás szabálya érvényes. Ha a formális paraméter ARRAY OF T, akkor az aktuális paraméter bármilyen T elemû tömb lehet. Ez az úgynevezett nyitott tömb (open array). Ha a formális paraméter BYTE típusú, akkor az aktuális paraméter lehet CHAR vagy SHORTINT típusú is. Ha a formális paraméter ARRAY OF BYTE típusú, akkor bármilyen típusú aktuális paraméter megengedett! Ha a formális paraméter eljárás típusú, akkor az aktuális paraméter lehet egy legkülsõ (0. szinten beágyazott) szinten deklarált eljárás vagy megfelelõ típusú eljárásváltozó. Nem lehet azonban elõredefiniált eljárás. Az eljárás visszatérési értéke nem lehet sem rekord, sem tömb.

Példák:
PROCEDURE ReadInt ( VAR x : INTEGER ); VAR i : INTEGER; ch : CHAR; BEGIN i := 0; Read( ch ); WHILE ( "0" <= ch ) & ( ch <= "9" ) DO i := 10 * i + ( ORD ( ch ) - ORD ( "0" )); Read ( ch ); END; x := i; END ReadInt PROCEDURE WriteInt ( x, n : INTEGER ); (* 0<= x <= 10000 *) VAR i : INTEGER; buf : ARRAY 5 OF INTEGER; BEGIN i := 0; REPEAT buf [ i ] := x MOD 10; x := x DIV 10; INC ( i ); UNTIL x = 0; WHILE ni DO Write ( " " ); DEC ( n ); END; REPEAT DEC ( i ); Write ( CHR ( buf [ i ] + ORD ( "0" ))); UNTIL i = 0; END WriteInt PROCEDURE log2 ( x : INTEGER ) : INTEGER; VAR y : INTEGER; BEGIN y := 0; WHILE x1 DO x := x DIV 2; INC ( y ); END; RETURN y; END log2

Típushoz kötött eljárások

Az Oberon-2 objektum-orientált nyelv, így lehetõség van objektumosztályok definiálására, amelyek nemcsak adattagokkal rendelkeznek, mint a rekordok, hanem metódusokkal is. Az objektumosztályokat ugyanúgy kell definiálni, mint a rekordokat. Definíciós modulban a metódusok fejlécét is a rekord mezõi közé kell írni, míg az implementációnál külön kell definiálni oket. Ezeket nevezzük típushoz kötött eljárásoknak. Az osztályt, melynek objektumaihoz az eljárás tartozik, fogadónak nevezzük, és ezt, mint kitüntetett formális paramétert az eljárás neve elé kell írni.
ProcedureHeading = PROCEDURE [Receiver] IdentDef [FormalParameters]

Receiver = "(" [VAR] ident ":" ident ")"

Példák
PROCEDURE ( t : Tree ) Insert ( node : Tree ); VAR p, father : Tree; BEGIN p := t; REPEAT father := p; IF node.key = p.key THEN RETURN END; IF node.key < p.key THEN p := p.left ELSE p := p.right END UNTIL p = NIL; IF node.key < father.key THEN father.left := node ELSE father.right := node END; node.left := NIL; node.right := NIL END Insert; PROCEDURE ( t : CenterTree ) Insert ( node : Tree ); (* redefinition *) BEGIN WriteInt ( node ( CenterTree ).width ); t.Insert^ ( node ) (* calls the Insert procedure bound to Tree *) END Insert;

Elõredefiniált eljárások

Függvény eljárások:


Név Argumentum típus Eredmény típus Leírás

ABS ( x ) numerikus típus x típusa abszolút érték
ADR ( v ) akármilyen LONGINT v változó címe
ASH ( x, n ) x, n : egész LONGINT x * 2 ^ n, aritmetikai eltolás
CAP ( x ) CHAR CHAR megfelelo nagybetû
CHR ( x ) egész típus CHAR x ASCII kódú karakter
ENTIER ( x ) valós típus LONGINT legnagyobb x-nél nem nagyobb egész
LEN ( v, n ) v : tömb LONGINT v mérete az n. dimenzió mentén
n : INTEGER (az elso dimenzió a 0.)
LEN ( v ) v : tömb LONGINT LEN ( v, 0 )-val ekvivalens
LONG ( x ) SHORTINT INTEGER konverzió nagyobbra
INTEGER LONGINT
REAL LONGREAL
MAX ( T ) T=alaptípus T T maximális értéke
T=SET INTEGER halmaz maximális eleme
MIN ( T ) T=alaptípus T T minimális értéke
T=SET INTEGER halmaz minimális eleme (0)
ODD ( x ) egész típus BOOLEAN x MOD 2 = 1
ORD ( x ) CHAR INTEGER x ASCII kódja
SHORT ( x ) LONGINT INTEGER konverzió kisebbre (csonkítás lehetséges)
INTEGER SHORTINT
LONGREAL REAL
SIZE ( T ) akármilyen egész típus T mérete byte-okban

Egyéb eljárások:


Név Argumentum típus Leírás

ASSERT ( x ) x : logikai kifejezés kilép a programból, ha x értéke hamis
ASSERT ( x, n ) x : logikai kifejezés, n : egész konstans kilép a programból n visszatérési értékkel, ha x értéke hamis
COPY ( x, v ) x : karaktertömb vagy karakterfüzér, v : karaktertömb v := x
DEC ( v ) egész v := v - 1
DEC ( v, n ) v, n : egész v := v - n
EXCL ( v, x ) v : SET; x egész v := v - { x }
HALT ( x ) egész konstans kilép a programból x visszatérési értékkel
INC ( v ) egész v := v + 1
INC ( v, n ) v, n : egész v := v + n
INCL ( v, x ) v : SET; x egész v := v + { x }
NEW ( v ) mutató rekordra vagy rögzített méretû tömbre allokál egy v^ típusú objektumot
NEW ( v, x0, ..., xn ) v : mutató nyílt tömbre, xi : egész allokál egy v^ típusú töböt x0..xn méretekkel

4.2. Fordítási egységek

A fordítási egység egy modul vagy egy definíció (definíciós modul). A modul deklarációk (konstans, típus, változó, eljárás) gyûjteménye és utasítások szekvenciája. A definíció egy modulra vonatkozik. Azokat a deklarációkat tartalmazza, amelyek láthatóak egy kliens modul számára. Oberon-2-ben ezt nem kell feltétlenül a programozónak elkészítenie, hanem automatikusan is generálható a modulból. Ekkor a modulban egy "*" karakternek kell követnie a kívülrõl látható azonosítók nevét, illetve a csak olvasható változókat egy "-" jelnek.

CompilationUnit = Module | Definition Module = MODULE ident ";" [ImportList] DeclarationSequence [BEGIN StatementSequence] END ident "."

ImportList = IMPORT import {"," import} ";"

Import = ident [":" ident]

Definition = DEFINITION ident ";" [ImportList] DefSequence END ident "."

DefSequence = [CONST {ConstDeclaration ";"}] [TYPE {TypeDeclaration ";"}] [VAR {VariableDeclaration ";"}] {ProcedureHeading ";"}

Az import listában a fordítási egységben kliensként használt modulokat kell felsorolni. Egy M modulból exportált x objektumra M.x-ként lehet hivatkozni. Ha imoprtálásnál "M0 : M"-et használtunk, akkor az M0.M.x helyett használhatjuk az M0.x-et. A BEGIN utáni utasítássorozat akkor hajtódik végre, amikor a modul a rendszerhez adódik (betöltõdik). Ez az inicializáló rész. A definícióban egy rekord típusnak csak egy részét is deklarálhatjuk (ez lesz a publikus projekciója). Az itt deklarált részek láthatóak a klienseknél, a többi nem.

Példa:
MODULE Trees; IMPORT Texts, Oberon; (* exports: Tree, Node, Insert, Search, Write, Init *) (* exports read-only: Node.name *) TYPE Tree* = POINTER TO Node; Node* = RECORD name- : POINTER TO ARRAY OF CHAR; left, right: Tree END; VAR w : Texts.Writer; PROCEDURE ( t : Tree ) Insert* ( name : ARRAY OF CHAR ); VAR p, father : Tree; BEGIN p := t; REPEAT father := p; IF name = p.namel^ THEN RETURN END; IF name < p.name^ THEN p := p.left ELSE p := p.right END UNTIL p = NIL; NEW ( p ); p.left := NIL; p.right := NIL; NEW( p.name, LEN ( name ) + 1 ); COPY( name, p.name^ ); IF name < father.name^ THEN father.left := p ELSE father.right := p END END Insert; PROCEDURE ( t : Tree ) Search* ( name : ARRAY OF CHAR ) : Tree; VAR p : Tree; BEGIN p := t; WHILE ( p # NIL ) & ( name # p.name^ ) DO IF name < p.name^ THEN p := p.left ELSE p := p.right END END; RETURN p END Search; PROCEDURE ( t : Tree ) Write*; BEGIN IF t.left # NIL THEN t.left.Write END; Texts.WriteString ( w, t.name^ ); Texts.WriteLn ( w ); Texts.Append ( Oberon.Log, w.buf ); IF t.right # NIL THEN t.right.Write END END Write; PROCEDURE Init* ( t : Tree ) BEGIN NEW ( t.name, 1 ); t.name [ O ] := OX; t.left := NIL; t.right := NIL END Init; BEGIN Texts.OpenWriter ( w ) END Trees.
Az ebbol automatikusan generált definíciós állomány:
DEFINITION Trees; IMPORT Texts, Oberon; TYPE Tree = POINTER TO Node; Node = RECORD name- : POINTER TO ARRAY OF CHAR; left, right: Tree PROCEDURE Insert ( name : ARRAY OF CHAR ); PROCEDURE Search ( name : ARRAY OF CHAR ) : Tree; PROCEDURE Write; END; PROCEDURE Init ( t : Tree ); END Trees;