Programmeren in REXX/Voorbeeld 2
Ons tweede uitgewerkt programma is handig om na te gaan welke conversies kunnen werken en welk resultaat we mogen verwachten. We leerden de conversiefuncties kennen in Programmeren in REXX/Functies deel 2#Omzetten van formaten. Het is echter niet altijd zeer intuïtief om te weten welke functie tot het beoogde resultaat leidt.
Een alfabetisch karakter omzetten naar een decimaal getal (C2D) lukt al bijvoorbeeld niet. En, moeten we het karakter "A" naar hexadecimaal of naar decimaal omzetten om zijn plaats in de karaktertabel te kennen ? Hoe kan het karakter "A" binair worden voorgesteld ? Enzovoort. Al deze vragen worden op een overzichtelijke manier beantwoord met dit programmaatje.
REXX-code: XtoX.rex
1 | /***************************************************************** 2 | Dit programma toont alle REXX conversie functies (bv. C2X) 3 | +-----------------------------------------------------------+ 4 | | formaat: | XTOX waarde | 5 | +-----------------------------------------------------------+ 6 | Waarde kan ook in hexadecimaal formaat gegeven worden '..'X 7 | Probeer bijvoorbeeld XTOX 15 of XTOX 'F1'X 8 | 18 Jul 1989: creatie 9 | 20 Dec 2000: B2X en X2B functies toegevoegd 10 | ******************************************************************/ 11 | parse arg arg . 1 kar1 2 /* Argument ophalen */ 12 | hex=0 /* veronderstel geen hexadecimale notatie */ 13 | if kar1="'" | kar1='"' then /* als begin een ' of " is */ 14 | if translate(right(arg,1))='X' then /* en rechts een X */ 15 | parse value 1 translate(arg,"'",'"'), 16 | with hex arg /* vertalen en hex=1 */ 16 | 17 | if hex then do /* als hexadecimaal is */ 18 | parse var arg "'" arg "'" /* karakters er uithalen */ 19 | if \datatype(arg,'X') then do /* verifiëren of geldig X */ 20 | say '"'arg'" is geen geldige hex data' ; exit 5;end 21 | arg=x2c(arg) /* omzetten naar karakter */ 22 | end 23 | /* Nu hebben we geldige karakters, dus alle conversies proberen */ 24 | call test C2D 25 | call test C2X 26 | call test D2C 27 | call test D2X 28 | call test X2C 29 | call test X2D 30 | call test X2B 31 | call test B2X 32 | exit 33 | /* Functie TEST gaat na of conversie werkt en toont resultaat */ 34 | TEST: parse arg functie /* Welke conversie ? */ 35 | signal on syntax /* intercepteer syntax fouten */ 36 | interpret "val="functie"(arg)" /* voer uit */ 37 | say left(functie'('arg')='val,20)left("=X'"C2X(val)"'",30) 38 | return 39 | /* SYNTAX procedure wordt opgeroepen als conversie mislukt */ 40 | SYNTAX: 41 | say functie'('arg')=onmogelijk' /* zeg dat het fout is */ 42 | return
- 1 tot 10: mooie inleidende commentaar voor het programma. Ook historiek van veranderingen.
- 11: ophalen van het argument, waarbij eerste karakter in kar1 wordt gezet;
- 13: als links een ' of een " staat kunnen we te doen hebben met een hexadecimale notatie;
- 14: maar dan moet rechts een "X" staan;
- 15: dan zetten we hex=1 en vertalen we een eventuele " in '. Gewoon altijd het argument vertalen mag niet, want dan zou het argument geen ' of " meer mogen zijn;
- 17: als we nu een hexadecimale notatie hebben...
- 18: halen we de hexadecimale karakters eruit...
- 19: we gaan na of de karakters geldige hexadecimale karakters zijn, en...
- 20: tonen foutbericht en stoppen de procedure met code 5 als dat niet het geval is.
- 21: anders zetten we de hexadecimale karakters om naar gewone karakterstring;
- 24: nu roepen we een test subroutine op, één voor één met de namen van de mogelijke conversiefuncties;
- 34: de functie pikt de naam van de conversiefunctie op;
- 35: dit is de voornaamste regel in deze procedure. Door een signal on syntax zetten we de detectie van syntaxis fouten aan. Indien een fout wordt ontdekt springen we naar het label syntax:;
- 36: omdat we de naam van de functie als variabele binnenkregen moeten we een interpret bevel gebruiken om de functie op te roepen. Sommige conversies zijn echter niet toegelaten, voornamelijk als een niet numerieke string een getal zou moeten worden via de D2... functies;
- 37: heeft de functie wél een resultaat gegeven, dan kunnen we dat afdrukken, en de functie beëindigen;
- 40: de syntax: routine wordt dus opgeroepen bij een ongeldige conversie. We melden dit ook en keren tenslotte ook terug naar de hoofdroutine.
Meer over signal on syntax leren we in het hoofdstuk Debugging. Laten we naar een paar keer het programma uitvoeren:
C:\Users\Dmitri>rexx d:\RexxProgrammas\xtox "42"x C2D(B)=66 =X'3636' C2X(B)=42 =X'3432' D2C(B)=onmogelijk D2X(B)=onmogelijk X2C(B)=♂ =X'0B' X2D(B)=11 =X'3131' X2B(B)=1011 =X'31303131' B2X(B)=onmogelijk
Onze hexadecimale waarde '42'x is in het begin van het programma als dusdanig herkend en omgezet naar het karakter "B". Dit karakter proberen we nu op alle mogelijke manieren om te zetten. Het is geen numeriek getal, dus dat de omzetting van decimaal mislukt was te verwachten. Merk ook op dat het nuttig is zowel de karakterwaarde van het resultaat als de hexadecimale notatie ervan te tonen. We zien inderdaad dat X2C(B) een raar karakter produceert. Hexadecimaal heeft het de waarde '0B'x, en dat ligt in de reeks controlekarakters. Als we het programma met karakter "A" als parameter uitvoeren, hebben we een mooi voorbeeld van wat er kan gebeuren:
C:\Users\Dmitri>rexx d:\RexxProgrammas\xtox A C2D(A)=65 =X'3635' C2X(A)=41 =X'3431' D2C(A)=onmogelijk D2X(A)=onmogelijk X2C(A)= =X'0A' X2D(A)=10 =X'3130' X2B(A)=1010 =X'31303130' B2X(A)=onmogelijk
Bij de X2C omzetting bekomen we de waarde '0A'x en dat is een Line-Feed... dus we gaan inderdaad naar de volgende lijn om het resultaat te tonen, en het alignement van de tekst gaat verloren.
In het begin van het hoofdstuk vroegen we ons af hoe we de binaire voorstelling van het karakter "A" konden bekomen. Wel, uit deze resultaten kunnen we besluiten dat X2B() al zeker niet de goede oplossing is, we moeten een dubbele conversie hanteren, namelijk X2B(C2X(A)). Inderdaad:
C:\Users\Dmitri>rexx d:\RexxProgrammas\xtox 41 C2D(41)=13361 =X'3133333631' C2X(41)=3431 =X'33343331' D2C(41)=) =X'29' D2X(41)=29 =X'3239' X2C(41)=A =X'41' X2D(41)=65 =X'3635' X2B(41)=01000001 =X'3031303030303031' B2X(41)=onmogelijk
Die 41 is de hexadecimale voorstelling van het karakter A, dus binair bekomen we "01000001"b. Idealiter zou ons programma dus ook alle dubbele omzettingen moeten uitproberen zodat het voor ons nog gemakkelijker wordt. Maar dit laten we aan de lezer over.
In het laatste voorbeeld is enkel de B2X omzetting niet meer mogelijk omdat we geen binaire string als input hebben gegeven.
Zonder verdere uitleg geven we nog een tweede, gelijkaardig programma cadeau. Het toont alle formaten waarin een bepaalde datum kan worden gemaakt met de date() functie:
REXX-code: RexxDates.rex
/* Toon alle Rexx date() variaties +-----------------------------------------------------------+ | format: | REXXDATES <inputdatum> | +-----------------------------------------------------------+ 21 Dec 2000: Accepteer een datum als input */ parse arg indatum '' t ; indatum=strip(indatum) fmts='Basedate Days European Month Normal Ordered', 'Sorted Usa Weekday' fmtsep='European Normal Ordered Sorted Usa' NbFm=words(fmts) /*----------------------------------------------------------------*/ /* Geval 1: geen datum meegegeven, toon vandaag in alle formaten */ /*----------------------------------------------------------------*/ if indatum='' then do say 'De DATE functie kan volgende formaten produceren:' do i=1 to NbFm call rexdate word(Fmts,i) end i exit end /*----------------------------------------------------------------*/ /* Geval 2: we kregen wel een datum binnen */ /*----------------------------------------------------------------*/ if length(indatum)=10 then if substr(indatum,5,1)substr(indatum,8,1)='--' then indatum=left(indatum,4)substr(indatum,6,2)substr(indatum,9) ValidFmts='' do i=1 to NbFm ValidFmts=ValidFmts CkDate(word(fmts,i)) end If ValidFmts='' then do Say 'Datum' indatum 'is geen geldige REXX datum formaat.' exit 56 end say 'De DATE functie kan volgende formaten produceren voor "'indatum'"' ld=length(indatum)+19 Do while ValidFmts<>'' out.='' Do 3 while ValidFmts<>'' parse var ValidFmts fmt ValidFmts Out.0tit=Out.0tit left(fmt,12) do i=1 to NbFm fmto=word(Fmts,i) out.fmto=out.fmto GivDate(fmtO,indatum,Fmt) end i end Say right('? ===>',ld+5) Out.0tit do i=1 to NbFm fmto=word(Fmts,i) t='Date('left(fmto,8)','indatum',"?")' say ' 'left(t,ld) '= ' Out.fmto end i end exit REXDATE: parse arg fmt if wordpos(fmt,fmtSep)=0 then say ' 'left('Date('fmt')',15)'= ' date(fmt) else say ' 'left('Date('fmt')',15)'= ' left(date(fmt),12), left('Date('fmt',,,"-")',20)'= ' date(fmt,,,'-') return CkDate: Signal on syntax name InvDate call date 'U',indatum,arg(1) return arg(1) /* Formaat is mogelijk */ InvDate: return '' /* Formaat is onmogelijk */ GivDate: Signal on syntax name InvDate2 return left(date(arg(1),arg(2),arg(3)),12) InvDate2: return '*Onmogelijk*'
Dit is een mogelijke output van het programma:
C:\Users\Dmitri>rexx d:\RexxProgrammas\rexdates De DATE functie kan volgende formaten produceren: Date(Basedate) = 734470 Date(Days) = 334 Date(European) = 30/11/11 Date(European,,,"-")= 30-11-11 Date(Month) = November Date(Normal) = 30 Nov 2011 Date(Normal,,,"-") = 30-Nov-2011 Date(Ordered) = 11/11/30 Date(Ordered,,,"-") = 11-11-30 Date(Sorted) = 20111130 Date(Sorted,,,"-") = 2011-11-30 Date(Usa) = 11/30/11 Date(Usa,,,"-") = 11-30-11 Date(Weekday) = Wednesday
en indien een datum is opgegeven:
C:\Users\Dmitri>rexx d:\RexxProgrammas\rexdates 20111216 De DATE functie kan volgende formaten produceren voor "20111216" ? ===> Basedate Sorted Date(Basedate,20111216,"?") = 20111216 734486 Date(Days ,20111216,"?") = 235 350 Date(European,20111216,"?") = 23/08/63 16/12/11 Date(Month ,20111216,"?") = August December Date(Normal ,20111216,"?") = 23 Aug 55063 16 Dec 2011 Date(Ordered ,20111216,"?") = 63/08/23 11/12/16 Date(Sorted ,20111216,"?") = 550630823 20111216 Date(Usa ,20111216,"?") = 08/23/63 12/16/11 Date(Weekday ,20111216,"?") = Sunday Friday
Dit tweede voorbeeld hoeft misschien een extra woordje uitleg: nu geven we een datum mee als parameter. Maar we zeggen nergens welk formaat die datum heeft. In dit geval kan 20111216 zowel een geldige Base datum als een geldige Sorted datum zijn. Daarom test RexDates welke formaten het kunnen zijn. En dus bevat het antwoord die twee kolommen, de eerste voor het geval dat input een Base datum is, de tweede als het een Sorted datum zou betreffen.