A Limbo programozási nyelv

Utasítások, vezérlési szerkezetek

Értékadás

Értékadó operátorok

= &= |= ^= <<= >>= += -= *= /= %=

Egyszerű értékadás (=)

Általában a jobb és bal oldali operandusnak azonos adattípusúnak kell lennie. Az értékadás értéke az új bal oldali operandusa. Minden értékadó operátor jobbaszociatív.

Az =-vel jelölt közönséges értékadásban a jobb oldal értékét értékül adjuk a bal oldali objektumnak. Az egyszerű értékadásoknál a bal oldali operandus lehet balértékek zárójelezett listája, a jobb oldali operandus pedig sor, vagy olyan adt, melynek tagjai számosságban és típusban is megegyeznek a listabeli balértékekkel. A sor elemei, vagy az adt adattagjai sorban hozzá vannak rendelve a lista balértékeihez. Például:

p: Point; x, y: int; (x, y) = p;


az p pont koordinátáit x és y egészekre bontja.

Ezen szabályok rekurzívan hajtódnak végre, így ha a bal oldal valamely komponense balértékek zárójelezett listája, a jobb oldalon szereplő megfelelő adt-hez vagy sorhoz lesz hozzárendelve.

Ha az egyszerű értékadás bal oldali operandusa egy adt, a jobboldali pedig egy sor, akkor az értékadás a sor tagjainak értékét hozzárendeli az adt adattagjaihoz, ezeknek számosságban és típusnak egyeznie kell a sor tagjaival.

A konstans nil értékül adható bármely referencia típusú balértéknek.

Az értékadásás bal oldalán is szerepelhet nil, ami azt jelzi, hogy a neki megfelelő értéket figyelmen kívül hagyjuk. Ez vonatkozik bal oldalon szereplő sor bármely balértékére. Az előző példánkat felhasználva:
(x, nil) = p;

a p pont x tagját az x változó értékére állítja.

Egy speciális megfontolás érvényes a sztringekre. Ha egy Unicode karaktert tartalmazó int-et adunk értékül egy indexelt sztringnek, az indexnek általában egyeznie kell a sztringen belül. Speciális esetben az index értéke megegyezhet a sztring hosszával, ekkor a karaktert hozzáfűztük a sztringhez, és annak hossza eggyel növekedett.

Az e1[e2:] alakú tömb szeletek is speciális esetet képeznek. Ilyen kifejezések az = bal oldalán fordulhatnak elő. A jobb oldalnak az e1 típusával megegyező tömbnek kell lennie, melynek hossza kisebb vagy egyenlő kell legyen mint (e1 hossza) - e2. Ebben az esetben az e1 elemei e2 pozíciótól kezdődően kicserélődnek a jobb oldali tömb elemeire. A tömb hossza változatlan marad.

Összetett értékadás (op=)

A legtöbb kétoperandusú operátor szerepelhet az értékadó operátorban, melynek általános alakja op=. Ha e1 és e2 két kifejezés, akkor

e1 op= e2;

egyenértékű a
e1 = (e1) op (e2);

alakkal. A tömörebb forma előnye, hogy e1 csak egyszer számolódik ki.

Szekvencia

A ";" elválasztja az utasításokat, üres utasítás is lehet.

Utasítások sorozatából alkothatunk egy összetett utasítást, blokkot. A blokk kezdetét és végét "{" és "}" jelzi. A blokkban elhelyezhetünk deklarációt, az ott deklarált változó láthatósága a blokk végéig tart.

Elágazás

Az egyirányú elágazás általános formája:

if ( kifejezés ) utasítás(ok) if ( kifejezés ) utasítás(ok) else utasítás(ok)

Ha a kifejezés értéke, mely int típusú kell legyen, igaz (nem nulla), akkor az első utasítás vagy utasítások sorozata hajtódik végre. Ellenkező esetben a második. Például:
if(guess == randnum) sys->print("Correct!\n"); else sys->print("Oh, sorry.\n");



Az if utasítás beágyazható. Az else mindig az ugyanazon blokkban lévő legközelebbi else-nélküli if-hez tartozik. Például:
if(i == j) { if(k == l) { ... } if(m == n) { ... } else { # ez az else ... # 'if(m == n)'-hez tartozik } } else { # ez az else ... # 'if(i ==j)'-hez tartozik }


A többirányú elágazás általános alakja:

case kifejezés { minősítő => utasítás ... }

A kifejezés int vagy string típusú kell legyen. A minősítő(k) lehet konstans illetve intervallum, és az értékek között nem lehet átfedés. Ha nincs megfelelő minősítő, és adva van az alapértelmezett ág (*), akkor ennek utasításai hajtódnak végre. Mindegyik minősítő és az őt követő utasítássorozat blokkot alkot.
A case utasítás szintaxisa a C/C++ switch utasításához hasonló. Például:

case i { 1 or 8 => sys->print("Begins with a vowel\n)"; 0 or 2 to 7 or 9 => sys->print("Begins with a consonant\n"); * => sys->print("Sorry, didn't understand\n"); }

A C-vel ellentétben, a kiválasztott minősítőhöz tartozó blokk végrehajtása után a vezérlés nem csorog rá a kővetkező minősítőre. Nincs szükség explicit break utasításra.
A case utasításoik egymásba ágyazhatóak, például:

case x { 1 => case y { 0 => ... 1 => ... } 2 => ... }

Ciklus

A Limbo nyelvben három ciklus utasítás van: while, do és for ciklusok.
Mindegyik ciklusutasításhoz tartozhat egy címke, melyet a break és continue ugró utasításokkal együtt használhatunk.

Ismert lépésszámú ciklus (for)

Általános alakja:

for(exp1; exp2; exp3) mag;

A zárójelben lévő három kifejezés bármilyen érvényes Limbo kifejezés lehet.A leggyakrabban használt kifejezések a következők:
* exp1 az index változó inicializálása vagy rá vonatkozó értékadás
* exp2 teszt a ciklus folytatásához
* exp3 az index változó egyes iterációk során bekövetkező módosítása (csökkentése v. növelése)

A mag lehet üres, egyszerű utasítás, vagy blokk. Például:

i: int; for(i = 1; i <= 100; i++) sys->print("%d ", i);

A fenti példában az i indexváltozót a ciklus előtt inicializáltuk, de ez nem kötelező, a köv. kódrészlet is helyes:

for(i := 1; i <= 100; i++) sys->print("%d ", i);

Nem ismert lépésszámú ciklusok

While

Általános alakja a következő:

while(feltétel) utasítás;


Ha a feltétel igaz, végrehajtódik az utasítás. Az utasítás lehet üres, egyszerű utasítás, vagy blokk.
Példa:
n := 1; while(n <= 100) sys->print("%d ", n++);

Do... while

Az előző kettővel ellentétben, a do... while hátultesztelős ciklus. Az iteráció végén ellenőrzi a feltétel teljesülését, így a ciklus magja legalább egyszer végrehajtódik.
Általános alakja a következő:
do mag; while(feltétel);

A mag lehet üres, egyszerű utasítás vagy blokk. A mag végrehajtása addig ismétlődik, amíg a feltétel igaz.
Példa:
n := 1; do { sys->print("%d ", n++); } while(n <= 100);

Ha a mag egyetlen blokk utasításból áll, az olvashatóság végett azt is zárójelek közé (()) szokták tenni.

Vezérlésátadó utasítások

A nyelv két vezérlésátadó utasítással rendelkezik: break és continue.
A kulcsszó után egyik esetben sem kötelező azonosítót megadni. Ha nem adunk meg azonosítót, a vezérlés átadódik a ciklus legbelső részutasítása végére, amelyben kulcsszó rézutasításként szerepel.

A break utasítás

Lehetővé teszi a ciklusok idő előtti elhagyását, valamint a case és alt utasításokból való kilépést.
Ha a ciklus beágyazott, a break csak az őt tartalmazó blokkban lévő ciklust terminálja. Példa:

for(t:=1; t<=5; t++) { i := 1; for(;;) { sys->print("%d ", i++); if(i == 10) break; } }


A fenti kódrészlet ötször kiírja a számokat 1-től 9-ig. A break utasítás csak a belső for ciklust terminálja.

A continue utasítás

A continue utasítás a break utasításhoz kapcsolódik. A ciklusmagban található continue utasítás hatására azonnal (a ciklusmagból még hátralévő utasításokat figyelelmen kívül hagyva) megkezdődik a következő iterációs lépés.
Példa:

for(;;) { n := sys->read(fd, buf, len buf); if(n <= 0) break; if(int buf[0] != 'm' || n != 37) continue; }

A fenti kódrészlet bájtokat olvas folyamatosan egy fájlból, amíg end-of-file jelet nem talál.

Címkék

A címkék nevet adnak a for, while és do... while ciklusoknak, valamint a case és alt utasításoknak. Felhasználhatjuk őket címkézett szerkezet belsejében lévő break és continue utasításokban, hatásukra átadódik a vezérlés a címkézett blokk végére. Nincs go to utasítás.

A címkéket két tipikus esetben használják. Az egyik, amikor belső ciklus számára lehetővé teszik az őt befoglaló külső ciklusra break vagy continue kiadását. A másik felhasználás, amikor a ciklust befoglaló külső utasítást megszakítják belső case vagy alt utasításból.

Tekintsük a következő példát. Az utasításokhoz egy vagy több opció is csatolható:

Args: for(i := 0; i < len in; i++){ case in[i] { 'v' => opt |= Verbose; 's' => opt |= Suppress; 'o' => opt |= OutFile; * => break Args; } }


Mindaddig, amíg az in string i. eleme érvényes opció (v, s vagy o), a feldolgozás folytatódik, megkezdődik a for ciklus újabb iterációja. Mihelyt más karaktert talál, a break utasítás terminálja a végrehajtást.
Ha a címkét nem break utasítással használják, a for ciklus újabb iterációt hajt végre.

A következő példában láthatjuk, hogyan használható fel a címke többszörösen beágyazott ciklusból való kilépésre:

u := 0; line := "<A HREF=" URL := ""; Outer: for(i := 0; i < len line; i++) if(line[i] == '<') { while(line[++i] != '>') { if(line[i:i+6] == "A HREF") { i += 6; if(line[i] == '=') { i += 2; while(line[i] != '"') URL[u++] = line[i++]; } } break Outer; } }