Hogyan írjunk interpretert 90 perc alatt, ~ 100 sorban [III. Inter]

 

Az előző részben, ha homályosan is, de elkezdett körvonalazódni annak a script nyelvnek az utasításkészlete, aminek a feldolgozására és végrehajtására lesz képes az az interpreter, amit most el is kezdünk megvalósítani.

Itt bemutatom egy, a minimumon  túlmutató képességű rajzgép utasítás-készletét.

Valószinűleg sokak számára feltűnt, hogy ennek a nyelvnek a szintaxisa (lásd a képen) a korábbi sorozat assemblerének szintaxisánál összetettebb, hiszen itt az utasítások egy részének nem csak egy paramétere lehet, ráadásul, a paraméterek tipusa sem csak egyféle. Lehet pl. szám is változó neve is. Némi könnyebbség, hogy a változóknak itt már van nevük (A..Z). Igaz, ezek a nevek előre definiáltak, rögzítettek, (ú.n. beépített változók). De igérem a végén megmutatom, hogyan lehet úgy kibővíteni az interpretert, hogy saját, tetszés szerinti nevű változókat is képes legyen kezelni, legalábbis, egy bizonyos hosszméreten belül.

Ha már bemutattam a képen látható utasításkészletet, gyorsan el is magyarázom a benne található utasításokat, azok funkcióját. Fontos már most tudni, hogy az interpreter rajzgépének a tolla két tulajdonsággal bír, ezek a toll szine és az általa húzható vonal vastagsága. Ugyanígy, a rajzgéppel kiírható szövegek esetén a font szine és mérete is programozható.

-------------------

LET  Ezzel a kulcsszóval lehet egy-egy beépített változóhoz értéket rendelni. A listán látható VAR szó helyett A-tól Z-ig használhatunk (kis- vagy nagy-) betűket. A sorrendjük indifferens, lehet mindjárt a Z-vel is kezdeni, ha úgy tetszik. A vessző utáni n egy integer lehet 0-tól 65535-ig. Ezután ezekre az értékekre a nevükkel, pontosabban a betűjelükkel hivatkozhatunk.

 

INC Ez az utasítás a mögötte álló változó értékét növeli meg n-nel.

DEC Ez az utasítás a mögötte álló változó értékét csökkenti n-nel.

GOTO Az utasítás a mögötte lévő két paraméter (x,y) által meghatározott pozícióra áll a kép canvas-án.

UP, DOWN, LEFT, RIGHT Ezek az utasítások a paraméterül kapott számmal megegyező pixelnyi hosszúságú szakaszt rajzolnak a canvas-ra, x,y pozíciótól kezdődően, a nevükkel egyező irányba, adott szinnel és vonal-vastagsággal  

RECT A paraméterben megadott pixelnyi magasságú és szélességű Négyzetet rajzol a canvas-ra.

SAVE A paraméteréül kapott szám mint fájlnév a canvas tartalmát menti el egy .bmp (vagy .png) fájlba.

PRINT A paraméteréül kapott egy szavas stringet írja ki a canvas x,y pozíciójára, az előzőleg beállított (vagy default) font mérettel és szinnel.

LOOP Ez az interpreterünk számlálós (más néven növekményes) ciklusa. A paraméteréül kapott szám mint ciklusszámláló szerepel.
ENDLOOP a loop ciklus végét jelzi.

FSIZE A canvas font méretét állítja be.

FCOLOR A canvas font szinét állítja be. Lehetőségek: 0,1,2,3,4,5,6,7,8

PENWDT Paramétere a toll vastagsága. Nulla értékű paraméter nem értelmezhető.

PENCOLOR A toll szinét állítja be. Lehetőségek: 0,1,2,3,4,5,6,7,8 (lásd a II. részben közölt képet)

LINETO a két paraméter által meghatározott x,y pozícióig húz, a pen vastagságának és szinének megfelelő egyenest, az aktuális pozíciótól.A kezdő x,y pozíció mindig 0,0 és a canvas bal felső sarkától számolódik.

--------------------

Ennek az utasításkészletnek alig egyharmadnyi szubszetjével (részhalmazával) is lehetne programozottan rajzolni, viszont az utasítás "többlet" itt úgy hasznosul, hogy a lehetőségeket bővíti, vagy a megírandó programok hosszát csökkenti. A RECT utasítás például egy négy soros RIGHT, DOWN, LEFT, UP utasítás-szekvenciát vált ki. Tehát négy utasítás helyett egy. A LINETO utasítás egy GOTO-t spórol nekünk és egy másikat az "irány" utasítások közül. De emellett azzal az unikális többlettel is bír, hogy képes vonalat rajzolni átlós irányban is. A legszükségesebben túlmutató ú.n. "plusz" utasításoknak általánosságban az a hozamuk, hogy felhasználói oldalon kevesebbet kell gépelni, a scriptek sokoldalúbbak, emellett még rövidebbek is lehetnek.  

Én ezt az utasításkészletet (lásd a képen) így alakítottam ki, mert erre volt igényem, ezt kivánta meg a feladat. De semmi akadálya a szettet áttervezni, elvenni belőle vagy kiegészíteni ezzel-azzal. Kiinduló alapnak mindenesetre, megfelel.    

 

 

all in

 

A fenti képen, barack szinű alapon látható az utasítás-készlet és annak grammatikája. 
Ehhez némi adalék:

-- A LET, INC és DEC kulcs-szavak után álló VAR az angol ABC valamely betűjét jelöli. Ezen a pozíción más, tehát egyéb karakter, vagy numerikus érték NEM ÁLLHAT.

-- Ahol a * karakter áll, ott az angol ABC betűi (tehát változó azonosítók) és numerikus értékek egyaránt szerepelhetnek, természetesen a kettőből valamelyik. Erre utal a lista utolsó sora is. VAR VAGY n. 

-- Ahol n áll, ott kizárólag numerikus értékek alkalmazhatók, tehát oda változó, avagy annak  betűjele nem kerülhet.

-- Kakukktojás a PRINT utasítás paramétere. Itt ugyanis bármilyen nyomtatható karakter használható.  Emellett változók értékei is kiirathatók, ha egy pontot teszünk közvetlenül a változó betűjele elé. Példa:  PRINT .C
 

-------------------

Na de mi van a sorszámokkal?

Hát igen, aki az előző előtti részt olvasta, az a LOOP bemutatásánál a magyarázó képet elnézve azt gondolhatta, hogy itt olyasféle interpreter van készülőfélben, ami ugyanúgy sorszámozza a programsorokat, mint a rossz emlékű BASIC.
Mindenkit megnyugtatok, erről szó sincs!  Az interpreter nem fog sorszámokat használni a sorok azonosítására. Azaz, de igen, fog, csak nem úgy. Szóval, majd alább kiderül, hogy  mi meg hogyan.   

Most pedig rátérünk az interpreterre. 

A pascal több féle módon képes string tipusú változókat kezelni.
A legkorábbi, legősibb megvalósítás egy-egy string maximális hosszát 255 karakterben határozza meg és tulajdonképpen a stringeket egy-egy karakter tipusú vektorként (egy dimenziós tömbként) kezeli (lásd a képen).
Ez azt jelenti, hogy a stringekben lévő karakterek 1 indexhatártól kezdődően indexelhetőek, a string hossza pedig a nulladik indexen tárolódik (lásd a képen).

A scriptjeinket, programjainkat is ilyen stringek (sorok) alkotják. Ezeket a sorokat beolvashatjuk egy két dimenziós tömbbe is (lásd a képen). Ekkor két számmal hivatkozhatunk a teljes programunk bármely karakterére.
Az egyik szám a program-sort, a másik pedig a sorban az n-edik karakter pozícióját jelöli.
Ez azért fontos, mert most nem úgy fogjuk a programjainkat feldolgozni, ahogy korábban, az assembler esetében, hanem egy más, egyszerűbb módon. Az előző sorozatban szót ejtettem arról, hogy a program-sorokat elég lenne két helyen "elvágni", hogy a bennük levő tokeneket kinyerjük. Az egyik hely, a sorlezáró karakter előtti pozíció, leválasztotta volna az esetleges kommentet. A másik vágást az első token (itt kulcsszó) utáni pozíción kell megejteni, hogy a két token elváljon egymástól. Ezután csak a tokenek két végén esetleg meglévő SPACE karaktereket kell leszűrni egy TRIM utasítással és meg is vagyunk (lásd a képen).  Hát, éppen ezt fogjuk csinálni most. Drámai mértékben le fogja egyszerűsíteni a munkánkat.

Az utasításaink, az élő nyelvekhez hasonlatosan, mondatokba szerveződnek. Így van ez egyébként szinte minden más programozási nyelv esetében is. A különbség az, hogy programnyelvek esetében a mondatok, az élő nyelvek mondataitól eltérően, erősen formalizáltak, kötöttek. Szigorúan meg van bennük határozva a szórend és attól eltérni nem is nagyon lehet.

Szóval, ilyeneket sem lehet, hogy:

Ubul a balatonon lángossal tömi a fejét.
A balatonon lángossal tömi a fejét Ubul.
A balatonon Ubul a fejét tömi, lángossal.
Lángossal tömi a fejét a balatonon Ubul.
Ubul a fejét tömi, lángossal, a balatonon.

Erre jó példa még a texasi farm-gazdaságunkban működtetett, kilogram alapú, fejőnő analizátor program alábbi részlete:

LET tehen$  = INKEY$
LET fejono$ = INKEY$

IF (fejono$ > tehen$) THEN GOSUB FitnessClub ELSE GOSUB Teheneszet

 Az első lépés, hogy meghatározzuk, a scriptjeink egy-egy mondata maximum hány darab tokent tartalmazhat.
Ha a már ismertetett utasításkészletet vesszük alapul (lásd a képen), akkor azt találjuk, hogy ez a szám négy, hiszen az első token maga az utasítás. A második és a negyedik token lesz a két paraméter (ha az utasítás nem érné be kettőnél kevesebb paraméterrel), a harmadik pedig egy szeparátor, ami  semmi mást nem tesz, csak két paramétert vagy tokent választ el egymástól. A mi esetünkben ez a szeparátor lehetne az egyenlőségjel az értékadásnál:

LET A = 10

De szükség lesz a vessző karakterre is mint szeparátorra, hiszen például a GOTO utasítást követi egy felsorolás, ami a goto célpozícióját (X,Y) közli.

GOTO  140,30

Ha jól megnézzük, a vessző önmagában is elégséges a feladathoz, hiszen a
LET A, 10  forma is remekül értelmezhető, máshova pedig amúgy is vessző fog kelleni. Ezzel az egyszerűsítéssel maga a feldolgozás is egyszerűbbé válik, mert az egy-egy sorban előforduló tokeneket így csak három karakter választhatja, szeparálhatja el egymástól. Ezek: A TAB (tabulátor) a SPACE és a vessző (',') karakter. Elvileg a TAB-ot is kizárhatnánk, de elég sokan használják, különösen, ha programozók, így a TAB kizárása erősen ellenjavallt.
 

A tokenizer

Nézzük is, hogy fog festeni a programunk azon része, ami kinyeri a scriptjeinkből az érdemi infót:

Lesz egy statikus, egy dimenziós string tömbünk. Ebben fogjuk a feldolgozandó sorból kinyert tokeneket tárolni, atmenetileg. Addig, amíg a végrehajtásuk meg nem történik. Azután lépünk is a következő sorra.

Na de várjunk csak. A tokenek egyike a vessző amúgy is szeparátor karakter, ami az interpreter végrehajtó egysége számára már haszontalan. Akkor minek is tárolnánk el?  Nem fogjuk eltárolni, így a tokenek száma négyről háromra csökken. Kevesebb adat, egyszerűbb, rövidebb, érthetőbb kód. A tömb tehát három karakter vagy karaktersor (token) tárolására lesz alkalmas. Ehhez a tömbhöz tartozik majd egy byte tipusú index, hogy a tömbön belül a kivánt tokenre tudjunk hivatkozni a feldolgozás, vagy a végrehajtás során. Ez az index, mivel a tömbünk csak három elem tárolására lesz képes, a 0, 1, és 2 értékek valamelyikét veheti fel. Egyebet nem.

A tömb neve TOKEN lesz, az indexé pedig Ti  (ez egyébként a Token index rövidítése).  

   TOKEN : Array [0..2] of string;      // Tokenek STATIKUS tombje
   Ti         : byte = 0;                           // Token tomb indexe

Ahhoz, hogy a programsorok feldarabolását el tudjuk végezni, szükségünk lesz egy függvényre, vagy eljárásra aminek a paramétere lesz maga a darabolandó programsor, a visszatérési értéke pedig a TOKEN nevű tömb, amelyben a TOKEN[0] helyen van a sorból kinyert utasítás, a TOKEN[1] és TOKEN[2] helyeken pedig az esetleges paraméterek, ha egyáltalán lesznek. Mert pl az ENDLOOP-nak nincs semmiféle paramétere. Tulajdonképpen visszatérési érték nem is kell, mivel a TOKEN nevű tömb globális, tehát az interpreterünk bármely pontján írható, olvasható. Szóval, ez a daraboló függvény (vagy eljárás) kapni fog egy egzotikus azonosítót, Tokenizer lesz a neve.  
Valójában elegánsabb lenne úgy megírni az egészet ,hogy a TOKEN nevű tömböt csak a daraboló függvény legyen képes írni, a többi pedig csak olvasni, de így rövidebb, egyszerűbb a kód.

Hogy honnan fogjuk tudni, hogy van-e paraméter vagy ha van, akkor az mennyi, azt a Ti nevű index fogja elárulni, Ha ennek értéke 0, akkor egy sincs, ha egy, akkor egy, ha kettő, akkor pedig két paramétere van az utasításnak.
Talán sokakban felmerül, hogy na de mi van, ha a paraméterek száma több vagy kevesebb az elvártnál?
A kérdés jogos, előfordulhat ilyen, de erre az esetre is van megoldás. Később még visszatérünk rá.

Na de hol lesznek a feldolgozandó programsorok? Hát azoknak is létrehozunk egy tömböt. Ez már sorokat  (scripteket) fog tárolni, így a tipusa string lesz és persze ez is kapni fog egy indexet.  A tömb neve Prg lesz, az indexe Pi és mivel statikus (tehát fix méretű) lesz a tömb, így kelleni fog egy felső index is, ami azt jelzi, hogy a tömb melyik sora az, ami a scriptünk utolsó sorát tartalmazza. Ez lehet, hogy így egy kissé homályos, nehezen érthető, így megvilágítom LED izzóval is:
Mondjuk, létrehozunk egy 255 sor befogadására képes tömböt. De a scriptjeink ennél a 255 sornál, mondhatni mindig rövidebbek lesznek, hetvenöt sorosak, húsz sorosak, negyenkét sorosak, stb. Ezért kelleni fog egy index, ami a scriptünk utolsó sorára mutat, hogy tudjuk, hol ér véget a feldolgozás. Ezt az értéket a PrgEND nevű változó fogja tárolni. Tekintve, hogy a felső határnak (PMAX) én paraszt,tényleg a 255-öt határoztam meg, így a Pi és a PrgEND nevű indexek is byte tipusúak lesznek.   

   Prg         : array [1..255] of string;   // program (script) STATIC tombje
   Pi            : byte = 1;                           // program sorok indexe
   PrgEND  : byte = 1;                           // prog (script) vege

Az interpreter úgy kezdődik, hogy lesz egy eljárás (a ProgLoad), ami a scriptünket betölti a Prg nevű tömbbe.
Ez a művelet a scriptet soronként olvassa be, így elég minden új sor beolvasása után a Pi nevű változót eggyel növelni (inkrementálni).
Amikor a sorok beolvasása befejeződött, akkor a Pi nevű változó értékét átadjuk a PrgEND nevű vűltozónak, a Pi-t pedig visszaállítjuk az eredeti értékére, egyre.  

Ha ez megvan, akkor a Prg nevű tömbünk legelső sorára mutat a tömb indexe, a Pi. Így: Prg[1]. A sor megegyezik a scriptünk első sorával.
Ezt, mint paramétert, átadhatjuk a daraboló függvényünknek, a Tokenizernek. Az pedig szépen feldarabolja a sort és megtölti annak tartalmával az előzőleg gondosan kiürített TOKEN nevű tömbünk rekeszeit.  A dolog egyszerűbb, mint azt apósom bármelyik barátja hinné, íme:

Figyelem!

A Ti és a Pi nem ugyanaz. A T-s a Tokent, a P-s pedig a Programot indexeli. A singleton i pedig a Tokenizer függvény lokális változója.  Egy szimpla ciklusváltozó. A programsor az s változóban van.

00   Ti := 0;  
01   for i:= 0 to 2 do  TOKEN[i] := '';  // a TOKEN tomb  kiuritese
 
02   for i := 1 to length(s) do          // Tokenekre 'tordeles'  
03   begin
04     if (s[i] in [#09,',',#32]) and (TOKEN[Ti] <> '') then  Ti := Ti + 1
05     else TOKEN[Ti] := TOKEN[Ti] + upcase(s[i]);

Ebben azt hiszem semmi izalmas nincs még egy totál kezdő számára sem, kivéve a negyedik-ötödik sort. Ebben az történik, hogy az aktuális sorunkat (ez a Prg[Pi]) annak elejétől végéig megvizsgáltatjuk és ha a vizsgált karakter egy TAB (ez a #09) vagy egy vessző, netán SPACE (ez #32) ÉS a TOKEN nevű tömbünk aktuális rekesze (ez a TOKEN[Ti]) NEM üres, akkor a TOKEN következő, még üres rekeszére lépünk, Ez a TOKEN[Ti+1].
Viszont, ha a vizsgált karakter NEM TAB, NEM vessző és NEM SPACE, akkor a karaktert hozzá kell fűzni a TOKEN tömb aktuális (erre mutat a Ti) rekeszének tartalmához. Így lesz az L betűből a következő körre LE szután meg LET. A G betűből GO,  majd GOT végül GOTO.

A folyamat magyarabbul: Ha szeparátor karakter (tab, vessző, space) érkezik, akkor az előző tokennek vége (hiszen ezt jelzi a szeparátor is, ezért van ott), így új rekeszre lépünk, ha viszont NEM szeparátor az érkező karakter, akkor hozzáfűzzük a többi részéhez, vagy ha a rekesz még üres, akkor csak a rekeszbe rakjuk és kész.

Persze egyéb tennivalót is végez a Tokenizer ebben a negyedik és ötödik sorban, mint például a karakterek nagybetűssé konvertálása (UPCASE), mert ahogy korábban a VM esetében, úgy itt sincs különbség a forráskód (script) kis- és nagybetűi között. GotO, GOTO, goto, GOto, egyre megy.  

Megint valami érdekeset fogok közölni: Az interpreterünkkel rövidesen végzünk is. Pedig így, ránézésre hülyeségnek tűnik a kijelentés, nem?
Lássuk, mi van még hátra? A változók esete, azok menedzselése - ami bot egyszerűségű lesz - és persze a végrehajtó egység, ami szintén nem lesz egy atommáglya.

Nézzük a változókat. Említettem, hogy beépített változók lesznek, az azonosítóik pedig az angol ABC betűinek halmazát kölcsönzik. Ebből mindössze huszonhat van, tekintve, hogy mi a kisbetűket is nagyvonalúan nagybetűssé alakítjuk.

Az ASCII kód-tábláról talán már mindenki hallott, aki olvassa ezeket a sorokat. Ha nem, akkor javasolt pótolni. Ez a kódtábla tartalmazza az írásjelek kódjait (is). Az Angol ABC nagybetűinek kódjai a hatvanötödik sorszámtól kezdődően kaptak helyet a táblában, ABC sorrendben. Tehát, az 'A' a 65, a 'B' a 66, a 'C' a 67, a 'D' pedig a 68 sorszám alatt található és így tovább a többi (nagy)betű is, egészen 'Z'-ig bezárólag, ami a kilencvenedik. Az ABC teljes (nagy)betűkészlete a 65. és a 90. között foglal helyet.
Vannak az ASCII táblában más karakterek is. Pl. amikor én a forráskódban a #09-et vagy a #32-t szerepeltettem, akkor a TABULATOR (ez a 09) vagy a SPACE (szóköz) karakterre hivatkoztam. Ez utóbbi ASCII kódja a 32.

A pascal lehetőséget ad arra, hogy ha akarjuk, akkor a tömbjeink alsó indexhatára nullától eltérő szám legyen. Ezért a változóink értékeinek tárolására ezt a lehetőséget ki is használjuk. A változóink értékei egy vektorban (1DIM tömb) fognak tárolódni (lásd a képen), a pozíciójukat (index) pedig az azonosítójuk (nevük) fogja meghatározni.
Itt nulladik pozíció, mint olyan, nem lesz. Az 'A' lesz az első, a 'B' a második, a 'C' a harmadik és így tovább.

Ez, a változóinkat tároló tömb, így fest majd a forráskódban:

   VARs     : array[65..90] of word;       // beepitett valtozok

Ennek is lesz indexe? Hát persze. Kell is, hogy legyen, mert enélkül nem férnénk hozzá a tárolt értékekhez de még a tárolást sem tudnánk index nékül megvalósítani. Tehát, index, az lesz. Még hozzá, az éppen hivatkozott karakter ASCII kódja lesz az a bizonyos index. Ha a karakter történetesen az  'A' akkor 65, ha 'B' akkor 66, ha 'C' akkor meg 67 és így tovább egészen Z-ig, ami 90. A tömb tipusa pedig azért lesz word, mert ennek a tipusnak az értékkészlete (0..65535) passzol a legjobban a céjainkhoz.

Hogy néz ki ez működés közben?
Tegyük fel, hogy a feldolgozandó sor ez:

LET   A,25   

Ez tokenizálva így fest majd:

TOKEN[0] = 'LET',
TOKEN[1] = 'A',
TOKEN[2] = 25

Ekkor már tudni fogjuk a TOKEN[0] tartalmából (ez ugye a LET) hogy értékadás következik, azt is tudni fogjuk a TOKEN[1] tartalmából ('A'), hogy az A változónak kell a 25 értéket adnunk,
Mivel tudjuk, hogy az 'A' ASCII kódja 65, így a 25-ös számot a VARs[65]-be kell beírnunk, így:

VARs[65] := 25;

Na jó, de honnan fogja az interpreterünk futásidőben tudni, hogy mondjuk az 'A', az 'N', vagy az 'U' karakternek mi az ASCII kódja? Ezt az ORD függvény árulja majd el  neki.  Az ORD függvény bemenete egy tetszőleges karakter, a visszatérési értéke pedig a bemenetként kapott karakter ASCII kódja.

ORD('A') = 65,
ORD('Z') = 90.

 
Ha ki akarjuk olvasni mondjuk az 'A' változó értékét egy TEMP változóba, akkor azt így fogjuk elérni. TEMP := VARs[ord('A')]; Ennek eredménye az lesz, hogy a TEMP megkapja az A v'áltozó értékét, a huszonötöt.
A visszaírás hasonlóan történik: Mondjuk, ha a TEMP-hez hozzáadunk még 10-et, akkor a TEMP értéke 35 lesz.  Ennek az értéknek a VARs tömbbe való visszaírása így működik:

VARs[ord('A')] := TEMP;

 

QD source

 

A fentebbi forráskód nem lesz feltöltve kód-megosztóra.

A célja annyi, hogy a programot szerkezetileg láttassa, egyes részek (pl. a ProgLoad, a Tokenizer, vagy a változók) működését bemutassa, elemezhetővé tegye.

A program egyébként tesztelhető, kipróbálható.
Begépelés és lefordítás után a 71-76 sorokat egy test.prg nevű fájlba mentve, a 78. sorban látható módon felparaméterezve  az outt.txt  kimeneti fájl tartalma a változók neve és értékük listája lesz. Formázottan, kb. alábbihoz hasonlóan (ASCII kód, VAR név, VALUE):

 

65	A	4
66	B	91
67	C	0
68	D	0
69	E	5
70	F	0
71	G	66
72	H	0
73	I	0
74	J	0
75	K	0
76	L	0

A test.prg file tartalma tetszés szerint (de szintaxis-helyesen) megváltoztatható. Ha ez történik, akkor a kimenet (outt.txt) tartalma  is változni fog.

Figyelemmel kell lenni arra, hogy a programban jelenleg egyáltalán nincs még minimális hibatűrés sem.

(Folyt. köv.)

Post Scriptum:

Ezúton szeretném köszönetemet kifejezni a Graph It nevű program alkotójának, azaz saját magamnak, hogy a jelen posztban bemutatott illusztrációk  készítésekor a Graph It program sokoldalúságán megtámaszkodhattam. Hallatlan könnyebséget jelentett ez számomra.

Még egyszer köszönöm.
 Tényleg.  

Hozzászólások

Szerkesztve: 2021. 07. 17., szo – 11:24

Ja, a sorszámozás magyarázata kimaradt, de már nem szerkesztek bele (így is elég hosszú),  inkább ide írom:

A programban sorszámok nem lesznek olyan klasszikus módon használva, ahogy a régi BASIC-ekben. Azonban a programok, mivel eleve tömbbe lesznek beolvasva, soronként, így sorszámot is "kapnak", amit fel fogunk használni a LOOP-ok (ciklusok) esetében, az ismételt végrehajtás során.  
Végül is, afféle implicit sorszám használat ugyan lesz, de a scriptek sorait nem kell majd számozni.

Még annyit, hogy  ebben, a korábbitól eltérően nincs lehetőség a sorok végén kommentelni. Csak teljes soros kommentet lehet használni. A komment sorokat a pontosvessző karakterrel kell kezdeni és ha futtatható kódot tartalmaznak, akkor az nem fog lefutni. Ily módon a nem kivánt kódrészek átmenetileg kiiktathatóak. Ezzel az eredeti kódot gyorsan át lehet szerkeszteni.   

;  comment

; LOOP 10
; RECT C,D
; INC C,25

; ENDLOOP

LOOP 10
RECT C,D
; INC C,25
INC D,15

ENDLOOP

szivesen elolvastam volna de sajnos a kepek torottek :( biztos nem tudsz rola, igy gondoltam szolok!

A másik threadben Hiena megválaszolta neked, hogy mi a helyzet. Ezúton is köszönd meg neki, hogy felvilágosított. Azt azért te sem képzelheted, hogy majd mindenki hozzád igazítja a paplanhuzatát. 

De, egyáltalán, mivel tudnék én senki, a te szakmai épülésedhez hozzájárulni? Éppen erre a "broken pictures" sorozatra vagy kiéhezve?
Hát itt tartasz? Te IT világ piros ásza, te számítástechnika dzsungelharcosa,  te bitek, bájtok kalasnyikovja, minden sávszélességek mojito koktélja.

Nem lehet, hogy az van, rossz helyen csicskulsz, és még a neked kiadott laptop biztonsági policy-jét sem te, hanem egy supervisor állítja be és tartja rigorózus kontroll alatt?

Szerkesztve: 2021. 07. 17., szo – 09:01

Érdekességképpen a példa legtöbb utasítása egy alapszintű FORTH környezet létrehozása után FORTH nyelven egyszerűen implementálható.

variable pen_color
variable pen_wdt
variable f_color
variable f_size
variable f_bold
variable x
variable y

: penwdt pen_wdt ! ;
: pencolor pen_color ! ;
: fcolor f_color ! ;
: fsize f_size ! ;
: fbold f_bold ! ; 

: goto y ! x ! ; 
: up y @ SWAP - y ! ;
: down y @ + y ! ;
: left x @ SWAP - x ! ;
: right x @ + x ! ;

Próba: $ gforth  # vagy a közelmúltban bemutatott egyszerű FORTH fordító
Fentit egérrel beilleszteni, és mehet a

10 20 goto
3 up
4 right

Ebből már csak az maradt ki, hogy ténylegesen húzzon vonalat a képernyőn, vagy valamilyen bitmapben.

Más: Adott esetben tömböt simán karakterrel is lehetne indexelni, nem kellene az ORD-függvényt koptatni:

VARs: array ['A'..'Z'] of word;

Igen, a lineto utasítást és a save mint file i/o művelet kell még implementálni. A rect az 4 lineto-t tartalmazó parancs. Ebből a save utasítást mindenképpen a futtatókörnyezetben kell implementälni, mivel kell a futtatókörnyezetben valami file i/o.

A lineto implementálható FORTH-ban is. Pixeltömb kreálható (CREATE pix_arr maxx @ maxy @ * ALLOC), DO .... LOOP szintén ismert FORTH-ban. A C! parancs szintén barátunk lesz ehhez. A pixelírás ebből így néz ki:

: pxwr maxx @ * + pix_arr + C! ;           \ colorcode posx posy

8 2 3 pxwr        \ 8-as színkód megy posx=2 posy=3 pixelre.