Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

関数実装についてアドバイスください。

ここで質問するような内容じゃないのかもしれませんが、意味解析などでJavaコードを使っていることもあってここでさせてください。

プログラミング言語を作ろうとしていて、今関数部分を作っているところです。
今プロトタイプ宣言なしのC言語のように呼び出し元より上で宣言しておけば呼び出せるようにはなりました。
ですが、Javaのようにどこで宣言していても呼び出せるようにしたいと思っています。

現状は、構文解析と意味解析のところで定義済み関数の呼び出しのコードがあったらその関数の定義を渡すような形にしているのですが、この方法では定義済みでないと呼び出せません。
そこで、定義済みでない関数の呼び出しに対してとりあえず定義しておいて、実際に定義されたときに置き換えるような形にしようかとも思ったのですが、その方法にするにしてもどのように書けばいいかわかりません。

説明が下手かもですが、もう少し詳しく書いてみます。
自作言語のプログラムの実行は、Hashtableの関数定義群にある{始まり}関数から始まることになっています。
そのHashtableには関数名とその定義を対で記憶しています。
関数の定義はProgramNodeクラスでできていて、zikkouメソッドで実行させています。
他にも命令の種類ごとにクラスを作っていて、それぞれzikkouメソッドを作っています。
ProgramNodeクラスには関数の中身の該当するクラスのオブジェクトを順にVectorに記憶しています。
実行時に呼び出しの部分は、呼び出し先のProgramNodeクラスのオブジェクトになっています。

Javaのメソッドのような関数を実装するための方法につて、アドバイスでもいいのでなにかあれば教えてください。

6

回答

94219

閲覧

6件の回答

評価

10

いわゆる言語処理系ではソースを1回走査するのをパスと
いったりします。質問者さんは1回のパスで構文解析と意
味解析を同時に行うイメージをお持ちだと思います。

そういう処理も可能かも知れませんが複雑でわかりずらい
作りになってしまいます。

前方参照の解決は2パスにすることで解決するのが良い方
法だと思います。

1パス目は構文だけを解析し構文木を生成しておきます。
2パス目では生成した構文木を走査して意味解析をしま
す。こうすると2パス目でソース上に宣言された構文要素
全体を見渡した処理が行えますので設計がシンプルにでき
ます。

評価

0

指摘の通り、構文解析と意味解析を同時に行っています。
私は「ふつうのコンパイラをつくろう」と「JavaCC コン
パイラ・コンパイラ for Java」という本を参考に試行錯誤
しながら作っています。

構文木を生成するにはどうすればどうすればいいのかがよ
くわかってません。
現状は構文解析しつつ実行に必要な情報を自作クラスに記
憶させつつで、最初に書いたように構文解析と意味解析を
同時に行っているのだと思います。
とりあえずどこかにただの構文木を作らせて後でそれを解
析するための方法がよくわかっていません。
構文木から実行に不要な部分を取り除いた抽象構文木につ
いても考えたことがあり、自作クラスはそれを参考に自分
なりに作ってみたものになります。

どこから勉強していけばよいのでしょうか?

評価

0

>構文木を生成するにはどうすれば

<メソッド定義> ::= <メソッド名> '(' <パラメータ群> 
')' <メソッド本体>

のように構文を定義し右のパターンが出現したとき左辺の
構文要素に「還元する」というような流れで構文を解析す
るようになっていると思います。質問者さんのプログラム
でも還元の際にProgramNodeに情報を覚えさせるといった
処理をしているはずです。ここで右辺の情報を使って左辺
の構文要素を表す構文木を生成するという感じです。

もっともwikiをみるとJavaCCに付属のJJTreeというツール
でより簡単に構文木が生成できるとあるので、これを使っ
た構文解析例を検索するのが手っ取り早いかも知れませ
ん。ちょっとみたところ参考になりそうなページがありそ
うです。

構文木を作ることに成功すれば、生成した構文木をたどっ
て「どんな名前のメソッドが定義されているか」などを調
べるコードのイメージがわくのではないかと思います。

評価

0

JJTreeというのがあるのは知っていますが、見てみて私にはよくわからないというか、JJTreeを使えるようになるよりそこのところを自前でやった方が早いんじゃないかと思ったので、JJTreeを使わない方向でやっています。

構文解析で自分が定義した構文が見つかった時に実行に必要な情報を自作の〜Nodeクラスに記憶させるようにしています。
"これがあれば実行できるような形でデータを記憶していく"のと"とりあえず構文木を作る"というのとはやはり違うんですよね?

以下に関数の定義を載せてみます。
//関数定義の例
/*ここから{関数名}
    関数の中身
ここまで{関数名}*/
//引数や戻り値についてはまだ定義できていません。
void kansuu() :
{
    Token ns=null,ne=null;
    //トークンの情報を記憶するための変数
    ProgramNode p=new ProgramNode();
    //関数本体を作成
}
{
    <KOKOKARA>ns=<KANSUU>
    codes(p) //関数の中身をpに追加
    <KOKOMADE>ne=<KANSUU>
    {
        if(ns.image.equals(ne.image)){
        //始まりと終わりの関数名が一致していたら
            program.put(ns.image,p);
            //関数名nsの関数pを関数リストに追加
        }
    }
}

関数呼び出しのときにリストにない関数を呼び出そうとした場合、とりあえず空の関数を作ってそれを呼び出す形にしておいて、あとで関数本体が作られたときにその中身を空部分にコピーするという形でなんとかどこで定義していても呼び出せるようにはなりました。

でもこういうのは呼び出しているというより呼び出し文があったらその関数に置き換えてるという感じで、なんか自分で違うような気もしてます。

評価

0

非常に大雑把にいうとインタープリタ言語はまさに質問者
さんがやっているような方式になっていて型の一致とか未
定義の関数を呼び出したとかいったもろもろは実行時のチ
ェックで済ませる方針だったりします。

一方JavaやC++といったレベルのコンパイラー言語の場合
はコンパイル時に深く意味解析をするので構文木を使うこ
とになるでしょう。

要は言語仕様次第ですね。

評価

0

私は自作したいと思っているエディターでeclipseのように裏でコンパイルしてエラー表示させたりしたいとは思っています。
ですが、コンパイラが中間ファールや実行ファイルを作るものだとすると、少なくとも現状はインタープリタになると思いますし、これからもそのような形になるかもしれません。
実行ファイルを作れたらいいなという思いはありますが、機械語は全く分からないもので。

現状構文エラーはJavaCC頼り、実行時エラーはスルー(?)しているような状態です。
この構文エラーや実行時エラーもきちんと対処できるようにしたいのですが、まだその方法がよくわかっていないためにそんな状況です。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。