Tkvaria

Tutaj będa różne rzeczy zwiazane z tk, pomysły, rozwiązania problemów, albo ogólnie śmietnik gdzie sie wrzuca coś co nie dokładnie pasuje do pozostałych artykułów o tk

YYLVAL

Polecam doczytać w dokumentacji bisona jak to wszystko ładnie działa.

kilka wypisów:

%union {
    int val;
    string *name
}

To pozwala korzystac z yylval jako z uni
tzn:
lexer uzupelnia tak: yylval.val = 4; lub yylval.name = new string("yo");
a bison korzysta tak:

%token <name> NAME
%token <val> NUM
%type <val> sum

%token <val> powoduje ze wszędzie tam gdzie chcemy skorzystać z $1 jako atrubuty NUM, bedziemy sie odnosić do yylval.val
%type <val> sum powoduje ze produkcja sum ma atrybut typu int (czyli korzysta z uni yylval.val)

Mini produkcje

{} - < Takie akcje semantyczne, zwykle wpisujemy na koniec produkcji, myslac ze to tylko akcja :), natomiast dla bisona to jest tak jakbyśmy tam inną produkcję wpisali:

program-> prod {cout<<$1} // to wyswietli 666
prod -> inna_prod {$$ = 666} id {cout<<$2 <<endl;} // to tez wyswietli 666

bo to jest to samo co:

program-> prod {cout<<$1}
prod -> inna_prod szatan id {cout<<$2}
szatan -> /*pusty */ {$$ = 666;}

Więc jeśli umieszczamy akcje miedzy produkcjami, to ją też musimy liczyc jako jedną ze zmiennych $x

Makefile dla opornych

Widzialem kilka fajnych makefilów ostatnio :). Poniewasz wiekszość osob i tak nie ma pojęcia o co w nich biega to tutaj mala pomoc:

Nasze źrodla to np:
main.cc <- zawiera maina i w ogóle odpalamy z tego (./prog lub ./prog.exe)
tabsym.cc <- nasza wymęczona tablica
lexer.l <- flex (z niego weźmiemy lexer.h i lexer.c)
parser.y <- bison (tez parser.c i parser.h)
global.h <- wszystkie nagłówki i deklaracje tutaj

Co zrobimy żeby to działało ? Najpierw ręcznie:

Z pliku lexer.l chcemy wydobyć np lexer.h i lexer.c. Czyli piszemy w linii poleceń

flex —header-file=lexer.h -o lexer.c lexer.h

teraz z pliku parser.y chcemy wydobyc parser.c i parser.h , wiec piszemy

bison -d -o parser.c parser.y

No a teraz jak już mamy wszystkie pliki to mozemy najzywczajniej stworzyc sobie naszego programa :)
Wiekszosc w make'ach ślepo tworzy milion linii, zeby to wsztklo połącyc w jedno, pliki o, zależności, kupę powtórzonego kodu. A make przecież to ma automatyzowac wszystko, wiec jeśli chodzi o tak mały projekt, to chyba nie ma sensu az tak to komplikować.
Narazie wydaliśmy 2 linijki a teraz wydamy 3'cią i wszystko będzie gotowe :D

g++ parser.c main.cc tabsym.cc lexer.c -Wall -o prog

I juz :D. 3 linijki zamiast kilkudziesieciu w makeu.

No ale make jest i tak przydatny, wiec to w make wrzucimy:

Najpierw definiujemy nasze zrodla.

SRC = parser.c lexer.c tabsym.cc main.cc

Potem wszystkie naglowki

HDR = parser.h lexer.h global.h

Teraz niesmiertelny:

all : prog

Teraz po kolei to co robilismy w lini komend:

Czyli z lexer.l wyciskamy lexer.c i lexer.h

lexer.c lexer.h:  lexer.l
    flex --header-file=lexer.h -o lexer.c lexer.l

To samo z parser.y

parser.c parser.h: parser.y
    bison -d -o parser.c parser.y

No i na koniec nasz prog, nasz prog zalezy oczywiscie od wszystkich plikow zrodlowych, naglowkowych, bo chcemy zeby byl przerabiany od nowa (gdy napiszemy make), za kazdym razem gdy jeden z tych plilkow sie zmieni. Czyli ze wszystkich plikow zrodlowych , naglowkowych wydobywamy nasz program, (oczywiscie kompilator ma gdzies nasze pliki naglowkowe wiec do g++ dajemy tylko pliki źródłowe).

prog : $(HDR) $(SRC)
    g++ $(SRC) -o prog

czyli prog zalezy od nagłówków i źródeł.
No i na koniec

.PHONY: clean

clean :
    rm -f prog parser.c parser.h lexer.c lexer.h

.PHONY oznacza cel sztuczny (czyli taki ktory ma byc zawsze robiony), bo inaczej gdbysmy mieli plik clean, to ten cel nigdy by sie nie wykonal, bo plik by byl zawsze akutalny , bo nie mial by byc wzgledem czego nieakutalny :).

I to wszystko: calosc:

SRC = lexer.c parser.c  tabsym.cc main.cc
HDR = lexer.h parser.h global.h

all    :    prog

lexer.c lexer.h:  lexer.l
    flex --header-file=lexer.h -o lexer.c lexer.l
parser.c parser.h: parser.y
    bison -d -o parser.c parser.y

prog : $(HDR) $(SRC) 
    g++ $(SRC) -Wall -o prog

.PHONY: clean

clean :
    rm -f prog parser.c parser.h lexer.c lexer.h

Teraz jesli dodamy jakies plik .c .cc lub .h wystarczy je dopisac odpowiednio do SRC i HDR i bedzie gralo :)

Oczywiscie jest jedna wada: otoz gdy choc jeden plik sie zmieni, to wszystko bedzie kompilowane od nowa, ale dla tak malego programu chyba to nei ma znaczenia :)

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