more SLC descriptions
This commit is contained in:
parent
577ac45bb6
commit
f56a322328
215
BASED.md
215
BASED.md
@ -1219,7 +1219,7 @@ Ennek megfelelően valami hasonló lehetne:
|
||||
|
||||
#: DECLARE_MIN_MAX_INT
|
||||
#inserts {
|
||||
#: MIN_INT
|
||||
#: MIN_INT // HIBÁS: Ezt nem lehet, insertálni deklarációt nem fog menni...
|
||||
#IF(#LT) {
|
||||
#DROP
|
||||
} [
|
||||
@ -1227,7 +1227,7 @@ Ennek megfelelően valami hasonló lehetne:
|
||||
#DROP
|
||||
]
|
||||
#
|
||||
#: MAX_INT
|
||||
#: MAX_INT // HIBÁS: Ezt nem lehet, insertálni deklarációt nem fog menni...
|
||||
#IF(#GT) {
|
||||
#DROP
|
||||
} [
|
||||
@ -1238,6 +1238,12 @@ Ennek megfelelően valami hasonló lehetne:
|
||||
}
|
||||
#
|
||||
|
||||
Megj.:
|
||||
|
||||
Ahogy fentebb írom is, ez a példa már NEM működik, mert az insert-stack temporális jellege miatt deklarációra
|
||||
való mutatókat nem tudunk ezen a ponton kezelni! Tehát az insert-bufferbe ilyet nem írhatunk (error).
|
||||
Viszont! Fontos lenne tudni írni a szótárba valami módon tehát ezt kihelyettesíthetővé tenni szerintem...
|
||||
|
||||
Vegyük észre, hogy itt a #insert mögötti blokkban szereplő dolgok mind az olvasó fej jelen pozíciója MÖGÉ kerülő
|
||||
nyers programszöveg - itt az olvasó fej már ugyebár beolvasta a `DECLARE_MIN_MAX_INT` szót, tehát az mögött állunk
|
||||
és olyan eredményt kapunk, mintha az adott dolgokat titkon oda gépelte volna valaki, szóval amint kész ezen szó
|
||||
@ -1280,7 +1286,7 @@ A szimbólumtábla egy szimbóluma megnyitható "fájlszerűen", ezért a defin
|
||||
|
||||
**Megj.: Talán ezzel kiváltható a #inserts kulcsszó használata! De egy plusz stack-el is...**
|
||||
|
||||
## A #write
|
||||
## A `#write` működése?
|
||||
|
||||
Ezzel a szimbólumtáblával manuálisan már sok mindent megoldhatunk és a #insert-el együtt ez powerful már,
|
||||
de a fordítóprogram-jellegű használathoz jó, ha szerepel itt "#write[0](..) {...}" szó is. Ez az inserts-hez
|
||||
@ -1297,6 +1303,22 @@ A nyelvnek a köztes reprezentációja lényegében szintén emberileg olvashat
|
||||
adott menet közepén a fordítás. Természetesen ez akkor van, ha az alaprendszert fordítónak használjuk és nem
|
||||
egy interpreternek éppen.
|
||||
|
||||
## Az `#include` működése
|
||||
|
||||
#include(wordset.tsc)
|
||||
|
||||
Fontos, hogy az include-oláskor C-szerűen mennek a dolgok. Valószínűleg kell majd #ifdef-es guard, meg #define is.
|
||||
Az include-oláshoz az úgynevezett session-storage-t használjuk és amikor ide jutunk, akkor a parzer / engine
|
||||
szépen beolvassa a fájlt (hibajelzés, hogy ha nincs olyan) és ezt a kis szöveget kihelyettesíti úgy, hogy a zárójel
|
||||
között vagy a pointer van - vagy arra szükség sincs és csak kerül be a session-be a cucc egyszerűen... plusz feldolgozzuk...
|
||||
|
||||
## Kommentek
|
||||
|
||||
A kommentek kezelését az engine végzi. Ha valaki programnyelvet akar vele definiálni, akkor meg kell adni a listát a
|
||||
sorvégi komment karaktersorozatról, meg a többsoros kommentekéről (parancssori paraméter). Default-ként a C-s dolgok...
|
||||
|
||||
Fontos: ezeket a parzoláskor egyszerűen skippeljük!
|
||||
|
||||
## Template makró és egyéb példák
|
||||
|
||||
Fordítandó (pl. JASS transpilerhez):
|
||||
@ -1390,10 +1412,10 @@ Ebből legyen - útána meg már elvileg nem olyan nehéz:
|
||||
|
||||
Egy adattagokat(változókat) is tartalmazó szó és példa a "lokális" változó-elérésre...
|
||||
|
||||
#: MY_WORD a b c
|
||||
#a
|
||||
#: MY_WORD @a; @b; @c;
|
||||
@a
|
||||
#inc
|
||||
#a(.)
|
||||
@a(.)
|
||||
#
|
||||
|
||||
Máshonnan elérni így lehet, namespace-elés szerűen:
|
||||
@ -1430,12 +1452,12 @@ tehát alapból van egy rendszerezettség (például egy adott state-machine glo
|
||||
|
||||
Ha nagyon ki akarjuk hagyni a namespace-elést, akkor a változó név helyett írjunk "típust" (ez csak név!) és üres szót neki:
|
||||
|
||||
#: MY_VAR int
|
||||
#: MY_VAR @int;
|
||||
#
|
||||
|
||||
Esetleg:
|
||||
|
||||
#: MY_VAR int #
|
||||
#: MY_VAR @int; #
|
||||
|
||||
Használat:
|
||||
|
||||
@ -1492,16 +1514,24 @@ Ha programnyelvet csinálunk:
|
||||
Lehetővé tesszük, hogy a meta-nyelv indításakor (pl. parancssori paraméterrel) a gyári szavak prefixje
|
||||
megváltoztatható legyen... Tehát ne #dup hanem mondjuk @dup legyen és a tieiddel ezt lekövetheted...
|
||||
|
||||
slc -prefix='#' my.app
|
||||
slc -prefix='#' my.slc
|
||||
|
||||
Ha nem akarunk prefix-et, akkor meg kellhet mondani, hogy mi a "záró jelző" karakter
|
||||
|
||||
- ez alapból a prefix maga, de csak ahogy önmagában áll, tehát a fenti példában '#' (lásd a példáinkat)
|
||||
- üres prefix esetén alapból a pontosvessző karakter (forth-os szokás)
|
||||
|
||||
slc -ender='%'
|
||||
slc -ender='%' hello.lang
|
||||
|
||||
Default: Üres prefix és pontosvessző ender. Ezzel forth-szerű interpretert kapunk!
|
||||
Szükséges lehet még a 'kukacolás' átírása is valami másra, például így:
|
||||
|
||||
slc -at='~~' hello.lang2
|
||||
|
||||
Default: Üres prefix és pontosvessző ender, kukac -at mellett. Ezzel forth-szerű interpretert kapunk!
|
||||
|
||||
Az slc-nek továbbá szükséges lehet egy "prefix kód". Ez gyakorlatilag "auto-include"-olás:
|
||||
|
||||
slc -prefixcode='based.slc' hello.basd
|
||||
|
||||
Megj.:
|
||||
|
||||
@ -1510,13 +1540,43 @@ Megj.:
|
||||
prefixje is megfelelően kell változzon. Ennek az egyik egyszerű módja, ha "gyári szó deklaráláshoz" külön szó van,
|
||||
de igazából ezt a "külön szót" valószínűleg mi magunk is megint csak implementálhatjuk valamilyen insertálással!
|
||||
|
||||
Megoldás erre: #builtin: prefixelendő_szó
|
||||
Megoldás erre:
|
||||
|
||||
## Névtelen szó
|
||||
#builtin: prefixelendo
|
||||
#swap
|
||||
#dup
|
||||
|
||||
Ezt követően hívható a szó prefixelt változata
|
||||
|
||||
#push(5)
|
||||
#push(4)
|
||||
#prefixelendo
|
||||
#intprint // 5
|
||||
#intprint // 5
|
||||
#intprint // 4
|
||||
|
||||
Megj.: A #builtin esetben mindig van elég byte hely majd a parzernek helyben átírni a szót a megfelelőre (whitespace-es törléssel)
|
||||
|
||||
## Névtelen szó - mégsem lesz!!!
|
||||
|
||||
Hasznos leíírni, hogy szó nélkül, csak zárójel esetén mi történjen...
|
||||
|
||||
Ez lehet hogy baromira elbonyolít mindent mondjuk... Talán csak API-t kéne adni erre ki...
|
||||
Ez lehet hogy baromira elbonyolít mindent mondjuk... Talán inkább csak API-t kéne adni a zárojeles parzolás segítésére...
|
||||
|
||||
## Hibakezelés: MISSING szó
|
||||
|
||||
#MISSING:(...)[...]{...}...#
|
||||
|
||||
Ha a runtime olyan szót talál, amit elvileg FUTÁSKOR odaérve nem definiáltak le, akkor ez a missing meghívódik.
|
||||
|
||||
Igazából ezen a ponton hibakezelést írhatunk le!
|
||||
|
||||
## Hibakezelés: Zárójelezés
|
||||
|
||||
Az engine amikor interpretál, akkor megkapja az "indentáltság mértéke" karbantartott értékét!
|
||||
Ez nagyon fontos, mert ezzel értelmes humán hibaüzeneteket tudunk kapni a zárójelezés elmaradásakor!
|
||||
|
||||
Tehát alapvetően az engine maga ellenőríz zárójelezést, nem kell a programnyelv tervezőjének ezt magát megcsinálni!
|
||||
|
||||
## Pipeline és párhuzamosság
|
||||
|
||||
@ -1551,60 +1611,92 @@ szöveges interfésszel, hanem simán csatornákkal és egy DAG-ot leírva, azé
|
||||
## Reprezentáció
|
||||
|
||||
struct word {
|
||||
int16_t len; // (*): A codelen helyett teljes rekord hossz (ender-el bezárólag), a codelen számítható: a vars hossza+len!
|
||||
char name[]; // inline! Tehát nem egy pointer!
|
||||
char vars[]; // inline! zero-terminált! Ebben simán lineárisan keresünk csak! Kevés változót használjanak felsorolva!
|
||||
char data[]; // inline! Előbb a változók, aztán a kód - így írható primitív forth-szavakkal is ha kell!
|
||||
char colon; uint8_t flags; // ':' + whitespace, but the whitespace becomes flags!
|
||||
char name[]; // inline! Tehát nem egy pointer! Zero terminált a whitespace / zárójel felülírásával.
|
||||
char vars[]; // inline! Maga a tárhely is itt lesz! Elfér 4 byte ' @i,' vagy ' @j<EOL>' miatt!
|
||||
char data[]; // inline! Előbb a változók, aztán a kód - így írható primitív forth-szavakkal is kódszó (pl. outputra).
|
||||
};
|
||||
|
||||
Variációk:
|
||||
A szavakat az "ender" string terminálja, a hosszt itt alapból nem tároljuk. Amikor a parzer látja
|
||||
a szó defet, akkor a változókat (pl. C stack-en) megjegyzi és a szimbólumtáblába ezt a címet írja!
|
||||
A változó helyén a programszövegben pont elfér 4 byte, tehát magában ott lesz! A szimbólumtábla rámutat,
|
||||
a forth szó belében pedig ha látjuk a @-os kezelését, akkor szimbólumtábla nélküli írásra fordítható át,
|
||||
tehát a lokális használat sokkal hatékonyabb tud lenni, mert közvetlen memória címzés történik!
|
||||
|
||||
* pozitív vagy nulla len: 4-byte-os változók (n db) és SLC kód szövegként.
|
||||
* negatív len legfelső bit 1: A legfelső bitet nullázva és a címet egy nagy indexként véve megkapjuk a "complex_word" indexet
|
||||
Ehhez arra is szükség van, hogy egy szónak maximum 0..255 db változója lehessen - ugyanis legalább egy karakter a név
|
||||
és a @-al kezdődő szó, vagyis a -at='^^' esetén pl. azzal... Szóval úgy két byte a kódban a hely, de az első parzerbe
|
||||
szükséges szóval csak a maradék byte-ot (a névből számmá alakítással) tudjuk az interpreterben fordítani! Ilyen névtér
|
||||
nélküli kukacozás csak szódefinícióban lehetséges - ezért az engine tudhatja, hogy mindig át kell ezt írni és ha a
|
||||
szót a definíciója szerint "lefuttatunk" interpretálva, akkor pedig azt, hogy mindig már az átírt alak áll ott (nem a név)!
|
||||
|
||||
Mint az egyértelmű, a szó deklaráció a kód szövegben legalább 4 byte-ot igénybe vesz:
|
||||
Ha már THREADED formába került a szó, akkor lényegében egyfajta FORTH-os jit-elésen már átesett, de ez nem akkor
|
||||
történik, amikor a szót a parzer meglátja és a szimbólumtáblába bekerül, hanem amikor ELŐSZÖR MEGHÍVÓDIK.
|
||||
Ugyanis amikor először látja az engine a szót, lehet hogy nem fordítható, még pl. későbbi szóra hivatkozik!
|
||||
Viszont ekkor nem is szükséges még "csinálni" vele semmit a szimbólumtáblába a kezdőcímének beírásán túl, meg
|
||||
hasonló dolgokon túl! Amikor az első hívás történik, akkor megpróbáljuk, hogy mekkora lenne a méret az átírással - ehhez
|
||||
ideiglenes tárhely sem szükséges, csak egy plusz számítási menet, amikor még NEM csináljuk meg, csak kiszámoljuk mibe
|
||||
kerülne és mennyi ram kell hozzá - kifér-e majd a jelen dolog helyére! Ha igen, akkor átírjuk + használjuk a stack-et.
|
||||
|
||||
Flag-ek:
|
||||
|
||||
0. offset_top
|
||||
1. offset_top
|
||||
2. offset_top
|
||||
3. offset_top
|
||||
4. offset_top
|
||||
5. offset_top
|
||||
6. TYPE0: a típus első bitje
|
||||
7. TYPE1: a típus második bitje (00 - szöveg és futtatatlan, 01 - szöveg és futtatott, 11 - THREADED és futtatott
|
||||
|
||||
EDIT:
|
||||
|
||||
Jelenleg úgy néz ki, hogy itt hét bitet is használhatnék - kérdés kéne-e vagy legyen még valami flag ott, mondjuk
|
||||
egy továbbfejlesztési lehetőségként. Szerintem az utóbbi és akkor a data[] inline helyett a hossz után ptr lesz!
|
||||
|
||||
Mi az a "threaded kód"?
|
||||
|
||||
A "threaded kód" lényegében karaktersorozat helyett egy uint32/64_t-sorozat, ahol szavak helyett indexek vannak
|
||||
az adott szavak definíciós pontjaira - DE! Viszont a zárójelek is el vannak kódolva. Alapvetően uint32_t, de
|
||||
a felső bit ha beáll akkor uint64_t kell két egymást követőt értelmezni (alsó 32 bit jön előbb módon!)
|
||||
|
||||
Ettől jellemzően tömörebb és hatékonyabb lesz az interpretálás hiszen nem kell szöveget parzolni, nincs szótár lookup!
|
||||
Viszont megeshet, hogy "nincs elég hely" tárolni az adatokat a forrás szöveg helyén és nem optimalizálható olyankor...
|
||||
|
||||
|
||||
Érdekes elképzelés lehet egy kifejezetten UTF8-szerű encoding is, hogy lehetőleg 8-16-32-64 bitet használjunk csak / szó!
|
||||
Szerintem ez fontos lenne, hogy az optimalizációt többször hajthassuk végre és ne csak hosszú szavakat használó esetben!
|
||||
|
||||
Mint az egyértelmű, a szó deklaráció a kód szövegben legalább 4 byte-ot igénybe vesz és minden változó-deklarálás is!
|
||||
|
||||
* ':'
|
||||
* whitespace
|
||||
* egy karakter a névből (kötelező)
|
||||
* lezáró karakter
|
||||
* a szó tartalma se lehet amúgy üres és legalább egy whitespace és egy karakter nevű szó áll majd ott...
|
||||
* Egy whitespace - ebből lesz a "flag" is!
|
||||
* Minimum egy karakter a névből (kötelező)
|
||||
* lezáró karakter majd a végén
|
||||
* Bár a szó tartalma lehet üres - de jellemzően van ott valami, csak erre nem számíthatunk!
|
||||
|
||||
Emiatt ha a legfelső bit egyes volt a word-ben, akkor ott egy négybyte-os indexre cserélhetjük a dolgot, ami egy complexword index:
|
||||
A szó deklarációnál a "FLAG" mezőben az "adat elérési offsetje" felső bitjei jelennek meg.
|
||||
Ez fontos, mert így tudjuk tárolni a változók egészét, kihagyni ott a zéró termináltatást!
|
||||
Mivel a szimbólumtábla miatt a "név" már számunkra nem szükséges, azt a minimum egy byte
|
||||
nevet is használhatjuk tárolásra és az offset alsó byte-ja van benn ott! Ez jó így, mert
|
||||
ezáltal 8+6 bit eltolást tudunk, ami elég sok változót és névhosszakat enged meg (~16kb)
|
||||
|
||||
struct complex_word {
|
||||
int64_t len; // a szó végére mutat, tehát ahol az input folytatódik!
|
||||
char *name;
|
||||
|
||||
int64_t var_count;
|
||||
char **var_names; // tömb: session storage-ba
|
||||
int32_t var1, var2, var3, var4; // cache
|
||||
|
||||
// Regular word definition code
|
||||
int64_t main_len;
|
||||
char *main_data;
|
||||
|
||||
// (...)
|
||||
int64_t parentheses_len;
|
||||
char *parentheses_data;
|
||||
|
||||
// [...]
|
||||
int64_t squarebracket_len;
|
||||
char *squarebracket_data;
|
||||
|
||||
// {...}
|
||||
int64_t brace_len;
|
||||
char *brace_data;
|
||||
};
|
||||
|
||||
Bármikor átalakítható egy szó egy "complexword"-re!
|
||||
A másik esetben, a változóknál a @ + legalább egybetűs név + pontosvessző + whitespace/sorvége
|
||||
miatt, ahogy fent említettük szintén hasonló betárolást végez az interpreter engine!
|
||||
|
||||
Megjegyzés:
|
||||
|
||||
* Azért ilyennek választottam, mert a ": " miatt a forrás szöveget beolvasva pont marad ott 16 byte mindenképp!
|
||||
* Lásd még a rust compilerről szóló youtube videó valahol fentebb az SLC-s blokk előttről!!!
|
||||
* Azért ilyennek választottam, mert a ": " miatt a forrás szöveget beolvasva pont marad ott 16 bit mindenképp!
|
||||
* Lásd még a ZIG compilerről szóló youtube videó valahol fentebb az SLC-s blokk előttről miért jó ez cache-ileg így!
|
||||
* Ugyanis: a számított mezők jobbak, mint nagyobb / másik memória terület szükséges volna csak a node-ok tárolgatására.
|
||||
|
||||
## Minden esetben threaded kód optimalizálás
|
||||
|
||||
Most arra jutottam, hogy minden esetben csináljuk meg a threaded kódot. Annyi, hogy ha nem fér el a régi helyén, akkor úgy
|
||||
hát tegyük be a session storage-ba alternatívaként és mivel ez mindig akkor történik, amikor első lefutás van, ezért ott
|
||||
tudjuk, hogy milyen nevet / sőt target indexet tart jelenleg ez a szó fenn - ilyenkor az összes előfordulást megkeressük
|
||||
és át fogjuk majd írni az újra és kész. Ez egy kicsit hosszadalmas is lehet, ha nagy a kód, de csak 1x történik és hát
|
||||
jó ritkán történik majd csak meg...
|
||||
|
||||
## Az engine-hez szükséges
|
||||
|
||||
Szerintem ezeket inline-olható, function pointerré kéne "elkódolnom", akár úgy, hogy feles paraméterek vannak + szétifelés!
|
||||
@ -1615,18 +1707,19 @@ Szerintem ezeket inline-olható, function pointerré kéne "elkódolnom", akár
|
||||
* Szimbólumtáblára MAP adatstruktúra (pl. kismap - név alapú lookupra + változó tároláshoz is [key alapján tudható melyik!])
|
||||
* Complexword-ök "vektora" (mindig indexelés alapú! Nem pointerezős!
|
||||
* Session storage - egy charakter vektorhoz hasonló, de csak állandóan növő storage + session reset rá.
|
||||
* IO accessor (fájlműveletek, pipe-ok stb.)
|
||||
|
||||
^^Ezzel a memória kialakítással a memory layout:
|
||||
|
||||
| engine kód | callstack | adatstack | szimbólumtábla -> ... <-complexwords|insert_stack-> ... <- sessionstorage | src |
|
||||
| enginecode | callstack | datastack | symbolmap -> ... <-malloc_store|insert_stack-> ... <- session_storage | src |
|
||||
|
||||
Igazából embedded környezetben, ilyen kismap-szerű (vagy még egyszerűbb) szimbólumtáblával tehát a legtöbb dolog
|
||||
vagy konstans, vagy csak két ponton van balról-jobbra és jobbról-balra folyton akár végtelenig (memhatár) növő rész!
|
||||
|
||||
A complexwords egy embedded megoldásban "mozoghat", tehát mivel csak indexálással érjük el és NEM pointeresen, ezért
|
||||
A `malloc_store` egy embedded megoldásban "mozoghat", tehát mivel csak indexálással érjük el és NEM pointeresen, ezért
|
||||
ott memcpy-vel mozgatható, ha kezd elfogyni vagy a szimbólumtábla, vagy a session storage.
|
||||
|
||||
A jobbra-balra mozgó complexwords-ön túl még egy ilyen memóriát tudnánk csinálni, tehát egy cövek két oldalát is használva!
|
||||
A jobbra-balra mozgó indexes elérésű malloc-ozgatón túl még egy ilyen memóriánk van, tehát egy cövek két oldalát is használva!
|
||||
Így került oda a "insert-stack", ami interpretereknek nagyon hasznos és a callstack-beli ID-tól függően (mélység szám)
|
||||
bizonyos szavak esetén az src-t kiegészíthetjük vele! Tehát "írhatunk az inputra" úgy, hogy épp egy feldolgozott szó mögé!
|
||||
Ez az insert-álás!
|
||||
@ -1649,6 +1742,10 @@ Megj.:
|
||||
|
||||
## Egyéb implementációs szempontok
|
||||
|
||||
* Legyen "sima C" kompatibilitási okokból a fordító
|
||||
* Legyen "sima C" kompatibilitási okokból a fordító amivel írom.
|
||||
* Legyen az "engine" a fentiek fényében független a környezetétől (függvény pointeres megoldás)
|
||||
* Lehetőleg könnyen self-host-olható legyen az egész történet (minimális számú szóból a többi implementálható)
|
||||
|
||||
Egy jó kezdőszett FORTH szavakból:
|
||||
|
||||
https://github.com/davidjade/MiniForth/blob/master/Words.c
|
||||
|
Loading…
x
Reference in New Issue
Block a user