Programmeren in REXX/Fundamenten
In dit hoofdstuk bespreken we de voornaamste karakteristieken van REXX, zoals zijn typische kenmerken, bouwstenen en syntaxis.
Typische kenmerken
bewerkenFundamenteel aan REXX is dat alle woorden binnen het programma intern als karakterreeks worden opgeslagen. Het is enkel wanneer een numerieke bewerking verwacht wordt, dat er een interne conversie naar getallen zal gebeuren. Het resultaat van de bewerking wordt weer een karakterreeks.
Dit heeft als voornaamste gevolg dat er geen declaraties moeten worden gemaakt voor variabelen, zoals in de meeste andere programmeertalen wel het geval is.
We zullen karakterreeksen vanaf nu meestal met de Engelse term string benoemen.
Vervolgens werkt REXX meestal als een interpreter. Hiermee wordt bedoeld dat REXX het programma lijn voor lijn zal uitvoeren, en daarvoor telkens in twee stappen wordt gewerkt. De eerste stap is een interpretatie van de programmalijn en omzetting naar interne code (te vergelijken met een compilatie). De tweede stap is dan de eigenlijke uitvoering. Er zijn wel optimalisaties voorzien om niet telkens de eerste stap te moeten doen, bv. in geval van een lus.
Dit heeft als gevolg dat REXX het in uitvoeringssnelheid in de meeste gevallen niet kan winnen van gecompileerde programma's. Daartegenover staat echter dat de ontwikkeling van een werkend programma veelal heel wat sneller kan. De interpretatie-stap kan ook voordelen hebben, bijvoorbeeld bij het debuggen.
Op mainframe besturingssystemen (z/OS en z/VM) bestaan wel compilers voor REXX.
Door velen wordt REXX ten onrechte beschouwd als enkel een vervanger voor scripting (BAT-files). Dat was wel het eerste doel op mainframes. We zullen echter zien dat de taal volledig genoeg is om er ook alle andere klassieke programmeertaken mee te vervullen. Op de huidige snelle PC's (en zeker de mainframes) kan een tragere uitvoeringstijd een verwaarloosbare handicap zijn, en ruimschoots gecompenseerd worden door de snelheid van ontwikkeling. Zeker in die gevallen waar het programma slechts sporadisch of zelfs éénmalig moet worden uitgevoerd zal REXX een zeer interessant alternatief voor andere programmeertalen zijn.
Door de weinig complexe schrijfwijze, het gebruik van leesbare (Engelse) termen en het ontbreken van declaraties is het ook een uitgelezen programmeertaal voor minder geroutineerde of beginnende computergebruikers.
Lees hier wat Mike Cowlishaw, de "uitvinder" van REXX, als voor- en nadelen van zijn programmeertaal opsomt.
Bouwstenen
bewerkenEen REXX programma is opgebouwd uit een reeks zinnen die opgebouwd zijn uit:
- een of meer spaties, die genegeerd worden;
- een reeks "tokens" (zie verder);
- geen of een reeks spaties die ook genegeerd worden;
- een punt-komma dat de zin afsluit. Aan het eind van een lijn is deze optioneel.
Daarnaast kan het programma doorspekt zijn met commentaren (zie verder).
We zullen deze zinnen vanaf nu aanduiden met de "officiële" term statement. De statements worden één voor één geanalyseerd en uitgevoerd. De analyse gebeurt van links naar rechts. Dit laatste kan soms van groot belang zijn.
Merk op dat een regel in het bestand dat de broncode bevat uit meer dan één statement kan bestaan, mits ze gescheiden zijn door een punt-komma.
De term token staat voor de bouwstenen waarmee REXX statements zijn opgebouwd. De tokens kunnen gelijk welke lengte hebben, tenzij er limieten worden opgelegd door de implementatie. Het beschikbaar geheugen is anders de ultieme limiet. De soorten tokens zijn:
- constanten
- symbolen (instructies, functies of variabelen)
- getallen
- operatoren
- speciale syntactische tekens
- labels
Laten we al deze elementen nu in detail bekijken, te beginnen bij commentaren.
Commentaren
bewerkenCommentaren worden in REXX begrensd door een beginnende /* en een eindigende */.
Commentaren kunnen over meerdere lijnen gaan en een commentaar kan een ander commentaar bevatten (geneste commentaren). Ze kunnen praktisch overal voorkomen, ook midden in een instructie. REXX zoekt naar die begin- en eindaanduidingen zonder rekening te houden met andere inhoud. Dus indien ergens in een constante van het programma één van die tekens voorkomt, kan het mislopen.
Op sommige systemen moet een REXX programma beginnen met een commentaar op de eerste positie, desnoods een leeg commentaar zoals in een volgend voorbeeld. Andere implementaties of systemen vereisen dat niet. Toch blijft het een goede gewoonte om het programma te beginnen met een korte beschrijving, eventueel aangevuld met de naam van de programmeur, een creatiedatum en de historiek van de veranderingen. Ook als het programma moet kunnen gebruikt worden op verschillende systemen is het veilig te starten met een commentaar in de eerste positie.
/* */ say "Hello" /* Dit zal Hello op het scherm zetten */ /* Deze lijnen zijn /* écht waar ! */ allemaal commentaar */ if i=5 /* en alleen dan */ then say "Adieu"
Constanten
bewerkenConstanten zijn karakterreeksen die begrensd worden door enkelvoudige of dubbele aanhalingstekens (' of ").
Uiteraard moeten begin- en eindteken van een constante hetzelfde zijn.
say "Nice weather, isn't it?"
definieert een constante (een zin). Omdat in de zin een apostrof nodig is, wordt de zin omgeven door de dubbele aanhalingstekens.
Een alternatieve oplossing bestaat erin om het apostrof-teken te ontdubbelen, dus:
say 'Nice weather, isn’’t it?'
Daar dit minder leesbaar is, gebruiken we deze oplossing enkel in die gevallen waar het niet anders kan, bijvoorbeeld omdat zowel apostrof als dubbel aanhalingsteken nodig is in de contante.
Een constante kan ook met hexadecimale of binaire notatie opgegeven worden:
'1de8'x "11110110"b
Hiermee kunnen bijvoorbeeld onleesbare controlekarakters gebruikt worden.
Constanten worden dus "letterlijk" genomen door het programma en zijn als dusdanig een element van een statement tijdens de uitvoering.
Getallen
bewerkenEen getal is een reeks cijfers, met eventueel een decimaal punt (geen komma dus!). Negatieve getallen worden voorafgegaan door een minteken. Exponentiële notatie kan ook worden gebruikt. Een enkele 0 voor het decimaal punt mag weggelaten worden.
Dit zijn allemaal geldige getallen:
17 98.07 -101 .14 12.2e6 73e-128 '-17.9' ' 11.22 '
De laatste 2 getallen (als een constante voorgesteld) zijn evengoed geldig, want we leerden al dat REXX alles opslaat als karakterreeksen tot eventueel blijkt dat er moet mee gerekend worden. Spaties worden uiteraard niet meegenomen in de berekeningen.
Symbolen
bewerkenMet symbolen bedoelen we woorden bestaande uit een reeks karakters.
Welke karakters kunnen gebruikt worden hangt af van het systeem waarop men werkt en van de codepages die gebruikt worden. Er is ook een verschil tussen ASCII (PC) en EBCDIC (mainframes). Op z'n minst kunnen volgende karakters gebruikt worden:
A tot Z, a tot z, 0 tot 9, . (punt) en _ (liggend streepje)
Als een symbool niet met een cijfers of een punt begint, dan kan het een variabele voorstellen. Dit betekent dat men er een waarde kan aan toekennen d.m.v. een assignatie (variabele = uitdrukking). Begint het symbool met een punt of een cijfer, dan is het een constant symbool. In die zin zijn getallen constante symbolen.
Dus, een symbool zal, al naargelang zijn plaats in het programma, beschouwd worden als een variabele of als een instructie (bevel, functie).
De initiële waarde van een token is gelijk aan zijn naam in hoofdletters. Dus zullen in
say a
beide woorden gezien worden als respectievelijk SAY en A. Het token SAY wordt door REXX daarna wel herkend als een bevel dat, wat erop volgt, op het scherm moet afdrukken. Het token A zal als variabele gezien worden vermits het niet tussen aanhalingstekens staat en niet met een cijfer of punt begint. Vermits in dit voorbeeld geen waarde werd toegekend blijft haar initiële waarde zolang "A". We zullen dus uiteindelijk die letter "A" op het scherm zien verschijnen.
In onderstaand voorbeeldje is het symbool "do" op de eerste regel tweemaal gebruikt. De eerste maal is het een bevel en de tweede maal een variabele.
do do=1 to 10 say do end
Dit is een perfect geldig statement, doch omwille van de leesbaarheid, ten stelligste af te raden. Gebruik geen variabel-namen die overeenkomen met die van een instructie of functie.
Speciale vormen van symbolen zijn dus bevelen en functies:
Bevelen
bewerkenDeze zullen in meer detail beschreven in volgende hoofdstukken, maar de lijst ervan is zo beperkt dat we ze hier toch even kunnen opsommen: ADDRESS, ARG, CALL, DO, DROP, EXIT, IF, INTERPRET, ITERATE, LEAVE, NOP, NUMERIC, PARSE, PROCEDURE, PULL, PUSH, QUEUE, RETURN, SAY, SELECT, SIGNAL en TRACE. Van een aantal kan men nu al vermoeden waartoe ze zullen dienen. Soms gebruiken we hiervoor ook de term instructies zoals in het Engels instructions. Verwar dit niet met een volledige programmalijn, die we een statement noemen, noch met een regel in het bronbestand, want dit kan meer dan één statement bevatten.
Functies
bewerkenFuncties komen later uitgebreid aan bod. Hun algemene vorm is:
functienaam(parameter1,parameter2,...)
waarbij de parameters van elkaar gescheiden worden door komma's. De lijst van parameters moet aan de functienaam plakken, er mag geen spatie tussen geplaatst worden.
De lijst standaardfuncties is zeer groot. Ze hier opsommen zou ons nu te ver leiden. We komen de meeste ten gepaste tijde wel tegen.
Functies kunnen ook verschillen naargelang het besturingssysteem. Er bestaan tevens externe functiepakketten die kunnen worden toegevoegd aan de basisset. We zullen ook leren hoe we zelf functies kunnen schrijven.
We geven toch een voorbeeldje van een (basis-)functie:
a="tekst" say length('Dit is een' a)
Dit zijn de stappen die door de REXX interpreter worden uitgevoerd (herinner u, de interpretatie werkt van links naar rechts):
- het token "a" is niet gekend als bevel of functie, maar is wel een geldige naam voor een variabele;
- het gelijkheidsteken beveelt om een waarde toe te kennen aan de variabele (assignatie);
- de toegekende waarde is alles tot aan het einde van het statement (hier impliciet het einde van de zin). De variabele a krijgt dus de waarde "tekst"
- het token "say" wordt herkend als een bevel. Het moet alles wat volgt tot aan het einde van het statement op het scherm afdrukken;
- het token "length" wordt vervolgens herkend als een standaardfunctie die de lengte van zijn parameter moet opzoeken en teruggeven;
- het deel tussen haakjes is de eerste, en in dit geval enige parameter voor de functie. REXX ontdekt dat het een constante gevolgd door een variabele is. De variabele wordt door haar waarde vervangen, en de parameter voor de functie wordt uiteindelijk "Dit is een tekst";
- de functie length telt nu het aantal karakters en komt tot de waarde 16;
- het bevel say ziet dus nog enkel het getal 16 staan, en drukt dit op het scherm af.
Labels
bewerkenLabels (letterlijk: etiketten), zijn plaatsen naar waar een programma kan springen (met de bevelen SIGNAL of CALL, of nog als begin van een interne functie).
Een label bestaat uit een token gevolgd door een dubbele punt.
signal MijnLabel /* We springen verder */ [instructies] exit /* we verlaten het programma */ MijnLabel: /* Hier kunnen we naartoe springen */ [Andere instructies] exit /* we verlaten het programma */
Ook voor labels geldt dat ze hoofdletter ongevoelig zijn, dus MijnLabel en MIJNLABEL zijn hetzelfde.
Het is zelfs mogelijk meerdere labels na elkaar te gebruiken, waarbij ze dan te beschouwen zijn als elkaars synoniem.
Leestekens
bewerkenVervolgteken
bewerkenDe komma aan het eind van een tekstregel, geeft aan dat de instructie wordt vervolgd op de volgende lijn.
/* Gesplitste instructie */ Say "Dit is een", "tekst."
De komma zal na het samenvoegen van de lijnen vervangen worden door een spatie. Merk ook op dat het voor commentaren over meerdere regels niet nodig is vervolgtekens te gebruiken.
Scheidingsteken
bewerkenDe puntkomma laat toe statements van elkaar te scheiden, zoals hier:
/* Meerdere instructies op één regel */ Say "Hello" ; say "Nice weather, isn't it?"
Programma opbouw - statements. REXX interpreteert statement per statement. Een statement eindigt
|
Bewerkingstekens
bewerkenHiermee bedoelen we o.a. de tekens die toelaten op te tellen of af te trekken. We hebben, in volgorde van voorrang bij uitvoering:
- Prefix \ + of - aan het begin van het woord betekenen ze respectievelijk NOT, positief of negatief.
- ** machtsverheffing (exponentiatie) [5 ** 2]
- * / % // vermenigvuldiging, deling, modulo en restdeling.
- + - optellen en aftrekken
- || concatenatie - karakterreeksen aan elkaar plakken ["ABC" || "DEF" = "ABCDEF"]
- == \== strikt gelijk of ongelijk
- = \= gelijk of ongelijk
- > >= \> groter dan, groter of gelijk aan, niet groter dan
- < <= \< kleiner dan, kleiner of gelijk aan, niet kleiner dan
- >> >>= \>> << <<= \<< strikte vorm van voorgaande
- >< <> \= niet gelijk aan (op drie manieren geschreven)
- & logische en (AND)
- | && logische of (OR) en exclusieve of (XOR)
Bij vergelijkingen wordt het kortste lid tot gelijke lengte van het langste lid gemaakt door er spaties aan toe te voegen alvorens de vergelijking aan te vatten. Bij de strikte vormen van vergelijkingen wordt deze verlenging niet toegepast.
Een machtsverheffing gaat dus voor op een vermenigvuldiging of een deling. Deze gaan dan weer voor op optelling en aftrekking, enz. Men kan de volgorde van berekening aanpassen door ronde haakjes te gebruiken. (3+4)*5 is dus niet hetzelfde als 3+4*5.
Het resultaat van een vergelijking is 1 (waar) of 0 (onwaar). Bij logische bewerkingen mogen enkel 1 of 0 gebruikt worden.
Voorbeelden:
\1 /* «0» (niet 1) */ -7 * (4+3) /* «-49» */ -7*4 + 3 /* «-25» (volgorde belangrijk)*/ 'ABC'=='ABC' /* «1» (= waar) */ 'ABC'=='ABC ' /* «0» (= niet waar) */ 'ABC'\='abc' /* «1» (= waar) */ (1 | 0) & 1 /* «1» (= waar) */ 12 % 7 /* «1» (modulo) */ 12 // 7 /* «5» (rest van deling) */
Concatenatie gaan we nu uitleggen.
Concatenatie
bewerkenDe term concatenatie (met het bewerkingsteken ||) komt van het Engelse werkwoord to concatenate en betekent: aaneenkoppelen/-voegen/-ketenen. Er worden dus twee delen aan elkaar geklit. Maar dit vraagt toch om wat meer uitleg.
Het aantal spaties tussen tokens van statements heeft geen belang, ze zullen gereduceerd worden tot één spatie. (Verwar dit niet met spaties tussen woorden in een constante). Ook de komma die gebruikt wordt om een instructie op een volgende regel verder te zetten zal vervangen worden door één enkele spatie.
Laten we nu even kijken naar volgende reeks statements:
x=1 ; y=2 say x y /* Toont «1 2» */ say x y /* Toont evenzeer «1 2» */ if x y = '1 2' then say 'OK' /* «OK» want dit is waar! */ say xy /* «XY», want xy niet opgevuld */ say x||y /* «12», want concatenatie */ if x||y=12 then say 'OK' /* «OK» want ook Waar! */ if x||, y = 12 then say 'OK' /* «OK» want eveneens "Waar" */
Tot zover het wel of niet samenvoegen van variabelen. Maar bestudeer nu volgende voorbeelden aandachtig:
x=1 ; y=2 say x"\="y /* «1\=2» */ if x"\:"y then say 'OK' /* Syntax error ! */ if x"-"y = '1-2' then say 'OK'/* «OK» */ if x"-"y = 1-2 then say 'OK' /* Niets, want "1-2" is niet -1 */
We willen hiermee duidelijk maken dat een concatenatieteken niet nodig is wanneer variabelen aan constanten worden geplakt. Maar willen we constanten aan constanten ketenen, dan is het concatenatieteken meestal wel nodig:
say "Dit is een zin""!" /* «Dit is een zin"!» */ say "Dit is een zin"||"!" /* «Dit is een zin!» */ say "Dit is een zin"'!' /* «Dit is een zin!» */
Bij het eerste voorbeeld wordt het dubbele aanhalingsteken door een enkel vervangen zoals we al hadden geleerd.
Speciale variabelen
bewerkenREXX kent 3 speciale variabelen:
- RC, bevat de returncode van het laatst uitgevoerde commando dat door het besturingssyteem is uitgevoerd.
- RESULT, bevat het resultaat van de laatst uitgevoerde functie.
- SIGL, bevat het lijnnummer vanwaar men is gekomen met een signal of call bevel.
Deze variabelen zullen ten gepaste tijde meer uitleg krijgen.
Stems
bewerkenREXX kent een speciaal soort variabelen, namelijk stems. De term komt van het Engels en betekent in dit geval dat het variabelen zijn die dezelfde stam hebben. Ze laten o.a. toe om vectoren (arrays) en matrices te simuleren, maar ze zijn meer dan dat.
De stam van een stem bestaat uit een geldig token, gevolgd door een punt. Zo zijn "jan." en "p." geldige namen voor stems. Zoals voor andere variabelen is de basiswaarde van een stem gelijk aan zijn naam in hoofdletters, hier dus "JAN." en "P.".
Een stem is enkel een impliciete definitie van een verzameling variabelen. Er zullen pas elementen in voorkomen indien we waarden toekennen aan samengestelde variabelen. Een stem heeft geen voorgedefinieerde dimensie, er kunnen zoveel elementen aan toegevoegd worden als het geheugen van de computer toelaat.
Een samengestelde variabele definieert dus een element van een stem. Ze is opgebouwd uit de stamnaam en een symbool dat na de punt is geplaatst. Dit tweede deel noemen we de staart (Engels: tail). Zo zijn "jan.7", "jan.hello", "jan.i" en "jan.i.j" geldige elementen van de stem "jan.".
jan.7 = 25 jan.hello = "Gisteren" i=5 ; jan.i = 17 hello="Hello" ; jan.hello = "Goeiedag" j=7 ; jan.i.j=12
Tijdens de uitvoering zal REXX de staart interpreteren. Hebben we een constante of een getal, dan hoeft geen verdere interpretatie. Dus, bij jan.7 is "JAN.7" de naam van het element van de stem "JAN.". Dit element krijgt in ons voorbeeld de waarde «27».
In het tweede statement stelt hello een geldige variabele voor. Vermits deze variabele geen waarde kreeg, is haar impliciete waarde "HELLO" en wordt "JAN.HELLO" een element van de stem "JAN.". Aan dit element wordt dan «Gisteren» toegewezen.
In het derde geval hebben we een variabele in de staart waarvan de waarde «5» is. Dus wordt het element "JAN.5" een waarde «17» toegekend.
Net zo zal na de toekenning van de waarde «Hello» aan de variabele hello, het element "JAN.Hello" (let op de hoofd- en kleine letters), de waarde «Goeiedag» gaan bevatten.
Als we tenslotte bij de laatste regel komen, hebben beide i en j een waarde gekregen. Daarom leidt de assignatie dat het element "JAN.5.7" de waarde «12» krijgt. Op deze manier kan men een matrix met 2 (of meer) dimensies simuleren, doch voor REXX zijn het gewoon allemaal elementen van een stem.
Als een stem gebruikt wordt om een reeks elementen te bevatten, gebruikt men vaak numerieke staarten (jan.1, jan.2, ...). Er is dan een soort conventie dat het aantal elementen (de dimensie) van de aldus opgebouwde vector in het element "stem.0" wordt bijgehouden. Verplicht is dit niet, doch sommige programma's of functies volgen deze conventie om aan te geven hoeveel elementen ze als resultaat hebben terug gegeven.
Noteer dat een eenvoudige assignatie als
piet. = 'Onbekend'
álle elementen van de stem deze standaardwaarde toekent (ook zij die reeds een waarde hadden). Dus zullen zowel PIET.7 als PIET.Morgen een waarde «Onbekend» hebben zolang er geen nieuwe waarde wordt gegeven.
Merk tenslotte op dat een samengestelde variabele er zeer complex kan uitzien:
a="Dit is een hele volzin" jan.a=17
zal aan het element "JAN.Dit is een hele volzin" de waarde «17» toekennen. Dit geeft interessante mogelijkheden waar we later nog op zullen terugkomen.
Programmabuffer ofte Stack
bewerkenGegevens die ingevoerd worden vanuit externe bronnen - meestal het toetsenbord, maar ook andere programma's - kunnen in de zogenaamde stack of programmabuffer worden opgeslagen in afwachting van verwerking. Het is één van de manieren om gegevens uit te wisselen tussen programma's.
We kunnen deze buffer vergelijken met een stapel speelkaarten. Er kunnen kaarten onderaan of bovenaan worden toegevoegd, maar ze worden steeds van bovenaf terug weggenomen.
Worden de kaarten bovenaan bijgevoegd, dan zullen ze dus als eerste terug worden weggenomen en spreken we van LIFO (Last In, First Out). De laatst toegevoegde wordt als eerste terug weggenomen.
Worden ze echter onderaan toegevoegd, dan zullen ze pas als laatste terug van de stapel worden genomen en spreken we van FIFO (First In, First Out). De kaarten kruipen als het waren van onder naar boven, waarbij de eerst toegevoegde eerst wordt weggenomen.
De bevelen die met de stack werken bespreken we in een volgend hoofdstuk.
Besluit van dit hoofdstuk. In dit hoofdstuk leerden we over constanten, variabelen, bevelen en functies. We leerden de lees- en bewerkingstekens kennen. Ook hoe labels eruitzien en wat de stack eigenlijk is. De uitleg over stems mocht zeker niet ontbreken. Termen als token, assignatie en statement werden aangebracht. Probeer deze nog eens voor de geest te halen zodat we ze als gekend mogen beschouwen bij het aanvatten van volgende hoofdstukken. |