A bytecode interpreterek hogy kezelik a típusokat?
Egy interpretert írok egy egyszerű szkriptnyelvhez. A szkriptnyelvben dinamikus típusok vannak (lennének). Tehát nem kell típust specifikálnom. Példa:
a = 3.0 // Tudja hogy ez float lesz
a = true // Most már boolean
Még egy példa ami fontos a problémám szempontjából:
function foo(x, y, z)
{
return x + y + z
}
foo(1, 2, 3) // 6-ot ad vissza
foo("a", "b", "c") // "abc"-t ad vissza
Ami megvan, az a lexikális analízis, az elemzés és van egy szintaxisfám. Most csinálhatnék egy TreeWalker-t és lenne egy kész interpreterem, de ez nagyon lassú. Szeretném bytecode-ra fordítani. Hogy stack vagy regiszter alapú, az szerintem a probléma szempontjából lényegtelen (amúgy regiszter alapú lenne).
Szóval lennének alacsonyabb szintű parancsok. Minden különböző művelethez egy. Külön 2 float, 2 int, 2 string, stb.. összeadásához/kivonásához/stb... . Például ez a kód:
2 + 3
Ezt a kódot generálná:
ILOAD reg1, 2 ; első regiszterbe betölti a 2-t (intként)
ILOAD reg2, 3 ; 3-mat
IADD reg3, reg1, reg2 ; összead 2 integert amelyek a reg1 és 2-ben vannak és berakja reg3-ba.
Példa 2:
1 + 4.0
Ezt generálná:
ILOAD reg1, 1
ITOF reg2, reg1 ; Float-tá alakítja a reg1-ben lévő intet és reg2-be teszi
FLOAD reg3, 4.0
FADD reg3, reg1, reg2
Szóval minden műveletnek megvan a maga típusa, illetve konverziók is vannak. Természetesen ez még mind a fordításnál történik. Na most egy ilyet:
function foo(x, y)
{
return x + y
}
Hogyan fordítsak le? A dinamikus típusok miatt nem tudom kikövetkeztetni hogy milyen paraméterekkel hívják meg. Kell-e castelni, stb...
Hívhatják így:
foo(1, 2)
és így:
foo("a", "b")
sőt így is:
foo("a", 2)
stb...
Lehet hogy nagyon egyszerű a válasz, de nem jövök rá hogy ezt fordítási időben hogyan oldjam meg. Sajnos egyetemen olyan órám nem volt, ahol compilerekkel foglalkoztunk volna (csak 17 vagyok..).
Köszönök minden segítséget!





Fordíthatsz több körben, mármint előbb áttolhatsz egy elemzés, ami típusozással látja el az ilyen részeket pl.
Vagy fordítás közben magát a hívott helyen kibontod a függvényt az adott típusokra.
"Vagy fordítás közben magát a hívott helyen kibontod a függvényt az adott típusokra."
Még olvasgatok pár helyen, hogy hol hogyan oldották meg, de ez tűnik eddig a legkézenfekvőbbnek. Köszönöm!





A Python-ban egy objektumba burkolják az összeset. Így működnek rajta a műveletek. Én nem szeretem a ducktyping-ot, de az így működik.
Ha típus kikövetkeztetést használsz, akkor viszont nem lehetséges a példád, ez:
a = 3.0 // Tudja hogy ez float lesz
a = true // Most már boolean
"Ha típus kikövetkeztetést használsz, akkor viszont nem lehetséges a példád, ez:
a = 3.0 // Tudja hogy ez float lesz
a = true // Most már boolean"
Dehogynem. Ez éppen lehetséges. Van egy symbol table-m, amiben jegyzem hogy aktuálisan milyen típusú a változó és az alapján fordítom hozzá a bytecode-ot.





@21:50
A típus dinamikusan változhat. Akkor mit csinálsz ha fordítási időbe nem ismert a típusa?
@19:14
Nem egy objektumba hanem egy-egy objektumba. Implementáció szempontjából egy-egy osztályba.
Nem árt tisztába lenni az objektum és osztály fogalmával. Az objektumorientáltsággal.
Ezeket úgy szokás megoldani, hogy objektumorientált paradigma szerint tekintjünk a problémára.
x + y műveletet dinamikus tipusoknál (is), hogy x és y egy-egy objektumként tekintjük és a + operátort hajtjuk végre x és y-ra. Az x küld egy "+" üzenetet y-nak. Dehát minden objektumhoz tartozik operátor eljárás hogy hogy kell értelmezni rajta a + operátort. Kézenfekvő az operátor eljárásokat a típusokra tárolni és nem minden objektumhoz külön-külön tárolni. A "színfalak" mögött a következő történik (egy lehetséges megoldás): Fontos követelmény hogy minden objektum tudja önmagáról, hogy milyen típusú. Létrehozáskor pl egy struktúrába tároljuk a tárolt értéket és a típust. Aztán egy 3 kulcsból álló adatszerkezetből kiolvassuk a megfelelő operátor eljárást mely egy függvénypointert tartalmaz, melyet meghívunk x és y paraméterrel, a függvény létrehoz az x és y objektumból egy harmadik objektumot. 3 kulcs azért kell mert kell az hogy melyik tipusú objetkumhoz tartozó melyik másik típusú objektumhoz tartozó melyik függvénypointert keressük. Pl ha x egy integer akkor egy integerhez tartozó függvénypointert (operátor eljárást) keresünk, y az pl egy string akkor fuggvenytabla[integer,string,'+'] értékét keressük. Ennek az adatszerkezetnek gyorsnak kell lennie, azaz konstans elérési idejűnek. Továbbá lehet optimalizálni fordítási időben statikus kódelemzéssel, automatikus típuskikövetkeztetést tenni (meg egyéb optimalizációkat), ahol fordítási időben biztosra ismert a típus azt futási időt leszámítva vele ekvivalens optimalizáltabb kódra cserélni, de ilyen optimalizáció bőven ráér meg elég bonyolult is.(Persze tudnám még bonyolítani.)
Végül is hasonlóképpen oldottam meg mint anno a Lua-ban tették. Az adatokat így tárolom:
typedef struct
{
union
{
int ival;
float fval;
...
} RawVal;
uint8_t type;
} Value;
Az operátoraim elvontak (nem adott típusokra építkeznek), és hogy kicsit gyorsítsak, a string összefűzéseknek külön operátort adtam. Így a runtime type check és típuskonverzió nem vesz el túl sok időt.
Kapcsolódó kérdések:
Minden jog fenntartva © 2025, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!