コンパイラの電卓プログラム

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
toma

コンパイラの電卓プログラム

#1

投稿記事 by toma » 10年前

以下の課題を自分なりに考えたのですがまだ不足した部分があり、
うまくいかないのでご教授お願いします。
プログラムは課題の下に示します。(教科書のもの)
課題1
このプログラムが認識する構文全体を拡張BNFを用いて記しなさい。

私はこんな感じで考えております
   <statement>を開始記号にすると
<ステートメント> ::= <代入文>
| <プリント文>

<代入文> ::= <変数> '=' <式>

<プリント文> ::= '?' <式>


課題2
スタック計算でなく直接計算するように変更しなさい。
実行結果も添えること。

私の考えは以下になります。
スタックに値を残さないで直接結果をリターンするように各関数を書き直す。
void expression() ====> int expression()

int operate(Kind op, int d1, int d2) {

int d;
...

switch (op) {
case plus: d = d1+d2;
break;
case minus: d = d1-d2;
...


}
return d;
}


int expression() {
Kind op;
int result, result2;

result = term();
while (token.kind==Plus || token.kind==Minus) {
op = token.kind;
token = nextToken();
result2 = term();
result = operate(op, result, result2);
}
return result;
}

いろいろ足りない部分があると思うのでよろしくお願いします。

以下がソースコードになります。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef enum { /* トークンの種類 */
Print, Lparen, Rparen, Plus, Minus, Multi, Divi,
Assign, VarName, IntNum, EofTkn, Others
} Kind;

typedef struct {
Kind kind; /* トークンの種類 */
int val; /* 整数値 */
} Token;

void input(void);
void statement(void);
void expression(void);
void term(void);
void factor(void);
Token nextTkn(void);
int nextCh(void);
void operate(Kind op);
void push(int n);
int pop(void);
void chkTkn(Kind kd);

#define STK_SIZ 20 /* スタックサイズ */
int stack[STK_SIZ+1]; /* スタック */
int stkct; /* スタック管理 */
Token token; /* トークン格納 */
char buf[80], *bufp; /* 入力用 */
int ch; /* 取得文字を格納 */
int var[26]; /* 変数a-z */
int errF; /* エラー発生 */

int main(void)
{
while (1) {
input(); /* 入力 */
token = nextTkn(); /* 最初のトークン */
if (token.kind == EofTkn) exit(1); /* 終了 */
statement(); /* 文実行 */
if (errF) puts(" --err--");
}
return 0;
}

void input(void)
{
errF = 0; stkct = 0; /* 初期設定 */
buf[0] = '\0';
fgets(buf, 80, stdin); /* 80文字以内の入力 */
bufp = buf; /* 先頭文字に位置づけ */
ch = nextCh(); /* 最初の文字取得 */
}

void statement(void) /* 文 */
{
int vNbr;

switch (token.kind) {
case VarName: /* 代入文 */
vNbr = token.val; /* 代入先保存 */
token = nextTkn();
chkTkn(Assign); if (errF) break; /* '=' のはず */
token = nextTkn();
expression(); /* 右辺計算 */
var[vNbr] = pop(); /* 代入実行 */
break;
case Print: /* print文(?) */
token = nextTkn();
expression();
chkTkn(EofTkn); if (errF) break;
printf(" %d\n", pop());
return;
default:
errF = 1;
}
chkTkn(EofTkn); /* 行末チェック */
}

void expression(void) /* 式 */
{
Kind op;

term();
while (token.kind==Plus || token.kind==Minus) {
op = token.kind;
token = nextTkn(); term(); operate(op);
}
}

void term(void) /* 項 */
{
Kind op;

factor();
while (token.kind==Multi || token.kind==Divi) {
op = token.kind;
token = nextTkn(); factor(); operate(op);
}
}

void factor(void) /* 因子 */
{
switch (token.kind) {
case VarName: /* 変数 */
push(var[token.val]);
break;
case IntNum: /* 整数定数 */
push(token.val);
break;
case Lparen: /* ( 式 ) */
token = nextTkn();
expression();
chkTkn(Rparen); /* ) のはず */
break;
default:
errF = 1;
}
token = nextTkn();
}

Token nextTkn(void) /* 次トークン */
{
int num;
Token tk = {Others, 0};

while (isspace(ch)) /* 空白読み捨て */
ch = nextCh();
if (isdigit(ch)) { /* 数字 */
for (num=0; isdigit(ch); ch = nextCh())
num = num*10 + (ch-'0');
tk.val = num; /* 値格納 */
tk.kind = IntNum;
}
else if (islower(ch)) { /* 変数 */
tk.val = ch - 'a'; /* 変数番号0-25 */
tk.kind = VarName;
ch = nextCh();
}
else {
switch (ch) {
case '(': tk.kind = Lparen; break;
case ')': tk.kind = Rparen; break;
case '+': tk.kind = Plus; break;
case '-': tk.kind = Minus; break;
case '*': tk.kind = Multi; break;
case '/': tk.kind = Divi; break;
case '=': tk.kind = Assign; break;
case '?': tk.kind = Print; break;
case '\0': tk.kind = EofTkn; break;
}
ch = nextCh();
}
return tk;
}

int nextCh(void) /* 次の1文字 */
{
if (*bufp == '\0') return '\0'; else return *bufp++;
}

void operate(Kind op) /* 演算実行 */
{
int d2 = pop(), d1 = pop();

if (op==Divi && d2==0) { puts(" division by 0"); errF = 1; }
if (errF) return;
switch (op) {
case Plus: push(d1+d2); break;
case Minus: push(d1-d2); break;
case Multi: push(d1*d2); break;
case Divi: push(d1/d2); break;
}
}

void push(int n) /* スタック積込 */
{
if (errF) return;
if (stkct+1 > STK_SIZ) { puts("stack overflow"); exit(1); }
stack[++stkct] = n;
}

int pop(void) /* スタック取出 */
{
if (errF) return 1; /* エラー時は単に1を返す */
if (stkct < 1) { puts("stack underflow"); exit(1); }
return stack[stkct--];
}

void chkTkn(Kind kd) /* トークン種別確認 */
{
if (token.kind != kd) errF = 1; /* 不一致 */
}

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: コンパイラの電卓プログラム

#2

投稿記事 by みけCAT » 10年前

コードを提示する際は、BBcodeを有効にした状態でcodeタグで囲み、
かつきちんとインデントをしていただけると、見やすくてありがたいです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: コンパイラの電卓プログラム

#3

投稿記事 by beatle » 10年前

toma さんが書きました:いろいろ足りない部分があると思うのでよろしくお願いします。
というのはつまり、具体的に何処がダメなのかは自分で調べる気は無いが、でもダメな所はきっとあるので、皆さんで修正お願いします、ということでしょうか。
難しい要求ですし、丸投げに近い雰囲気を感じます。

「~~が分からないから教えて下さい」なら答えやすいです。
toma さんが書きました:うまくいかないのでご教授お願いします
「うまくいかない」だけだと情報がありませんので、「~となることを期待していたが、実際は…だった」のように書いてください。

かずま

Re: コンパイラの電卓プログラム

#4

投稿記事 by かずま » 10年前

コード:

<statement> ::= <assignment-statement> | <print-statement>
<assignment-statement> ::= <variable> '=' <expression>'
<print-statement> := '?' <expression>
<variable> ::= 'a' | 'b' | 'c' | ... | 'z'
<expression> ::= <term>
    | <expression> '+' <term>
    | <expression> '-' <term>
<term> ::= <factor>
    | <term> '*' <factor>
    | <term> '/' <factor>
<factor> ::= <number>
    | <variable>
    | '(' <expression> ')'
<number> ::= <digit>
    | <number> <digit>
<digit> ::= '0' | '1' | '2' | ... | '9'
これは、「BNF」であって、繰り返しの { } や 省略可能な [ ] を使った
「拡張BNF」ではありませんが、参考にはなるでしょう。

次のプログラムは、元のプログラムとほぼ同じ動作をしますが、元のプログラム
を変更したものではないし、数値を double で扱っていたり、べき乗演算子や
単項演算子を追加してあるので、そのものずばりの解答ではありません。しかし、
もしかしたら、参考になるかもしれません。

コード:

#include <stdio.h>   // printf, puts, fgets
#include <stdlib.h>  // strtod
#include <ctype.h>   // isspace, isdigit
#include <math.h>    // pow

unsigned char c;  const char *p, o[] = "+-*/^ ";  double m[26];

int get(void) { while (isspace(c = *p++)) ; return c; }

double expr(const char *b)
{
    double v;
    if (*b)  // binary operators; power is right associative
        for (v = expr(b + 2); c == b[0] || c == b[1]; )
            c == '+' ? v += expr(b + 2) :
            c == '-' ? v -= expr(b + 2) :
            c == '*' ? v *= expr(b + 2) :
            c == '/' ? v /= expr(b + 2) : (v = pow(v, expr(b)));
    else  // number, parentheses, unary operators, and variable
        get() == '.' || isdigit(c) ? v = strtod(p-1, (char **)&p), get() :
            c == '(' ? v = expr(o), c == ')' ? get() : (c = 1) :
            c == '+' ? v = expr(b) : 
            c == '-' ? v = -expr(b) :
            islower(c) ? v = m[c - 'a'], get() : (v = c = 1);
    return v;
}

int main(void)
{
    char s[800];  double v;  int i;
    while (fgets(s, sizeof s, stdin))
        p = s, get() == '?' ?
            v = expr(o), c ? puts("  --err--") : printf("  %.15g\n", v) :
        islower(c) && (i = c - 'a', get() == '=') && (v = expr(o), !c) ?
            m[i] = v : puts("  --err--");
    return 0;
}

閉鎖

“C言語何でも質問掲示板” へ戻る