• Nem Talált Eredményt

fejezet - A mau interpreter és a shell kapcsolata

In document A mau programozási nyelvVerziószám: (Pldal 133-140)

A mau programok illik hogy a következő sorral kezdődjenek:

#!mau

vagy valami ilyesmivel:

#!/bin/mau

Ez nem kötelező, de ajánlatos, mert ebből tudja a shell, hogy ez egy olyan fájl, amit a „mau” nevű programmal kell végrehajtani. Ezesetben tehát megpróbálja meghívni a mau interpretert, ami normális esetben épp a „mau” néven kell szerepeljen, s jó dolog ha olyan könyvtárban van, ami szerepel a $PATH rendszerváltozónkban. A shell ekkor ezt tehát meghívja, s átadja neki parancssori paraméterként a végrehajtandó mau program fájlját. Meg minden más parancssori paramétert is, amit e fájl után felsoroltunk. E parancssori paraméterek lekérdezésére a mau programból van lehetőség, ezt leírtam korábban e doksiban a „Mau rendszerváltozók és rendszerfüggvények” című fejezetben.

A mau program végetér az utolsó utasításnál. Ha előbb akarjuk befejezni, arra az

XX

parancs szolgál. (Mindkét X karakter NAGYBETŰS kell legyen!) Miután a főprog-ram a fájl első progfőprog-ramja kell legyen, ezért ezen XX parancsot okvetlenül muszáj használnunk azesetben, ha a főprogram után a fájlba függvényeket is írunk.

Amennyiben kifejezetten úgy akarjuk befejeztetni a mau programot, hogy a shell tudomására hozzuk hogy e mau program valami hibával ért véget, akkor használjuk az

X!

mau parancsot a kilépéshez. Ez ugyanaz, mintha egy C programból az

exit(EXIT_FAILURE);

paranccsal lépnénk ki.

A shell meghívható a mau programból is, nagyon hasonlóan mint az a C nyelv esetén szokásos a „system” rendszerfüggvénnyel, már amiatt is, mert az alábbi mau parancs éppen pontosan e „system” függvényt használja:

"Tartalomjegyzék-lista:\n"

SY "ls -l";

"Tartalomjegyzék-lista vége.\n"

Másik példa:

"Tartalomjegyzék-lista:\n"

#s@p="ls ";

#s@s="-l";

SY (@p)+(@s);

"Tartalomjegyzék-lista vége.\n"

Gondolom, e példák maguktól értetődőek. A megfelelő mau parancsnak persze épp amiatt SY a neve, mert ez a „system” nevű C függvényre utal.

Azt is megtehetjük stringekkel, hogy a stringbe letárolt „mau” programnyelven írt parancsokat végrehajtjuk! Ez efféleképp működik:

#c@h=3;

#s@s="{| 5; ?c @h; /; #c++@h; |} xx"

"Kezdődik a stringparancs végrehajtása!\n"

MAU @s;

"Befejeződött a stringparancs végrehajtása!\n"

"A \"h\" értéke most: " ?c @h; /;

A program kimenete:

Kezdődik a stringparancs végrehajtása!

3 4 5 6 7

Befejeződött a stringparancs végrehajtása!

A "h" értéke most: 8

A string végrehajtását, mint látható, a „MAU” nevű parancs végzi. Ez ugyan 3 karakter látszólag, illetve valóságosan is, igazából azonban ezt a „MA” nevű parancs végzi a rendszerben, épp csak legelső teendőjeként leellenőrzi, a legislegelső karakter a programkódban őutána az „U” betű-e. Ha nem az, akkor syntax error hibaüzenettel kilép.

Nem vagyok a híve a felesleges ellenőrizgetéseknek, de efféle egzotikus dolgot mint egy string végrehajtása, feltehetőleg nem fogunk gyakran végezni, s akkor egyetlen plusz bájt ellenőrzése még talán elfogadható, azért, hogy a forráskódból egyértelműen kiderüljön, mit is csinálunk ott. Azaz tudható legyen ez rögvest a parancs nevéből. Pláne mert azért az ilyesmi rizikós dolog, s esetleg biztonságügyileg is aggályosnak mondható. Jó azonban ha van, csak ésszel kell használni, mert néha igenis aranyat érhet!

Mint látható a példaprogram stringjéből, azt ugyanazzal az xx nevű utasítással kell lezárni mint a függvényeket. A dupla nagy X, vagyis az XX mint tudjuk a program végét jelzi, e dupla kis ixek pedig a „BREAK” utasítás. Ez direkt arra van hogy visszatérjünk rendben valami efféle huncutságból. Ezzel kell lezárni az efféle paraméter(vagy parancs?)stringeket.

Fontos tudni, hogy efféle, stringben tárolt parancs-sorozat végrehajtása közben nem működnek a címkéink! Az értékük kiolvasható ugyan, de hiába próbálunk ugrani rájuk, mert a programmemória mutatója ideiglenesen át van állítva a string kezdetére, azaz nem találja meg a címkék által jelölt pontokat.

Lehet azonban ugrálni a stringen belül, tudniillik ha konkrét numerikus értéket adunk az ugróutasításunk paraméteréül, amely a string valahányadik bájtját

jelöli. Végeredményben egy ciklus esetén is ugróutasításokról van szó, s látjuk a fenti példán, hogy a ciklus remekül működik!

Igenám, de előre tudható, hogy hajlamosak leszünk elfeledkezni a string végébe beleírni az „xx” karaktereket... Nos, nem kell aggódni! A mau interpreter jól tudja, hogy az ember, még a programozó is, „esendő lény”, és csak egy „csekélyértelmű medvebocs”, azaz azzal kezdi az efféle stringek végrehajtását, hogy a végükre odabiggyeszt egy ilyen kiegészítést:

" ; xx"

Vagyis, nem muszáj lezárni nekünk a stringeket az xx paranccsal, ezt az inter-preter megteszi helyettünk. Ha azonban megtesszük mi is, akkor az nem hiba.

Na most, a stringben letárolt parancs végrehajtásának lehetőségét amiatt épp ebbe a fejezetbe írtam be, mert ennek leginkább tényleg olyankor lehet jelentősége, amikor valamely általános célú programot írunk, melyet a shellen keresztül óhajtunk vezérelni, neki küldött parancs-stringekkel, hogy épp mit csináljon! Tipikusan ilyen lehet például egy parancssoros kalkulátorprogram...

Hogy ne csak a levegőbe beszéljek, itt van mindjárt egy, ami elméletileg úgy kb mindent tud, és mégis alig kell hozzá programsor:

#!mau // kalkulátorprogram

#s@$ff[10000]=0; // parancs-string maximális méretének a beállítása

§st; // Kezdődik a kalkulátorprogram

#i@$ff:7=0; // A beolvasandó bájtok kezdőindexe

#s@$ff[0]=0; // parancs-string lenullázása

Bw; Sr; "mau> "; Bd; Sd; // kalkulátor-prompt kiiratása

§ci; // A beolvasóciklus eleje

#c@$ff=?#c "()"; // beolvasunk 1 bájtot a standard inputról

if(@$ff)==10 T »§ki // Ha Entert ütöttünk le, vége a beolvasásnak, ugrás a végrehajtási részhez

#s@$ff[#i@$ff:7]=@$ff; // elmentjük a beolvasott bájtot

#i++@$ff:7; // Az index inkrementálása

»§ci // ugrás a beolvasóciklus elejére

§ki; // Végrehajtási rész

if(#s@$ff[0]) E »§st; // Ha üres a string, nem hajtja végre if(#s@$ff[0]==X) T XX;

#s@$ff[#i@$ff:7]=';; #i++@$ff:7; #s@$ff[#i@$ff:7]=10;

#i++@$ff:7; #s@$ff[#i@$ff:7]=32;

#i++@$ff:7; #s@$ff[#i@$ff:7]=0; #s!@$ff;

MAU @$ff; // végrehajtjuk a stringet

»§st // Ugrás a starthoz

A progi futásának outputja egy példasorral:

vz@Csiszilla /Releases/2014/U/Common/vz/MAU=>./mau calc.mau mau>

mau> "Ezt írom ki!\n"

Ezt írom ki!

mau> ?g 8*(3+2); / 40

mau> #l@h=100 mau> ?l (@h)+3; / 103

mau> #c@h=3

mau> {| 5; ?c @h; /; #c++@h; |} / 3

4 5 6 7 mau> X

Látható a fentiekből, ez tényleg tud mindent amire a mau nyelv képes... még ciklusokat is tud, meg a betáplált változóértékeket megjegyzi, „meg a satöbbi és a minden”. Egyedül arra kell figyelni a használata során, hogy a parancsbetáplálás közben ha változókat használunk, ne épp a #s@$f stringváltozót használjuk, és ne a #i@255:7 unsigned short int változót, meg ne a #c@255:0 unsigned char változót. Azaz ne a 255-ös indexű változókat úgy általában, mert az kell e proginak. Ezeknek épp amiatt ez a nehézkes név van adva a kalkulátorprogramban, hogy minimalizáljuk az esélyét annak hogy a Felhasználó épp olyan változót választ, amit használ a progi maga is.

Természetesen nyilván lehetne e progit is csicsázni még, mondjuk ha teszemazt az utasításvégrehajtás előtt a progi a maga változóit verembe menti vagy valami ilyesmi... De tojok rá, nekem így is nagyon megfelel, nem fogom agyonbonyolítani, különben is ez csak egy gyors kis példa. Csak annyit még, hogy a MAU parancs végrehajtása előtt amiatt van ott az a sok értékadás, hogy némi kényelmet nyújtsunk a Felhasználónak, azaz ugye a kiiratások végét le kell zárni egy / jellel, hogy új sort is írjon ki, de ez után ne neki kelljen mindig kirakni a pontosvesszőt... Valamint, muszáj nekünk lezárni a stringet a nullabájttal és „rendbehozni” a hosszát, mert ha megnézzük a kódot, látjuk hogy az elején 10000 hosszúra állítottuk be a stringet. És azután e hossz nem is változik neki, tudniillik mert csak indexeléssel pakolászunk bele bájtokat. Na most a MAU parancs a string végéhez ugyan hozzámásolja amit akar, de az édeskevés, mert a 10 ezredik karakter s az általunk betáplált parancs-string vége közt akármilyen memóriaszemét is lehetséges, vagy az előző utasításaink maradványai...

E témakörhöz tartozónak érzem a szintaxiskiemelő fájlt is, amit az mc editorához készítettem. Ezt itt nem közlöm, de a honlapomról letölthető e fájl épp aktuális verziója, a neve: mau.syntax

Erről azt kell tudni, hogy másoljuk be valahova ahová jólesik nekünk, és keressük meg a disztrónkban azt az mc-hez tartozó fájlt, aminek az a neve, hogy:

Syntax

Na most e fájlt szerkesszük úgy, hogy az utolsó 2 sora elé beszúrjuk ami kell nekünk a mau nyelvhez. Ezután a fájl legvége így kell kinézzen tehát valahogy:

file ..\*\\.([mM][aA][uU])$ Mau\sprogramming\slanguage ^((#![mM][aA][uU])|(#!.\*[mM][aA][uU])) include /home/vz/MAU/mau.syntax

file .\* unknown include unknown.syntax

Természetesen a

include /home/vz/MAU/mau.syntax

sor csak az én rendszeremre vonatkozik, azaz te az „include” szó után a mau.syntax fájl azon elérési útvonalát írd be, ami neked jó, ami ugye attól függ, te hova mentetted el e szintaxisfájlt.

Shell parancs eredményének tömbbe olvasása

E fejezethez tartozónak érzem az itt ismertetendő funkciót is. A rendszerem építése/hackelése közben nagyon sok esetben fordult elő, hogy olyan szkriptet kellett írnom, ami valami a shell által meghívott parancs eredményét kellett hogy „tovább-feldolgozza”. Ez úgy ment, hogy a SY mau paranccsal meghívtam a shellt, az utasítást úgy felparaméterezve hogy az eredményt egy ideiglenes fájlba mentse, majd ezt a fájlt beolvastam a mau programban, s végül amikor már nem kellett, töröltem.

Na most a módszer oké, de ezt mindannyiszor megírni, ahányszor erre szükség van... Ki a fenének van ehhez kedve?! Írtam erre egy megfelelő „full-extra-de-luxe” mau utasítást. Ennek szintaxisa a következő:

<< S, t, m

ahol az „S” egy stringkifejezés, a futtatandó parancsot tartalmazza (az esetleges paraméterekkel együtt), a „t” a stringtömb nevét meghatározó unsigned char típusú aritmetikai kifejezés, az „m” pedig a munkakönyvtár, ahova az átmenetileg létrehozott ideiglenes fájlt menti el. Az „m” is egy stringkifejezés, természetesen. A parancs megadható így is:

<< S, t;

ezesetben kötelező a végére a pontosvessző, ekkor az „m” értéke alapértelmezés szerint: "/tmp/". Természetesen ez az alapértelmezés is mint minden a mau nyelvben, állítható, ez - mint az alapértelmezések - fordítási direktívaként, azaz ha ezen könyvtáron változtatni akarunk, akkor a mau interpreter fordítása előtt írjuk át a nekünk tetsző módon a vz.h fájl ezen sorát:

#define DEFAULTTMPDIR "/tmp/"

Megjegyzendő, hogy az „m” string megadásakor teljesen mindegy, hogy az ott specifikált könyvtár végére odabiggyesztjük-e a záró "/" jelet vagy sem. Ha ugyanis nincs ott, majd maga az utasítás odapótolja, elég értelmes ehhez...

Ez az utasítás tehát végrehajtja a shell parancsot amit az S stringkifejezés tartalmaz, s az eredményét elmenti egy ideiglenes állományba, amit az „m”

string által megadott könyvtárban helyez el. A fájl neve MauTemporaryFileForShellCommand

de ezt még kiegészíti a futó processz PID stringjével is, nehogy esetleges másik futó mau programpéldányokkal ütközés legyen. Ezután e fájlt beolvassa abba a stringtömbbe, aminek a nevét a „t” unsigned char (azaz #c típusú) aritmetikai kifejezés határozza meg, majd törli az ideiglenes fájlt. Abban az esetben ha a fájl mérete nulla (mert a lefuttatott shell parancs nem produkált semmi kimenetet) nem változtatja meg a beolvasás számára specifikált stringtömb tartalmát, azaz nem törli annak régi tartalmát. Amennyiben azonban a fájl mérete nem nulla, akkor törli a régi tartalmat (ha volt egyáltalán korábbi tartalom - ha nem volt akkor értelemszerűen nem töröl), majd oda beolvassa az

ideiglenes fájl-ot, sorról-sorra, a stringtömb minden egyes stringjének a fájl egy sora felel majd meg. A sorok természetesen nullától számozódnak.

Az utasítás lefutása után az ifflag értéke 1, ha az ideiglenes fájl mérete nulla volt, vagy ha azt valamiért nem tudta megnyitni olvasásra. Ellenkező esetben e flag értéke 0. Valamint, a ?n rendszerváltozó tartalmazza a beolvasott sorok számát, azaz a stringtömbünket 0 és ?n-1 közt indexelhetjük.

Lássunk erre egy gyors példát: A „moc” zenelejátszótól kérdezzük le az épp játszott zeneszám mindenféle adatait parancssorból:

#!mau

#s@p="mocp -i 2>/dev/null";

<< @p, t, "/tmp/";

T "A MOC zenelejátszó nem fut!\n" »§ve;

{| ?n;

?s #s@t[[?n-?|]];

|}

/;

§ve ; XX

Egy futtatás eredménye:

vz@Csiszilla /Releases/2014/U/Common/vz/MAU=>mau mocim.old State: PAUSE

File: /Mount/Zene/Zene/Klasszikus/Grigoras_Dinicu_-_Vioara_-_Electrecord_remastered_(EDC_745)/08_Ciocarlia_(Gr._Dinicu).mp3

Title: 8 Grigoras Dinicu - Ciocarlia (Gr. Dinicu) (Remasterd Records) Artist: Grigoras Dinicu

SongTitle: Ciocarlia (Gr. Dinicu) Album: Remasterd Records

TotalTime: 02:50 TimeLeft: 01:45 TotalSec: 170 CurrentTime: 01:05 CurrentSec: 65 Bitrate: 320kbps AvgBitrate: 320kbps Rate: 44kHz

Ezen fellelkesülve e programot fejlesszük tovább, hogy egy gombnyomásra kiírja a DWM ablakkezelőnk tetejére az épp futó zeneszám címét! Ehhez persze felhasználunk olyan mau utasítást is amit még nem ismertettünk, de sebaj, később arról is olvashatunk majd. E program:

#!mau

#s@p="mocp -i 2>/dev/null";

#s@s="A MOC zenelejátszó nem fut!";

MC f, "-*-fixed-*-*-*-*-28-*-*-*-*-*-*-*";

<< @p, t;

E #s@s=[6,]@t[[1]];

#s@v=?#s "1" @s;

XX

Rémségesen hosszú, ugye?! Ja, ahhoz hogy működjön, nevezzük el mondjuk úgy, hogy „mocim.mau”, és vegyük fel a $HOME/.xbindkeysrc fájlba e sort vagy valami hasonlót:

# (JobbShift + m) A moc-cal epp jatszott zene cimet kiirja a DWM ablakkezelo tetejere

"mau /_/P/Mau/-/maubin/mocim.mau"

Mod3 + m

(A megadott gyorsbillentyű persze attól függ, neked épp melyik a szimpatikus, meg hogy épp miféle billentyűzetkiosztást használsz. Én természetesen egy abszolút különlegeset, sajátot, amit magam hekkeltem össze magamnak...) És még azt meri valaki mondani, hogy a mau nyelv NEHÉZ meg BONYOLULT?!

Oldjon meg valaki valami efféle feladatot ennél egyszerűbben, rövidebben... Te, aki e sorokat olvasod, gondolj csak bele: ez a program meghív egy shell paran-csot. Megvizsgálja, van-e egyáltalán bármiféle eredménye a végrehajtott parancsnak. (ha nincs, akkor nem fut a MOC...) Az eredményt beolvassa, majd kiválasztja egy bizonyos sorát ezen eredménynek, s ezt a stringet a megfelelőképp csonkolja nekünk. Beállítja, miféle fontkészlettel írjon a GRAFIKUS képernyőre, (!!! GRAFIKUSRA! Parancssoros program! !!!) ki is írja oda a stringet, vár egy billentyűlenyomásra, majd eltünteti onnan azt. ÉS MINDEZT MINDÖSSZE CSAK 8 programsorban! És minden programsor csak 1 utasítást tartalmaz, azaz ez egyszerűen 8 mau utasítás! (És elég lenne 7 utasítás is, ha a default fontkészlettel iratnánk ki az eredményt a grafikus képernyőre...)

Van egy másik lehetőség is arra, hogy shell parancs eredményét tömbbe olvassuk. Ennek szintaxisa és paraméterezése rém hasonló az előzőéhez:

<<< S, t;

aholis a paraméterek jelentése azonos az előzőével, tehát az S a végrehajtandó stringkifejezés, a „t” pedig a stringtömb neve. Ez se bántja az eredeti stringtömböt ha az S hossza nulla, vagy ha a shell parancs eredménye semmi.

Ellenkező esetben beolvassa soronként az eredményt a stringtömbbe, a ?n rendszerfüggvény értékét beállítja a sorok számára, amik itt is nullától számozódnak, tehát a „t” tömb 0 és ?n-1 közt lesz indexelhető.

Ha az S hossza 0, nem módosítja az ifflag állapotát. Ellenkező esetben, ha a shellparancs visszatérési értéke „semmi”, az ifflag értéke 1, különben 0 lesz.

A 3 db < jel egyetlen token, nem lehet köztük whitespace! A különbség ezen utasítás és az előző közt az, hogy itt nem kell megadnunk semmiféle könyvtárat ideiglenes fájl helyéül, mert nem is hoz létre a lemezen ideiglenes fájlt. A memóriában hoz létre magának ilyet... Emiatt gyorsabb is a futása, méréseim szerint az időszükséglete csak kb 85%-a az előzőének. Ami a visszaadott eredménystringeket illeti, itt is a shellparancs eredményének egy-egy sora lesz a „t” stringtömb egy-egy-egy-egy stringjébe (elemébe) téve, de fontos tudnunk, hogy az előző paranccsal ellentétben ezen sorok nem lesznek lezárva egy sorvége-karakterrel (azaz CHR$(10) karakterrel Linux alatt), hanem ezek helyén rögvest a stringzáró nullabájt szerepel!

A Moc zenelejátszóval kapcsolatos előző szkriptünk e parancsra átírva tehát így fest:

#!mau

#s@p="mocp -i 2>/dev/null";

#s@s="A MOC zenelejátszó nem fut!";

MC f, "-*-fixed-*-*-*-*-28-*-*-*-*-*-*-*";

<<< @p, t;

E #s@s=[6,]@t[[1]];

#s@v=?#s "1" @s;

XX

23. fejezet - A BRAINFUCK interpreter, avagy „ez itt a

In document A mau programozási nyelvVerziószám: (Pldal 133-140)