Əsrin Proqramlaşdırma Mühiti - İcraçı

Əsrin Proqramlaşdırma Dilinin İcraçısı C++ proqramlaşdırma dilində tərtib olunub və hazırkı buraxılış (0.0.1) təxminən 4000 sətir koddan ibarətdir. İcraçının qaynaq kodları ilə aşağıdakı keçiddən tanış ola bilərsiniz.

Əsrin İcraçısının Qaynaq Kodları

Əsrin dilinin icraçısı Türinq maşını prototipi əsasında hazırlanıb.

Türinq maşını

İcraçı oxuyan/yazan başlıqdan və icra lentindən ibarətdir. Kompilyasiya prosesinin ilkin mərhələsinin yekunu olaraq parsinq ağacının və simvollar cədvəlinin hazırlanmasından sonra list_new.cpp/build_exec_band() funksiyasının köməyilə icra lenti tərtib olunur. Bu funksiyanın dövr operatoruna aid olan kod kəsimi aşağıda göstərilib.

list_new.cpp/build_exec_band()


void build_exec_band(struct tok_list_el *tll, struct band_list_el band[], int *size){

  int i, hpos, old_size, old_size1, old_size2, id = tll->id;
  struct tok_list_el *tmp, *tll_last;
  struct token *tok;
  
  if (*size > BAND_SIZE){
    printf("Icra lenti dolub.\n");
    return;
  }

  switch(id){

  case START:
  case YENI_TIP:
  case SINIF_DECL:
  case SINIF_BODY:
  case PRG2:
  case FUNKS:
  case FUNK_BODY:
  case KOD1:
  case KODDATA:
  case HAL2:

    if (tll->childs[0]->id == DOVR_EXP){
      addto_band_adrs(tll->childs[0]->childs[2], band, size, &tll_last);
      addto_band_val(tll->childs[0]->childs[4], band, size);
    addto_band_new(tll_last, band, size, INIT_DOVR, 0); /* INIT_DOVR  */
      push_to_stek(&dovrstk, &band[*size-1]);
      
      old_size = *size; /* we'll jump here from STEP_DOVR  */
      addto_band_adrs(tll->childs[0]->childs[2], band, size, &tll_last);
      addto_band_val(tll->childs[0]->childs[7], band, size);
      addto_band_val(tll->childs[0]->childs[3], band, size);
      old_size1 = *size;
    addto_band_new(tll_last, band, size, CHECK_DOVR, 0); /* CHECK_DOVR  */

         build_exec_band(tll->childs[1], band, size);

      old_size2 = *size;
      addto_band_val(tll->childs[0]->childs[3], band, size);
      addto_band_adrs(tll->childs[0]->childs[2], band, size, &tll_last);
    addto_band_new(tll_last, band, size, STEP_DOVR, old_size); /* STEP_DOVR  */
      /* jump out of loop to next instr. 3 is ofst from INIT_DOVR  */     
      band[old_size1].head_pos = *size;  
      free_dovr_stek(&dovrstk, *size, old_size2);
      break;
    }
    ...


Dəyişənlərin ünvanlarının hesablanması



Dəyişənlərin ünvanları interp_new.cpp/get_address() funksiyası ilə hesablanır.

interp_new.cpp/get_address()


unsigned char *get_address(struct band_list_el *bll){

  unsigned char *adrs;

  adrs = start_adrs(bll->lgm);

  return adrs + bll->ofst;
}

Koddan göründüyü kimi get_address() funksiyası ünvanı hesablamaq üçün əvvəlcə start_adrs() funksiyası ilə dəyişənin yerləşdiyi yaddaş sektorunun başlanıc ünvanın əldə edir, daha sonra onun üzərinə dəyişənə kompilyator tərəfindən mənimsədilən sürüşməni əlavə edir.

interp_new.cpp/start_adrs()


unsigned char *start_adrs(int lgm){

  struct func_table_el *fptr;
  unsigned char *adrs;

  switch(lgm){
  case LOCAL:
    adrs = (unsigned char *)fnstk.top->dptr;
    break;
  case MEMBER:
    adrs = (unsigned char *)obstk.top->dptr;
    break;
  case GLOBAL:
    adrs = glbmt->area;
    break;
  }

  return adrs;
}

start_adrs() funksiyası dəyişənin aid olduğu yaddaş sektorunun aşağıdakı kimi müəyyənləşdirir. İlk olaraq hal-hazırda icra olunan funksiyanın yaddaş sahəsi yoxlanılır, daha sonra hal-hazırda istinad olunan obyektin nümunəsinin yaddaşdakı ünvanı, son olaraq isə qlobal obyektin yaddaşdakı ünvanı yoxlanılır. Qeyd edək ki, qlobal obyekt proqram işə salınan zaman icraçı tərəfindən yaradılır.

interp_new.cpp/main()

  /* init glb tips area  */
    glbmt = (struct var_table *)malloc(sizeof(struct var_table));
    glbmt->area = (unsigned char *)malloc(exec_band[0].dt_size);
     /* we saved start adrs [0].head_pos during band build  */
    prg_start = exec_band[0].head_pos;
    push_to_stek(&obstk, glbmt->area);

Əsrin icraçısının hazırki buraxılışında yaddaşın idarəolunmasında müxtəlif yaddaş sektorlarından və steklərdən istifadə olunur. Növbəti buraxılışda isə proqramın yaddaş sahəsi tamamilə Xətti Yaddaş Fəzasına keçiriləcək. Yaddaş proqramın ən çox istifadə etdiyi resurslardan olduğuna görə yaddaş idarəsi alqoritmini təkmilləşdirmək proqramın icra sürətinə də öz təsirini göstərəcək. Bununla yanaşı yeni tətbiq edəcəyimiz yaddaş idarəolunma alqoritmi hazırkı buraxılışda mümkün olmayan funksiylara parametrlərin ünvana görə ötürülməsi, funksiyaya cərgələrin, habelə istifadəçi təyin etdiyi tiplərdən olan dəyişənlərin, başqa sözlə obyektlərin ötürülməsini mümkün edəcək.

Hədəflədiyimiz xətti yaddaş fəzasını intel x86 arxitekturasında tətbiq olunan 3 və 4 səviyyəli yaddaş səhifə cədvəlləri vastəsilə əldə etməyi planlaşdırırıq.

Intel səhifə cədvəlləri


Biz Əsrin yaddaş idarəetmə alqoritmində hələlik 1 səviyyəli, hər birinin ölçüsü 100 Mb olan səhifələrdən istifadə etməyi planlaşdırırıq. Cədvəldə səhifələrin sayı isə 1024 ilə 4096 arasında olmasını nəzərdə tutmuşuq.

Ədədi və Şərti ifadələr

Ədədi və Şərti ifadələrin qiymətləndirilməsi üçün postorder nizamlama metodundan istifadə olunur. Ağac postoreder, preorder və inorder metodları ilə oxuna bilər. Kompilyatorlarda postorder və preorder metodlarından geniş istifadə olunur, belə ki, bu iki metod ifadələrin qiymətlərini mötərizəsiz hesablamağa imkan verir. Misal üçün, tutaq ki, aşağıdakı ifadə verilib:

5 + (10 * (4 - 2))

Bu ifadəyə uyğun ağac aşağıdakı kimi olar:


Bu ağacın postorder oxunuşu aşağıdakı kimidir:

 + 5 * 10 - 4 2

Hesablama zamanı sondan əvvələ doğru hesablayırıq.