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, da PC 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.