• Nem Talált Eredményt

fejezet - Az aritmetikai kifejezések

Az aritmetikai kifejezés egy olyan szimbólumsorozat, aminek a végeredménye valami szám. Hasonlóképp, a stringkifejezés olyan szimbólumsorozat, aminek a végeredménye egy string. A két fogalom azonban nem különül el egymástól élesen, mert a stringek többnyire megfeleltethetőek bizonyos számoknak (különösen, ha számjegyek sorozatából állnak...), meg amiatt is, mert gondoljunk csak bele, ha rész-stringet kell képezni, meg kell mondani számmal, hogy a rész-string az eredeti string hányadik karakterénél kezdődjön, s e szám már nemcsak konstans lehet, hanem akármilyen aritmetikai kifejezés is... Arról nem is beszélve, hogy egy szám is átalakítható stringgé, s persze ezesetben sem csak konstans számról lehet szó, hanem tetszőleges aritmetikai kifejezésről...

Mindezen dolgok miatt az aritmetikai vagy string kifejezések akár rémségesen összetettek is lehetnek. Egy ilyen kifejezés úgynevezett „tagokból” áll, melyeket műveleti jelek, vagy általánosabban fogalmazva operátorok kapcsolnak össze egymással, de egy tag maga is lehet több tag kombinációja.

Egy tag például egy változó, vagy egy konstans, vagy egy beépített mau rendszerfüggvény, vagy egy a Felhasználó által megvalósított mau nyelvű függvény hívása, egy címke, egy általános veremtárra utalás, egy speciális veremtárra utalás amiben függvény input paramétere van, valami speciális objektum által biztosított adat például amikor egy megnyitott input fájlból lekérjük a következő karaktert, vagy amikor egy tartalomjegyzék-változótól lekérdezzük hogy benne hány szimbolikuslink-bejegyzés van, vagy valami fontos rendszerváltozó értéke, vagy a mau forrásprogram egy karaktere, vagy valami rendszerflag értéke... szóval, rengeteg minden „micsoda” lehet egy kifejezésben, ami mind egy-egy „tag”. Na most, ezeket operátorok kapcsolják össze, ezek egy része ismerős lehet más programnyelvekből, például az összeadás (+), a kivonás (-), szorzás (*) és osztás (/) operátora, de van sok más is.

Az operátorok kiértékelésének sorrendjére más programnyelvekben bonyolult precedenciaszabályok vonatkoznak. A mau nyelvben ilyesmi egyszerűen NINCS. Itt az aritmetikai kifejezések alapértelmezés szerint jobbról balra értékelődnek ki. NEM TÉVEDÉS! Az utasításfeldolgozás természetesen balról jobbra halad továbbra is, de mert a kifejezéskiértékelő rutinok minden operátornál önmagukat rekurzívan meghívják, emiatt legelsőként a legutolsó operátor lesz végrehajtva. Azaz:

6*3+10 értéke 78 lesz, nem 28, ahogy azt a matematikában általában meg-szoktuk. Ezt azonban előírhatjuk zárójelekkel:

(6*3)+10

Természetesen akárhány zárójel is egymásba ágyazható.

Ami a precedenciaszabályokat illeti, ott ugyanis nem sok értelmét láttam annak, hogy ilyesminek a megvalósításával görcsöljek. Akad pár más programnyelv is, ha nem is a legismertebbek, melyekben nincsenek precedenciaszabályok, sőt, még ilyen zsebszámológépek is vannak. Továbbá, még talán rá is szánnám magamat erre, ha csak arról lenne szó, hogy a „+” és

„-” jelek alacsonyabb precedenciájúak mint a „*” és a „/”, de hát ugyebár van ezeken kívül még egy egész rakás más operátorunk is, no és ezek közt cseppet se egyszerű meghatározni valami logikus precedenciát! Ott van példának okáért a C nyelv is, tele van operátorokkal, és ezeknek rengeteg szintje létezik.

Ember legyen a talpán, aki fejből vágja, melyiknek épp mi a precedenciaszintje! Még én magam is úgy vagyok vele hogy nagy ívben tojok az egészre, és ha kicsit is bonyolultabb esetről van szó, egyszerűen úgy zárójelezem a kifejezést, hogy az teljesen egyértelmű legyen. Holott talán senki se vitatja hogy tényleg tudok C nyelven programozni, hiszen magát a mau nyelvet is javarészt C nyelven írtam (és kisebbrészt C++ nyelven). Ennek ellenére, ez a véleményem a C és C++ precedenciaszabályairól. Az, hogy ha picit is bonyolultnak tűnnek, zárójelezni kell és kész. Minden más megoldás kizárólag arra jó, hogy rengeteg tévedésre adjon lehetőséget!

Arról már nem is beszélve, hogy más programnyelvekben ugyanazon operátorok precedenciája teljesen eltérő is lehet... Gyakorlatilag annyi precedenciaszabály létezik, ahány programnyelv. Teljesen felesleges e zűrzavart fokoznunk azzal, hogy mi is bevezetünk valami megint újabb rendszert a mau nyelvbe. Mi a szösznek, hogy még mi magunk is belekeveredjünk?!

Nálunk, a mi mau nyelvünkben ilyesmi tehát nem fordulhat elő. Egyetlen szabály van csak, amit emiatt rém könnyű megjegyezni: MINDIG jobbról balra értékelődik ki a kifejezés, s bármi más sorrendet óhajtunk, azt zárójelezéssel érhetjük el! Ez világos és egyértelmű.

Ebből azonban következik pár dolog, amit észben kell tartanunk. Tegyük fel, az A változó nulladik unsigned char mezőjének értékéhez hozzá akarunk adni egyet. E mező jele:

#c@A:0

De mert alapértelmezés szerint úgyis a nulladikat tekinti ha nem jelöljük explicite a mezőindexet, így elég ennyit írnunk:

#c@A

Adjunk hozzá egyet:

#c@A+1

Nos, a fenti kifejezés szintaktikailag helyes, sajnos azonban nem az A változó értékének eggyel megnövelt értékét adja vissza nekünk, hanem a B változó aktuális (meg nem növelt) értékét! Ugyanis a @ karakter után ő egy aritmetikai kifejezést vár el, ami meghatározza neki a kiértékelendő változó NEVÉT, és az A+1 a mau nyelvben egy tökéletesen érvényes aritmetikai kifejezés, ami azt a számot jelenti, ami az „A” karakter ASCII kódjánál eggyel nagyobb érték. Az pedig azonos a „B” karakter ASCII kódjával ugyebár...

Azaz, amit akarunk, azt zárójelezéssel érhetjük el. Ezt kell írnunk:

(#c@A)+1

Vagy ezt:

#c(@A)+1

A mau nyelv operátorai (jelen pillanatban...):

Kétoperandusú operátorok + összeadás (stringekre is)

- kivonás (értelmezve van az is, amikor stringből vonunk ki egész számot, ennek magyarázatát lásd a stringek leírásánál...)

* szorzás

/ osztás

&& logikai ÉS

|| logikai VAGY

~~ logikai KIZÁRÓ VAGY (EXOR)

>< maradékképzés (csak egész számoknál)

<< bitenkénti eltolás balra

>> bitenkénti eltolás jobbra

== EGYENLŐ összehasonlító művelet

< KISEBB MINT összehasonlító művelet

> NAGYOBB MINT összehasonlító művelet

<= KISEBB VAGY EGYENLŐ összehasonlító művelet

>= NAGYOBB VAGY EGYENLŐ összehasonlító művelet

!= NEM EGYENLŐ összehasonlító művelet

<> NEM EGYENLŐ összehasonlító művelet (teljesen azonos a != művelettel)

&| indirektművelet-operátor, a magyarázatát lásd kicsit később a 7. fejezetben a stringeknél

Az összehasonlító műveletek természetesen stringek esetén is értelmezettek.

Fontos megemlíteni, hogy míg a C nyelvben létezik külön & és &&, valamint | és || operátor, mert az a nyelv megkülönbözteti egymástól az aritmetikai ÉS meg VAGY kapcsolatot a logikai értékek közt képzett efféle viszonyoktól, addig a mau nyelvben ilyen disztinkció nincs, az összehasonlító kifejezések által visszaadott eredményeket ugyanúgy a && és a || operátorokkal hozhatjuk ÉS illetve VAGY kapcsolatba, mintha valami bitműveleteket hajtanánk végre. Ez azért tehető és engedhető meg, mert a mau nyelvben kivétel nélkül minden összehasonlító művelet garantálja, hogy az eredménye KIZÁRÓLAG 0 vagy 1 lehet: 0 a hamis, és 1 az igaz esetben. Márpedig amennyiben csak 1 bitnyi értékekről van szó, mint nálunk az összehasonlító műveleteknél, úgy mindegyiknek az eredménye azonos:

1&1 = 1&&1 = 1, 1&0 = 1&&0 = 0, 0&0 = 0&&0 = 0, 1|1 = 1||1 = 1, 1|0 = 1||0 = 1, 0|0 = 0||0 = 0

Vagyis nálunk a logikai operátor lehet ugyanaz, mint a bitműveletekre szolgáló.

E célra a dupla jeleket választottam, a kettős & és | jeleket, mert jobban olvashatóak a forráskódban. Figyelemfelkeltőbbek.

Egyoperandusú azaz „unáris” operátorok is léteznek a mau nyelvben, melyek a tőlük jobbra eső teljes aritmetikai kifejezésre vonatkoznak. Ezekből azonban nincs sok. Ilyen mindenekelőtt maga a casting operátor, tehát a #c, #C, #i, #s stb karakterpárosok. Ilyen aztán a közismert „-” azaz mínuszjel, ami természetesen a mau nyelvben is az őt követő szám (pontosabban aritmetikai kifejezés értékének) mínusz egyszeresét jelenti. Ilyen a ~ jel, ami egész számok esetében bitenkénti negációt jelent, string esetén pedig azt, hogy a stringet megfordítja bájtonként, azaz az utolsó karakterből lesz az első, az utolsóelőttiből a második, stb. Ügyeljünk rá, hogy ez a több-bájtos kódolású karaktereket (pld UTF-8 kódolás esetén) elrontja! Van aztán még a ! jel, ami string esetén a string hosszát adja vissza bájtokban mérve.

Van aztán a !! jel is, azaz a dupla felkiáltójel, ami a string hosszát BETŰKBEN adja vissza! Ezen fogalom alatt természetesen UTF-8 kódolású karaktereket kell érteni. Minthogy az UTF-8 kódolás rém bonyolult valami, itt a mau interpreter rém egyszerűen jár el, ami a karakterek számolásának algoritmizálását illeti:

feltételezi, hogy az a string egy SZABÁLYOS string, azaz csupa érvényes UTF-8 kódolású karakterből áll! Azaz, úgy tekinti, hogy abban nincsenek más bájtok, csak karakterek, s azok mind szabályos UTF-8 kódolásban leledzenek. Ez azt jelenti, hogy az ő bájtokban mért hosszuk megállapítható az első bájtjuk alakjá-ról, mert az az első bájt vagy egy érvényes ASCII karakter, vagy ha annak kódja 128-nál nagyobb (vagy azzal egyenlő), akkor az abban levő bitekből kikövet-keztethető, még hány bájt tartozik a karakterhez. Amennyiben valami olyan stringet „kajáltatunk” meg vele ami nem ennek megfelelően „működi magát”, akkor e függvény (vagy minek nevezzem) hibás eredményt fog produkálni...

Íme egy kis példa a használatára:

#s@s="Macska";

#s@g="Álomkór";

#s@q="„ez”";

#s@k="miáúéi";

"Unsigned char:\n"

?s @s; " hossza = "; ?c !#s@s; /;

?s @s; " UTF-8 hossza = "; ?c !!#s@s; /;

?s @g; " hossza = "; ?c !#s@g; /;

?s @g; " UTF-8 hossza = "; ?c !!#s@g; /;

?s @q; " hossza = "; ?c !#s@q; /;

?s @q; " UTF-8 hossza = "; ?c !!#s@q; /;

?s @k; " hossza = "; ?c !#s@k; /;

?s @k; " UTF-8 hossza = "; ?c !!#s@k; /;

"Unsigned int:\n"

?s @s; " hossza = "; ?l !#s@s; /;

?s @s; " UTF-8 hossza = "; ?l !!#s@s; /;

?s @g; " hossza = "; ?l !#s@g; /;

?s @g; " UTF-8 hossza = "; ?l !!#s@g; /;

?s @q; " hossza = "; ?l !#s@q; /;

?s @q; " UTF-8 hossza = "; ?l !!#s@q; /;

?s @k; " hossza = "; ?l !#s@k; /;

?s @k; " UTF-8 hossza = "; ?l !!#s@k; /;

Eredménye:

Unsigned char:

Macska hossza = 6

Macska UTF-8 hossza = 6 Álomkór hossza = 9

Álomkór UTF-8 hossza = 7

„ez” hossza = 8

„ez” UTF-8 hossza = 4 miáúéi hossza = 9

miáúéi UTF-8 hossza = 6 Unsigned int:

Macska hossza = 6

Macska UTF-8 hossza = 6 Álomkór hossza = 9

Álomkór UTF-8 hossza = 7

„ez” hossza = 8

„ez” UTF-8 hossza = 4 miáúéi hossza = 9

miáúéi UTF-8 hossza = 6

Mint látható a fentiekből, a ! és a !! unáris operátor eredménye teljesen meg-egyezik azon stringek esetében, melyek nem tartalmaznak mást csak csupa ASCII karaktert.

Van aztán a dupla ~ jel, azaz a ~~ operátor: Ez csak stringek esetén értelmezett, ez is mint a szimpla hullámvonal, megfordítja a stringet, de ez már az UTF-8 kódolású karaktereket nem rontja el! Csupa ASCII karaktert tartalmazó stringek esetén az eredménye természetesen ugyanaz mint amit a szimpla hullámvonal, azaz az egyszerű ~ operátor esetén kapnánk. Egy kis példa a használatára, ahol szépen megmutatkozik a szimpla hullámvonal és a dupla hullámvonal használata közti különbség:

#s@s="Macska";

#s@g="Álomkór";

#s@q="„ez”";

#s@k="miáúéi";

?s @s; /;

?s ~@s; /;

?s ~~@s; /;

"---\n";

?s @g; /;

?s ~@g; /;

?s ~~@g; /;

"---\n";

?s @q; /;

?s ~@q; /;

?s ~~@q; /;

"---\n";

?s @k; /;

?s ~@k; /;

?s ~~@k; /;

"---\n";

Eredménye:

Macska akscaM akscaM ---Álomkór

r³Ãkmolà rókmolÁ

---„ez”

âzeâ

”ze„

---miáúéi i©úáÃim iéúáim

---Gyakori az is, hogy egy stringnek éppen pontosan az utolsó karakterére vagyunk kíváncsiak, ezt szolgáltatja nekünk a |$| operátor. Ha ez nem állna rendelkezésünkre, akkor csak bonyolult módon hivatkozhatnánk rá, lekérdezve a string hosszát, majd abból egyet kivonva. Ezen operátor azonban jelentősen megkönnyíti a dolgunkat, mint ezen alábbi példaprogramon látható. Ha nem értenéd, ne keseredj el, majd a stringekről szóló fejezetben „fel leszel homályosítva” arról, mi mit jelent benne:

#!mau

#s@s="Ez egy mau string";

?s @s; /;

#c@c=#s@s[--(!@s)]; // Ez az első variáció

?c @c; " = " ?k @c; /;

#c@c=|$|@s; // Ez a második variáció

?c @c; " = " ?k @c; /;

XX

Eredménye:

Ez egy mau string 103 = g

103 = g

Van továbbá !' alakú unáris operátorunk is. (Felkiáltójel-Aposztróf). Ez egy BETŰ, azaz #u típusú aritmetikai kifejezés előtt használható mint unáris operátor, és minthogy egy BETŰ nem más mint egy UTF-8 kódolású karakter, s egy efféle nem okvetlenül 1 bájtot foglal el (hanem maximum 6-ot), ezért ez az unáris operátor visszaadja a beolvasott BETŰről azt az információt, hogy ez épp hány bájtot igényel tárolásra az UTF-8 kódolás szerint. Példa erre:

#!mau

#s@s="álmos";

#u@u=#s@s; ?u @u; /; ?c !'@u; /;

#u@u=#c g; ?u @u; /; ?c !'@u; /;

#u@u="„ez”"; ?u @u; /; ?c !'@u; /;

XX

Eredménye:

á 2 g 1

3

Akad még pár unáris operátor az eddig említetteken kívül is:

|<|

|>|

|^|

|!!|

|!'|

de ezek mind szintén az UTF-8 kódolású #U stringekkel vagy az ezeket alkotó

#u BETŰkkel állnak összefüggésben, emiatt ehelyütt teljesen felesleges tárgyalni őket, ezekről részletes információ az ezen #u és #U típusokat bemutató fejezetben található.

Nem kizárt természetesen, hogy a mau nyelv későbbi kiadásaiban az unáris operátorok száma is bővülni fog.

Itt kell megemlítenem, hogy az előző fejezetben taglalt casting operátoroknak is létezik természetesen INDIREKT változata! Ez nem más, mint a

#(

Ami azt jelenti, hogy a # jelet közvetlenül kell kövesse egy nyitó kerek zárójel, nem állhat köztük whitespace. Ezesetben az interpreter beolvassa az ezután következő aritmetikai kifejezést mint unsigned char értéket, s elvárja hogy ezt egy csukó kerek zárójel zárja le. A beolvasott kifejezés eredményéül kapott bájtot próbálja meg értelmezni mint típusjelölő karaktert.

Mint azt nyilván mindenki sejti, a mau nyelvben számos olyan utasítás, függvény stb van, melyek paramétert várnak el, sőt, egynél több paramétert is igen gyakran. E paraméterek természetesen tetszőleges aritmetikai (vagy string) kifejezések. Tekintve hogy a mau nyelvben szinte bármi lehet aritmetikai kifejezés, még a változók neve is, ezért ha egy utasítás vagy bármi más egynél több paramétert vár el, s e paraméterek többszörös mélységben egymásba vannak ágyazva, ilyenkor cseppet se magától értetődő első pillantásra, hogy hol kezdődik az egyik paraméter, és hol végződik a másik! A paraméterek elválasztására nem használhatjuk a pontosvessző karaktert, mert az maga is utasítás, mint opcionális utasítás-szeparátor. A mau nyelv emiatt rendelkezik egy másik szeparátorral, a paraméter-szeparátorral, s ez a ",", azaz a vessző karakter. EZ IS OPCIONÁLIS, azaz nem kötelező kitenni, de kitehető bárhová ahová mi azt jónak látjuk, minden olyan helyre, ahol az interpreter éppen egy új aritmetikai kifejezést szeretne elkezdeni beolvasni. Azaz, nemcsak ilyesmi formában használhatjuk:

a, b, c,

Hanem még műveleti jelek után is, például:

#c@a=(@b)+,(@c)

Ugyanis a „+” jel után ő egy aritmetikai kifejezést vár, s ha azt vár akkor ott szerepelhet vessző is. Azt ugyanis egyszerűen átugorja. Ez azonban már nem megengedett hanem syntax errort eredményez:

#c@a=(@b),+(@c)

Ugyanis ahol operátort vár el, ott ugyebár nem aritmetikai kifejezést vár el, márpedig a vessző kizárólag azok előtt állhat.