%{ /* C宣言部 ─ 動作記述の中で用いる関数の定義や宣言 */ #define YYERROR_VERBOSE 1 /* エラーメッセージを親切にする */ #define YYSTYPE char* /* トークンの属性の型を宣言 --- このプログラムでは文字列型 */ #include #include /* malloc 関数を使うため */ #include /* strlen 関数を使うため */ #include /* 可変引数を使うため */ void yyerror(char* s) { /* エラーがあった時に呼ばれる関数を定義しておく */ fprintf(stderr, "%s\n", s); } int yylex(void); /* flex で生成される関数のプロトタイプ宣言 */ char* newlabel(void) { /* 新しいラベル名を生成する関数 */ static int counter = 0; /* 静的変数: 次に呼ばれた時に以前の値を保持している */ char* ret = (char*)malloc(5 * sizeof(char)); sprintf(ret, "l%03d", counter++); return ret; } /* 文字列のフォーマットを行なう関数: mysprintf printf と似ているが、出力する代わりに戻り値として返す。 アクション中で、printf を使って Oolong の命令を出力すると、 思い通りの順番にするのが難しいので、属性値として計算し、 最後にまとめて出力する。 警告: この関数は、メモリを大量にムダ使いするので、 実用に供するプログラムには使ってはダメ */ char* mysprintf(char* format, ...) { va_list arg; int len; char* ret; va_start(arg, format); len = vsnprintf(NULL, 0, format, arg); va_end(arg); ret = (char*)malloc((len + 1) * sizeof(char)); va_start(arg, format); vsprintf(ret, format, arg) ; va_end(arg); return ret; } /* ==, !=, >, >=, <=, < に直接対応する命令は JVM にないので、 いくつかの命令を組み合わせて定義する。 */ /* == */ char ieq[] = "isub\n" "invokestatic java/lang/Math/abs(I)I\n" "iconst_1\n" "invokestatic java/lang/Math/min(II)I\n" "iconst_1\n" "swap\n" "isub\n"; /* != */ char ine[] = "isub\n" "invokestatic java/lang/Math/abs(I)I\n" "iconst_1\n" "invokestatic java/lang/Math/min(II)I\n"; /* > */ char igt[] = "isub\n" "iconst_0\n" "invokestatic java/lang/Math/max(II)I\n" "iconst_1\n" "invokestatic java/lang/Math/min(II)I\n"; /* < */ char ilt[] = "swap\n" "isub\n" "iconst_0\n" "invokestatic java/lang/Math/max(II)I\n" "iconst_1\n" "invokestatic java/lang/Math/min(II)I\n"; /* >= */ char ige[] = "isub\n" "iconst_1\n" "iadd\n" "iconst_0\n" "invokestatic java/lang/Math/max(II)I\n" "iconst_1\n" "invokestatic java/lang/Math/min(II)I\n"; /* <= */ char ile[] = "swap\n" "isub\n" "iconst_1\n" "iadd\n" "iconst_0\n" "invokestatic java/lang/Math/max(II)I\n" "iconst_1\n" "invokestatic java/lang/Math/min(II)I\n"; /* PRINT */ char iprint[] = "dup\n" "getstatic java/lang/System/out Ljava/io/PrintStream;\n" "swap\n" "invokevirtual java/io/PrintStream/print(I)V\n"; /* PRINTLN (= PRINT + 改行)*/ char iprintln[] = "dup\n" "getstatic java/lang/System/out Ljava/io/PrintStream;\n" "swap\n" "invokevirtual java/io/PrintStream/println(I)V\n"; %} /* Bison 宣言部 */ /* 終端記号の宣言 */ %token NUM %token VAR %token IF %token ELSE %token PRINT %token PRINTLN %token EQ %token NE %token LE %token GE /* 演算子の優先順位と結合性の宣言 */ /* left は左結合を表す。右結合は right, 非結合は nonassoc となる。 */ %right '=' %nonassoc EQ NE %nonassoc '<' LE GE '>' %left '+' '-' %left '*' '/' '%' /* 下の演算子ほど優先順位が高い。 */ %% /* 文法記述とそれに対する動作 */ /* $$ は左辺の値、$n は右辺の n 番目の文法要素の値を表す。 */ program : statementSeq { printf("%s", $1); } ; statement : IF '(' expr ')' statement ELSE statement { char* l1 = newlabel(); char* l2 = newlabel(); $$ = mysprintf("%s%s%s%s%s%s%s", $3, mysprintf("ifeq %s\n", l1), $5, mysprintf("goto %s\n", l2), mysprintf("%s:\n", l1), $7, mysprintf("%s:\n", l2)); } | expr ';' { $$ = mysprintf("%s%s", $1, "pop\n"); } | '{' statementSeq '}' { $$ = $2; } ; statementSeq : { $$ = ""; } /* 空 */ | statementSeq statement { $$ = mysprintf("%s%s", $1, $2); } ; expr : NUM { $$ = mysprintf("ldc %s\n", $1); } | VAR { $$ = mysprintf("iload %s\n", $1); } | VAR '=' expr { $$ = mysprintf("%s%s%s", $3, "dup\n", mysprintf("istore %s\n", $1)); } | expr '+' expr { $$ = mysprintf("%s%s%s", $1, $3, "iadd\n"); } | expr '-' expr { $$ = mysprintf("%s%s%s", $1, $3, "isub\n"); } | expr '*' expr { $$ = mysprintf("%s%s%s", $1, $3, "imul\n"); } | expr '/' expr { $$ = mysprintf("%s%s%s", $1, $3, "idiv\n"); } | expr '%' expr { $$ = mysprintf("%s%s%s", $1, $3, "irem\n"); } | expr EQ expr { $$ = mysprintf("%s%s%s", $1, $3, ieq); } | expr NE expr { $$ = mysprintf("%s%s%s", $1, $3, ine); } | expr '<' expr { $$ = mysprintf("%s%s%s", $1, $3, ilt); } | expr LE expr { $$ = mysprintf("%s%s%s", $1, $3, ile); } | expr GE expr { $$ = mysprintf("%s%s%s", $1, $3, ige); } | expr '>' expr { $$ = mysprintf("%s%s%s", $1, $3, igt); } | PRINT '(' expr ')' { $$ = mysprintf("%s%s", $3, iprint); } | PRINTLN '(' expr ')' { $$ = mysprintf("%s%s", $3, iprintln); } | '(' expr ')' { $$ = $2; } ; %% /* 追加の C プログラム */ /* Oolong のテンプレートを生成する */ void mae(void) { int i, n = 5; /* n は用意する変数の個数 */ printf(".super java/lang/Object\n"); printf("\n"); printf(".method public static main([Ljava/lang/String;)V\n"); /* スタックの最大の大きさを指定する。 クラス実行時に Stack size too large というエラーメッセージが出る時は、 下の``16''をより大きな数に変更すること。 */ printf(".limit stack 16\n"); printf(".catch java/lang/ArrayIndexOutOfBoundsException from begin to end using handler\n"); /* JVM の変数 1〜5 を 0 に初期化する。 */ for (i = 1; i <= n; i++) { printf("iconst_0\n"); printf("istore %d\n", i); } /* 1〜5 番目のコマンドライン引数を整数に変換して、 JVM の変数 1〜5 に代入する。 */ printf("begin:\n"); for (i = 1; i <= n; i++) { printf("aload_0\n"); printf("ldc %d\n", i - 1); printf("aaload\n"); printf("invokestatic java/lang/Integer/parseInt(Ljava/lang/String;)I\n"); printf("istore %d\n", i); } printf("end: \n"); printf("goto exit\n"); printf("handler:\n"); printf("pop\n"); printf("exit:\n"); printf("\n"); } void ato(void) { /* Oolong のテンプレートを生成する */ printf("\n"); printf("return\n"); printf(".end method\n"); } extern FILE* yyin; int main(int argc, char** argv) { mae(); printf(" ; ↑↑↑の部分は、1〜5 番目のコマンドライン引数を変数 1〜5 に順に格納する。\n"); if (argc > 1) { yyin = fopen(argv[1], "r"); /* コマンドライン引数で受け取ったファイルを入力とする */ } printf(" ; ↓↓↓ この部分が Bison が生成した関数が出力するコード ↓↓↓\n"); yyparse(); /* yyparse は Bison が文法記述から生成する関数 */ printf(" ; ↑↑↑ この部分が Bison が生成した関数が出力するコード ↑↑↑\n"); ato(); return 0; }