A Cecil programozási nyelv

Operátorok és precedencia

Operátorok és precedencia

Operátorok a Cecilben

op_name ::= punct {punct} [id_cont]
  | “_” {“_”} name     az első aláhúzás nem része az üzenet nevének
id_cont ::= “_” {“_”} [name | op_name]

punct ::= one of “!#$%^&*-+=<>/?~\|”

A feni definícióban az op_name definíciója utáni megjegyzés amiatt a szintaktikus cukor miatt van, amiről fent, az Üzenetek című pont alatt már volt szó. Az operátorok szempontjából a Cecil jelentősen eltér a ma divatos, mainstream nyelvektől. Egyrészt előre definiálni szokták az operátorok egy véges halmazát, míg Cecilben végtelen sok operátor definiálható. Másrészt megszokott, hogy vannak előre definiált operátorok, amelyek a nyelv részét képezik, és a fordító valósítja meg őket. A Cecilben előre definiált operátorok sincsenek, ami megfelel a nyelv tervezőinek azon felfogásának, hogy a lehető legtöbb dolog legyen definiálható felhasználói szinten. A szokásos operátorok természetesen rendelkezésre állnak a standard könyvtárban, de a nyelv csupán azt definiálja, hogy hogyan lehet operátort készíteni. Innentől kezdve rajtunk, a felhasználókon - és a standard könyvtár íróin - múlik, hogy milyen operátorokat használhatunk a programok írása során. Ezek után nem meglepő tehát, hogy a nyelv az operátor precedenciákat illetően is sokkal gazdagabb eszközkészletet nyújt, mint azt máshol megszokhattuk.

Precedencia

Cecilben nincsenek előre meghatározott legkisebb és legnagyobb precedenciájú operátorok, sőt, egyáltalán nincsenek a nyelv által előre definiált precedencia szintek. Az operátorok precedencia szintjét precedencia deklarációkban lehet meghatározni. Az egyetlen megszorítás, hogy a precedencia deklarációk parciális rendezést definiáljanak az operátorok között. Ilyen értelemben minden operátornak saját precedencia szintje van, pontosabban az operátorok precedencia csoportokba sorolhatók, mely csoportok tényleges precedenciáját a többi csoporthoz képest a precedencia deklarációk határozzák meg. Az operátorok asszociativitásának meghatározása is a precedencia deklarációban történik.

A precedendcia deklaráció szintaxisa:

prec_decl ::= ``precedence'' op_list [associativity] {precedence} ``;''
associativity ::= ``left_associative'' | ``right_associative''   | ``non_associative''
precedence ::= ``below'' op_list | ``above'' op_list | ``with'' op_list
op_list ::= op_name { ``,'' op_name }

szemantikája pedig a következő: alapértelmezésben minden operátornak saját egyedi precedenciája van, ami független bármely más operátor precedenciájától, és minden operátor alapértelmezésben nem asszociatív. Ha egy kifejezésben olyan operátor szerepel többször egymás után, ami nem asszociatív, akkor kötelező zárójelekkel explicit módon jelezni, hogy milyen sorrendben kívánjuk a kiértékelést elvégezni.

A precedencia deklaráció hatása, hogy valamilyen kapcsolatot létesít operátorok precedencia szintjei között. Ugyanúgy mint SML-ben a precedencia deklaráció által hordozott információt a tartalmazó szkópon belül láthatjuk, és beágyazott szkópban ugyanazon operátorra vonatkozó deklaráció eltakarja a külső láthatósági szinten lévő deklarációt. Egy operátorra egy láthatósági szinten belül nem lehet több precedencia deklaráció.

Általánosan egy

precedence bin­op_1 , ..., bin­op_n
  asociativity
  below bin­op_B1 , ..., bin­op_Bn
  above bin­op_A1 , ..., bin­op_An
  with bin­op_W1 , ..., bin­op_Wn ;

alakú deklaráció a következőket vezeti be: minden bin-op_i azonos precedencia csoportba tartozik, és ez a csoport (a csoport minden tagja) gyengébb az összes bin-op_Bi, és erősebb az összes bin-op_Ai operátornál. Ha van bin-op_Wi, akkor minden bin-op_i azonos precedencia csoportba tartozik azokkal. Ilyenkor a bin-op_Wi-k eleve azonos precedencia csoportba kell tartozzanak. Különben bin-op_i-k új precedencia csoportot képeznek. Bin-op_i asszociativitását az "associativity" határozza meg, ha fel van tüntetve. Ha nincs feltüntetve, akkor az asszociativitása ugyanaz, mint a bin-op_Wi csoporté, ha pedig ilyen sincs, akkor alapértelmezés szerint nem asszociatív. Ez a definíció azért értelmes, mert az asszociativitásnak precedencia csoporton belül mindig azonosnak kell lennie. (Ezért természetesen az asszociativitás jelölése felesleges abban az esetben, ha szerepel with utáni operátor a deklarációban.)

Ezeket a szabályokat összevetve valóban egy parciális rendezést kapunk, ha kikötjük, hogy a precedencia csoportok közötti relációk által meghatározott gráfban nincsen irányított kör. Ezek után ha egy kifejezésben valahol két szomszédos operátor összehasonlítható a deklarációk által definiált parciális rendezéssel, akkor a zárójelek elhagyhatók. Egyéb esetben a zárójeleket kötelező kitenni. Azonos precedencia csoportba tartozó operátorok esetében az asszociativitási szabályokat kell figyelembe venni, ha az adott csoport nem asszociatív, szintén kötelező a zárójelezés.

Tekintsünk egy példát::

precedence ** right_associative; ­­ hatványozás
precedence *, / left_associative below ** above +;
precedence +, ­ left_associative below * above =;
precedence =, !=, <, <=, >=, > non_associative below * above;
precedence & left_associative below = above |;
precedence | left_associative below &;
precedence % with *;
precedence ! left_associative above =; -- tömb indexelő operátor

Ebben az esetben a

v ! (i + 1) < (v ! i) + 1

kifejezésben mindkét zárójel szükséges, mert a + és ! operátorok nem összehasonlíthatók a deklarációk által definiált parciális rendezéssel. Ugyanakkor + és ! erősebben kötnek, mint <, így további zárójelezés nem szükséges.

Megjegyzések:

  1. Cecilben egy adott blokkon belüli deklaráció (így a precedencia deklaráció is) az egész blokkban látható, beleértve a deklarációt megelőző programszöveg részt is.
  2. Egy precedencia deklaráció infix üzenet nevekre vonatkoznak, és nem egy metódusra. Ezt azért fontos megjegyezni, mert egy üzenetet több metódus is megvalósíthat, különböző típusú argumentumokra specializálva. Egy blokkon belül minden adott nevű metódus a megvalósított üzenet precedenciáján osztozik.
  3. A precedencia csoport fogalmát a deklarációk szamantikájával egyidejűleg definiáltuk. Ezzel egyenértékű definícióhoz jutunk, a következő úton: Azt mondjuk, hogy akkor tekintünk két operátort azonos precedenciájúnak, ha a náluk gyengébb illetve erősebb operátorok halmaza megegyezik. Ekkor egy ekvivalencia relációt definiáltunk, amely ekvivalencia reláció szerinti osztályokat hívjuk precedencia csoportnak.