Tablica symboli i generacja kodu - inne spojrzenie

Wolniejsze przemyslenia kogoś innego

Przykład z kodem jak to mniej więcej działa

Załóżmy że chcemy sobie zrobić tablice symboli dla prostej gramatyki. Program bedzie taki:

int a,b,c,d;
char z,x;

i tylko tyle, czyli pozwala na zwyklą deklaracje zmiennych.

Najpierw zdefiniujmy sobie gramatyke:

  1. Program będzie składał się z deklaracji odzielonych srednikiem : program -> dec ; dec ; dec ….
  2. Każda deklaracja bedzie skladac sie z typu i listy znakow jednoliterowych: dec -> type ID , ID , ID…..

Gramatyke juz mamy, teraz trzeba ja poprawic

program -> dec ; dec ; dec ….

tutaj trzeba wprowadzic rekurencje, mozna to zrobic na kilka sposobow (e to pusty symbol)

program -> dec ; program | e

lub

program -> program dec ; | e

oba sposoby sa dobre, i bison oba łyknie, ale że nie chcemy lewostronnej rekurencji to zastosuemy wariant pierwszy. Teraz deklaracja

dec -> type ID , ID , ID…..

rozdzielamy to na dwie produkcje i robimy rekurcje jak przedtem

dec - > type dec_list
dec_list -> ID , dec_list | e

oczywiscie to nie wypali, bo to co napisalismy opisuje ciag

ID , ID , …. ID ,

wiec zeby sie pozbyc , na koncu przerabiamy:

dec_list -> ID | dec_list , ID

To jest dosyc dobre ale mamy rekurencje lewostronna. Mozna by bylo

dec_list -> ID | ID , dec_list

ale to juz jest lewostronna faktoryzacja, i bison juz sobie z tym nie poradzi, wiec wracamy do poprzedniej postaci (lewostronna rekurencja dla bisona nie ma znaczenia {chyba} );

Czyli nasza gramatyka to:

program -> dec ; program | e
dec - > type dec_list
dec_list -> ID | dec_list , ID
type -> INT | CHAR

Teraz przepiszemy to ladnie do bisona

%{
#define YYSTYPE char
%}

%token ID CHAR INT

%%

program    :     program dec ';' 
        |    
        ;
dec        :    type id_list
        ;
id_list        :    ID
        |    id_list ',' ID
        ;
type                :    CHAR
        |    INT
        ;

Czas na flexa. Poniewaz tylko deklarujemy , więc nie potrzebne sa nam liczby. Nazwy zmiennych moga skladac sie tylko z char'ow, więc i typ mozemy zapisac w postaci chara. Czyli zadeklarujemy sobie atrybut jako char (atrybut czyli zmienna yylval):

#define YYSTYPE char

Flex musi wykrywac tylko:
';' '=' ',' musi zwrocic bezposrednio jako token
'char' 'integer' musi zwrocic token CHAR lub INTEGER i atrybut ustawic na 'c' lub 'i'
'a' 'b' i inne litery, musi zwrocic jako: token ID i atrybut 'a' 'b' itp

Takie cos bardzo ulatwi sprawe, bo tablica symboli zajmie sie tylko parser.

wc        [ \t]
ws        {wc}+
id        [[:alpha:]][[:alnum:]]*

%%

{ws}
"char"        {yylval = 'c';return CHAR;}
"int"        {yylval = 'i';return INT;}
{id}        {yylval = yytext[0];return ID;}
.            {return yytext[0];}
<<EOF>>        {return DONE;}

Mamy podstawy juz. Ale o co biega tak wogóle ??

Otóż. Lexer leci sobie przez nasz program i napotyka linijke

char a,b;

sprawdza po kolei kazde dopasowanie, wychodzi mu ze musi zwrocic po kolei

CHAR ID , ID ;

więc zwraca to w tej kolejności.
Bison to odbiera w takiej samej kolejnosci jak lexer mu to dostarczyl. Czyli najpierw bison widzi CHAR, szuka wiec po swoich produkcjach i widzi produkcje

type -> CHAR

więc jest ok, wiemy co to CHAR, bison tez wie i traktuje CHAR jako type.
Odbiera dalej dostaje ID, szuka po swoich produkcjach i znajduje

id_list -> ID

wiec ok, wiemy ze ID to id_list, mamy juz wiec

type id_list

lecimy dalej , dostajemy od lexera ',' szukamy w produkcjach, niestety bezposrednio sobie tego ',' nie wyczarujemy wiec zakladamy ze jakos uda sie nam to dopasowac potem, odkladamy ','

type id_list ,

dalej dostajemy ID, wiemu juz ze id_list to ID, ale zauwazamy tez ze jak dopiszemy sobie do naszej listy to ID to bedziemy mieli

type id_list , ID

a to co na koncu jest mozemy sobie doposowac do produkcji

id_list -> id_list ',' ID

czyli mozemy zmniejszyc swoja liste, co jest dla nas baaardzo korzystne, wiec podmieniamy

type id_list

zauwazamy ze to tez da sie zredukowac (reduce) zeby miec mniejsza liste wiec znowu podmieniamy z produkcji

dec -> type id_list

czyli mamy na swojej liscie tylko

dec

lexer zaraz nam dorzuci ';' (srednik) i to wogole sie da zredukowac do program, wiec bardzo chetnie to robimy i skonczylismy z calym tym balaganem.

Teraz uwagi:
To co robilismy to bylo shift/reduce, czyli reduce to jest wtedy kiedy mozemy sobie zmiejszyc liste bardzo prosto, a shift to wtedy kiedy chcemy sobie pobrac jeszcze jeden symbol zeby zredukowac wiecej.
Ogolnie redukujemy tak zeby dalo sie zredukowac wiecej, wiec zawsze staramy sie redukowac glebiej ze stosu. Czasami moze dojsc do problemu ze nie wiemy czy redukowac czy pobierac (sławne else).

Teraz moze sie okazac ze parser chce dokladniejszych informacji o tokenie z leksera. Takie cos robi sie za pomoca atrybutow symboli. Atrybut to zwykla dodatkowa zmienna sprzezona z tokenem. W atrybucie lexer moze przekazywac co chce parserowi. Czyli lexer wykryl ID, przekazal parserowi ze ma ID a w tej dodatkowej zmiennej przekaze mu np 'c' 'a' lub np stringa "moja_dluga_zmienna" lub wartosc liczby jak sobie policzymy, albo cokolwiek chcemy.

Rola lexera sie skończona (dla tej prostej gramatyki).

Teraz bison.

Przesledzmy jeszcze raz pokolei produkcje jakie sie wykonaly zeby wykryc wszystkie nazwy zmiennych czyli lexer przekazuje linijke (np)

a , b , c

A bison odczytuje po wielu produkcjach ID, ID , ID.
Jak on to dokladnie zrobil, lexer dla kazdej litery przekazuje pokolei ID, a potem bison
pierwsze ID przerabia z dec_list -> ID czyli mamy dec_list zamiast ID dalej mozemy zapisac

ID,ID,ID    dec_list -> ID                dec_list ,ID ,ID
   ID,ID    dec_list -> dec_list , ID        dec_list ,ID
      ID    dec_list -> dec_list , ID            dec_list

Czyli dec_list , ID jest redukowane do dec_list w dwoch ostatnich krokach.

Teraz zauzmy ze z kazdym symbolem z ktory lexer zwraca, laczy jakas zmienna (yylval dokladniej).
Dostep do tych zmienej mamy poprzez $$ $1 $2 …..
a dokladniej
dec_list -> ID {$$ <- odnosi sie do atrybutu dec_list, $1 odnosi sie do atrybuty ID}
Fajnie, tylko ze my nic nie przekazalismy jako dec_list, to parser sobie sam przydziela atrybuty do kazdej produkcji jakiej uzywa.

Zaluzmy teraz ze domyslnie w kazdym atrybucie siedzi 0, chyba ze parser je zmienii. (atrybuty sa typu int)
Zdefiniujemy sobie jakies czynnosci ktore parser ma wykonac gdy natrafi na dana produkcje

dec_list -> ID                {$$ = 1;}
dec_list -> dec_list , ID    {$$ = $1 + 2;}

czyli gdy napotka produkcję dec_list -> ID , to wpisze do atrybutu dec_list 1, a dla produkcji dec_list -> dec_list , ID wpisze do atrybuty dec_list z lewej strony , wartosc atrybuty dec_list z prawej strony + 1.

Co wazne zauwazenia dec_list z lewej i z prawej, to zupelnie inne dec_list !!! (w sensie wartosci atrybutu).
Kazdy z tych dec_list ma wlasny nie zalezny atrybut (ktory bison sobie kopiuje z yylval).

Czyli jak to będzie wyglądać:

ID,ID,ID    dec_list -> ID                dec_list ,ID ,ID << tutaj do $$ = 1
   ID,ID    dec_list -> dec_list , ID        dec_list ,ID << tutaj do $$ = 1 + 2
      ID    dec_list -> dec_list , ID            dec_list << tutaj do $$ = 3 + 2

Czyli wyjsciowe dec_list wyladuje z atrybutem 5.

Co ciekawe, domyslne wartosci tuz przy wejsciu do reguly

    dec_list -> ID            $$ == $1 czyli domyslna wartosc to wartosc atrybuty $1
    dec_list -> dec_list , ID    $$ == 1  domyslnie wartosc atrybuty dec_list z prawej strony
    dec_list -> dec_list , ID    $$ == 3

dzieje sie tak gdyz wartosc atrybuty $$ na początku jest domyslnie kopiowana z yylval, a samo yylval ostatnio bylo ustawione przez lexera dla ID. Potem $$ = 1; wpisuje 1 do yylval, ktore potem jest kopiowane do dec_list

To jest dosyc wazne, bo widac teraz dlaczego trzeba uwazac z tym yylval, i nie mozna sobie tak poprostu podpiac pod to klasy z jakas pamiecia na stercie. (o tym pozniej).

No dobra. To teraz tablica symboli.

Linijka deklaracji wyglada tak:

char a,b,c;

Czyli wiemy juz jak to przerobi bison: char, potem rozpozna a potem b potem c, więc mozemy zrobic tak:
gdy juz rozpoznamy typ, ustawimy globalnie ze teraz bedziemy wpisywac chary, a potem przy kazdym rozpoznaniu bedziemy dodawac nowa zmienna. To rzeczywiscie jest fajne, ale przy pascalu nam sie to nie przyda bo tym znamy na samym koncu, wiec lepiej teraz sie przygotujmy do tego. Zrobimy tak, że najpierw rozpoznamy wszystkie zmienne, wpiszemy sobie je jakos do bufora (listy), a potem gdy deklaracja sie skonczy, kazda z nich wpiszemy do tablicy symboli z odpowiednim typem.

program    :     program dec ';' 
        |    
        ;
dec        :    type id_list    {tu deklaracja sie skonczyla; wiec foreach(x in lista) tablica[x].type = $1 }
        ;
id_list        :    ID            {zeruj liste, ta regula jak wiemy wykona sie pierwsza; add(lista,$1);}
        |    id_list ',' ID  {add(lista,$3);}
        ;
type    :    CHAR {$$ = $1;//to domyslne, więc jest mozna nie pisac}
        |    INT     {$$ = $1;}
        ;

Teraz proste funkcyjki do przechowywania tymczasowej listy znakow, oraz prosta tablica symboli:

Wszystko wepchniemy do global.h i global.c

global.h

#ifndef GLOBAL_H
#define GLOBAL_H
#define YYSTYPE char
typedef struct Entry {    
    char name;
    char type;
} Entry;
 
extern char List[256];
extern Entry Table[256];
 
int look_up(char name,int k);
int push(char name);
#define reset() push(0)
int add(char name, char type);
#define get_count() add(0,0)
 
#endif

i global.c

#include "global.h"
#include <stdlib.h>
#include <stdio.h>
 
char List[256];
Entry Table[256];
 
int look_up(char name,int k){
    int i;
    for (i=0;i<k;++i){
        if (Table[i].name == name) return i;
    }
    return -1;
}
int push(char name ){
    static int c = 0;
    if (name=='\0') {
        int t = c;c=0;return t;
    }
    List[c++] = name;
    return c;
}
int add(char name, char type ){
    static int c = 0;
    if (name == '\0') return c;
    if (look_up(name,c) != -1) {
        printf("blad %c juz jest",name);
        exit(1);
    }
    Table[c].name = name;
    Table[c].type = type;
    c++;
    return 0;
}

add('a','c') // doda nam nowa zmienna do tablicy symboli typu 'c' , zwroci -1 gdy taka zmienna juz jest
get_count() // zwroci nam ile jest w tablicy symboli znakow
push('a') //wrzuci do tymczasowej listy 'a'
int z = reset(); // zresetuje tymczasowa liste i zwroci ile bylo znakow
int index = look_up('a') //zwroci indeks 'a' w tablicy symboli

oczywiscie wszytsko sprawdza sie gdy nie mamy wiecej niz 256 zmiennych w kodzie :)

No i bison:

dec        :    type id_list
                            {
                                int k = reset();
                                int i;
                                for (i=0;i<k;i++){
                                    add(List[i],$1);
                                }
                            }
        ;
id_list    :    ID            {reset();push($1);}
        |    id_list ',' ID    {push($3);}

I w mainie zeby wyswietlic tablice:

int k = get_count();
    int i = 0;
    for (i = 0;i<k;++i){
        printf("var [%c] type [%c]\n",Table[i].name,Table[i].type);
    }

No i gotowe:
tutaj kompletny kod do potestowania:
«url»

Kto odpowiedzialny za tablice symboli

Ogólnie przyjeło się że tablicę symboli wypełnia lexer, a parser uzupełnia tylko o rózne informacje.
Spowodowane jest to komunikacją lexer parser. Gdyby parser miał wpisywać nowe symbole do tablicy symboli to od lexera musiałby dostać dokładna informacje o tym co tamten wykrył, czyli dla linijki:

char moja_dluga_zmienna;

lexer musiałby przesłac caly napis 'moja_dluga_zmienna' do parsera, czyli zawartosc zmiennej yytext. To jest oczywiscie problem, gdzyż parser dla każdego tokenu ustawia atrybu kopiując jego wartosc z yylval. Czyli jakbyśmy przekazywali przez char *, to błby skopiowany tylko wskaźnik i mielbyśmy G. Wszystkie tokeny beda miały ten sam atrybut (czyli wartosc ostaniego odczytanego).

Mozna oczywiście to rozwiazać podstawiając np obiekt string, czyli

#define YYSTYPE std::string

No i to zadziała :D. W ten sposób rzeczywiście będziemy mogli zupelnie wszystko zostawić parserowi. Natomiast co z wydajnością ?

Taka prosta gramatyka:

%token TYPE ID
%%

program    :     dec ';' program
        |    
        ;
dec        :    TYPE list 
        ;
list    :    ID
        |    list ',' ID 
        ;

Za yylval robi string, odpalamy parser dla programu:

type a;

i co sie okazuje, wewnętrznie parser stworzył dokładnie 215 takich stringów !! (nie korzystajac przy tym z konstruktora kopiującego ;) ). Oczywiście przy dużej gramatyce to może by sie i opłacało , ale np produkcja list->id, potrzebowała az 5 stringów (tworzonych przez =).

Dlatego chyba najlepiej zrobić sobie dobry interfejs do tablicy symboli, niech lexer zajmuje sie tylko przekazywaniem wykrytych napisów do niej, a ona już by się troszczyła o to co ma zrobić.

Tablica symboli - jak to mozna zrobić, co sie moze dziać

Wstep

To bedzie opowiadanie o tym jak 3 'osoby' sobie pracuja zeby stworzyć tablice symboli.
Mamy flexa, menadzera tablicy symboli, i bisona.
Mniej wiecej co kto robi:
Flex siedzi na froncie i czyta tekst, to co przeczyta przekazuje menadzerowi i bisonowi, a oni sobie dalej cos z tym robią.
Menadzer zajmuje sie baza danych (czyli tam gdzie przechowuje sobie tablice symboli), ale ogolnie menda jedna sam nic nie robi, czeka az tamci go zmusza do tego.
Bison, najbardziej zorientowany z grupy, wie gdzie jestesmy, i wogole koordynuje cala prace, to on glownie tlumaczy menadzerowi co flex wyczytał mądrego.

Jak oni sie porozumiewaja ? Ogolnie to jest tak ze bison wymyslil sobie ze chce od flexa, dwie liczby. Napisal zeszyt w ktorym jest rozpiska co ma przekazac jako pierwsza i kiedy ma przekazac druga liczbe o ktora ma poprosc menadzera, natomiast o druga liczbe ma poprosic menadzera. Pozatym flex i bison wiecej ze soba nie gadaja. Reszta dyskusji juz jest miedzy bisonem a menadzerem, ale dyskusja juz jest jednostronna, czyli to bison mowi dokladnie co menadzer ma zrobic, albo co z tej bazy danych ma mu pokazac albu co w niej uzupelnic.

Zeszyt (moze byc nie dokladny)

Co flex zobaczy        |    Co ma dac        Komentarz
"program"             |    15    olac        15 ma symbol PROGRAM
innny_tekst            |    10    pytac        10 to symbol ID
"var"                |    68    olac        68 ma symbol VAR
jakas liczba            |    12    pytac        12 to NUM
itd

Ok to lecimy, najpierw z punktu widzenia kazdego po kolei.

Pierwszy flex

Tekst ktory dostaje flex jesta taki:

program nazwa;
var a,b:integer;
var c : real;
    function moja(d,e:integer;f:real):integer;
    var h:real;
    var j:array[1..2] of integer:
    begin
    .....
begin
.....

Reszta nas nie obchodzi:
Teraz flex:

1. program - czyta to, rozpoznaje, szuka u siebie w zeszycie co ma przekazac bisonowi, (czyli ma kopie zeszytu bisona), wyszukuje ze ma przekazac pierwsza liczba np 15, a druga moze olac. Naszczescie biosn to ladnie opisal, i liczbę 15 mozemy zapisac jako PROGRAM. Wiec flex przekazuje pierwsza liczba PROGRAM, a druga olewa.
2. nazwa - sytuacja podobna, szuka w zeszycie, wie ze to inny tekst znajduje 10, to odpowiad opisowy ID, wiec zwraca taka liczbe, natomiast o druga liczbe ma poprosci menadzera, wie prosi, ten mu mowi ze chce sie dowiedziec co ten takiego przeczytal, on mu mowi ze "nazwa", wiec tamten mu odpowiada ze ma przekazac 0, wiec przekazuje bisonowi, 10 i 0.
3. ';' - tutaj w rozpisce jest ze ma przekazac numer w tablicy ascii ';', wiec to robi, druga liczbe ma olac
4. var - ma przekazac jako pierwsz 68, co jest opisane jako VAR, druga olewa
5. 'a' - pierwsza liczba to 10, co odpowiada opisowi (symbolowi) ID, a o druga ma znowu zapytac menadzera, wiec ponownie pyta i sie okazuje ze ma zwrocic 1
6. ',' - przekazac ','
7. 'b' - ID i to co menadzer da dla 'b' czyli 2
8. 'var' - ponownie
9. … 10. ('c' - 10 i 3)
10. function - przekazue function
11. 'moja'- piewsza to znowu 10 (ID), druga przekazuje menadzer to jest 4
12. '(' - przekazuje '('
11. 'd' i 'e' - ponownie tak jak dla 'a', pierwsza to 10, druga to odpowiednio 5 i 6
12. 'integer' - sytuacja ciagle taka sama:
…..
XX. 'array' - znowu odczytuje, ma przekazac liczbe z opisem ARRAY, drugi symbol moze sobie olac
XX+1 '1' - tutaj okazuje sie ze ma przekazac liczbe z opisem 'NUM', a o druga liczbe zapytac menadzera,

…. i tak do konca

Co teraz bison robi ciekawego:

1. Dostaje od flexa liczbe 15, i 35423432, patrzy w swoj zeszyt, wie ze 15 odpowiada 'PROGRAM' (skoncze z tym juz, teraz tylko symbole), druga liczbe moze olec, (druga liczba to atrybut). Teraz bison, wie, ze gdy dostanie od flexa PROGRAM, to rozpoczyna sie deklaracja programu, wie tez ze tuz po tym symbolu ma powiadomic o tym menadzera, powiadomia wiec o tym menadzera. Teraz patrzy w swoj 'atlas' i widzi ze gdy dostanie program ma sie spodziewac, nastepnej pary liczb:
- ID z atrybutem jakims
- ';' z nieokreslonym atrybutem
- deklaracji jakichs (flex ma wtedy przekazac VAR)
- i jakichs funkcji (flex ma wtedy przekazac FUNCTION)
wiec czeka dalej, na to ID pierwsze, jak jego nie dostanie to zrobi rozrube i skonczy zabawe :)
2. Dostaje od flexa ID, wiec wszystko gra, wie ze wtedy znowu musi o tym powiadomic menadzera, i przekazac mu to ze dostal ID o atrybucie (dostal od flexa razem z ID liczbe 0), i ze to nastopilo po PROGRAM.
3. Czeka teraz na ';' - dostaje go
4. Czeka na VAR, i dostaje go
5. Patrzy w atlas - gdy dostanie VAR, to to beda deklaracje:, ma teraz spodziewac ze flex bedzie mu przekazywac ciagi ID oddzielone ',', jak flexowi sie znudzi to przekaze ':', i nastepnie INTEGER , albo REAL, albo ARRAY. A potem flex moze tak jeszcze raz, taka serie, i tak do usranej ….
6. Dostaje od flexa te ciagi ID, za kazdym razem gdy dostanie takiego ID w tym miejscu musi powiedziec o tym menadzerowi, ze dostal ID, po VAR, z takim atrybutem. Tutaj dostal dwa ID z atrybutami 1 i 2
7. Dostal ten ':' i INTEGER. Patrzy jeszcze raz w atlas, i wie ze teraz musi powiadomic menadzera ze sie skonczyla jedna seria VAR, i zakonczyl a sie liczba INTEGER. Wiec powiadaami.

…..

9. Dostaje FUNCTION, patrzy w atlas, teraz chce miec ID '(' i jakies argumenty a potem znowu ')' : i jakis INTEGER albo REAL. O wszystkim dokladnie powiadamia menadzera

….
10. No i tak w sumie do konca, dostaje , patrzy w atlas, wie czego sie spodziewac, i kiedy zrobic rozrube jak czegos nie dostanie, twardy gosc, ale i tak menadzer wszystkim trzesie.

Od strony menadzera.:

1. Flex nonstop sie pyta o jakies glupie liczby :)
2. Bison sie odzywa, mowi ze wykryl nowy program.
3. Flex pyta sie o liczbe dla slowa 'nazwa', menadzer patrzy do swojej bazy danych, baza danych jest pusta, wiec wpisuje w pierwsza pozycje 'nazwa', i zwraca flexowi liczbe 0.
4. Teraz nagle bison sie odzywa, mowi, ze wlasnie poznal numer programu mowi ze to 0. Menadzer patrzy w swoja tablice, i widzie ze jedyny wiersz, to jest wierz z tekstem 'nazwa'. Marudzi, ale jeszcze ufa bisonowi, wiec dopisuje do tego pierszego wiersza, ze jest to program.
5. Znowu odzywa sie flex, ze tym razem chce liczbe dla 'a', wiec, my to odnotowujemy w bazie, to bedzie drugi wiersz, zwracamy 1.
6. Bison skolei cos chce, mowi ze wykryl deklaracje, i ze pierwszy ID na jego liscie ma numer 1. No fajnie, tylko ze ja (menadzer), dla kazdej deklaracji musze wypelnic cala tabele, to nie takie kurde proste, numer 1, i co z tego, a typ a adres, a do kogo to ID nalezy wogole. Trudno, bison glupek, to my sobie to ID odlozymy na polke ta 1'dynke, i poczkeamy az bedziemy miieli wszysktie dane.
7. Znow flex, chce liczbe dla 'b' , cos za duzo tych liczb chce, sprawdzamy, czy czasem juz 'b' nie chcial, nic o 'b' nie wiemy, wiec musimy kurde wpiscac, nowy wiersz, zwracamy 2;
8. Znowu ten bison . Mowi ze ma nowy ID z atrybutem 2, no super, znowu na polke. Juz 1 i 2 stoja
9. Bison sie znowu odzywa, mowi ze flex wlasnie skonczyl serie ID wysylac, i ze wszystko zakonczyl symbolem INTEGER. No wreszcie cos wiemy, bison sie nie mylil mielismy deklaracje 2 integerow. Super, to zdejmujemy z polki, 1'dynke i 2'ke, patrzymy co to jest, oo to sa te 'a' i 'b' co w bazie mialem, super, jeszcze sie upewniamy, czy czasem te 'a' i 'b' juz nie naleza do kogos, … nie sa puste, no to my im ustawiamy kolumne typ , na INTEGER, wklepujemy wlasciciela , przpisumey im wlasciciela program, patrzymy gdzie ten program siedzial, byl pod 0'rem, wiec wklepujemy tam 0 w kolumne wlasciciel. Super teraz adresy, sa dwie zmienne, naleza do programy, obie INTEGER, wiec pierwsza walilmy pod 0 druga pod 4rke. No i ok.
10. Flex znowu chce jakos, liczbe, dla cyfry - my to mu wszystko notujemy kurde. Wpisujemy 'c', pod 3, domyslamy sie ze juz jakas deklaracja chyba bedzie, wiec sprawdzamy, ze narazie jedyny kto moze sobie cos deklaraowac na naszej liscie to jest program, on juz zadeklarowal dwie zmienne i to sa 'a' i 'b', skubaniec pewnie chce jeszcze jedna.
12. Bison sie pruje ze ma chyba nowa deklaracje, super, co on nie powie, tylko znowu zero konkretow, tylko ID z atrybutem 3, wiec na polke leci
13. Bison szybko sie uwinal, mowi ze ta deklaracja dotyczyla REAL; No duza nie byla, na polce tylko 3ka stoi. Bierzemy ja, ustawiamy wlasciciela program 'nazwa' czyli 0, typ REAL, adresik nastepny w kolejnosci, czyli 8.
14. Znow flex ze swoimi liczbami, chyba znowu deklaracja, tym razem 'moja', sprawdzamy czy jedyny co moze sobie pozwolic na zmienne, ma juz jakas 'swojš', nie ma, wiec ok, wpisujemy 'moja' pod 4'ke
15. Bison sie pruje ze chyba funkcja jest. Niech to , jak jest funkcja to znaczy ze teraz juz dwoch moze sobie zmienne deklarowac, ehhh. Sprawdzamy ktory z chlopakow programu jest funkcja. Pod 4ka siedzi 'moja', no super, Ustawiamy jej typ na FUNCTION. Teraz pewnie bison bedzie krzyczal ze kupe zmiennych nowych dla funkcji, wiec ja sobie zapamietam ze teraz wszystko ma miec nowego wlasciciela - 'moja' co jest pod 4ka
16. Flex sie pruje o liczby dla 'd' i 'e' , wiec spradzamy czy 'moja' czasem juz takich nie ma u siebie, nie ma , wiec wpisujemy, wiemy ze dopuki bison nie powie ze funkcja sie skonczyla, to wszytko bedzie nalezalo do 'moja', wiec odrazu ustawiamy wlascicila na 'moja', czyli 4kre
17. Bison proje sie ze CHYHBA dostal pierwsza serie parametrow, no super, nie lubie parametrow, wrzucam na polke co bison przekazal, czekam az potwiedzi i da typ.
18. Bison potwierdza, mowi ze parametry typu INTEGER. No super, wiec ustawiamy im typ na integer, ale adres glupio sie liczy, musze najpierw wiedziec ile jest wszystkich parametrow, i dopiero wtedy moge im dobre adresy powpisywac, ehhh. Trudno, odloze je na druga polke, polke z parametrami, poczekam az sie wszystkie skoncza.
… flex bison
20. No super parametry sie skonczyly, wiec zbieram z polki, mam tam 5 6 i 7, wszystko to parametry. Dobra, typy juz ustawilem, to teraz adres, adres sie glupio liczy, bo od tyly, i nie od 0 a od 12, no dobra, lece do ostatniego, to jest 7demka, i ustawiam 12 (ale ze to parametr, tojeszcze musze ustawic, ze to sie liczy przez jakeis BP, i ze to jest referencja, wszyskto jedno, ustawiam), lece do 6tki chce ustawic adres 12+8, bo 7 byla realem, ale ze to refrencja dla wszystkich jest 4, wiec ustawiam 16, a dla 5tki 20.
21. Bison krzyczy, ze wie co ta funkcja 'moja' bedzie zwracac, to bedzie INTEGER. No super troche za pozno. No ale ok, dla typow zwracany mamy specjalne wytyczne, dodajemy nowy wpis, tez o nazwei 'moja', ale juz wlasciciel 'moja', wiec 'moja' zwraca 'moja'… trudno. Adres na 8, typ BP i tez ref. Lecimy dalej
22. Flex znow chce, 'h' tym razem, bison jeszcze nie powiedzial ze to koniec funkcji, wiec pewnie znowu wlasciciel to 'moja', 'moja' jeszcze nei zdefiniowala 'h', wiec ok, zwracal kolejny wiesz (8);
23. Bison mowi ze znowu bedzie chyba deklaracja, narazie wie ze tylko z ID 8, dobra dobra, 8 trafia na polke, az poznam typ.
24. Bison mowi co to byl za typ, no to ok, co tam pod 8semka, 'h', wlasciciel 'moja', no to wpisuje tam adres 0, hmmmm, wytyczne mowia, ze adresy teraz beda do dolu rosnac, wiec musze go ustawic pod zerem, wiec to byl typ INTEGER, wiec wrzucam go na pozycje -4, typ BP
……
25. Bison i flex znowu jakas deklaracje, 'j' pod 9tka, na polke
26. Flex chce liczbe dla '1', hmmm podjerzane, trudno wrzucam, wlasciciel 'moja', dziwne nazwy jak dla zmiennych. pod 10tka
27. Znowu , tym razem '2', pod 11tka
28. Bison mowi ze juz wie co to za typ byl tej deklaracji, to byla ARRAY, z wlasciwosciami kilkoma 10 i 11, sprawdzam co to sa te 10 i 11, ooo to sa tamteg podejrzane zmienne, wytyczne mowia ze to peirwszy i ostatni, no ok, tyklo ja musze wpisac, pierwszy i rozmiar, no to ok, pierwszy to 1, rozmiar 2*4 (bo to INT), adres <sick> znowu odwrotnie, 2*4 = 8, pod zerem, ale juz byla jedna zmienna pod zerem dla 'moja', wiec pod -12 wpisujemy.

…..

Gdyby bylo
function glupia ……
var a,a:integer

Menadzer:
Znowu flex chce jakas liczbe, wrzucam 'a' pod 2ke, wlasciciel to jaksa funkcja 'glupia'. Trudno
Bison mowi ze jakas deklaracje, dobra dobra, wrzucam ID z 2ka na polke,
Flex znowu chcce jakas, liczbe, znowu daje 'a', no ja taki glupi nie jestem, dam mu ta sama liczbe, bo ten sam wlasciciel, 'glupia' chyba cos kombinuje, mam nadzieje ze nic glupiego :), daje flexowi znowu 2ke.
Bison krzyczy ze nowe ID w tej deklaracji, super co mnie to obchdzi, ID z atrybutem na polke.
Bison mowi ze wie juz co to bylo, to byl integer, i ze mam szybko instalowac.
No dobra, zdejmujemy z polki, pod 2ka jest a, ustawiamy typ na INTEGER, wlaściciel to nie program więc adres pod kreska czyli -4, nastepny z polki, 2, hmmm, pod 2ka jest 'a', typ i adres juz ustawilem, o ty…., jednak 'glupia' chce miec dwie takie same zmienne z nazwa 'a', juz ja ci pokaze, koniec zabawy !!!

Na koniec:
To jest tyko przykład - zresztą nie całkiem efektywny i nie kompletny, ma tylko pokazać jak to można zrobić nie całkiem super, ale jako tako :). Powinien wytłumaczyć co się dzieje i dać jakies podstawy do własnego kodu.

O ile nie zaznaczono inaczej, treść tej strony objęta jest licencją Creative Commons Attribution-Share Alike 2.5 License.