Generování syntaktických analyzátoru

Transkript

Generování syntaktických analyzátoru
A Tutorial
Generovánı́ syntaktických analyzátorů
George J. Klir
Jan Konečný
State University of New York (SUNY)
Binghamton, New York 13902, USA
[email protected]
Palacky University, Olomouc, Czech Republic
!
prepared for International Centre for Information and Uncertainty, Palacky University, Olomouc
!
!
!
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
1 / 28
lex & yacc
Nástroje pro psanı́ programů, které zpracovávajı́ (transformujı́) strukturované vstupy.
Dva hlavnı́ požadavky na takové programy:
rozdělenı́ vstupu do smysluplných jednotek.
nalezenı́ vztahů mezi těmi jednotkami.
lex je nástroj pro vytvářenı́ lexikálnı́ch analyzátorů (též lexerů).
yacc (Yet Another Compiler Compiler) je nástroj pro vytvářenı́ syntaktických
analyzátorů (též parserů).
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
2 / 28
Lex
Struktura vstupnı́ho souboru:
sekce definic
%%
sekce pravidel
%%
sekce C kódu
Výstupem je kód v C
sekce definic definuje makra a importuje hlavičkové soubory v C. Též je možno psát
sem jakýkoli kód v C.
sekce pravidel spojuje regulárnı́ výrazy s kódem v C. Když je rozpoznán text
odpovidajı́cı́ regulárnı́mu výrazu, je spuštěn odpovı́dajı́cı́ kód.
sekce C kódu obsahuje libovolný kód v C,
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
3 / 28
Prvnı́ easy example:
Example
Specifikace pro desetinná čı́sla
%%
[\n\t ] ;
-?(([0-9]+)|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)
{ printf("number\n"); }
. ECHO;
%%
main()
{
yylex();
}
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
4 / 28
Prvnı́ easy example:
Example (cont.)
Překlad:
> lex first.l
> cc lex.yy.c -o first -ll
Spuštěnı́:
.65ea12
number
eanumber
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
5 / 28
Počı́tánı́ slov
Vytvořı́me program na počı́tánı́ slov, podobný UNIXovému programu wc.
Definičnı́ sekce:
%{
unsigned charCount = 0, wordCount = 0, lineCount = 0;
%}
word [^ \t\n]+
eol \n
Sekce pravidel:
%%
{word} { wordcount++; charcount += yyleng; }
{eol} { charcount++; linecount++; }
. charcount++;
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
6 / 28
Sekce C kódu:
main()
{
yylex();
printf("%d %d %d\n", lineCount, wordcount, charcount);
}
nedělá to nic zvláštnı́ho, jen využı́vá toho, že lex defaultně čte ze standardnı́ho
vstupu.
pokud bychom to chtěli vylepšit. . .
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
7 / 28
main(argc,argv)
int argc ;
char **argv;
{
if (argc > 1) {
FILE *file;
file = fopen(argv[l], "r");
if (!file) {
fprintf(stderr,"could not open %s\n",argv[1]);
exit(1);
}
yyin = file;
}
yylex();
printf("%d %d %d\n",charCount, wordCount, linecount);
return 0;
}
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
8 / 28
Parsovánı́ přı́kazové řádky
% {
unsigned verbose;
char *progName;
% }
%%
-h |
"-?"|
-help { printf("usage is: %s [-help | -h | -? ]"
"[-verbose | -v] [(-file | -f) filename]\n", progName);
}
-v |
-verbose { printf ("verbose mode is on\n"); verbose = 1;
}
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
9 / 28
%%
main(argc, argv)
int argc;
char **argv;
{
progName = *argv;
yylex();
}
Problém: tohle ale ještě nečte z přı́kazové řádky, ale ze vstupu.
Řešenı́: můžeme předefinovat funkce input a unput, aby zacházely s argv.
Problém: ještě nemáme -file <filename>.
Řešenı́: lex umožňuje použı́t alternativnı́ počátečnı́ stavy a zahrnout tak
kontextovou závislost.
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
10 / 28
Generátor syntaktických analyzátorů
vstup: gramatika (LL(1), LR(1), SLR, LALR(1))
výstup: syntaktický analyzátor – rozhoduje platná slova gramatiky.
Prvnı́ co zkusı́me:
statement → NAME = expr
expr → NUMBER
| expr + NUMBER
| expr - NUMBER
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
11 / 28
Shift/Reduce Parsing; trocha velmi zjednodušené teorie
Generátor podle gramatiky vytvořı́ množinu stavů, každý z nich odpovı́dá možné
pozici v jednom nebo vı́ce částečně parsovaného pravidla.
Parser čte tokeny:
pokud token neukončuje pravidlo, uložı́me ho na zásobnı́k a přesuneme se jiného
stavu =shift, přesun,
symboly na zasobnı́ku tvořı́ pravou stranu pravidla, popnem je, a pushnem levou
stranu pravida =redukce.
při redukci je spuštěn odpovı́dajı́cı́ kousek kódu =akce.
http://vychodil.inf.upol.cz/publications/white-papers/lalr.pdf
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
12 / 28
Vstup pro yacc
vstup má stejnou strukturu jako vstup lexu.
Sekce definic
%token NAME NUMBER
Sekce pravidel
%%
statement: NAME ’=’ expr
| expr
;
expr: expr ’+’ NUMBER
| expr ’-’ NUMBER
| NUMBER
;
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
13 / 28
Hodnoty symbolů a akce
každý symbol má hodnotu
neterminálnı́ symboly majı́ hodnotu vytvořenou kódem v parseru
(ve skutečných parserech různé datové typy ⇒ union typedef YYSYTYPE).
Defaultně je vše int.
Kdykoli parser redukuje, spustı́ uživatelský kód asociovaný k pravidlu – akce.
Akce se odkazuje na hodnoty symbolů na pravé straně jako $1, $2. . . a nastavuje
hodnotu symbolu na levé straně přes $$.
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
14 / 28
Sekce pravidel (s doplněnými akcemi)
statement: NAME ’=’ expr
| expr { printf("= %d\n",$1); }
;
expr: expr ’+’ NUMBER { $$ = $1 + $3; }
| expr ’-’ NUMBER { $$ = $1 - $3; }
| NUMBER { $$ = $1; }
;
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
15 / 28
Lexer
Abychom mohli vyzkoušet náš parser, potřebujeme mu dodat tokeny.
%{
#include "y.tab.h"
extern int yylval;
%}
%%
[0-9]+ { yylval = atoi(yytext) ; return NUMBER;}
[ \t] ; /* ignore whitespace */
\n return 0; /* logical EOF */
. return yytext[0];
%%
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
16 / 28
> yacc -d calc.y
> lex calc.1
> cc -c calc y.tab.c lex.yy.c -ly -ll
> calc
99+12
= 111
> calc
2 + 3-14+33
= 24
> calc
100 + -50
syntax error
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
17 / 28
Aritmetické výrazy a nejednoznačnost
expr: expr ’+’ expr { $$ = $1 + $3; }
| expr ’-’ expr { $$ = $1 -$3; }
| expr ’*’ expr { $$ = $1 * $3; }
| expr ’/’ expr
{ if ($3 == 0)
yyerror( "divide by zero") ;
else
$$ = $1 / $3;
}
| ’-’ expr { $$ = -$2; }
| ’(’ expr ’)’ { $$ = $2; }
| NUMBER { $$ = $1; }
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
18 / 28
Ta gramatika má ale problém – nejednoznačnost.
Example
Parsujeme ”2+3*4”:
2
přesuň NUMBER
E
redukce E → NUMBER
E+
přesuň +
E+3
přesuň NUMBER
E+E
redukce E → NUMBER
’
Ted můžeme přesunout ’*’ a později redukovat přes pravidlo E→E*E nebo rovnou
redukovat E→E+E
Neřekli jsme, který operátor má přednost, ani nic o asociativitě.
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
19 / 28
Mohli bychom to řešit přı́mo v gramatice:
expr: expr ’+’ mlexp
| expr ’-’ mlexp
| mlexp
;
mlexp: mlexp ’*’ primary
| mlexp ’/’ primary
| primary
;
primary: ’(’ expr ’)’
| ’-’ primary
| NUMBER
;
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
20 / 28
Můžeme to ale dodat explicitně
%left ’+’ ’-’
%left ’*’ ’/’
%nonassoc UMINUS
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
21 / 28
statement: NAME ’=’ expr
| expr { printf ("= %d\n", $1) ; }
expr: expr ’+’ expr { $$ = $1 + $3; }
| expr ’-’ expr { $$ = $1 -$3; }
| expr ’*’ expr { $$ = $1 * $3; }
| expr ’/’ expr
{ if ($3 == 0)
yyerror("divide by zero");
else
$$ = $1 / $3;}
| ’-’ expr %prec UMINUS { $$ = -$2; }
| ’(’ expr ’)’ { $$ = $2; }
| NUMBER { $$ = $1; }
;
%%
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
22 / 28
Proměnné a typované tokeny
(a vı́ce vyhodnocovaných výrazů)
%{
double vbltable[26];
%}
%union {
double dval;
int vblno;
}
%token <vblno> NAME
%token <dval> NUMBER
%left ’+’ ’-’
%left ’*’ ’/’
%nonassoc UMINUS
%type <dval> expression
%%
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
23 / 28
statement-list: statement ’\n’
| statement-list statement ’\n’
statement: NAME ’=’ expr { vbltable[$l] = $3; }
| expr { printf ("= %g\n", $1) ; }
expr: · · ·
| NAME { $$ = vbltable[$1]; }
;
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
24 / 28
%{
#include <y.tab.h>
#include <math.h>
extern double vbltable[26];
%}
%%
([0-9]+)|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)
{
yylval.dval=atof(yytext); return NUMBER;
}
[ \t];
[a-z] { yylval.vblno = yytext[0] - ’a’; return NAME; }
"$" { return 0; /* end of input */ }
\n | . return yytext[0];
%%
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
25 / 28
v souboru y.tab.h:
#define NAME 257
#define NUMBER 258
#define UMINUS 259
typedef union {
double dval;
int vblno;
} YYSTYPE;
extern YYSTYPE yylval;
Proto uvádı́me
%token <vblno> NAME
%token <dval> NUMBER
%type <dval> expression
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
26 / 28
Jak to ještě můžem vylepšit
libovolná jména pro proměnné.
funkce (sqrt, log, exp) DOMACÍ UKOL
...
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
27 / 28
Chcete vědět vı́c?
Bison manual:
http://www.gnu.org/software/bison/manual/bison.pdf
LALR Gramatiky (VV)
http://vychodil.inf.upol.cz/publications/white-papers/lalr.pdf
lex & yacc, 2nd Edition By Doug Brown, John Levine, Tony Mason, Publisher:
O’Reilly Media Released: October 1992
J. Konečný (DAMOL)
Generovánı́ syntaktických analyzátorů
12. května 2014
28 / 28

Podobné dokumenty

13 FormalniPřeklad

13 FormalniPřeklad 3 entries saved by goto default Optimizer space used: input 40/12000, output 218/12000 218 table entries, 204 zero maximum spread: 257, maximum offset: 42

Více

13 FormalniPřeklad

13 FormalniPřeklad 9/600 distinct lookahead sets 4 extra closures 14 shift entries, 1 exceptions 7 goto entries 3 entries saved by goto default Optimizer space used: input 40/12000, output 218/12000 218 table entries...

Více

Moduly a namespace

Moduly a namespace −> LexBuffer<’cty> −> Ast.Expr try Parser.start (Lexer.tokenize) lexbuf with e −> let pos = lexbuf.StartPos failwithf "Error at line %d col %d: %s" pos.Line pos.Column e.Message

Více

JSON Schema v praxi - Zdroják

JSON Schema v praxi - Zdroják textovém souboru (vhodné např. k verzování), jehož obsah je v nezávislém, standardním formátu (řádka knihoven pro různé jazyky). Můžeme tak bez problémů tuto „specifikaci“ pro data přijímaná naším ...

Více

Fakulta jaderná a fyzikáln¥ inºenýrská

Fakulta jaderná a fyzikáln¥ inºenýrská je nutná nová instalace stabilizace polohy plazmatu. Teoretická £ást této práce obsahuje rovnice pro horizontální a vertikální ur£ení polohy plazmatu a koecientu asymetrie. Dále je tu odvozena Gra...

Více

Cvicení - Geocomputation

Cvicení - Geocomputation Většinou se první probírají množiny, které se ale špatně popisují bez znalosti proměnných a výroků. Obejít se bez proměnných a množin u výroků nebo množin u proměnných také nejde. Začínáme ...

Více

Program, jeho syntax a sémantika - Vilem Vychodil

Program, jeho syntax a sémantika - Vilem Vychodil Doporučená literatura: Sperber M., Dybvig R., Flatt M., Van Straaten A., Findler R., Matthews J.: Revised6 Report on the Algorithmic Language Scheme. Journal of Functional Programming 19(S1)2009, 1...

Více