コンパイラ作成(25) 関数名のチェック
今回の目標
前回同様、コンパイルエラーの検出。
// 関数の呼び出し int main() { print(answer()); } int answer() { // Answer to the Ultimate Question of Life, the Universe, and Everything return 42; } int answer() { // Answer to the Ultimate Question of Life, the Universe, and Everything return 42; }
関数名の重複をチェックしてコンパイルエラーに。
@functions
Hashを使ってデータを管理していくよ。
# コンストラクタ def initialize(fname) @fname = fname # ソースファイルのファイル名 @asmfname = fname.sub(/\.myc$/,'.s') # アセンブリコードのファイル名 @exefname = fname.sub(/\.myc$/,'') # 実行ファイル名 # print "asmfname=",@asmfname,"\n" @lex = Lexer.new(@fname) @funcname = nil @literalcnt = 0 @literaltable = [] @needprint = false @functions = Hash.new end
Parserクラスのinitializeで初期化。
function
functionメソッドで登録していくよ。
# 関数の構文解析 def function() rettype = nil kind, str = @lex.gettoken if kind == TK::RESERVE && str == "int" then rettype = str kind, str = @lex.gettoken end if kind == TK::EOF then return false end if kind != TK::ID then perror "expected identifier" end @funcname = str kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ")" then perror end codegen ".global "+@funcname codegen @funcname+":" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "{" then perror end while statement do end codegen " ret" if @functions[@funcname] != nil then perror "redefinition of \"" + @funcname + "\"" end @functions[@funcname] = [rettype,[]] @funcname = nil return true end
関数名をkey、関数の戻り値の型をvalueとしてはHashに登録していってる。既に登録されてたらコンパイルエラーだよ。valueの中の空リストは関数の引数の型をリストにしたものなんだけど、今はまだ引数のある関数には対応できてないんで単に空リストになってる。これで関数の戻り値の型を持てたので、本当はreturn文で型があってるかチェックすべきなんだけどまだやってないよ。int型以外の値を返せないからね。やんなきゃいけないことが多いなあ。
動作チェック
えい!
~/myc$ myc err8.myc err8.myc:16:15 error: redefinition of "answer" ~/myc$
よしよし。うまくいったよ。型情報の持ち方だけど、何も考えずに安易な方法にしちゃったんで、将来見直しが必要かもなあ。文字列でintって単純に表わしてるけど、これだと複雑な型だと表わせないよなあ。配列、ポインタ、構造体。こういうのサポートするときもう一回良く考えよう。
二回続けて落穂ひろいしたけど、次回は何か新しいことしたいなあ。