%{ /* import 宣言はここに書く */ import static java.lang.Character.isDigit; import java.io.IOException; import java.io.PushbackInputStream; %} // 生成されるクラス名 %class MyParser // 次のトークンを返す式 %next yylex() // 直前のトークンを表す式 %get token // 属性値の型と式 %semantic double: yylval %token NUMBER EOL /* jacc cannot handle '\n' properly */ // 演算子の優先順位 %left '+' '-' %left '*' '/' %% /* 文法記述とそれに対する動作(還元時に実行されるプログラム) */ /* 通常の BNF の → の代わりに : を書く。各 BNF は ; で区切る。 */ /* 最初に start symbol を書く */ input : /* 空 */ | input line EOL {} ; /* $$ は左辺の属性値、$n は右辺の n 番目の文法要素の属性値を表す。 */ line : { System.exit(0); } | expr { System.out.printf("\t%g%n", $1); } ; expr : expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; } | '(' expr ')' { $$ = $2; } | NUMBER { $$ = $1; } ; %% /* フィールドやメソッドの定義はここに書く */ // エラーのときに呼ばれるメソッド private void yyerror(String msg) { System.out.println("ERROR: " + msg); System.exit(1); } private int token; private double yylval; // unread()(C の ungetc())をするため private PushbackInputStream in = new PushbackInputStream(System.in); // scanf("%lf", ...) の代わりのメソッド double myParseDouble() throws IOException { double ret = 0; int c; while (isDigit(c=in.read())) { ret = 10 * ret + (c-'0'); } if (c!='.') { in.unread(c); return ret; } double x = 0.1; while (isDigit(c=in.read())) { ret += x * (c-'0'); x *= 0.1; } in.unread(c); return ret; } // yylex(字句解析)メソッドを jflex を使わず定義している。yylex の戻り値は、トークンの種類。 int yylex() { try { int c; do { c = in.read(); } while (c == ' ' || c == '\t' || c == '\r'); if (isDigit (c) || c == '.') { in.unread(c); yylval = myParseDouble(); /* トークンの属性値は yylval というフィールドに代入しておく。*/ return token=NUMBER; /* NUMBER というトークンを返す。*/ } else if (c == '\n') { return token=EOL; } else if (c == -1) { return token=0; /* 終了を表す。*/ } /* 上のどの条件にも合わなければ、文字をそのまま返す。*/ return token=c; } catch (IOException e) { return token=0; } } public static void main(String[] args) { MyParser calc = new MyParser(); /* 変更する */ calc.yylex(); // 最初のトークンを読んでおく calc.parse(); // parse the input }