Konstruktory překladačů Miroslav Beneš Dušan Kolář Konstruktor Lex generátor lexikálních analyzátorů M. E. Lesk, 1975 - pro OS Unix flex - Vern Paxson, 1990 - GNU verze určeno pro generování výstupu v C/C++ existují i varianty pro další jazyky Konstruktory překladačů 2 Konstruktor YACC Konstruktory Lex a YACC generátor syntaktických analyzátorů yacc = "Yet Another Compiler-Compiler" S. C. Johnson, 1975, pro UNIX bizon kompatibilní implementace v rámci GNU projektu nadace OSF vhodné pro generování C/C++, navazuje na lex/flex Konstruktory překladačů 3 Konstruktory překladačů 4 1
Konstruktory Lex a YACC Specifikace lex. analyzátoru scanner.l dig [0-9] {dig+ return NUM;. return yytext[0]; parser.y %term NUM e: e + e ( e ) NUM ; definice pravidla uživatelské funkce digit [0-9] {digit+ return NUM; void main() { while( yylex() > 0 ) { Konstruktory překladačů 5 Konstruktory překladačů 6 Definice Parametry programu %option case-insensitive %option noyywrap %option yylineno Uživatelské deklarace %{ int promenna; % Definice pojmenovaných výrazů ws ([ \n\t]+) Vzory (regulární výrazy) Znaky: a \. "/*" Množiny znaků: [a-z0-9_] [^*] [ \r\n\t] Operátory: 0*1+ 0(1 0)?1 Pravý kontext: abc/de Začátek a konec řádku: ^x x$ Expanze jména: {DIGIT Startovací podmínka: <STR>[^"] <*>. Konec souboru: <<EOF>> Konstruktory překladačů 7 Konstruktory překladačů 8 2
Postup analýzy odpovídá-li textu více vzorů, vybere se ten, který odpovídá delšímu lexému je-li takových vzorů více, vybere se první z nich nenajde-li se žádný vzor, opisují se znaky na výstup Důsledky nejprve uvádíme konkrétnější, potom obecnější vzory [az][a-z0-9]* { return ID; "print" { return PRINT; NE! vzory nemusí být disjunktní, např. [0-9]+ {... [0-9]+(\.[0-9]+)?(E[-+][0-9]+)? {... pro filtr jsou pokryty jen ty možnosti, kdy se má text modifikovat "//".* ; pro lexikální analyzátor musí být pokryty všechny možnosti [ \n\t\r]+ ; [0-9]+ { return NUM;. { return yytext[0]; Konstruktory překladačů 9 Konstruktory překladačů 10 Předdefinované proměnné a funkce int yylex() generovaný lexikální analyzátor char* yytext řetězec obsahující lexém pozor! --- pro lex je typu char[] int yyleng délka lexému (počet znaků v yytext) int yylineno aktuální číslo řádku (%option yylineno) Předdefinované proměnné a funkce FILE* yyin soubor, z něhož se čte FILE* yyout soubor, do něhož se zapisuje int input() čtení znaku z yyin void output(char x) zápis znaku do yyout void unput(char x) vrácení znaku do yyin Konstruktory překladačů 11 Konstruktory překladačů 12 3
Příklad - filtr %{ int num_lines = 0; int num_chars = 0; % \n ++num_lines; ++num_chars;. ++num_chars; void main() { yylex(); printf("# of lines = %d\n", num_lines); printf("# of chars = %d\n", num_chars ); Příklad lex. analyzátor %{ #include <stdlib.h> #define NUM 257 int intattr; % WHITE [ \t\r\n]+ DIGITS [0-9]+ {WHITE ; {DIGITS intattr = strtol(yytext,null,10); return NUM;. return yytext[0]; Konstruktory překladačů 13 Konstruktory překladačů 14 Příklad startovací podmínky %x POZN "/*" BEGIN(POZN); <POZN>[^*]+ ; <POZN>"*"+[^*/]* ; <POZN>"*"+"/" BEGIN(INITIAL); Domácí úkoly 1. Vytvořte deterministický konečný automat přijímající všechny řetězce znaků 0 a 1, které reprezentují binární číslo dělitelné třemi. (Návod: Stavy budou odpovídat zbytkovým třídám modulo 3). 2. Vytvořte pro lex filtr, který vypíše ze zadaného programu v jazyce C všechny řetězce. Uvažujte řetězce obsahující escape sekvence pro řídicí znaky (např. \n), speciální znaky (např. \") a znaky zadané osmičkovým (např. \377) nebo šestnáctkovým (např. \x7f) kódem. "//" { int ch; while( input()!= '\n' ) ; 3. Vytvořte pomocí programu lex program, který ze zadaného programu v jazyce C odstraní všechny komentáře a nahradí je mezerou. Uvažujte rovněž situaci, kdy je otevírací komentářová závorka součástí řetězce, např. v příkazu printf("/* %s */", str); Konstruktory překladačů 15 Konstruktory překladačů 16 4
Specifikace synt. analyzátoru Deklarace deklarace pravidla uživatelské funkce %term NUM e : NUM e + e void main() { yyparse(); Uživatelské deklarace %{ int promenna; % Deklarace terminálních symbolů, jejich priority a asociativity %term NUM ID Deklarace startovacího neterminálu %start e Deklarace atributů Konstruktory překladačů 17 Konstruktory překladačů 18 Terminální symboly jednoznakové term. symboly - + ; pojmenované term. symboly - %term ID RELOP generické symboly identifikátor, číslo, řetězec,... třídy symbolů relační operátory, aditivní operátory, multiplikativní operátory,... kódy se přidělují automaticky %term ID NUM STR IF THEN ELSE %term RELOP ADDOP MULOP Pravidla gramatiky e : t + e t ; t : f * t f ; f : ( e ) NUM ; neterminál vše, co není deklarováno jako terminální symbol startovací neterminál podle prvního pravidla nebo %start e Konstruktory překladačů 19 Konstruktory překladačů 20 5
Příklad Asociativita %term NUM ADDOP MULOP e : e ADDOP t t t : t MULOP f f f : '(' e ')' NUM e + e + e ~~~~~^ %left + %right + %nonassoc + -> reduce -> shift -> error Konstruktory překladačů 21 Konstruktory překladačů 22 Priorita Příklad e + e * e ^ %right '=' %left '+' '-' %left '*' '/' %right '^' pr(+)>pr(*) -> reduce pr(+)<pr(*) -> shift nízká priorita vysoká priorita %term NUM %left + - %left * / %left UMINUS e : e '+' e e '-' e e '*' e e '/' e '-' e %prec UMINUS '(' e ') ' NUM Konstruktory překladačů 23 Konstruktory překladačů 24 6
Sémantické akce e: e + e { printf( e->e+e\n ); Akce se provádějí během redukce a: a { a => a: a b a b: { Akce uvnitř pravidel se převedou na neterminál s e-pravidlem a akcí na konci Tato úprava může způsobit konflikty Konflikty způsobené akcemi uvnitř pravidel A -> a { a_1(); b c { -> B ^ a { a_2(); b d { -> C ^ Konflikt redukce/redukce B -> e / C -> e Konstruktory překladačů 25 Konstruktory překladačů 26 Konstruktor javacc Generuje lexikální a syntaktický analyzátor v jazyce Java Lexikální analýza - regulární výrazy Syntaktická analýza - rekurzivní sestup, LL(k) gramatika v EBNF (rozšířená BNF s regulárními operátory) java -cp JavaCC.zip COM.sun.labs.javacc.Main Calc.jj Formát specifikace /* volby generátoru */ options { IGNORE_CASE = true; DEBUG_PARSER = true; /* třída analyzátoru */ PARSER_BEGIN(Calc) public class Calc { public static void main(string args[]) throws ParseException { Calc parser = new Calc(System.in); parser.expr(); PARSER_END(Calc) Konstruktory překladačů 27 Konstruktory překladačů 28 7
Formát specifikace /* lexikální analyzátor */ SKIP : { " " "\r" "\t" TOKEN : { < EOL: "\n" > TOKEN : { <ADD: "+"> <SUB: "-"> <MUL: "*"> <DIV: "/"> <MOD: "mod"> TOKEN : { <CONSTANT: ( <DIGIT> )+ > <#DIGIT: ["0" - "9"]> TOKEN : { < SEMICOLON: ";" > Formát specifikace /* syntaktický analyzátor */ void expr() : { { term() (( "+" "-" ) term())* void term() : { { factor() (("*" "/" "mod") factor())* void factor() : { { <CONSTANT> "(" expr() ")" Konstruktory překladačů 29 Konstruktory překladačů 30 8