PARADYGMATY I JĘZYKI PROGRAMOWANIA lex/flex i yacc/bison w- 4 (2014)
Kompilator 2 faza wstępna faza końcowa faza postkońcowa Skaner Parser Analizator semantyki Optymizator Generator kodu
lexer/scanner 3 Analiza leksykalna proces zamiany ciągów znaków na ciąg tokenów x = 4 3*2 LEKSEM TOKEN x ZMIENNA = PRZYPISANIE 4 LICZBA - O_MINUS 3 LICZBA * O_MUL 2 LICZBA
parser 4 Analiza syntaktyczna analiza ciągów tokenów (leksemów); ich struktura gramatyczna; błędy składni LEKSEM TOKEN x ZMIENNA = PRZYPISANIE 4 LICZBA - O_MINUS 3 LICZBA * O_MUL 2 LICZBA x = 4 3*2 = x - 4 * 3 2 ^ 4
Analizator semantyki 5 Analiza semantyczna znaczenie n np. sprawdzanie typów, wiązanie obiektów, etc. Kod: double a = "napis"; Błąd analizy semantycznej: error: incompatible types in initialization
Optymalizator 6 zmniejszenie/zwiększenie pewnych własności programu wynikowego np. przyspieszenie programu
Generator kodu 7 Generacja kodu proces, w którym kompilator zamienia program źródłowy na wykonywalny, maszynowy x = a + b * c load c! mul b! add a! store x!
8 lex/flex & yacc/bison
lex/flex 9 lex narzędzie do budowy analizatorów leksykalnych M.E. Lesk i E. Schmidt (Lex A Lexical Analyzer Generator) obecnie rzadko używany flex (fast lexical analyzer generator) Open source (darmowy)
yacc/bison 10 yacc narzędzie do tworzenia analizatorów syntaktycznych (S.C. Johnson. Yacc: Yet Another Compiler- Compiler) wygenerowany parser potrzebuje analizatroa leksykalnego obecnie rzadko używany bison Open source (darmowy)
...związek z kompilacją 11 faza wstępna faza końcowa faza postkońcowa Skaner Parser Analizator semantyki Optymizator Generator kodu Lex/Flex (*.l skrypt) Yacc/Bison (*.y skrypt)
Schemat działania 12 KOD ŹRODŁOWY a = b + c * d WZORCE GRAMATYKA = id1 + id2 * id3 id4 ^ lex/flex yylex() ANALIZA LEKSYKALNA yyparse() yacc/bison ANALIZA SYNTAKTYCZNA KOD ASEMB. ======== load id3 mul id4 add id2 store id1
Schemat działania 13 skrypt.l skrypt.y lex flex yacc bison kod skanera skrypt.lex.c skrypt.tab.c kod parsera wejście a.out wyjście
Jak działa flex? 14 Flex używa skryptu *.l do generowania skanera *.l skrypt lex lex.yy.c skaner: czyta plik wejściowy (input file) przetwarza plik na ciąg tokenów tokeny przekazuje do parsera
Jak działa flex? 15 Skrypt dla flex- a: %{ #include <stdio.h> %} %% begin printf("started\n"); hello printf("hello!\n"); thanks printf( Welcome\n"); end printf("stopped\n"); %% KOMPILACJA i WYKONANIE $ flex examplea.l! $ gcc -o examplea lex.yy.c lfl! $./examplea! begin! Started! hello! Hello!! end! Stopped! thanks! Welcome! hello thanks! Hello!! Welcome!
Tokenizacja 16 %{ #include <stdio.h> #include <y.tab.h> %} %% begin return BEGIN; hello return HELLO; thanks return THANKS; end return END; %% Zamiast drukowania (printf(...)) dokonujemy identyfikacji TOKENU i zwracamy jego nazwę. Nazwa ta będzie używana przez program yacc, do opisu gramatyki.
Wyrażenia regularne 17 /* sekcja definicji */ %{ #include <stdio.h> %} %% /* sekcja reguł */ [a-z] printf("lowercase_word\n"); [A-Z] printf("uppercase_word\n"); [a-za-z] printf("word\n"); [0-9] printf("integer\n"); [0-9.] printf("float\n"); ";" printf("semicolon\n"); "(" printf( Left_parentheses\n"); ")" printf( Right_parentheses\n"); %%
Format reguł flex- a 18 dopasuj tekst z wejścia do wzorców (REGEX) zwracaj typ tokenów (TOKEN_TYPE) Format:! REGEX!{ /* Kod */!!!return TOKEN_TYPE;!!}!!!
Reguły flex- a 19 Flex dopasowuje tokeny o największej długości Wejście: abc Reguła: [a- z]+ Token: abc(nie "a" i nie "ab ) Flex używa zasady: pierwsza dobra reguła wejście: jeden Reguła_1: "jeden { printf("hello,"); Reguła_2: [a- za- z]+ { printf ("World!"); } à Wyjście: Hello, (nie World! )
Wymiana danych 20 yytext - przechowuje wartość przeczytaną przez lex yyval - jest używana do wymiany wartości między lex a yacc Przykład [0-9] yyval = atoi(yytext); return NUMBER; Tutaj pod yyval została wstawiona wartość otrzymana z konwersji napisu wejściowego, dopasowanego do wyrażenia regularnego, która pierwotnie została zapisana w zmiennej yytext. Prócz tego zwrócony został typ wartości (NUMBER) w ten sposób yacc dowie o typie i może go używać we własnych definicjach.
Wymiana danych 21 definicje tokenów metoda podstawowa inne informacje YYSTYPE (lex i yacc: definicje) %{ #define YYSTYPE double %} lex: deklaracje extern YYSTYPE yyval; yacc: deklaracje YYSTYPE yyval;
Ogólny skrypt lex- a 22 plik skryptu dla lex- a: /** sekcja definicji **/!...! %%! /** sekcja reguł **/!...! %%! /** Sekcja kodu C kopiowanego dosłownie **/!...
calc1.l 23! /* sekcja definicji */! %{! #include <stdlib.h>! #include <stdio.h>! #include "calc1.h"! void yyerror(char*);! extern int yylval;! %}!
calc1.l 24! /* sekcja definicji */! %{! #include <stdlib.h>! #include <stdio.h>! #include "calc1.h"! void yyerror(char*);! extern int yylval;! %}! nagłówek calc1.h
calc1.l 25! /* sekcja definicji */! %{! #include <stdlib.h>! #include <stdio.h>! #include "calc1.h"! void yyerror(char*);! extern int yylval;! %}! nagłówek funkcji błędów
calc1.l 26! /* sekcja definicji */! %{! #include <stdlib.h>! #include <stdio.h>! #include "calc1.h"! void yyerror(char*);! extern int yylval;! %}! zmienna komunikacyjna, zewnętrzna
calc1.l 27 /* sekcja reguł */! %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 28 /* sekcja reguł */! wyrażenia regularne, wzorce %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 29 /* sekcja reguł */! puste znaki %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 30 /* sekcja reguł */! cyfry; jedna lub więcej %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 31 /* sekcja reguł */! zwracana nazwa tokenu %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 32 /* sekcja reguł */! jeden znak; operator %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 33 /* sekcja reguł */! kropka oznacza dowolny znak (nie \n) %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 34 /* sekcja reguł */! reakcja na kropkę %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 35 /* sekcja reguł */! przekazywana zmienna i jej zawartość %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 36 /* sekcja reguł */! przekazywany typ tokenu %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
calc1.l 37 /* sekcja reguł */! funkcja błędów %%! [ \t]+ ;! [0-9]+ {yylval = atoi(yytext); return INTEGER;}! [-+*/] {return *yytext;}! "(" {return *yytext;}! ")" {return *yytext;}! \n {return *yytext;}!. {char msg[25]; sprintf(msg,"%s <%s>",! "invalid character",yytext); yyerror(msg);}!!
38 yacc/bison
Jak działa bison? 39 Bison używa skryptu z pliku *.y nazwa.tab.c nazwa.y bison nazwa.parse.c Parser czyta ciąg tokenów i próbuje zidentyfikować struktury gramatyczne zgodnie ze specyfikacją
Gramatyka 40 Gramatyka zbiór reguł formalnych dla napisów z danego języka. Reguły opisują zasady tworzenia napisów (tokenów) ze znaków zgodnie z zadaną składnią
41 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
42 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
43 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA Startujemy z lewej strony.widzimy, że mamy id. WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
44 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA id jest wyrażeniem WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
45 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA Parser wie, że 3 jest wyrażeniem. W dalszych próbach oprze się na następnym znaczniku. WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
46 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA Produkcja z plus pasuje bo jest to następny token z wejścia. WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
47 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA Przesuwamy się do następnego tokenu, który jest id, a więc jest wyrażeniem (E) WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
48 Prosty przykład Leksem Token 3 LICZBA + O_PLUS 2 LICZBA - O_MINUS 1 LICZBA Wiemy, że E + E to wyrażenie (E). Powtarzamy proces aż do wyczerpania możliwości... WEJŚCIE: 3 + 2 1 E => E + E E E E * E E / E id Gramatyka
Gramatyka i format yacc 49 Gramatyka G = {{E, I}, T, P, E} 1. E => I! 2. E => E + E! 3. E => E * E! 4. E => ( E )! 5. I => a! 6. I => b! 7. I => Ia! 8. I = Ib! 9. I = I0! 10. I => I1!! T = {+, *, (, ), a, b, 0, 1} = s. terminalne P = {produkcje: 1-10}, E start Format programu yacc! Exp : Id {...}! Exp + Exp {...}! Exp * Exp {...}! ( Exp ) {...}! ;! Id : a {...}! b {...}! Id a {...}! Id 0 {...}! Id 1 {...}! ;!
calc1.y 50!! /* sekcja definicji */! %{! #include <stdlib.h>! #include <stdio.h>! int yylex(void);! #include "calc1.h"! %}! %token INTEGER!
calc1.y 51 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }!
calc1.y 52 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }! reguły gramatyczne
calc1.y 53 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! akcje expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }!
calc1.y 54 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }! Kolejność operacji zapewnia kolejność produkcji: najpierw plus/minus co oznacza, że mulex jest realizowane jako pierwsze. Można deklarować łączność działań: %left PLUS MINUS! %right NOT!! etc.
calc1.y 55 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }! zwykły wydruk; informacja
calc1.y 56 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! $1 $2 $3 expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }! specjalne zmienne przechowujące dopasowane wyrażenia z prawej strony produkcji
calc1.y 57 %% /* sekcja REGUŁ */! program:! line program! line:! expr:! mulex:! term:! line! expr \n { printf("%d\n",$1); }! n! expr + mulex { $$ = $1 + $3; }! expr - mulex { $$ = $1 - $3; }! mulex { $$ = $1; }! mulex * term { $$ = $1 * $3; }! mulex / term { $$ = $1 / $3; }! term { $$ = $1; }! ( expr ) { $$ = $2; }! INTEGER { $$ = $1; }! obliczona lewa strona produkcji i umieszczana na stosie
calc1.y 58 /* sekcja kodu C */! %%! void yyerror(char *s)! {! fprintf(stderr,"%s\n",s);! return;! }! int main(void)! {! /*yydebug=1;*/! yyparse();! return 0;! }! wywołanie głównej funkcji parsera
calc1.y 59 /* sekcja kodu C */! %%! void yyerror(char *s)! {! fprintf(stderr,"%s\n",s);! return;! }! int main(void)! {! /*yydebug=1;*/! yyparse();! return 0;! }! definicja funkcji błędów
Praktyka 60 uruchomić yacc/bison na pliku skryptowym z definicjami uruchomić lex/flex na pliku definicji syntaktycznych skompilować żródło wygenerowane przez yacc skompilować plik wygenerowany przez lex/flex skompilować inne potrzebne pliki połączyć wszystkie wynikowe pliki w plik wykonywalny
Przykład: poprawianie tekstu 61 lex/flex: Usuwanie zbędnych pustych linii, wielokrotnych spacji, spacji przed znakami przestankowymi takimi jak.,?! itd. /** definicje **/ punct [,.;:!?] text [a- za- Z] %% /** reguły **/ ) +/{punct} {printf( ) );} ) /{text} {printf( ) );} {text}+ +/ ) {while (yytext[yyleng- 1]== ) yyleng- - ; ECHO;} ({punct} {text}+)/ ( {ECHO; printf( );} ( +/{text} {while (yytext[yyleng- 1]== ) yyleng- - ; ECHO;} {text}+" "+/{punct} {while (yytext[yyleng- 1]== ) yyleng- - ; ECHO;} ˆ + ; + {printf( );}. {ECHO;} \n/\n\n ; \n {ECHO;}
Kalkulator (flex)[3] 62 %{! #include <stdlib.h>! #include "calc.tab.h"! %}! NUMBER [0-9]+! OP![+-/*]! %%! {NUMBER}!{ yylval.value =!!strtol(yytext, 0, 10); return NUMBER; }! ({OP} \n)!{ return yytext[0]; }!.!!{ ; }!
Praktyka - makefile 63 YFLAGS!= -d # header files! PROGRAM = calc! OBJS!= $(PROGRAM).tab.o lex.yy.o # + inne! SRCS CC!= $(PROGRAM).tab.c lex.yy.c # + inne!!= gcc! all: $(PROGRAM)!.c.o: $(SRCS)!!$(CC) -c $*.c -o $@ -O! $(PROGRAM).tab.c: $(PROGRAM).y!!bison $(YFLAGS) $(PROGRAM).y! lex.yy.c: $(PROGRAM).l!!flex $(PROGRAM).l! calc: $(OBJS)!!$(CC) $(OBJS) -o $@ -ll -lm! clean:;!rm -f $(OBJS) core *~ \#* *.o $(PROGRAM) \!!y.* lex.yy.* $(PROGRAM).tab.*!
Literatura 64 1. Lesk, Schmidt. Lex A Lexical Analyzer Generator. 2. Johnson. Yacc Yet another compiler- compiler. 3. T. Niemann. LEX & YACC TUTORIAL (epaperpress.com) 4. M. Landwehr. Flex/bison tutorial. ( hgp://www.capsl.udel.edu/courses/cpeg421/2012/slides/) 5. www/developerworks/aix/tutorials/au_lexyacc 6. Flex/Bison Tutorial. Aaron Myles Landwehr <aron+ta@udel.edu>
Pytania i dyskusja 65?
66 Dodatki na wszelki wypadek...
Kalkulator 67 Zadanie 1. Używając programów lex/flex oraz yacc/bison napisać program prostego kalkulatora z odwróconą notacją polską, z działaniami +,-,*,/. Zadanie 2. Niech T={0, 1, (, ), +, *,!, e} oznacza zbiór symboli terminalnych. Zaprojektować CFG (context free grammar), która generuje wyrażenia regularne nad alfabetem {0,1}. (Patrz: HopcroR, Rozdział 5. G={V, T, P, S}, gdzie V jest zbiorem zmiennych, T oznacza symbole terminalne, P jest zbiorem produkcji, S jest symbolem startowym). Zadanie 3. Rozważyć CFG zdefiniowaną produkcjami: S => asbs bsas ε. Udowodnić, że język L(G) jest zbiorem wszystkich napisów o równej liczbie symboli a i b.
calc.l [4] 68 %{ #define YYSTYPE double #include "calc.tab.h" #include <math.h> extern double yylval; %} D [0-9.] %% [ \t] { ; } log return LOG; pi return PIVAL; sin return SIN; cos tan and return COS; return TAN; return AND; not xor or reg ans fix sci eng const return NOT; return XOR; return OR; return REGA; return ANS; return FIX; return SCI; return ENG; return CONST; bintodec return BINTODEC; dectobin return DECTOBIN; {D}+ { sscanf( yytext, "%lf", &yylval ); return NUMBER ; } [a- za- Z_]+ return IDENT; "[" return OPENREG; "]" return CLOSEREG;
calc.l [4] cd. 69 "<<"!return LEFTSHIFT;! ">>"!return RIGHTSHIFT;! "++"!return INC;! "--"!return DEC;! "+"!return PLUS;! "-"!return MINUS;! "~"!return UNARYMINUS;! "/"!return DIV;! "*"!return MUL;! "^"!return POW;! "!!return FACT;! ("!return OPENBRACKET;! ")"!return CLOSEBRACKET;! "%"!return MOD;!!! "^^"!return XOR;! "("!return OPENBRACKET;! ")"!return CLOSEBRACKET;! "%"!return MOD;! "^^"!return XOR;! "!!"!return NOT;! "="!return ASSIGN;! "&&"!return LAND;! " "!return OR;! " "!return IOR;! "&"!return AND;! "~~"!return COMPLEMENT;! "\n"!return EOLN;!!
Metaznaki wzorców 70 Metaznak Dopasowanie. dowolny znak oprócz nowej \n newline * zero lub więcej kopii poprzedniego wyrażenia + jedna lub więcej kopii poprzedniego wyrażenia? zero lub jedna kopia poprzedniego wyrażenia ^ początek linii $ koniec linii a b a lub b (ab)+ jedna lub więcej kopii ab (grupowanie) "a+b" literał "a+b [] klasa znaków
Przykłady regexp i dopasowań 71 Wzorzec Dopasowanie abc abc abc* ab abc abcc abccc... abc+ abc abcc... a(bc)+ abc abcbc abcbcbc... a(bc)? a abc abcbc... [a- z] dowolna litera a, b,...,z [- az] - lub a lub z [a- za- Z0-9]+ jeden lub więcej znaków alfanumerycznych [ \t\n]+ biały znak (odstęp, tabulator, nowa linia) [^ab] wszystko oprócz a, b [a^b] a lub ^ lub b [a b] a lub lub b a b a lub b
Redukcja produkcji 72 gramatyka operacje na stosie E => E * E (r2) => E * z (r3) => E + E * z (r1) => E + y * z (r3) => x + y * z (r3) 1. x + y * z shig 2 x. + y * z reduce(r3) 3 E. + y * z shig 4 E +. y * z shig 5 E + y. * z reduce(r3) 6 E + E. * z shig 7 E + E *. z shig 8 E + E * z. reduce(r3) 9 E + E * E. reduce(r2) emit mulhply 10 E + E. reduce(r1) emit add 11 E. accept
73 za tydzień...!?