ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

ギャオスと仲間たちコミュのプログラム言語論

  • mixiチェック
  • このエントリーをはてなブックマークに追加
プログラム言語論の課題を一応作ってみましたが、実際こんなのでいいのか微妙です。
誰かコメント入れてください。


%{
#define YYSTYPE double
struct NODE{
  int type;
  double value;
  int flg;
  struct NODE *next;
  struct NODE *back;
};
%}
%left '+' '-'
%left '*' '/'
%right UNARYMINUS
%%
lines: /* empty */
 | lines '\n'
 | lines expr '\n' { view();printf("\t= %f\n", $2);KILL();}
 ;
expr: number { $$ = $1;addVALUE($1);}
 | '-' expr %prec UNARYMINUS { $$ = -$2;}
 | expr '+' expr   { $$ = $1 + $3;addMARK(1, $1, $3);}
 | expr '-' expr   { $$ = $1 - $3;addMARK(2, $1, $3);}
 | expr '*' expr   { $$ = $1 * $3;addMARK(3, $1, $3);}
 | expr '/' expr   { $$ = $1 / $3;addMARK(4, $1, $3);}
 | '(' expr ')'    { $$ = $2;}
 ;
number: digit_sequence { $$ = $1; }
 ;
digit_sequence: digit { $$ = $1; }
 | digit_sequence digit { $$ = 10 * $1 + $2; }
 ;
digit: '0' { $$ = 0; }
 | '1' { $$ = 1; }
 | '2' { $$ = 2; }
 | '3' { $$ = 3; }
 | '4' { $$ = 4; }
 | '5' { $$ = 5; }
 | '6' { $$ = 6; }
 | '7' { $$ = 7; }
 | '8' { $$ = 8; }
 | '9' { $$ = 9; }
 ; 
%%
#include <stdio.h>
#include <ctype.h>
static struct NODE *start = NULL;
static struct NODE *end = NULL;

int main(int argc, char *argv[])
{
 yyparse();
}

int yylex()
{
 int c = getchar();
 if(c == EOF) {
  return 0;
 }
 return c;
}

int yyerror(char *s)
{
 fprintf(stderr, "calc1: %s\n", s);
}

void addVALUE(int value){
  struct NODE* node;
  node = (struct NODE *)malloc(sizeof(struct NODE));
  node->value = value;
  node->flg = 0;
  node->type = 0;

  node->next = start;
  if (node->next != NULL){ node->next->back = node; }
  start = node;
  node->back = NULL;

  if (node->next == NULL){ end = node; }
}

void addMARK(int mark, double a, double b){
  struct NODE* node;
  struct NODE* search;
  int fa = 0, fb = 0;
  node = (struct NODE *)malloc(sizeof(struct NODE));
  node->value = 0.0;
  node->flg = 0;
  node->type = mark;

  search = end;
  for(;;){
    if (search->value == a && search->flg == 0 && fa == 0) {
      fa = 1; search->flg = 1;
    }
    if (search->value == b && search->flg == 0 && fb == 0) {
      fb = 1; search->flg = 1;
    }
    if (fa == 1 && fb == 1){ break; }
    if (search->back == NULL){ break; }
    search = search->back;
  }

  if (search == start) {
    node->next = start;
    node->next->back = node;
    start = node;
    node->back = NULL;
  } else {
    node->back = search->back;
    search->back = node;
    node->back->next = node;
    node->next = search;
  }
}

void view(){
  struct NODE* node;
  node = start;

  while(node != NULL){
    switch(node->type){
      case 0:
        printf("%f ", node->value); break;

      case 1:
        printf("+ "); break;

      case 2:
        printf("- "); break;

      case 3:
        printf("* "); break;

      case 4:
        printf("/ "); break;
    }
    node = node->next;
  }
  printf("\n");
}

KILL()
{
  struct NODE* node;
  struct NODE* bnode;
  node = start;

  for(;;){
    bnode = node->next;
    free(node);
    if(bnode == NULL){ break; }
    node = bnode;
    bnode = node->next;
  }
  start = NULL;
  end = NULL;
}
/* eof */

コメント(4)

2-9は文字ベースの力業でやりました
strdup()せずにポインタ返して、そこ書き換えてひどい目にあったのは内緒のひみつ。

%{
#define YYSTYPE char*
#include <stdio.h>
#include <ctype.h>

static char sbuf[1024];
%}

%left '+' '-'
%left '*' '/'
%right UNARYMINUS
%%

lines: /* empty */
| lines '\n'
| lines expr '\n' { printf("\t%s\n",$2);}
;

expr: number { $$ = $1;}
| '-' expr %prec UNARYMINUS
{
snprintf(sbuf, sizeof(sbuf), "-%s ", $2);
$$ = strdup(sbuf);
}
| expr '+' expr
{
snprintf(sbuf, sizeof(sbuf), " + %s %s ", $1, $3);
$$ = strdup(sbuf);
}
| expr '-' expr
{
snprintf(sbuf, sizeof(sbuf), " - %s %s ", $1, $3);
$$ = strdup(sbuf);
}
| expr '*' expr
{
snprintf(sbuf, sizeof(sbuf), " * %s %s ", $1, $3);
$$ = strdup(sbuf);
}
| expr '/' expr
{
snprintf(sbuf, sizeof(sbuf), " / %s %s ", $1, $3);
$$ = strdup(sbuf);
}
| '(' expr ')' { $$ = $2; }
;

number: digit_sequence { $$ = $1;}
;

digit_sequence: digit { $$ = $1; }
| digit_sequence digit
{
snprintf(sbuf, sizeof(sbuf), "%s%s", $1 ,$2);
$$ = strdup(sbuf);
}
;

digit: '0' { $$ = "0"; }
| '1' { $$ = "1"; }
| '2' { $$ = "2"; }
| '3' { $$ = "3"; }
| '4' { $$ = "4"; }
| '5' { $$ = "5"; }
| '6' { $$ = "6"; }
| '7' { $$ = "7"; }
| '8' { $$ = "8"; }
| '9' { $$ = "9"; }
;

%%
int main(int argc, char *argv[])
{
yyparse();
}

int yylex()
{
int c=getchar();
if(c==EOF)
return 0;
return c;
}

int yyerror(char *s)
{
fprintf(stderr, "infix: %s\n", s);
}

一応そこそこちゃんと動いてはいます。
gccでコンパイルするときに軽く警告出るけど気にしない。
ついでだからこっちも書いておこう


2-9ですが、方針としては

・数値として扱う必要は全くなし
・片っ端から文字列操作
・戻り値が文字列になるようにして、最後にまとめて表示

としました。

で、前置記法の基本パーツとなるもの(ex: 1+2 , 3*4 等)について考えると
1+2 -> + 1 2
3*4 -> * 3 4
となればいいことになります。これだけなら単純ですが、複数項ある場合を考えると

1*2+3/4 -> + * 1 2 / 3 4
となるわけですが、これは
1*2+3/4 -> + (1*2の出力) (3/4の出力)
という結果が欲しいことになります。

(1*2)や(3/4)を処理した段階で出力を行うようなプログラムだとうまくいかないので、(1*2)や(3/4)の出力を文字列の戻り値とすることで解決します。

これがわかれば、出力すべきものは
(演算子) (左の値) (右の値)
となればいいことがわかるので、これを満たす文字列操作を作成していってください。

伴って、扱う型は文字列ポインタ(char*)に変更し、"digit" も「文字列としての数字」を表すように変更する必要があります。
また、C言語では文字列はポインタ経由での操作となるので、戻り値を素直に処理するとうまくいきません。
効率は悪いですが、文字列の複製などで対処しました。

全ソースは最後に回すとして、重要な箇所はexprでの処理で、
| expr '+' expr
{
snprintf(sbuf, sizeof(sbuf), " + %s %s ", $1, $3);
$$ = strdup(sbuf);
}
こんな感じになりました。snprintfじゃなくてsprintfでいいと思いますが。
要するに、sprintfを使って文字列バッファsbufに
(演算子) (左の値) (右の値)
という文字列を作成し、その複製を戻り値$$とします。他の演算子でも同様です。
2桁以上の数字が入力された場合(digit_sequence)も同様に複製で処理します。
最後に、数字は前述の通り文字列としての数字として処理します。
digit: '0' { $$ = "0"; }
| '1' { $$ = "1"; }
(略)


・変更用のガイドライン
さすがに丸ごとコピーはどうかと思うんで、適当にいじってみてください。
snprintf(文字数制限付きsprintf)はsprintfにして問題ないです。
sbuf[1024]も変数名、領域サイズともに適当にいじってOKです。
UNARYMINUS(単項マイナス)は無くても問題ないでしょう。指示が曖昧ですし。
int yyerror()の出力部分でinfix とかなってますが適当にプログラム名とか(calc_prefix)とかに直すといいかと。


・というわけで
適当にいじってみてください。出力される空白がおかしいのでその辺をいじるといいかも。
なるほど!
文字列の戻り値を格納すればいのか。
そんな手があったとはぜんぜん思いつきませんでしたよ。
参考になりました〜。

ちなみに私の貼り付けたソースのほうはわざわざ数字と演算子を切り出して構造体でリストを作ってます。
まぁ、編入の入試を思い出してみましょうw

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

ギャオスと仲間たち 更新情報

ギャオスと仲間たちのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング