SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Ruby開発者・まつもとゆきひろ氏の新言語「Streem」のソースコードを読んでみよう! ~ 文法と構造を規定する「lex.l」と「parse.y」


lex.lからCのソースを生成する

 それでは、lex.lのソースがどう処理されるかについて、streemのMakefileファイルを利用して説明しましょう。

makeとMakefile

 Makefileは、伝統的にCでプログラムを開発する際に作成するファイルで、さまざまなマクロの定義や、ソースファイル間の依存関係と依存先を生成/更新するためのコマンドを記述します。makeコマンド(MicrosoftのSDKではnmake.exe)が、Makefileに記述されているコマンドを実行します。

 IDEを利用して開発している方は見たことがないかもしれませんが、Makefileは、Javaのpom.xml(Mavenが処理する)やbuild.xml(Antが処理する)の原型のような存在です。

streemのMakefileは、streemリポジトリのルートディレクトリに置かれています。

 Makefileからlex.lに関連する箇所を抜き出したものが次のリストです。

リスト3:Makefileからlex.lに関連する箇所を抜き出したもの
LEX = flex
src/lex.yy.c : src/lex.l
        $(LEX) -osrc/lex.yy.c src/lex.l

 最初の行では、flexというコマンドを以降ではLEXというマクロで参照することを指示しています。flexはlexのフリーな実装です。lexのトリビアとして、Googleの現会長エリック・シュミットが同僚と開発したということが知られています。マクロ化しているのは、環境によってはflexではなくlexやその他の互換コマンドを利用できるようにしているからです。

 重要な点は、上記で2行目に引用した箇所です。

 2行目は、src/lex.yy.cという拡張子.cのファイル、つまりCのソースファイルが、src/lex.lに依存しているということを定義しています。Makefileでいう依存とは、「:の左辺のファイルが存在しないか、右辺のファイルより更新日付が古ければ、以降の行のコマンドを実行する」という意味です。つまり、lex.lをLEX(最初のマクロ定義からflex)に入力すると、lex.yy.cというCのソースファイルが生成されるということになります。

 実際にコマンドラインから、streemのルートディレクトリへ移動して、次のように入力すると、srcディレクトリにlex.yy.cというCのソースファイルが生成されることが確認できます。

flex -osrc/lex.yy.c src/lex.l

 なお、Windowsで試す場合には、http://gnuwin32.sourceforge.net/packages/flex.htmからWin32版のflex.exeを入手して、PATHの通ったディレクトリへコピーした後、コマンドプロンプトからstreemのディレクトリへ移動し、次のように入力すればlex.yy.cが生成されます。

flex -osrc¥lex.yy.c src¥lex.l

 テキストエディタなどで眺めるとわかりますが、生成されたlex.yy.cは、人間が読むのは相当至難なコードです[4]

[4] ここは重要な点ですが、このことはflexが生成したソースファイルが読みにくいというよりも(それにしても読みやすいとは言いがたいですが)、Cで効率を重視した字句解析を行うソースファイルそのものが読みにくいと考えるべきです。これは当然、書きにくくデバッグしにくいということでもあります。そこにflex(lex)の存在意義があります。

 また、lex.yy.cを読むと、Cのソースファイルとしては不完全で、未定義の変数が利用されている箇所にも気づかれると思います。たとえば、op_barは定義されていません。この理由は、Makefileの最終目的であるTARGET(bin/streem、Windows_NETの場合はbin/streem.exeに展開される)の依存をたどるとわかります。

 TARGETの依存関係は次のように記述されています。

$(TARGET) : src/parse.o

 右辺にあるsrc/parse.oをたどります。

src/parse.o : src/y.tab.c src/lex.yy.c
        $(CC) -g -c src/y.tab.c -o src/parse.o

 見ると、y.tab.cとlex.yy.cの2つのCソースファイルに依存しているにもかかわらず、parse.oというオブジェクトファイルを生成するCC(Cコンパイラ)への入力がsrc/y.tab.cだけとなっています。つまり、lex.yy.cは単独でコンパイルされるファイルではないということです[5]

[5] これはまつもとさんがそのように構成しているということであって、単独でコンパイル可能な.lファイルを記述することも可能です。

 なお、後で説明をするparse.yには、

#include "lex.yy.c"

という行が含まれています。つまり、lex.yy.cの元となるlex.lは、最初からparse.yに組み込まれることを前提として記述されているわけです。当然、op_barといった変数はparse.yに定義されています。

図4:lex.lがstreemになるまで
図4:lex.lがstreemになるまで

 

 では、今度は構文解析に移りましょう。

次のページ
構文解析

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

arton(アートン)

専門は業界特化型のミドルウェアやフレームワークとそれを利用するアプリケーションの開発。需要に応じてメインフレームクラスから携帯端末までダウンサイジングしたりアップサイジングしたりしながらオブジェクトを連携させていくという変化に富んだ開発者人生を歩んでいる。著書に『Ruby③ オブジェクト指向とはじめての設計...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8409 2015/01/27 08:41

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー