input läs tecken stoppa tillbaka ett tecken skicka ett token och dess attribut parser Eliminera white space och kommentarer Gruppera lästa tecken till tokens identifierare, nyckelord, numeriska konstanter, strängar, operatorer Problem i tex FORTRAN, jämför DO 5 I = 1.25 DO 5 I = 1,25 specifikation av tokens mha reguljära uttryck bl a tillståndstabell för ändlig automat lex källkod lex.l lexikalanalysator lexkompilator lex.yy.c lex.yy.c C- kompilator a.out input a.out sekvens av tokens
Används oftast tillsammans med Yacc GNUs varianter heter Flex och Bison (ev gflex, gbison) Den speciella variabeln yylval definieras av Yacc Lex genererar en lexikalanalysator som heter yylex()så varje gång man vill ha ett token anropar man denna funktion. För att få med en del nödvändiga saker ska programmet länkas med lex-biblioteket. gcc lex.yy.c ll Ett lexprogram består av tre delar: deklarationer översättningsregler hjälpfunktioner kodat i C, direkt till lex.yy.c deklarationer kan vara %{ konstanter variabler %} reguljära definitioner, tex letter [A-Za-z]
översättningsregler reguljärt uttryck, mönster för ett token C-kod, utförs när p 2 matchar input. Returnerar vanligen token till parsern. En global variabel yylval används för att returnera attribut. yytextpekar på första bokstaven i lexemet yyleng längden på lexemet Om flera mönster matchar 1) Längsta lexeme väljs (t ex väljer <= och inte <) 2) Första mönstret väljs Lookaheadoperatorn Mönstret r1/r2 betyder matcha r1 endast om match för r2 följer därefter.
Vissa tecken har speciell betydelse och måste omges av om man vill använda dem i sina mönster. (oftast fungerar \ också, tex \ ). $ ^ [ ] -? * + ( ) / { } < > Det mesta ser ut som vanliga reguljära uttryck, fast det finns en del utökningar, några exempel: [abc] [a-d] a b c a b c d. [0-9]+. [0-9]+ ^ $ ^ [ [ ] \ [^\ ]*\ {letter} %{ #include <strings.h> #define HELTAL 200 #define RELOP 201 #define LE 100 #define NE 101 #define LT 102 #define EQ 103 #define GE 104 #define GT 105 static int yylval; /* for standalone use */ %} /* Reguljära definitioner */ delim [ \t\n] ws {delim}+ digit [0-9] integer {digit}+ other.
{ws} { /* No action and no return */ } {integer} { yylval = atoi(yytext); return HELTAL; } "<" { yylval = LT; return RELOP; } "<=" { yylval = LE; return RELOP; } "=" { yylval = EQ; return RELOP; } "<>" { yylval = NE; return RELOP; } ">" { yylval = GT; return RELOP; } ">=" { yylval = GE; return RELOP; } {other} { /* ignore everything else */ }! int main(int argc, char *argv[]) { int ch; do { ch = yylex(); if (ch == HELTAL) { printf("tal = %d\n", yylval); } else if (ch == RELOP) { switch (yylval) { case LE: printf("mindre än el lika med\n"); break; case NE: printf("skiljt ifrån\n"); break; case LT: printf("mindre än\n"); break; case EQ: printf("lika med\n"); break; case GE: printf("större än el lika med\n"); break; case GT: printf("större än\n"); break; } } } while (1); }
" Skapa filen på Unix-systemet och använd lex, kompilera och kör. % lex exempel.l % gcc lex.yy.c ll % a.out bbbbb12bbbb tal = 12 <= mindre än el lika med #$$#%&& Yacc specification translate.y Yacc compiler y.tab.c y.tab.c C compiler a.out Input a.out Output Specificationen består av tre delar: y.tab.c
#$$ %{ C-deklarationer, direkt till y.tab.c %} Deklarationer av grammatiksymboler %token NUM (terminalsymboler från lexikal- %token ID analysator måste deklareras) %start program (grammatikens startsymbol) %left PLUS (vänsterass., högre prioritet) %left MULT Vid konflikter 1) shift-reduce - skifta 2) reduce-reduce - reducera enligt första produktionen #$$ ' Högersida: både terminaler och icketerminaler <left side> : <alt 1> {semantic action 1} <alt 2> {semantic action 2} icketerminal <alt n> {semantic action n} ; Allt som inte deklarerats som %token tolkas som icketerminaler Varje terminalsymbol har ett värde (returneras från lex i yylval) som kan användas i de semantiska reglerna (actions). Även icketerminaler kan ges värden. En följd av C-satser. Utförs när motsvarande produktion reduceras
#$$ ' $$ är vänstersidans värde och $i är värdet av den i:te symbolen på högersidan Defaultaction: { $$ = $1; } Semantiska regler kan också placeras mitt ibland högersidans symboler. A : B {z = 10;} C {x = $3;} Eftersom inget bibliotek liby.a (-ly), som för lex, finns måste följande rutiner finnas (t ex i tredje delen av yaccfilen) yyerror() main() $$ Minimala nödvändiga funktioner int main(int argc, char *argv[]) { return yyparse(); } int yyerror(char *s) { fprintf(stderr, %s\n, s); } yyparse(): Drivrutin för parsningen, skapas av yacc Anropar yylex (som skapas av lex) Returnerar 0 om allt OK, 1 om fel. yydebug: variabel med defaultvärde 0. Debugutskrifter som visar exakt vad parsern gör om 0. yychar: variabel som innehåller numret på senast lästa token (lookahead). Yacc tilldelar varje token ett nummer.
#$$ Om man anger optionen d vid anropet av yacc genereras en header-file y.tab.c som innehåller definitioner av de tokens som deklareras i yaccfilen, och de typer av värden som som symboler kan anta. Kan behöva inkluderas i vissa filer. #$$ Översättning från infix notation till postfix (beräkna även uttryckets värde) start expr \n expr expr + expr {print( + )} expr * expr {print( * )} ( expr ) num {print(num.value)} name {print(name.name)}
%{ #include y.tab.h %} delim [ \t] ws {delim}+ digit [0-9] int {digit}+ id [a-z] #$$ () {int} { yylval.value = atoi(yytext); return NUM; } [\+\*\(\)\n] { return yytext[0]; } {id} { yylval.id.value = yytext[0]-'a'+1; strcpy(yylval.id.name, yytext); return NAME; } {ws} { /* No action and no return */ } #$$ () %{ #include <string.h> #include <stdio.h> #include <stdlib.h> extern int yylex(); extern int yyparse(); extern int yyerror(const char *msg); %} %union { int value; struct { int value; char name[256]; } id; } %token <id> NAME %token <value> NUM %left '+' %left '*' %type <value> expr
#$$ () start : expr '\n { printf("\nval = %d\n", $1); exit(0); } ; expr : expr '+' expr { $$ = $1 + $3; printf("%c ", '+'); } expr '*' expr { $$ = $1 * $3; printf("%c ", '*'); } '(' expr ') { $$ = $2;} NUM { $$ = $1; printf("%d ", $1); } NAME { $$ = $1.value; printf("%s ", $1.name); } ; #$$ ()! int yyerror(const char *msg) { fprintf(stderr, "Error: %s\n", msg); return 0; } int main(int argc, char **argv) { yyparse(); return 0; }
#$$ ( % lex infix.l % yacc d infix.y % gcc y.tab.c lex.yy.c ll % a.out 12+3*4 12 3 4 * + val = 24 % a.out 10+b 10 b + val = 12 %