質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.45%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

コンパイラ

コンパイラは、プログラミング言語で記述したソースコードを、コンピュータの実行形式であるオブジェクトコードに変換するプログラムです。

Q&A

解決済

4回答

1531閲覧

C言語の自作コンパイル while文の表現

j0ker_9

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

コンパイラ

コンパイラは、プログラミング言語で記述したソースコードを、コンピュータの実行形式であるオブジェクトコードに変換するプログラムです。

0グッド

2クリップ

投稿2022/07/02 01:15

C言語の自作コンパイラを作成したいと思っています。以下のlex, yacc, cのコードを用いてwhile文を表現したいのですが、うまくいきません。test.c の T_WHILEの部分がおかしいのかなと思うのですが、どのように変更したらよいか教えていただきたいです。

test.c

1#include <stdio.h> 2struct stab { 3 int val; 4 char name[20]; } stab[100]; 5int stabuse = 0; 6struct node { 7 int type, left, right; } ntab[400]; 8int ntabuse = 1; 9#define T_STLIST 1 10#define T_ASSIGN 2 11#define T_READ 3 12#define T_PRINT 4 13#define T_ADD 5 14#define T_SUB 6 15#define T_MUL 7 16#define T_DIV 8 17#define T_REM 9 18#define T_NUM 10 19#define T_VAR 11 20#define T_WHILE 12 21#define T_IF 13 22#define T_LT 14 23#define T_GT 15 24int lookup(char*); 25int node(int, int, int); 26void dotree(int); 27extern char *yytext; 28#include "y.tab.c" 29#include "lex.yy.c" 30 31int main() { 32 yyparse(); 33 return 0; 34} 35int lookup(char *s) { 36 int i; 37 for(i = 0; i < stabuse; ++i) 38 if(strcmp(stab[i].name, s) == 0) return i; 39 if(stabuse >= 99) { 40 printf("table overflow.\n"); exit(1); } 41 strcpy(stab[stabuse].name, s); return stabuse++; 42} 43int node(int t, int l, int r) { 44 int i = ntabuse++; 45 ntab[i].type = t; 46 ntab[i].left = l; 47 ntab[i].right = r; 48 return i; 49} 50void dotree(int i) { 51 int stk; 52 printf(" .section .rodata\n"); 53 printf(".Lprompt: .string\"> \"\n"); /* プロンプト */ 54 printf(".Lread: .string\"%%ld\"\n"); /* 読み取り用書式 */ 55 printf(".Lprint: .string\"%%ld\\n\"\n"); /* 書き出し用書式 */ 56 printf(" .text\n"); 57 printf(".global main\n"); 58 printf("main:\n"); 59 printf(" pushq %%rbp\n"); 60 printf(" movq %%rsp,%%rbp\n"); 61 stk = (8*stabuse + 15) / 16; /* 変数の個数*8 で 16 の倍数へ切り上げ */ 62 stk *= 16; 63 printf(" subq $%d,%%rsp\n", stk); 64 emittree(i); 65 printf(" leave\n"); 66 printf(" ret\n"); 67} 68 69void emittree(int i) 70{ 71 static int labelno = 1; 72 int l; 73 switch(ntab[i].type) { 74 case T_STLIST: if(ntab[i].left) emittree(ntab[i].left); 75 emittree(ntab[i].right); 76 break; 77 case T_READ: printf(" movq $.Lprompt,%%rdi\n"); 78 printf(" movq $0,%%rax\n"); /* 浮動小数点レジスタを使わない */ 79 printf(" call printf\n"); 80 printf(" leaq %d(%%rbp),%%rsi\n", -(ntab[i].left+1)*8); 81 printf(" movq $.Lread,%%rdi\n"); 82 printf(" movq $0,%%rax\n"); /* 浮動小数点レジスタを使わない */ 83 printf(" call scanf\n"); 84 break; 85 case T_PRINT: emittree(ntab[i].left); 86 printf(" popq %%rsi\n"); 87 printf(" movq $.Lprint,%%rdi\n"); 88 printf(" movq $0,%%rax\n"); /* 浮動小数点レジスタを使わない */ 89 printf(" call printf\n"); 90 break; 91 case T_NUM: printf(" pushq $%d\n", ntab[i].left); 92 break; 93 case T_VAR: printf(" pushq %d(%%rbp)\n", -(ntab[i].left+1)*8); 94 break; 95 default: printf("NotImplemented: %d\n", ntab[i].type); 96 break; 97 case T_ASSIGN: emittree(ntab[i].right); 98 printf(" popq %d(%%rbp)\n", -(ntab[i].left+1)*8); 99 break; 100 case T_ADD: emittree(ntab[i].left); 101 emittree(ntab[i].right); 102 printf(" popq %%rdx\n"); 103 printf(" popq %%rax\n"); 104 printf(" addq %%rdx,%%rax\n"); 105 printf(" pushq %%rax\n"); 106 break; 107 case T_SUB: emittree(ntab[i].left); 108 emittree(ntab[i].right); 109 printf(" popq %%rdx\n"); 110 printf(" popq %%rax\n"); 111 printf(" subq %%rdx, %%rax\n"); 112 printf(" pushq %%rax\n"); 113 break; 114 case T_MUL: emittree(ntab[i].left); 115 emittree(ntab[i].right); 116 printf(" popq %%rdx\n"); 117 printf(" popq %%rax\n"); 118 printf(" imulq %%rdx\n"); 119 printf(" pushq %%rax\n"); 120 break; 121 case T_DIV: emittree(ntab[i].left); 122 emittree(ntab[i].right); 123 printf(" popq %%rsp\n"); 124 printf(" popq %%rax\n"); 125 printf(" movq $0,%%rdx\n"); 126 printf(" idivq %%rsp\n"); 127 printf(" pushq %%rax\n"); 128 break; 129 case T_REM: emittree(ntab[i].left); 130 emittree(ntab[i].right); 131 printf(" popq %%rsp\n"); 132 printf(" popq %%rax\n"); 133 printf(" movq $0,%%rdx\n"); 134 printf(" idivq %%rsp\n"); 135 printf(" pushq %%rdx\n"); 136 case T_LT: emittree(ntab[i].left); 137 emittree(ntab[i].right); 138 printf(" popq %%rcx\n"); 139 printf(" popq %%rax\n"); 140 printf(" cmp %%rcx,%%rax\n"); 141 printf(" jge "); 142 break; 143 case T_GT: emittree(ntab[i].left); 144 emittree(ntab[i].right); 145 printf(" popq %%rcx\n"); 146 printf(" popq %%rax\n"); 147 printf(" cmp %%rcx,%%rax\n"); 148 printf(" jle "); 149 break; 150 case T_IF: l = labelno++; 151 emittree(ntab[i].left); 152 printf(".L%d\n", l); 153 emittree(ntab[i].right); 154 printf(".L%d:\n", l); 155 break; 156 case T_WHILE:      // この部分 157  l = labelno++;  // ラベル番号更新 158 printf(".L%d:\n", l); // ラベル l 生成 159 emittree(ntab[i].left); // 分岐条件判定 160 printf(".L%d\n", l+1); // 不成立なら l+1に移動 161 emittree(ntab[i].right); // 成立していたら、中身を実行 162 printf(".L%d\n", l); // 前のラベル l に戻る 163 printf(".L%d:\n", l+1); // ラベル l+1 生成 164 break; 165 } 166}

test.yacc

1%token NUM; 2%token IDENT; 3%token READ; 4%token PRINT; 5%token WHILE; 6%token IF; 7%left '+' '-'; 8%left '*' '/'; 9%% 10prog : IDENT '{' stlist'}' { dotree($3); return 0; } 11; 12stlist : { $$ = 0; } 13| stlist stat { $$ = node(T_STLIST, $1, $2); } 14; 15stat : var '=' expr ';' { $$ = node(T_ASSIGN, $1, $3); } 16| READ var ';' { $$ = node(T_READ, $2, 0); } 17| PRINT expr ';' { $$ = node(T_PRINT, $2, 0); } 18| WHILE '(' cond ')' stat { $$ = node(T_WHILE, $3, $5); } 19| IF '(' cond ')' stat { $$ = node(T_IF, $3, $5); } 20| '{' stlist '}' { $$ = $2; } 21; 22 23cond : expr '<' expr { $$ = node(T_LT, $1, $3); } 24| expr '>' expr { $$ = node(T_GT, $1, $3); } 25; 26expr : term { $$ = $1; } 27| expr '+' term { $$ = node(T_ADD, $1, $3); } 28| expr '-' term { $$ = node(T_SUB, $1, $3); } 29; 30term: prim{ $$ = $1; } 31| term '*' prim { $$ = node(T_MUL, $1, $3); } 32| term '/' prim { $$ = node(T_DIV, $1, $3); } 33| term '%' prim { $$ = node(T_REM, $1, $3); } 34; 35 36prim : NUM { $$ = node(T_NUM, atoi(yytext), 0); } 37| var { $$ = node(T_VAR, $1, 0); } 38| '(' expr ')' { $$ = $2; } 39; 40var : IDENT { $$ = lookup(yytext); } 41;

test.lex

1alpha [a-zA-Z] 2digit [0-9] 3white [\n\t ] 4%% 5while { return WHILE; } 6if { return IF; } 7read { return READ; } 8print { return PRINT; } 9{alpha}({alpha}|{digit})* { return IDENT; } 10{digit}+ { return NUM; } 11[-+()=;{}<>*/%] { return yytext[0]; } 12{white} { ; }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

itagagaki

2022/07/02 02:07

> うまくいきません これでは伝わりません。何がどうなるのですか? whileが含まれなければ期待どおりの結果になるのですか?
j0ker_9

2022/07/02 02:31

----------------- main { read x; i = 0; while(x > 0){ i = i + x; x = x - 1; } print i; ----------------- このようなwhile文を含んだファイルを作成しコンパイルすると、以下のように表示されます。 >cc test4.s test4.s: Assembler messages: test4.s:41: Error: unknown pseudo-op: `.l1' whileではない場合(ifや加算など)の場合はうまく動作します。
melian

2022/07/02 04:13

printf(".L%d\n", l); // 前のラベル l に戻る ですが、ここは jmp 命令ではないでしょうか。 printf("jmp .L%d\n", l); // 前のラベル l に戻る
guest

回答4

0

解決済みになっていますが、解決していません。次のコードがエラーになります。

C

1main { 2 i = 0; 3 while (i < 5) { 4 print i; 5 i = i + 1; 6 } 7 i = 3; 8 while (i > 0) { 9 print i; 10 i = i - 1; 11 } 12}

投稿2022/07/19 10:43

kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

わかりました

C

1 emittree(ntab[i].right); // 成立していたら、中身を実行 2 printf(".L%d\n", l); // 前のラベル l に戻る

ここではntab[i].right(ステートメントまたはブロック)は条件分岐オペコードを出力しないわけですから、

C

1 emittree(ntab[i].right); // 成立していたら、中身を実行 2 printf("jmp .L%d\n", l); // 前のラベル l に無条件ジャンプ

じゃないですか?

投稿2022/07/02 04:12

itagagaki

総合スコア8402

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

j0ker_9

2022/07/02 04:19

長いコードの解析ありがとうございました。とても助かりました。
guest

0

whileはラベル2つつかうから、l = labelno++では足りない気がするけど、これは本質ではなさそう。
whileのボディが「 emittree(ntab[i].right); // 成立していたら、中身を実行」で解釈されるわけだけど、その最後はjmpを出力しないのでは。なので 「 printf(".L%d\n", l); // 前のラベル l に戻る」ここのラベルだけ生えてきてエラーになる。
「printf("jmp .L%d\n", l); // 前のラベル l に戻る」かな。

投稿2022/07/02 03:25

matukeso

総合スコア1608

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

おそらくwhileだけでなくifもダメなのでは。

printf(".L%d\n", l+1); // 不成立なら l+1に移動

これアセンブラ命令になっていませんね。
たとえばx86アセンブラなら、条件付きのジャンプとしてjzjcjojsがあります。
無条件ジャンプならjmpです。

「不成立なら」の条件はどこで得られるのでしょうか。その仕組みが入っていないように見えます。

投稿2022/07/02 02:40

itagagaki

総合スコア8402

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

j0ker_9

2022/07/02 02:49

case T_IF: l = labelno++; emittree(ntab[i].left); printf(".L%d\n", l); emittree(ntab[i].right); printf(".L%d:\n", l); break; 条件式では、まずラベル番号を用意します。(emittree() を呼び出して) 条件判定命令 と分岐命令の一部を生成してから、成り立たなかった時の飛び先を埋めます。ついで中身 の文を生成し、その後でラベルを生成します。では、試してみましょう。 ----------------------- while以外の部分はほとんどコピペであり、if文の説明は上記のように書かれていました。実際に以下のようなif文を動かしてみましたが正常に動作しました。そのため、問題はwhile文のみかと思います。 実際に動かしたif文 main { read x; if(x > 0) print 1; if(x < 0) print 0; }
itagagaki

2022/07/02 02:55 編集

T_WHILEで emittree(ntab[i].left); で条件式の評価を行って、その先で最終的に T_GT 等でオペコード printf(" jle "); が出力されることが期待されていて、帰って来てから printf(".L%d\n", l+1); // 不成立なら l+1に移動 でオペランド(ジャンプ先ラベル)を出力しているのですね。 T_GTには到達しているのでしょうか? 出力された .s ファイル全体はどうなっているのですか?
j0ker_9

2022/07/02 03:24

出力された.sファイルは以下のようになっています。長文になりますがよろしくお願いいたします。 .section .rodata .Lprompt: .string"> " .Lread: .string"%ld" .Lprint: .string"%ld\n" .text .global main main: pushq %rbp movq %rsp,%rbp subq $16,%rsp movq $.Lprompt,%rdi movq $0,%rax call printf leaq -8(%rbp),%rsi movq $.Lread,%rdi movq $0,%rax call scanf pushq $0 popq -16(%rbp) .L1: pushq -8(%rbp) pushq $0 popq %rcx popq %rax cmp %rcx,%rax jle .L2 pushq -16(%rbp) pushq -8(%rbp) popq %rdx popq %rax addq %rdx,%rax pushq %rax popq -16(%rbp) pushq -8(%rbp) pushq $1 popq %rdx popq %rax subq %rdx, %rax pushq %rax popq -8(%rbp) .L1 .L2: pushq -16(%rbp) popq %rsi movq $.Lprint,%rdi movq $0,%rax call printf leave ret
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.45%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問