%{ /* 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; }