JCup / JFlex ● Compilergenerierungswerkzeuge in java ● Gestatten linksrekursive Grammatiken ● ● ● ● Erzeugen LALR(1)-Parser, in etwa vergleichbar mit yacc JFlex generiert den passenden Lexer Letzte getestete Version: JCup 11.a, 11b ● Liegt in Form von jar-Files vor ● Ist nun sehr einfach in der Handhabung Jflex: Das Installationsverzeichnis enthält lib/jflex-1.5.0.jar 1 JCUP/JLEX Vers.11 ● ● JCup Doku und Download: http://www2.cs.tum.edu/projects/cup/ Jflex http://www.jflex.de/ ● ● Mit Version jflex-1.6.0 haben die Beispielanwendungen nicht funktioniert. Ausweg ist Verwendung von Version 1.5 http://sourceforge.net/projects/jflex/files/jflex/1.5.0/ 2 Jcup/JFlex ● http://www2.cs.tum.edu/projects/cup/ ● Folgende Files werden benötigt: ● java-cup-11b.jar ● java-cup-11b-runtime.jar ● Jflex.jar bzw. jflex-1.5.0.jar 3 Bearbeitung eines Beispiels ● java -jar jflex.jar example.lex → Yylex.java ● java -jar java-cup-11b.jar < example.cup → parser.java sym.java ● javac -cp java-cup-11b-runtime.jar:. parser.java Yylex.java sym.java → parser.class ● java -cp java-cup-11a-runtime.jar:. parser 4 Bestandteile cup-File Package und import user code action code {: . . .:}; parser code {: . . .:}; init with {: . . .:}; scan with {:. . .:}; Symbollisten es werden die terminalen und nicht terminalen Symbole beschrieben. Die terminalen Symbole werden in einer Klasse sym.class abgelegt, die die Verbindung zw. Lex und Cup herstellt. Precedence-Declarations Die Reihenfolge von oben nach unten bestimmt die Priorität precedence left linksassoziativte Terminals precedence right rechtsassoziativen Terminals precedence nonasssoc Regeln 5 Regelteil ● ● Regeln werden in einer einfacher BNF ähnlichen Notation angegeben Werte, die mit Symbolen verbunden sind, werden über Namen, die dem Symbol durch einen Doppelpunkt folgen, angegeben. ● ● ● ● expr:e Auf diesen Bezeichner kann man in der nachgestellten Aktion Bezug nehmen. Der Typ ist in den Symboldeklarationen festgelegt und muss eine Klasse sein. (Integer, String …) Durch autoboxing und autounboxing ist eine explizite Konvertierung nach beispielsweise int unnötig. 6 Beispiel 1 expr, term, factor import java_cup.runtime.*; parser code {: public static void main(String args[])throws Exception { new parser(new Yylex(System.in)).parse(); } :} terminal SEMI, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN; terminal Integer NUMBER; non terminal expr_list, expr_part; non terminal Integer expr, term, fac; 7 Regelteil Wiederholung durch Endlosrekursion expr_part für Ausgabe expr_list ::= expr_list expr_part | expr_part; expr_part ::= expr:e {: System.out.println(" = "+e+";"); :} SEMI; expr ::= expr:l PLUS term:r {: RESULT=l.intValue() + r.intValue(); :} | expr:l MINUS term:r {: RESULT=l.intValue() ­ r.intValue(); :} | term:l {: RESULT=l.intValue(); :}; term ::= term:l TIMES fac:r {: RESULT=l.intValue() * r.intValue(); :} | term:l DIVIDE fac:r {: RESULT=l.intValue() / r.intValue(); :} | fac:l {: RESULT=l.intValue(); :}; fac ::= MINUS NUMBER:n {: RESULT=­n; :} | NUMBER:n {: RESULT=n; :} | LPAREN expr:e RPAREN {: RESULT=e; :}; 8 Der Lexer dazu import java_cup.runtime.Symbol; %% %cup %% ";" { return new Symbol(sym.SEMI); } "+" { return new Symbol(sym.PLUS); } "­" { return new Symbol(sym.MINUS); } "*" { return new Symbol(sym.TIMES); } Class Integer "/" { return new Symbol(sym.DIVIDE); } "(" { return new Symbol(sym.LPAREN); } ")" { return new Symbol(sym.RPAREN); } [0­9]+ { return new Symbol(sym.NUMBER, new Integer(yytext())); } [ \t\r\n\f] { /* ignore white space. */ } . { System.err.println("Illegal character: "+yytext()); } 9 Verbindung JCup / JLex sym.java / sym.class //­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ // The following code was generated by CUP v0.10k // Mon Jan 24 11:38:40 CET 2011 //­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ /** CUP generated class containing symbol constants. */ public class sym { /* terminals */ public static final int MINUS = 4; public static final int DIVIDE = 6; public static final int NUMBER = 9; public static final int SEMI = 2; public static final int EOF = 0; public static final int PLUS = 3; public static final int error = 1; public static final int RPAREN = 8; public static final int TIMES = 5; public static final int LPAREN = 7; } 10 import java_cup.runtime.*; Variante 2 parser code {: public static void main(String args[]) throws Exception { System.out.println("Start:"); new parser(new Yylex(System.in)).parse(); } :} /* Terminals (tokens returned by the scanner). */ terminal SEMI, PLUS, MINUS, TIMES, DIVIDE, MOD; terminal UMINUS, LPAREN, RPAREN; terminal Integer NUMBER; /* Non­terminals */ non terminal expr_list, expr_part; non terminal Integer expr; /* Precedences */ precedence left PLUS, MINUS; precedence left TIMES, DIVIDE, MOD; precedence left UMINUS; 11 /* The grammar */ expr_list ::= expr_list expr_part | expr_part ; expr_part ::= expr:e {: System.out.println("= " + e); :} SEMI ; expr ::= expr:e1 PLUS expr:e2 {: RESULT = e1.intValue() + e2.intValue(); :} | expr:e1 MINUS expr:e2 {: RESULT = e1.intValue() ­ e2.intValue(); :} | expr:e1 TIMES expr:e2 {: RESULT = e1.intValue() * e2.intValue(); :} | expr:e1 DIVIDE expr:e2 {: RESULT = e1.intValue() / e2.intValue(); :} | expr:e1 MOD expr:e2 {: RESULT = e1.intValue() % e2.intValue(); :} | NUMBER:n {: RESULT = n; :} | MINUS expr:e {: RESULT = 0 ­ e.intValue(); :} //%prec MINUS | LPAREN expr:e RPAREN {: RESULT = e; :} 12 ; j_pl0 import java_cup.runtime.*; parser code {: public static void main(String args[]) throws Exception { new parser(new Yylex(System.in)).parse(); } :} terminal SEMI, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN,OUTPUT, INPUT, ASSIGN, GT, LT, GE, LE, EQUAL, NE, DOT, COMMA, BEGIN, CALL, CONST, DO, ELSE, END, IF, ODD, PROC, THEN, VAR, WHILE, EOFile, YYEOF; terminal Integer NUM; terminal String Ident; non terminal program, block, blockDecl,constDecl,constList,varDecl,varList, procDeclList,procDecl,procHead,statement,statementList, ass,if1,while1, while2, exprList, expression, term1, term, factor, cmpOP, condition; 13 program ::= block DOT System.exit(1);:}; block {: System.out.println("all OK"); ::= blockDecl statement {: System.out.println("Block accepted");:}; blockDecl::= constDecl varDecl procDeclList {: :}; constDecl::= CONST constList SEMI {: :} | {: :}; constList::= constList COMMA Ident EQUAL NUM {: :} | Ident EQUAL NUM {: :}; varDecl ::= VAR varList SEMI {: :} | {: :}; varList ::= varList COMMA Ident {: :} | Ident {: :}; procDeclList::= procDeclList procDecl {: :} | {: :}; procDecl ::= procHead block SEMI {: :}; 14 procHead ::= PROC Ident SEMI {: :}; statement::= BEGIN statementList END | ass expression | if1 statement | while1 while2 statement | CALL Ident | INPUT Ident | OUTPUT expression {:System.out.println("Begin .. End accepted"); :} {: :} {: :} {: :} {: :} {: :} {:System.out.println("Output accepted");:}; statementList ::= statementList SEMI statement {:System.out.println("... epted"); :} | statement {:System.out.println("StaementList 2 accepted"); :}; ass if1 ::= Ident ASSIGN ::= IF condition THEN while1 ::= WHILE while2 ::= condition DO {: :}; {: :}; {: :}; {: :}; 15 expression::= term1 PLUS exprList | term1 MINUS exprList | term1 term1 | ::= MINUS term term {: :} {: :}; exprList::= exprList PLUS term | exprList MINUS term | term term ::= | | {:System.out.println("Expression accepted");:} {: :} {: :}; {:System.out.println("ExprList:ExprList + Term accepted"); :} {:System.out.println("ExprList:ExprList - Term accepted"); :} {:System.out.println("ExprList:Term accepted"); :}; term TIMES factor {:System.out.println("Term:Term * Fact accepted"); :} term DIVIDE factor {:System.out.println("Term:Term / Fact accepted"); :} factor {:System.out.println("Term:Fact accepted"); :}; factor::= NUM:x {:System.out.println("Fact Fact "+x+" accepted"); :} | Ident :x {:System.out.println("Fact Fact "+x+" accepted"); :} | LPAREN expression RPAREN {:System.out.println("Fact Fact (... ) accepted"); :}; cmpOP ::= EQUAL | NE | LT | GT | LE | GE {: :} {: :} {: :} {: :} {: :} {: :}; condition::= ODD expression {: :} | expression cmpOP expression {: :}; 16