diff --git a/10 Problems with C++.pdf b/10 Problems with C++.pdf new file mode 100644 index 0000000..aa534cf Binary files /dev/null and b/10 Problems with C++.pdf differ diff --git a/BASED.md b/BASED.md index 3b4ebf5..f2605bd 100644 --- a/BASED.md +++ b/BASED.md @@ -60,16 +60,57 @@ Meg ez is (basic block-os módszer): } ^^Ezek "kezelik" a memóriákat és resource-okat RAII-módon, konstruktorral és destruktorral. -Szerintem a struct-hoz képest is jobb szó. +Szerintem a struct-hoz és class-hoz képest is jobb szó. Lesz továbbá tagged union is - lásd lejjebb - és interfészek (ami tagged union-á fordul le, hacsak ki nem optimizálja a fordító). +Szerintem itt a 'T' helyett talán érdemes csak egy interfész-nevet megengedni??? + +# Rekord VS handler: copy és move konstruktorok / assignment + +Mindenképp jobbnak látom a C++ féle move szemantikát a rust-hoz képest: tehát ha én mondom meg mi történjen! +Egy érdekes (és megfontolandó) elképzelés viszont az ún. "destruktív move szemantika" ötlete, ugyanis az +nem feltétlen optimális, ahogy a C++ például csak destruálható állapotba kell tegye azt, ahonnan move-oltunk. +A destruktív move esetén a dolgot amiből move-oltunk, már nem használhatjuk. Lásd pdf itt a könyvtárban erről. +Ezt ott kifejtik miként lehetett volna C++ esetén is csinálni - de úgy, hogy továbbra is mi írjuk le mi történik! + +Egy érdekes kérdés a programnyelvre nézvest, hogy vajon jobb-e, ha nem generálódik automatikusan le a +copy assignment operátor, mert ugye ha pointereket tárolsz, akkor ez az automatikusan generált dolog őőő... +Szóval hibás, hisz amikor ownershiped is van akkor elég necces, mert lemásolva akkor osztott owner lett hirtelen... + +Viszont sima "hagyományos" adaton ez nem probléma, tehát: + + handle Vec2f { + public: + float x; + float y; + }; + +^^erre kényelmes, hogy generálódik! + +Lehetséges variációk - amitől a programnyelv kényelmes és "viszonylag biztonságos" is egyben: + +1. Csak move generálódik, a default move az swap-olást jelent a másikkal (miint c++ esetén) +2. Generálódik copy assign meg konstruktor, de csak akkor, ha nincs pointer adattagod - ez ugye garantálja, hogy nem történnek "meglepetések" +3. Lesz struct és lesz "handle" a handle-nek van konstruktor-destruktorja, de a struct-nak nincs. A struct-nak is lehet "függvénye" és persze ott is minden public default-ban. A struct továbbá automatikusan másol. Ha a struct-ban akarok hívni malloc-ot, vagy free-t meg hasonló @resource-os dolgokat, akkor @unsafe-elnem kell - tehát ha pointer van benne, akkor az ownership így érzésem szerint általában nem nálam van egyébként sem és ugye mivel destruktor sincs, ezért a "meglepetés" egyébként sem történhet meg..... esetleg ezt struct helyett "rekord"-nake kéne nevezni, hogy semmiképp se kezdjék megpróbálni az emberek "mindenre is ezt használni" ha C/C++ felől jönnek.... + +Jelenleg a harmadik variáció tűnik nyerőnek a "rekord" adattal. Fontos! A tagged és egyéb union-ok SEM tudnak konstru-destruktort! + +Ezzel ilyet tehát leírhatunk (a rekordnak a public a default, a handle-nak valamely privátabb dolog): + + record Vec2f { + float x; + float y; + }; + +Megj.: Azért nem a "struct" nevet választom erre, hogy ne akarják a C/C++ felől jövők mindenre is ezt használni / erőltetni! + # Memória és resource kezelés A lényeg ez (kb. "kötelező" RAII + extra szabályok dangling ref / pointer ellen): - referenciákat lehet paraméterként megadni elvárt típusnak ÉS visszatérési értékként is metódusoknál -- De referenciát eltárolni struct mezőben nem lehet - sima változóban félreteheted magadnak a stack-en ha nem akarod mindig kiírni az expression-t. +- De referenciát eltárolni handle mezőben nem lehet - sima változóban félreteheted magadnak a stack-en ha nem akarod mindig kiírni az expression-t. - Pointerekre meg van egy ownership modell, hogy a handle-khez (class/ struct megfelelője) tartozik egy "resources" vagy "pointers" blokk... Valszeg csak külön syntax van erre, nem akarok ilyen hosszú szavakat kiírva látni, mert öli a produktivitást, hanem mint a C++ban pl van az inicializálós rész a konstruktornál, úgy a class-nál valami hasonló kis syntax erre... - Ugye pointereket vissza tudnak adni és el tudnak fogadni paraméterként @resource-nak és @release-nek jelölt függvények (mint pl. a malloc meg a free). - Az ilyen (így jelölt) függvényeket, csak konstruktorból és destruktorból tudod meghívni!!! @@ -93,7 +134,7 @@ T& genyoka() { Természetesen ha @unsafe-nek jelölöd a függvényeid, akkor eltekinthetsz ezektől - olyankor mondjuk hozzáférsz mások pointer adattagjaihoz, használhatsz és adhatsz vissza pointer-eket és hasonlók. De ha a hívó maga nem unsafe, akkor nem tudja eltárolni a pointered, mert ugye nem tud rá változót csinálni (ha van valami var/auto jellegű típuskikövetkeztetés, akkor ott le kell állítani, ha nincs akkor eleve nem probléma, mert le se tudja írni az illető). Ugyebár természetesen az ilyen módon jelölt függvények továbbá mindenféle módon jelölt függvényt simán meg tudnak hírni - tehát pl. malloc-ot, vagy egy adatbázis kapcsolat létrehozást / elengedést is. -A @unsafe-t lehet hogy class/struct pointer blokkjára is engedhetek rátenni, ami azt jelentené, hogy az ő pointereit más is látja, esetleg egész class-ra / struct-ra, amelybe lévő dolgok így öröklik lefele +A @unsafe-t lehet hogy handle pointer blokkjára is engedhetek rátenni, ami azt jelentené, hogy az ő pointereit más is látja, esetleg egész dologra, amelybe lévő dolgok így öröklik lefele? Mondanom se kell: GC nyilván nincs így! @@ -109,6 +150,41 @@ A C++ esetén ugye probléma, hogy van "new", de mi eleve nem használunk ilyen Megj.: A "generational arenas" esetleg legyen támogatott? Ez egy olyan dolog, hogy az elemekhez amit allokálsz, odakerül egy "int generation" mező prefixként. Az indexek ezt követően "fatpointer" szerűek (mondjuk 2x32bit, vagy akár 2x16) és a "generációt" is tárolják "optimistic locking" szerűen. Ha törlésre kerül egy elem, akkor a generációja (-1*) szorozva lesz: ebből látni, hogy nem ugyan az, mint amire még az indexem valid lenne, tehát már dead object. Ellenben ez a memória cella / terület újra kiadható (és nem kell hosszú free-list, mert elég párat tárolni és utána szkennelni (akár külön szálon szkennelni amikor kiadok a listáról - ez a szál nyilván thread pool-os jelleggel már létezik, csak pihen) és ilyenkor a területre beadható a ((-1*gen) + 1) új érték, ami miatt a generáció ugye megint csak nem az lesz, mint amit hivatkoznak! Ez valszeg nem kéne nyelvi elem legyen, de attól függ mennyire arénázunk. +### allokálgatás manuálisan + +Szerintem talán nem is szükséges ez nyelvi elemként - elég, ha a standard library "jó filozófiával" lesz csinálva (lásd kismap) + +## Context struct + +Lásd: + + https://www.youtube.com/watch?v=XoiFOK2m0pc + +## Temporary storage + +Érdekes ez a JonBlow-féle temp storage koncepció magában is - de szerintem az alapeseteink jól kezelik ezt (C++hoz képest is), csak talán +tanulni lehetne abból, hogy a Jai erre milyen kényelmi featúrákat ad... + + https://jai.community/t/temporary-storage/133 + https://www.youtube.com/watch?v=MeF4a75kxk0 + +Tehát ad vissza string-eket, meg hasonlókat mind temporary storage-en a videójában... +Így lehet referencia / ptr csak minden (érték-szemantika helyett), az owner meg scope / context függő `thread_local` lényegében. +Tehát mondjuk egy játékban minden frame-nél hívódik meg a "release-all" függvény, ami a temporary storage-t elengedi. + +Az előny is, hogy egyben kerülnek elengedésre, meg azért tegyük hozzá hátrány is, a RAII/defer még mindig jobban tetszik... + +## Defer + +Jó kérdés, hogy legyen-e a RAII-s megoldáson kívül zig-szerű defer is. Szerintem nem kéne, csak összezavarja az embereket. +Annyiból lehet érdekes, hogy ugye a RAII-s handle behozza a copy / move szemantikát, amit a defer magában nem hoz még be, +de ígyis-úgyis érteni kell mi történik. Inkább a copy/move legyen kicsit logikusabb, mint C++ban szerintem (nem error-prone). + +## "Stackvector" + +Furcsa, hogy senkinek nem jutott eszébe, hogy a stack-en is lehessen vektor-t csinálni. Ezt C++ nyelven meg is lehet csinálni egy +kis kézi asm-el, meg hasonlókkal, de elég trükkösnek hangzik (és ott ez error prone, mert nem nyelvi elem). Kérdés kéne-e ilyen? + # Párhuzamosság / Multithreading Az "async"-ot szerintem felejtsük el... Én undorítónak tartom - a corutin támogatás is jobb annál... @@ -148,6 +224,12 @@ Ilyen go-jellegű, gorutinos történet talán jó volna, esetleg pipeline és t Low-prio... Őszinte leszek - igazából a memory model csak az "igazán" fontos, a többire lehet hogy elég egy pthread... +## Új ötlet + +Nyelvi elem a metaprogramhoz hasonló pipeline-os és assoc-os és funkcionális tételekre + Java-féle library + memory modell + gorut + +^^Ez szerintem egy jó kombó, igazából sokkal kényelmesebb, mint a rust async mágia, de elég powerful oszt csőváz. + # Standard library - Az "igazán" standard libnek szerintem nem kell "prefix" meg "namespace". @@ -171,6 +253,26 @@ Legyen a kismaphoz hasonló - tehát (template) paraméterként kapnak a handle- # Egyéb dolgok +## Szintaxis + +Jobb szeretném, ha a C-hez közelebb állna a syntax, mint pl. a zig, vagy rust csinálja. Sőt az optimális az lenne, ha bármely C kód fordulna is - amennyiben egy @unsafe-t ráteszünk az adott függvényre. Ez persze nem strict cél, hanem hogy "általában így legyen" mondjuk az már jó lenne. Tehát nem feltétlen lenne jó mindent is implementálni a C-ből, de pár alap dologban legyünk hasonlatosak szerintem. + + +## Bitfield-ek + +Lásd C/C++ esetén: + + struct s { + unsigned int a : 10; + unsigned long b : 60; + }; // 10 bitet használ az 'a', majd 60 bitet a 'b' - tehát 2*8 = 16 byte-on kell tárolni a struct-ot! + +Lásd: + + https://en.cppreference.com/w/cpp/language/bit_field + +Ez szerintem low-level helyekre jó lehet... Ha jól definiálom mi történik, akkor padding / align helyett (mellett) is talán. + ## Jobb pointerek Valahogy jó lenne elérni, hogy a default a "restrict" kulcsszóhoz legyen közelebb. Szerintem tömbre a default a restrict, de @@ -518,7 +620,7 @@ public: Zombie z; Boss b; - // One can add methods as-is + // One can add methods as-is, when implements not added to union, only these count string to_string(string s) { switch(this->tag) { case Player: @@ -569,7 +671,7 @@ Első változat: ... } - handle A implements IStringifyable { + handle B implements IStringifyable { ... } @@ -579,6 +681,29 @@ Első változat: ... } +Megj.: Szerintem ettől generic-szerűbb lesz a dolog, ha típusnak generik paraméternek adható egy "interfész"! + Olyankor csak konkrétan az interfész nevével (vagyis nem T implements IAkarmi, csak simán IAkarmi, de kiírható (ha 2 kell). + Továbbá simán amikor függvény paraméternek használjuk, akkor lehessen rajta "constexpr elágazni" a típusán! + +Vagyis írhatunk ilyeneket: + +handle { + ... +} + +Vagy akár ilyet is (bár ezt kevéssé javaslom, de hasznos lehet): + + void process(IStringifyable arr[]) { + switch(add.elemtype) { + case A: + ... + case B: + ... + } + } + +Ifelni is lehet - olyankor például csak egy adott típusra valami speciális pluszt adni, kezelendő esetet... + Második változat: Lásd tagged union-oknál a "negyedik változat". Szerintem ez a legszebb!!! A tagged union tud implementálni interfészt és így @@ -588,10 +713,12 @@ Második változat: Az interfészekre lehet többszörös öröklődés (egymás közt is, meg itt is). -FONTOS: A shared object-ek között NEM ajánlódnak ki ilyen jellegű típusok! Csak struct-ok! +FONTOS: A shared object-ek között NEM ajánlódnak ki ilyen jellegű típusok! Csak rekordok! ## Láthatóság +TODO: Egyszeűsítendő? + * public * readable * resource @@ -606,7 +733,7 @@ Ezekből több blokk is lehet (C++ szerűen) és többször is szerepelhetnek, e A public gondolom elsőre is érthető s a default. A readable az lényegében egy segítő ficsőr, hogy ne legyen annyi "getter": Ugyanis ez azt adja, hogy mások kívülről olvashatják, de én belül még írhatom. -A resource csak a konstruktorban és destruktorban érhető el. Tehát az "életciklus" alatt readonly ("ezt kezeli a handler"). +A resource csak a konstruktorban és destruktorban írható. Tehát az "életciklus" alatt readonly ("ezt kezeli a handler"). A protected itt érdekes, mert nincs öröklődés, ellenben a handle-k megadhatják kiket protektálnak (kiknek védnökei): @@ -654,14 +781,6 @@ annak azt mondom: az örökléshez meg nem kell semmi ilyesmi, szóval eddig pon https://willcrichton.net/notes/the-coming-age-of-the-polyglot-programmer/ -## Strukturált formában tárolt programszöveg (tm) fordítás - -Az első pontot nehezebb érteni, ha nem ismerjük a kontextust: Van ez az ötletem, ami forth-szerű, de faszerkezetben strukturált "compiler", ahol a forth szavaknak lehetnek "gyerekei" és ilyen blokkjai. A (...) blokk is csak egy block, mint a [..], vagy akár a {...}. Ugyanúgy forth szavakat definiálunk és meg tudjuk mondani, hogy a compiler az adott szóhoz érve mit csináljon. Tehát konkrétan scriptelni tudjuk a fordítót. Most nem mennék bele ebbe, de pár kiegészítéssel (papíron ez részletesen is megvan) kb. az összes "értelmes" nyelvet lehet így "parzolni". A fordításban csak egymásba pipe-olva állítjuk elő a következő és következő változatokat több pass-ban - és az utolsó pass ugye mondjuk fizikai fájlba ír, vagy interpreter esetén mondjuk végrehajt. Ezzel debuggolni is tök könnyű a "compiler/interpreter"-t vagy nézni hol mit optimalizál. - -Viszont ha ezt megcsinálom, akkor ezt az itt leírt nyelvet, ezzel implementálva alapból van metaprogramozásunk - méghozzá mindennél erősebb. De ugye nem feltétlenül kényelmes is... Pl. a jai, vagy a zig comptime lehet hogy sokkal kényelemsebb. - -Megj.: A "néhány kiegészítés / részlet" azok olyasmik mint hogy nyilvántartjuk az indentálást (mind a hibajelzések, mind az ilyen python-szerű fos nyelvek miatt), kicsit trükközni kell a <..> jellegű dolgoknál (pl. generics, template), vagy épp hogy a "fordító scriptelése" azt is jelenti, hogy tudunk előre-olvasni vagy akár kézzel karakterenként olvasni és úgy mozogni az "input szöveg nyelén" ahogy Csörnyei mondaná Magyarosan a "handle"-t... Tehát ezzel tényleg nagyon testre szabható mi történik. Alapból stack-ed is van, mint a forth-nak, meg mellé vannak a fordító adatszerkezetei is (bár esetleg azokat csak akkor éred el, ha speciális jellegű szót definiálsz). - ## Tagged enum / smartenum Ez hasonlít a taged union-hoz, de a union esetén a típus határozza meg a TAG-et. @@ -722,7 +841,7 @@ A legjobb egy Zig-szerű megoldás lenne - jelenleg ezt mondanám a választáso void testcode() { // onfail clause is mandatory if function declaration has an onfail - unlike with exceptions - // Its "errors as values" philosophy and implemented via struct returns / double return values. + // Its "errors as values" philosophy and implemented via record returns / double return values. FileHandle h = open_file("help.txt") onfail(err) { // Must handle all of the cases (or have a "default"?) NOT_FOUND: @@ -736,6 +855,53 @@ A legjobb egy Zig-szerű megoldás lenne - jelenleg ezt mondanám a választáso } } +A konstruktor hívási ponton való hibakezelés ugyanígy működhet - ezért is megint csak jó, hogy az onfail egy nyelvi elem, mert +normális esetben a konstruktornak nincs visszatérési értéke (logikailag a konstruált dolog az értéke). Ezzel mégis működik. + +FONTOS (destruktor error): + + Gondoljunk bele, hogy destruktor / release is dobhat hibát (például a resource egy adatbázis kapcsolat és nem lezárható). + Emiatt FONTOS, hogy a destruktor is csinálhasson "onfail"-t és ilyenkor a scope végére is KELL tennünk onfail-t. + Ezt a fordító ellenőrízni tudja! Hiszen látja, hol záródik a scope. Ha move-olunk (nem-destruktívan), akkor ez jól alakul!!! + + Kód: + + handle DatabaseConnection { + private: + socket s; + public: + DatabaseConnection(string target) { ... } + + // Maybe DatabaseError.CannotClose ??? That is we might say a subset? + ~DatabaseConnection() onfail DatabaseError { + ... + fail DatabaseError.CannotClose; + ... + } + } + + void dostuff() { + DatabaseConnection dc; + dc.insert(...); + dc.commit(); + } onfail(err) { + CannotClose: + ... + } + + Megjegyzés: A scope végére helyezett onfail talán kiválthatná a lokálisat / függvényhívás pontján lévőt? + Ekkor talán javasolt lenne a scope-osnál a teljes típust kiírni, mert többféle tagged enum lehet? + +FONTOS: + + A fentieknek működnie kell konstruktor esetén is (destruktornál viszont semmiképp). Gondolj bele: az kell a RAII-hoz! + VISZONT! Talán jó lenne nem RAII-nak nevezni a handle-s dolgokat, mert exception jellegű távolhatás nem lesz! + + Lásd még, ahol a Muratori emiatt nem szereti a RAII-t: + + https://www.youtube.com/watch?v=OP_w_oEU1Wk + https://www.youtube.com/watch?v=xt1KNDmOYqA + Alternatíva: - Volt az a gondolatom az error-stream hibakezelésről, ahol error stream-eket lehet definiálni @@ -743,9 +909,7 @@ Alternatíva: Ezt elég barokkosan kidolgoztam, de az volt a bajom vele, hogy túl összetett és talán nehezen érthető. Az, hogy a hibák simán értékek, mint go-ban, vagy rust-ban az sokkal egyszerűbb - és ennek a nyelvnek az egyik előnye az egyszerűség lenne ugyebár pont! -## Szintaxis - -Jobb szeretném, ha a C-hez közelebb állna a syntax, mint pl. a zig, vagy rust csinálja. Sőt az optimális az lenne, ha bármely C kód fordulna is - amennyiben egy @unsafe-t ráteszünk az adott függvényre. Ez persze nem strict cél, hanem hogy "általában így legyen" mondjuk az már jó lenne. Tehát nem feltétlen lenne jó mindent is implementálni a C-ből, de pár alap dologban legyünk hasonlatosak szerintem. +Szerintem hanyagoljuk az alternatív irányt. A fenti nagyon szép és egyszerű ahhoz képest, sokkal áttekinthetőbb... ## Visszafelé kompatibilitás @@ -791,6 +955,16 @@ is konzisztensebb, mintha elé írom C-szerűen zárójelben a cast-olást... Mivel a nyelvben az (alap)típus mögött nem jön zárójel (konstruktor hívást jelent), ezért ez minden további nélkül megoldható a fordítóban és mentálisan tényleg konzisztensebb... +## Annotációk + +Ez egy jó kérdés - lehet hogy kéne, lehet hogy nem... A @Resource miatt lehet hogy ez egy jó dolog volna. + +Ez is érdekes, hogy ne csak feltétlen string legyen: + + https://www.youtube.com/watch?v=QzDVKN2kcek + +Itt beszél a név ütközésekről és egyéb hátrányokról és Jai-ban például struct literált akar ott pl. talán... + ## AOP - Szerintem pár dolog azért hasznos lenne az aspectj-ből átvéve. Úgy, hogy nem lesz reflection, azért elég sok mindent pótolna ez! @@ -835,6 +1009,217 @@ Nem igazán támogatom, bár ha véletlen sikeres nyelv volna, nehéz lenne mego Linux-first, onnan aki szeretné implementálja át portolással más OS-re szerintem... +# A fordítóprogram szerkezete + +Nem a tipikus rekurzív leszállós, vagy LALR jellegű fordítást csináljuk, hanem a korábbi ötletem mentén történne! +Ez egy FORTH-szerű nyelv (stack alapú), ami viszont kiegészül hierarchikussággal és zárójelezéssel! + +Lényegében itt is "szavak" vannak csupán, melyek whitespace-el elválasztottak, de adott a hierarchia-kezelés, sőt +a python-féle őrültek miatt "kiolvasható" a tabuláltság mértéke (tab/space-szám) is, ami a hibajelzést is segíti. + +Lényegében így a lexer nagyon-nagyon egyszerű, a parzer pedig már "library" jellegű. Az összes ilyen metaszót +konvenció szerint # jellel kezdjük: tehát például #include, vagy #if, esetleg #dup. + +- Ha a fordításhoz akarunk új szavakat hozzáadni, azt konvenció szerint *.mag fájlokkal tesszük meg! +- Az ilyen fájlok lényegében DSL-t írnak így le nekünk alapvetően és #include-al hozandók be! +- Alapvetően #passes(...) {...} leíróval írható le, hogy milyen meneteket futtatunk, mely *.mag fájlokkal! + +## Pipeline és párhuzamosság + +Megj.: Bár van fájl kezelés, de szerintem alapvetően inkább a pass leíró kezelje, hogy mikből mely inputok + jönnek létre! Azért is, mert ott akkor össze lehetne huzalozni - erre kell egy módszer valahogy! (imp-exp) + +Talán valami ilyesmi: + + #passes(pass1 #both(pass2a pass2b) pass3 pass4) { + #pipe(pass1[0] pass2a[0]) + #pipe(pass1[1] pass2b[0]) + #pipe(pass2a[0] pass3[0]) + #pipe(pass3[0] pass4[0]) + #pipe(pass2b[0] pass4[0]) + } + +Ezzel a megadott lépéseket futtatjuk az input fájlon úgy, hogy az adott standard output/error (és egyéb max 8) +kimeneteiket a pipe-ok segítségével gráfosan kötjük össze. Természetesen csak DAG lehet az a gráf, különben +sose fog lefutni a történet majd - erre gondolom nem árt majd valami check is. + +Ez igazából "meta-nyelvi elem" is lett így, hiszen a pass-ok és a both esetén is a paraméterek másolatokat +fognak csak kapni a stack-jeikből és azok "párhuzamosan" futnak, amennyiben ez lehetséges. Ha nem lehet, akkor +természetesen sorban futnak le, balról-jobbra kiértékeléssel. Mindazt amit művelnek a kimeneteiken, egymásba +kötve a pipe kötéssel tudják kommunikálni - ezzel low powered device-on egyszerűen továbbra is csak egy stack +kell, de szükség van "temporális fájlokra" amikbe az implementáció gyűjti az eredményeket a későbbi menetnek, +de párhuzamosítható lépések írhatók le fordítóprogramhoz, illetve magát az alap "forth-szerű" nyelvet is így +kiegészítettünk potencionálisan egy jó kis aszinkronitással. + +Ennek a megfelelője talán meglehetne a BASED programnyelven is egyébként! Esetleg ott nem stdout/err meg ilyen +szöveges interfésszel, hanem simán csatornákkal és egy DAG-ot leírva, azért sok minden kifejezhető. + +## Strukturált formában tárolt programszöveg (tm) fordítás + +Az első pontot nehezebb érteni, ha nem ismerjük a kontextust: Van ez az ötletem, ami forth-szerű, de faszerkezetben strukturált "compiler", ahol a forth szavaknak lehetnek "gyerekei" és ilyen blokkjai. A (...) blokk is csak egy block, mint a [..], vagy akár a {...}. Ugyanúgy forth szavakat definiálunk és meg tudjuk mondani, hogy a compiler az adott szóhoz érve mit csináljon. Tehát konkrétan scriptelni tudjuk a fordítót. Most nem mennék bele ebbe, de pár kiegészítéssel (papíron ez részletesen is megvan) kb. az összes "értelmes" nyelvet lehet így "parzolni". A fordításban csak egymásba pipe-olva állítjuk elő a következő és következő változatokat több pass-ban - és az utolsó pass ugye mondjuk fizikai fájlba ír, vagy interpreter esetén mondjuk végrehajt. Ezzel debuggolni is tök könnyű a "compiler/interpreter"-t vagy nézni hol mit optimalizál. + +Viszont ha ezt megcsinálom, akkor ezt az itt leírt nyelvet, ezzel implementálva alapból van metaprogramozásunk - méghozzá mindennél erősebb. De ugye nem feltétlenül kényelmes is... Pl. a jai, vagy a zig comptime lehet hogy sokkal kényelemsebb. + +Megj.: A "néhány kiegészítés / részlet" azok olyasmik mint hogy nyilvántartjuk az indentálást (mind a hibajelzések, mind az ilyen python-szerű fos nyelvek miatt), kicsit trükközni kell a <..> jellegű dolgoknál (pl. generics, template), vagy épp hogy a "fordító scriptelése" azt is jelenti, hogy tudunk előre-olvasni vagy akár kézzel karakterenként olvasni és úgy mozogni az "input szöveg nyelén" ahogy Csörnyei mondaná Magyarosan a "handle"-t... Tehát ezzel tényleg nagyon testre szabható mi történik. Alapból stack-ed is van, mint a forth-nak, meg mellé vannak a fordító adatszerkezetei is (bár esetleg azokat csak akkor éred el, ha speciális jellegű szót definiálsz). + +### Fordítási időben / interpretálási időben futó szavak + +Ezt a turbobuf-szerű dolgot lehet aztán úgy is használni, ahogy a Seed7 programnyelv deklarál típustól függően +például egy min/max függvényt! Erről videót itt lehet látni: + + https://youtu.be/9m8gdgbAIrE?t=580 + +Náluk ez így néz ki: + + const proc: DECLARE_MIN_MAX(in type: T) is func + begin + const func T: min (in T: x, in T: y) is return x < y ? x : y; + const func T: max (in T: x, in T: y) is return x >= y ? x : y; + end func; + ... + DECLARE_MIN_MAX(integer); + DECLARE_MIN_MAX(MyType); + +Tehát náluk a "deklaráció" és definíció ugyanúgy kód, mint a függvény egyéb más törzsei, ám ha top-levelként, +azaz fordításkor futtatják ezt a procedúrát (nálunk "szó" lenne), akkor ugye deklarálódnak a dolgok. + +No mármost, nálunk ez ehhez hasonlatosan néz ki: + + #: DECLARE_MIN_MAX + ( #readword ) // runs first + // code that uses compiler primitives to add / build the word + #end + +Azonban ez a forma nem éppen a legkényelmesebb. Ugyanis alapvetően a #: segítségével definiálunk egy "szót" a forth +nyelvhez hasonlatosan, ám nálunk a szavaknak lehet hierarchiája és így amikor a "fordító" meglátja az adott szót, +nem csak a definícióját (függvény-szerűen, threaded-code szerűen) végrehajtja, hanem az alatta adott szavak úgy +futnak le, hogy lehetnek (...) és [...] és {...} részei is (egészen a #end-ig). Az adott szakaszban csak a belső +hierarchiát látó kódot írhatjuk, tehát a fenti példában a #readword egy egész szót olvas (mint magunk is), de azt +a (...) követő részéből a szónak - ha nincs ilyen, akkor semmi sem történik. A zárójeleken kívüli résznél lévő +kód pedig a külső, a szó megjelenésének első betűjén álló olvasó (és író) fejjel fut. + +Rendelkezésünkre áll egy(esetleg kettő?) forth-szerű stack, erre a #push stb. szavakkal lehet hivatkoznunk. +Itt a push a nyelv struktúrája miatt használható "42" helyett "#push(42)" - ez azért kell, hogy a valódi prognyelv +immediate-jei normálisan használhatók maradjanak, ugyanis nem tűnik jónak, ha alapesetben minden szám itt a stack +tetejére csak felmenne, ha egy szó helyett egy számot látunk épp... + +Egy minimál forth(-szerű) szószett áll rendelkezésre: #swap, #drop, #iadd, #imul, fadd(?), fmul(?), stb. +De nem ilyen 32 / 10 szavas forth-ból érdemes kiindulni (bár lehetne), hanem valami praktikusból! + +Lásd például innen jó sok dolog: + + http://www.murphywong.net/hello/simple.htm + +Emellett olyan szavak is rendelkezésre állnak, amelyek a "fordítót scriptelik". Ilyen például a #readword, ami +a stackre olvassa a "talált szót" és mozgatja az input nyelét. A stack-en alapvetően uint32 méretű szám lehet! +Természetesen rendelkezésre áll még a #readchar, #readline, #readuntil is, továbbá a #write(...) is! + +Régi forth-os esetekre lásd hasonlóért: + + https://www.forth.com/starting-forth/11-forth-compiler-defining-words/ + +A #readword használata a következő: + +- A szó string belseje olvasható karakterenként: 42 #wordchar, #wordchar[42], 1 #wordchar(41) +- Ehhez természetesen a stack-re kerül a szó TÖVE (zárójelek nélkül) és hossza is (ebben a sorrendben)! +- Szerintem nem árt egy #strcmp('for') jellegű dolog, amivel a word szó része olvasható... 0-1-et tesz a stackre! +- Jó lehet még: #strprefix('starts-with-text'), esetleg #strsuffix(...) is ami 0 vagy N ad (N db megfelelő char!) +- Ez a kialakítás pazarlónak tűnhet a 32 bites stack miatt, de így nem lesz ram szivárgás a rendszerben! +- Egyébként a reprezentációt nem kötöttem ki ezzel - szóval lehet 4 karaktert tárolni egy szóban itt ám! +- Ennek megfelelően kell #popword utasítás, ami leszedi az egész szót a stack-ről ha nem érdekel már! +- Szükség lehet például még #dupword utasításra is - ezek végül is "string kezelő" rutinok... +- Az olvasó fej a szó (nem-zárójeles része) mögé mozog. + +A #readword(raw) a zárójelezéssel együtt az egészet olvassa be, míg #readword(full) ugyanez, de wspace*->space! +Szükséges még a #skipword és #skipword(full) szavak, melyek spórolósabbak, mint olvasni és drop-olni... + +Vegyük észre, hogy a zárójeleket ezzel alapesetben nem kezeltük: + +- #has(), #has[], #has{} - ezek kiadják, a szót követőe nyélen állva, ahhoz tartozik-e adott szekció (0 vagy 1) +- Nem tartozhat több (..) egy adott szóhoz (se a másik eseteknél)! Tehát mindegyik blokkféleségből max egy van! +- De a fordító scriptelős feldolgozásnál ott viszont állhat többször és a sorrendiséget fejezi ki csupán! +- #enter(), #enter[], #enter{} - ezekkel bemegyünk olyan módba, hol az adott blokk vége eof-ként értődik +- #exit - ez lép eggyel vissza - de az adott enterrel megkezdett dolog VÉGE mögé állunk (feldolgoztuk) +- FONTOS: Az enter-exit lényegében automatikus, amikor a definícióban a megfelelő helyre írjuk a kódunkat, tehát + a zárójelek belsejébe írtuk azt a részt, amikor a zárójelen belüli dolgokat dolgozzuk fel... + +Viszont azt is vegyük észre, hogy a szó tartalmazhat whitespace-t, ha teljességében olvassuk be + +### Insert-álás az input stream-en + +Factor programnyelven láttam olyat, hogy a bejövő szó-folyam aktuális pontjánra "írhatok" is akár... +Ezt valahogy talán jó lenne támogatni, de lehet hogy felesleges. Ezzel nem csak a #write outputra írása lenne, +hanem jelenleg ahol tart a végrehajtás, azt a programszöveget tudnánk módosítani. A fordításhoz szerintem ez +nem kifejezetten ad plusz funkcionalitást, de az interpreterként működéshez viszont igen... + +Ennek megfelelően valami hasonló lehetne: + + #: DECLARE_MIN_MAX_INT + #inserts { + #: MIN_INT + #IF(#LT) [ + #DROP + ] { + #SWAP + #DROP + } + #end + #: MAX_INT + #IF(#GT) [ + #DROP + ] { + #SWAP + #DROP + } + #end + } + #end + +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ó +futtatása, a rendszer a #: deklarációs szót fogja az inputján látni. Ezzel az input kiegészítésre került! + +A #inserts-hez tartozhatnak paraméterek: + + 42 21 #inserts (N K) { #push($N) #writechar #push($K) #push(2) #imul #writechar } + +Természetesen ez sokkal érdekesebb, ha nem így magában áll, hanem szó deklarációban, de a lényeg, +hogy a zárójelek között felsorolhatok dolgokat, ami a stack-ről értéket kap. A $$ a $ escape-elése csupán... + +Természetesen itt kellhet típust adni: + + 'some text word' 21 #inserts (word(W) float(F)) { .... } + +### Szimbólumtábla + +Rendelkezésre áll továbbá a "szimbólumtábla" is, ami egy map adatszerkezet és szónévből egy definícióra mutat +illetve annak tulajdonságainak elérésére, illetve új bejegyzések beírására hasznos szavaink is lesznek... + +Szükség lehet ezen a ponton fullword(W) jellegű paraméterre is, ami a zárójelekkel együtt értett teljes szót +jelképezi, de szerintem alapjában a word, mint típus, lényegében "string" típus ugye a hossz + tárolás mód.. + +A szimbólumtábla egy szimbóluma megnyitható "fájlszerűen", ezért a definícióját write paranccsal megírhatjuk! + +**Megj.: Szerintem ezzel kiváltható a #inserts kulcsszó használata!!!** + +### A #write + +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 +hasonló módon, de külső fájlba (vagy standard kimenetre, error-ra) tud írkálni. + +Természetesen ennek megfelelően kell még #fopen[0](out.txt) jellegű dolog is, ami a nullás fájlt nyitja (mondjuk +4-8 ilyen lehessen) és #fclose[0] szó is. Kell még stdout[0] és stderr[1] jellegű dolog is... + +Igazából a "fordító" úgy fordít, hogy sok menetben, folyamatosan át-transzformálja a programszöveget egy másik +programszöveggé, melynek elején van egy #include, ami behozza az adott új "makrókat" és azokkal az új menet mit +fog pontosan is csinálni már... Ennek megfelelően például az ifeket hamar ugrásokra tudjuk fordítani stb. + +A nyelvnek a köztes reprezentációja lényegében szintén emberileg olvasható szöveges kód és meg is állítható egy +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. + ## LSP - Sajnos egyetértek JonBlow-val és fasságnak tartom... De ha valaki akar, próbáljon írni egyet oké... diff --git a/Move semantics in C++ and Rust_ The case for destructive moves _ by Radek Vít _ Medium.pdf b/Move semantics in C++ and Rust_ The case for destructive moves _ by Radek Vít _ Medium.pdf new file mode 100644 index 0000000..e662248 Binary files /dev/null and b/Move semantics in C++ and Rust_ The case for destructive moves _ by Radek Vít _ Medium.pdf differ diff --git a/hla2mth.pdf b/hla2mth.pdf new file mode 100644 index 0000000..054228c Binary files /dev/null and b/hla2mth.pdf differ