FreeBASIC マニュアルのトップに戻る

FreeBASIC DevFbcLexerMacros

目次→FreeBASIC のハッキング→FreeBASIC でのハッキングのための情報Lexer(字句解析器) と Preprocessor(前処理)Macros←オリジナル・サイト

マクロ



ソース・コードで使われるいくつかの用語(二重の意味に注意):

マクロの格納方法

マクロは、トークン実行としてではなく、基本的に、生のテキストとして格納されます。(たとえば、GCC の libcpp )
パラメータのない単純な #defines の本体は、1つの文字列として保存されます。
パラメータ付きのマクロは、「マクロ・トークン」のシーケンスとして格納されます。
3種類のマクロ・トークンが、あります:
注意: マクロのトークンは、実際は symb.bi:FB_DEFTOK 構造です。そして、そこに含むものを伝えるために FB_DEFTOK_TYPE_* 値を持つ id 項目を持っています。

For example:

	#define add(x, y) x + y

becomes:

	parameter(0), text(" + "), parameter(1)

And the expansion text will be:

	argument(0) + " + " + argument(1)


テキストとしてマクロを格納することは、かなり簡単な実装です。しかし、これは、マクロ本体を何度も何度も再解析することを要求します。
たとえば、GCC は、前処理トークンと、トークンランを使って働くので、マクロは、トークンとして格納されます。マクロ本体を、何度もトークン化する必要がないので、展開は非常に速くなります。
fbc の実装は、同じほどには柔軟でなく、効率的でもないでしょう。しかし、それほど複雑でなくて(コードとメモリ管理に関して)、上側も持ちます: ## (PP トークン結合)の実装は、重要では有りません。
マクロの本体を記録している間、## は単に省略されます。ここで、トークン実行の場合のように、トークンは、明示的に合併される必要があります。


マクロは、いつ展開されるか

トークンは先読みするため、マクロは、トークン化の間に、展開されなければなりません。さもないと、間違ったトークンが、トークンの待ち行列にロードされるかもしれません。
結局、パーサーは、先読みの間さえ、最後のトークンだけを見ます。

lexNextToken() で、各々の英数字識別子は、それがキーワードかマクロであるかどうか調べるために、symbモジュールで調べられます。
マクロとキーワードは、同じハッシュテーブルに保たれます。
マクロは、キーワードの名前を持てないことに注意すべきです; "#define integer" は、エラーを引き起こします。
マクロが見つけられると、それはすぐに展開されます。そして、プロセスも、マクロ(pp-define.bas:ppDefineLoad()) "loading" で呼ばれれます。

マクロが構文解析を呼ぶ

マクロが引数をとる場合、マクロの「呼び出し」は、構文的に、ちょうど関数呼出しのように、解析されます。
マクロ展開は、lexNextToken()(トークンのソース)の中で既に起こるので、ここの解析は、少し巧妙です。
前進は、現在のトークンを交換する(そして失う)ことによってのみ可能です。
トークンキューとトークン先読みは、信頼できません。
代わりに、それは、マクロの引数を解析する間、現在のトークンを、前に移して交換できるだけです。

lexNextToken() が引数を解析するために使用されるので、 引数自体の中のマクロは、引数が、テキスト形式で、解析し記録されている間、再帰的にマクロ展開されます。
引数テキストは、展開中の使用のために格納されます。

したがって、マクロの引数は、そのマクロそれ自身が展開される前に、展開されます。それは、良いが悪い特徴といえます:

#define stringify(s) #s
stringify(__LINE__)

この結果、FB では 2 になります。しかし、C では __LINE__ です。
Cでは、### とともに使われるとき、マクロ・パラメータは展開されないからです。
C で、2 を得るためには、2つのマクロを用いなければなりません:

#define stringize(s) #s
#define stringify(s) stringize(s)
stringify(__LINE__)

マクロ展開テキストを組み合わせる

展開テキストは、マクロ本体のトークンからの文字列構築です。
マクロ・パラメータについては、引数テキストは、パラメータ・トークンに保管されるインデックスを用いて、マクロ呼び出しパーサーによってつくられる引数配列から、検索されます。
パラメータの文字列化は、ここで行われます。

組み込み定義のための専門があります (__LINE__, __FUNCTION__, __FB_DEBUG__, etc.):
コールバックは、それらの「値」を取り戻すために用いられます。たとえば: __LINE__ のコールバックは、単純に lexer の現在の行番号を含んでいる文字列を返します。

展開

マクロ展開テキスト(deftext)は、lexer によって保存されます。そして、ここで、ファイル入力バッファから読む代わりに、lexer は、しばらく 保存 から文字の並びを読みます。
マクロ・テキストで、文字の並びをスキップすることは、ファイル入力で文字の並びをスキップするのと似ています: 一度、スキップされて失われたら、戻ることはありません。
従って、「古い」(通過された)マクロ・テキストは、そこに決してありません。現在の文字と、これから通過するテキストだけです。
新しいマクロ・テキストは、既存のマクロ・テキストの前に付加されます。このように、マクロの内側のマクロは、展開されます。

この実施は、マクロ再帰を検知することを(簡単には)許しません。
マクロ・テキスト・バッファのどの文字の並びが、どのマクロに属しているか、跡を追うのは、難しいです。しかし、それは、マクロを、適切に、押し込んで、取り出すことができるために必要です。
GCC の libcpp で見られるように、それはトークンの実行を実施することで、より簡単に行うことができるかもしれません。
しかし、C は、第一に再帰的マクロを許しません:C では、マクロの識別子は、そのマクロ本体の中では未定義です(展開を起動しません)。
それは fbc の状況とは異なります。というのは、繰り返しになりますが、マクロ本体が終わったことを検出する方法が、実装されていないからです。

現在、fbc は、最初の(トップレベルの)マクロ展開について経過を追うだけです。なぜなら、その特定のマクロが終端に達したことを検出するのが簡単(これ以上のマクロ・テキストがないとすぐ)だからです。

そういうわけで、再帰はここで見つけられます:

#define a a
a

そして、ここでも:

#define a b
#define b a
a

しかし、ここにはありません: (fbc が無限ループを実行するだろうことに注意してください。)

#define a a
#define m a
m

FreeBASIC の開発者用情報 に戻る
目次に戻る
ページ歴史:2016-03-12 13:20:01
日本語翻訳:WATANABE Makoto、原文著作者:DkLwikki

ホームページのトップに戻る

表示-非営利-継承