コンパイラ作成について

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

コンパイラ作成について

#1

投稿記事 by おさむ » 13年前

PL0のコンパイラを作成しています。以下のプログラムは、パーサのプログラムです。
今は、中間コードの生成をやっています。
関数generateは、中間コードを生成する関数です。
以下のパーサプログラムは、簡単な四則演算プログラムに対して正しく動作します。
以下のプログラムに、手続き,制御文,入出力が処理できるようなコードを追加したいのですが、
どのように書けばよいのかがわかりません。
backpatchを使えばできるそうなんですが、どのように書けばよいのか教えてください。
よろしくお願いします。

コード:

%{
#define MAXLENGTH 16

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "code.h"
#include "optype.h"

struct LIST
{
  char name[MAXLENGTH+1];
  int flag;
  int addr;
  struct LIST *next;
};

 struct LIST head;
 int flg;
 FILE *fp;
 int address=0;
 int n=0;

%}

%union {
    int num;
    char ident[MAXLENGTH+1];
}

%token SBEGIN DO ELSE SEND
%token FOR FORWARD FUNCTION IF PROCEDURE
%token PROGRAM READ THEN TO VAR
%token WHILE WRITE

%left PLUS MINUS
%left MULT DIV

%token EQ NEQ LE LT GE GT
%token LPAREN RPAREN LBRACE RBRACE
%token COMMA SEMICOLON COLON INTERVAL
%token PERIOD ASSIGN
%token <num> NUMBER
%token <ident> IDENT

%%

program
:{fp = fopen("result.code", "wb");} PROGRAM IDENT SEMICOLON outblock PERIOD{close(fp);}
        ;

outblock
        : var_decl_part subprog_decl_part statement
        ;

var_decl_part
        : /* empty */
        | var_decl_list SEMICOLON
        ;

var_decl_list
        : var_decl_list SEMICOLON var_decl
        | var_decl
        ;

var_decl
: VAR id_list
        ;

subprog_decl_part
        : subprog_decl_list SEMICOLON 
        | /* empty */
        ;

subprog_decl_list
        : subprog_decl_list SEMICOLON subprog_decl
	| subprog_decl
        ;

subprog_decl
        : proc_decl
        ;

proc_decl
        : PROCEDURE proc_name SEMICOLON local_begin inblock local_end{delete();}
        ;

proc_name
: IDENT{insert($1,2);}
        ;

inblock
        : var_decl_part statement
        ;

statement_list
        : statement_list SEMICOLON statement
	| statement
        ;

statement
        : assignment_statement
	| if_statement
	| while_statement
	| for_statement
	| proc_call_statement
	| null_statement
	| block_statement
	| read_statement
        | write_statement
        ;

assignment_statement
: IDENT ASSIGN expression{lookup($1);}{generate(STO,0,0,address++);}
        ;

if_statement
        : IF condition THEN statement else_statement
        ;

else_statement
        : ELSE statement
	| /* empty */
        ;

while_statement
        : WHILE condition DO statement
        ;

for_statement
        : FOR IDENT ASSIGN expression TO expression DO statement{lookup($2);}
        ;

proc_call_statement
        : proc_call_name
        ;

proc_call_name
        : IDENT{lookup($1);}
        ;

block_statement
        : SBEGIN statement_list SEND
        ;

read_statement
        : READ LPAREN IDENT RPAREN{lookup($3);}
        ;

write_statement
        : WRITE LPAREN expression RPAREN
        ;

null_statement
        : /* empty */
        ;

condition
        : expression EQ expression
	| expression NEQ expression
	| expression LT expression
	| expression GT expression
	| expression LE expression
	| expression GE expression
        ;

expression
        : term
	| PLUS term
	| MINUS term
        {
            generate(OPR,0,0,0);
         }
	| expression PLUS term
        {
            generate(OPR,0,0,1);
        }

	| expression MINUS term
        {
            generate(OPR,0,0,2);
         }
	;

term
        : factor
	| term MULT factor
	| term DIV factor
	;

factor
: var_name
| NUMBER{generate(LIT,0,0,$1);}
	| LPAREN expression RPAREN
	;

var_name
: IDENT{lookup($1);}{generate(LOD,0,0,n++);}
	;

arg_list
        : expression
	| arg_list COMMA expression
	;

id_list
: IDENT{insert($1,flg);}{generate(LIT,0,0,0);}
| id_list COMMA IDENT{insert($3,flg);}{generate(LIT,0,0,0);}
	;

local_begin:{flg++;}
local_end:{flg--;}


%% 


yyerror(char *s)
{
  extern yylineno;
  extern *yytext;

  fprintf(stderr, "%s\n", s);
  fprintf(stderr, "line%d  %s\n",yylineno,yytext);
}

insert(char *name, int flag){
  struct LIST *p,*q,*new;
  p = head.next;
  q = &head;

  while(p != NULL){
    q = p;
    p = p->next;
  }
  new = malloc(sizeof(struct LIST));

  if(new == NULL){
    printf("error\n");
    return;
  }
  strcpy(new->name,name);
  new->flag = flag;
  new->next = p;
  q->next = new;
  printf("insert:%s  %d\n",name,address);
  printAll();
}


lookup(char *name){
  struct LIST *p,*q,*list;
  p = head.next;
  q = &head;

  while(1){
    
    if(strcmp(q->name,name) == 0){
      list = q;
    }
    if(p == NULL) break;
    q = p;
    p = p->next;
  }
  printf("lookup:");
  printf(" %s    %d\n",list->name,list->flag);
}


delete(){
  struct LIST *p, *q;
  p = head.next;
  q = &head;

  while(1){
    if(q->flag == 2){
      if(p->flag != 2){
	q->next = p->next;
      }
    }
    if(p == NULL) break;
    q = p;
    p = p->next;
  }
  printf("\n");
  printf("delete local\n");
  printAll();
}


printAll(void){
  struct LIST *list_ptr;
  printf("------------------table----------------------\n");
  for(list_ptr = head.next; list_ptr != NULL; list_ptr=list_ptr->next){
    printf("%s      %d\n",list_ptr->name, list_ptr->flag);
  }
  printf("---------------------------------------------\n");
}


generate(int opcode, REG base, REG index, REG address){
  OPCODE *op;
 
  op = malloc(sizeof(OPCODE));
  op->opcode = opcode;
  op->basereg = base;
  op->indexreg = index;
  op->address = address;

  fwrite(op,sizeof(OPCODE),1,fp);
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: コンパイラ作成について

#2

投稿記事 by softya(ソフト屋) » 13年前

そもそも私はPL0と言う言語を知らないのですが、どこかの大学の課題用のオリジナル言語でしょうか?
「PL/0 - Wikipedia」 これの事ですかね?
http://ja.wikipedia.org/wiki/PL/0

それと当たり前のようにyacc/lexが使われている説明を省略していますが大事なことなので書き漏らさないでください。
おさむさんの常識が私の常識でないということなんです。

さらに中間コードの仕様もありませんので中間コードにバックパッチが必要かどうかも判断できませんので、すべての情報を詳しく書きだしてください。
URLがあるなら、そのリンクがあったほうが良いと思います。
バックパッチが必要ということはワンパスで処理するコンパイラって事でしょうか?

できればコメントがもっとあると解析しやすいです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

おさむ

Re: コンパイラ作成について

#3

投稿記事 by おさむ » 13年前

説明不足で、申し訳ありません。
lexとyaccを使って、コンパイラを作成しています。
今回は、yaccの部分のプログラムをのせました。

PL0は、PL/0 - ウィキペディア - Wikipediaです。
今回のコンパイラが受理するプログラムは、以下の3つ(pl0a.p, pl0b.p, pl0c.p)です。
pl0a.p

コード:

program PL0A;
var n, sum;
begin
    n := 10;
    sum := 0;
    while n > 0 do
    begin
        sum := sum + n;
        n := n - 1
    end
end.
pl0b.p

コード:

program PL0B;
var n, x;
procedure prime;
var m; 
begin  
   m := x div 2;
   while x <> (x div m) * m do
      m := m - 1;
   if m = 1 then
      write(x)
end;
begin
   read(n);
   while 1 < n do
   begin
      x := n;
      prime;
      n := n - 1
   end
end.
pl0c.p

コード:

program PL0C;
var n, x, i;
procedure prime;
var m; 
begin  
   m := x div 2;
   while x <> (x div m) * m do
      m := m - 1;
   if m = 1 then
      write(x)
end;
begin
   read(n);
   for i := 2 to n do
   begin
      x := i;
      prime;
   end
end.

以下は、四則演算のプログラムです。

コード:

program EX1;
var x, y, z;
begin
   x := 12;
   y := 20;
   z := x + y
end.
この四則演算のプログラムが生成する中間コードは、以下のようになります。
lit 0 0 0
lit 0 0 0
lit 0 0 0
lit 0 0 12
sto 0 0 0
lit 0 0 20
sto 0 0 1
lod 0 0 1
opr +
sto 0 0 2

ワンパスで処理するコンパイラです。
関数insertは、記号表に変数名を追加します。関数lookupは、変数や手続き名が呼び出されるたびに
記号表から検索しています。関数deleteは、局部変数を削除しています。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: コンパイラ作成について

#4

投稿記事 by softya(ソフト屋) » 13年前

やはり、中間コードが設計されていないですね。
lit ロード・イミィデイト
sto ストア
lod ロード
opr 演算命令
の4つ以外にないと他の命令は作れません。
#include "code.h"
#include "optype.h"
の中にないならご自分で設計しないといけません。

とりあえず分岐のないWRITEコマンド辺りからどういう中間コードを落とすか考えて見ませんか?

【補足】
私はおさむさんがどんな講義を受けてきたか知らないので私の知識でしか答えられません。
そのため、講義の内容と大幅にずれる可能性があります。なので情報はできるだけ出してください。
それと学校の課題だと思いますが、あとで掲示板の内容を消してくれというのもお受けできません。
ということで、丸写しで出すのも出来ないと言うことは理解しておいてください。
※ なお課題の内容を掲示板に貼ること自体に問題がないかは確認しておいてください。

【追記】
なんか大昔にPL/0の文法を見たことがあるのを思い出しました。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

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