Razreševanje simbolov
Obiskovanje vozlišč
V prejšnji vaji smo izdelali parser, ki za dani program zgradi abstraktno sintaktično drevo (dejansko seznam ukazov). Vsak ukaz oziroma direktiva je predstavljen z objektom, izpeljanim iz razreda Node
. Zbiranje (angl. assembling) programa bomo izvedli v več fazah. Čez program se bomo v vsaki fazi sprehodili na enak način, z uporabo poenostavljenega vzorca obiskovalec (angl. visitor design pattern).
Razredu Node
bomo dodali metodi
void enter(Code code)
void leave(Code code)
ki ju pokličemo vsakič, ko med obhodom začnemo oziroma končamo z obdelavo posameznega vozlišča. V teh dveh metodah se boste ukvarjali predvsem z nastavljanjem lokacijskega števca.
- Namig 1: Ukazi lokacijski števec povečajo za dolžino kode ukaza (lahko napišete pomožno metodo
Node.length
, ki vrne dolžino ukaza v bajtih), nekatere direktive (START
,ORG
) pa ga neposredno nastavljajo. - Namig 2: Morda se vam tekom obiskovanja splača voditi dva lokacijska števca, pri čemer en kaže na lokacijo trenutnega ukaza, drugi pa na lokacijo naslednjega ukaza. Ta pravzaprav predstavlja vrednost
PC
– spomnimo se, daPC
kaže na naslednji ukaz.
Razredu Code
bomo dodali še metodi
void begin()
void end()
ki pripravi vse potrebno za začetek obiskovanja, npr. inicializira lokacijski števec, resetira bazni register.
Prvi prehod: definicija label
Del prvega prehoda imamo pravzaprav že narejenega: branje datoteke in dodajanje ukazov v seznam. Manjka le še dodajanje (morebitnih) label ukazov v tabelo simbolov. To lahko naredimo kar med branjem datoteke, ko dodajamo posamezne ukaze v seznam. Lahko pa najprej zgradimo cel seznam ukazov, nato pa v ločenem obhodu shranimo naslove label.
V vsakem primeru si definiramo metodo v razredu Node
void activate(Code code)
ki v dan objekt Code
shrani naslov labele trenutnega ukaza.
Drugi prehod: razreševanje desnih simbolov
Razredu Node
dodamo metodo
void resolve(Code code)
ki morebitni simbol (operand), ki pripada ukazu. Seveda bo potrebno to metodo povoziti v nekaterih naslednikih razreda Node
:
- direktive: nekatere direktive poleg razreševanja simbola še spremenijo nekatere atribute (npr. začetni naslov kode, vrednost baznega registra);
- ukazi formata 3: poleg razreševanja simbola moramo tudi preveriti, da je operand v dovoljenem intervalu;
- ukazi formata 4: ne podpirajo
PC
-relativnega in bazno-relativnega naslavljanja.
Kodo je možno generirati na več različnih (pravilnih) načinov. Smiselno pa je prednost dati PC-relativnemu naslavljanju pred bazno-relativnim in nadalje pred neposrednim naslavljanjem. Poskrbite za ustrezno obvestilo uporabniku v primeru, da naslavljanja ni mogoče razrešiti.
Na koncu razredu Code
dodajte metodo
void resolve()
ki se sprehodi čez seznam in izvede razreševanje kode za vsak Node
. Med obhodom ne pozabite klicati metod enter
in leave
za vsako vozlišče.