PARADYGMATY I JĘZYKI
PROGRAMOWANIA
lex/flex i yacc/bison
w-‐4 (2014)
Skaner
Parser
Analizator
semantyki
Optymizator
Generator
kodu
Kompilator
faza wstępna
faza końcowa
faza postkońcowa
lexer/scanner
¨
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
34
parser
¨
Analiza
syntaktyczna
¤
analiza ciągów tokenów (leksemów); ich struktura
gramatyczna; błędy składni
^
=
x
-‐
4
*
3
2
x = 4 – 3*2
LEKSEM TOKEN
x
ZMIENNA
= PRZYPISANIE
4 LICZBA
-‐ O_MINUS
3 LICZBA
* O_MUL
2 LICZBA
4double
a = "napis";
er
r
or:
i
n
c
omp
a
t
i
ble
types
i
n
i
ni
ti
al
iza
t
i
on
Kod:
Błąd analizy
semantycznej:
Analizator semantyki
¨
Analiza
semantyczna
¤
znaczenie
n
np. sprawdzanie typów, wiązanie obiektów, etc.
Optymalizator
¨
zmniejszenie/zwiększenie pewnych własności
programu wynikowego
¨
np. przyspieszenie programu
x = a + b * c
Generator kodu
¨
Generacja kodu
¨
proces, w którym kompilator zamienia program
źródłowy na wykonywalny, maszynowy
load c!
mul b!
add a!
store x !
lex/flex & yacc/bison
lex/flex
¨
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
¨
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)
L
ex
/Fl
e
x
(
*
.l
skrypt
)
Ya
(
*.
cc
y
/Bis
s
krypt
o
)
n
...związek z kompilacją
Skaner
Parser
Analizator
semantyki
Optymizator
Generator
kodu
faza wstępna
faza końcowa
faza postkońcowa
Schemat działania
KOD ŹRODŁOWY
a = b + c * d
WZORCE
lex/flex
GRAMATYKA
yacc/bison
ANALIZA
LEKSYKALNA
ANALIZA
SYNTAKTYCZNA
^
=
id1
+
id2
*
id3
id4
KOD ASEMB.
========
load id3
mul id4
add id2
store id1
yylex()
yyparse()
12Schemat działania
skrypt.l
skrypt.y
lex | flex
yacc | bison
skrypt.lex.c
skrypt.tab.c
a.out
kod parsera
kod skanera
wejście
wyjście
13lex
*.l skrypt
lex.yy.c
Jak działa flex?
¨
Flex używa skryptu
*
.l
do generowania
skanera
¨
skaner:
¤
czyta plik wejściowy (i
n
put
file
)
¤
przetwarza plik na ciąg tokenów
¤
tokeny przekazuje do parsera
Jak działa flex?
%{ #include <stdio.h> %} %% begin printf("Started\n"); hello printf("Hello!\n"); thanks printf(”Welcome\n"); end printf("Stopped\n"); %% $ flex exampleA.l! $ gcc -o exampleA lex.yy.c –lfl! $ ./exampleA! begin! Started! hello! Hello!! end! Stopped! thanks! Welcome! hello thanks! Hello!! Welcome!Skrypt dla flex-‐a:
KOMPILACJA i WYKONANIE
Tokenizacja
%{ #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
/*
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");
%%
17Format reguł flex-‐a
¨
dopasuj tekst z wejścia do wzorców (
REGEX
)
¨
zwracaj typ tokenów (
TOKEN_TYPE
)
¨
Format:
!
REGEX
!
{ /* Kod */!
!
!return
T
OKEN
_TYP
E
;!
!}!
!…!
Reguły flex-‐a
¨
Flex dopasowuje
tokeny o największej długości
¤
Wejście:
abc
¤
Reguła:
[a-‐z]+
¤
Token:
abc(nie
"a"
i nie
"ab”)
¨
F
l
ex używa zasady:
pierwsza
dobra
r
eguł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
¨
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
¨
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
plik skryptu dla lex-‐a:
/**
sekcja definicji
**/!
...!
%%!
/**
sekcja reguł
**/!
...!
%%!
/** Sekcja
kodu C kopiowanego dosłownie
**/!
...
calc1.l
!
/*
sekcja definicji
*/!
%{!
#include <
stdlib.h
>!
#include <stdio.h>!
#include "calc1.h"!
void yyerror(char*);!
extern int yylval;!
%}!
calc1.l
!
/*
sekcja definicji
*/!
%{!
#include <
stdlib.h
>!
#include <stdio.h>!
#include "calc1.h"!
void yyerror(char*);!
extern int yylval;!
%}!
nagłówek calc1.h
calc1.l
!
/*
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
!
/*
sekcja definicji
*/!
%{!
#include <
stdlib.h
>!
#include <stdio.h>!
#include "calc1.h"!
void yyerror(char*);!
extern int yylval;!
%}!
zmienna “komunikacyjna”, zewnętrzna
calc1.l
/*
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
/*
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);}!
!
wyrażenia regularne, wzorce
calc1.l
/*
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);}!
!
puste znaki
calc1.l
/*
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);}!
!
cyfry; jedna lub więcej
calc1.l
/*
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);}!
!
zwracana nazwa tokenu
calc1.l
/*
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);}!
!
jeden znak; operator
calc1.l
/*
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);}!
!
kropka oznacza dowolny znak (nie \n)
calc1.l
/*
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);}!
!
reakcja na “kropkę”
calc1.l
/*
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);}!
!
przekazywana zmienna i jej zawartość
calc1.l
/*
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);}!
!
przekazywany typ tokenu
calc1.l
/*
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);}!
!
funkcja błędów
yacc/bison
Jak działa bison?
¨
Bison
używa skryptu z pliku *.y
¨
Parser czyta ciąg tokenów i próbuje
zidentyfikować struktury gramatyczne
zgodnie ze specyfikacją
bison
nazwa .y
nazwa.tab.c
nazwa.parse.c
39
Gramatyka
¨
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ą
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
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
Startujemy z lewej
strony.Widzimy, że
mamy id.
43Prosty 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
id jest wyrażeniem
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
Parser wie, że 3 jest
wyrażeniem. W
dalszych próbach
oprze się na
następnym znaczniku.
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
Produkcja z plus
pasuje bo jest to
następny token z
wejścia.
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
Przesuwamy się do
następnego tokenu,
który jest id, a więc
jest wyrażeniem (E)
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
Wiemy, że E + E to wyrażenie (E).
Powtarzamy proces aż do
wyczerpania możliwości ...
Gramatyka i format yacc
¨
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’ {...}!
;!
49calc1.y
!
!
/*
sekcja definicji
*/!
%{!
#include <stdlib.h>!
#include <stdio.h>!
int yylex(void);!
#include "calc1.h"!
%}!
%token INTEGER!
50calc1.y
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ expr ’)’ { $$ = $2; }! | INTEGER { $$ = $1; }! 51calc1.y
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ expr ’)’ { $$ = $2; }! | INTEGER { $$ = $1; }!reguły gramatyczne
52calc1.y
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ expr ’)’ { $$ = $2; }! | INTEGER { $$ = $1; }!akcje
53calc1.y
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ 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
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ expr ’)’ { $$ = $2; }! | INTEGER { $$ = $1; }!zwykły wydruk; informacja
calc1.y
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ expr ’)’ { $$ = $2; }! | INTEGER { $$ = $1; }!specjalne zmienne
przechowujące dopasowane
wyrażenia “z prawej” strony
produkcji
$1 $2 $3
calc1.y
%% /* sekcja REGUŁ */! program:! line program! | line! line:! expr ’\n’ { printf("%d\n",$1); }! | ’n’! expr:! expr ’+’ mulex { $$ = $1 + $3; }! | expr ’-’ mulex { $$ = $1 - $3; }! | mulex { $$ = $1; }! mulex:! mulex ’*’ term { $$ = $1 * $3; }! | mulex ’/’ term { $$ = $1 / $3; }! | term { $$ = $1; }! term:! ’(’ expr ’)’ { $$ = $2; }! | INTEGER { $$ = $1; }!“obliczona” lewa strona
produkcji i umieszczana
na stosie
calc1.y
/*
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
/*
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
59Praktyka
¨
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
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;} 61
Kalkulator (flex)[3]
%{!
#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
YFLAGS != -d # header files! PROGRAM = calc!
OBJS != $(PROGRAM).tab.o lex.yy.o # + inne ! SRCS != $(PROGRAM).tab.c lex.yy.c # + inne! CC != 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
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
¨
?
65na wszelki wypadek...
Dodatki
Kalkulator
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]
%{
#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 return COS; tan return TAN; and return AND;
not return NOT; xor return XOR; or return OR; reg return REGA; ans return ANS; fix return FIX; sci return SCI; eng return ENG; const 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.
"<<" !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
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ń
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
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