指令の始めで、
'#' に続くキーワードは、マクロ展開なしで解析されます。
これは、PP キーワードの再定義が、(意図的に) PP 指示に効果がないことを意味します。
例 :
#define define foo
#define bar baz
は、間にあって、以下のように見えません:
#if とその仲間のような指示は、PP 表現パーサーを利用します。これはマクロを展開します。
結局、これは PP 表現のポイントです。
例 :
#define foo 1
#if foo = 1
#endif
#define と
#macro 指示は、まったくマクロを展開しません。マクロの本体は、あるがままで記録されます。
pp.bas:ppDefine() は、最初にマクロの識別子を解析します。
間にスペースなしで、後に
'(' が続いていれば、それから、パラメータ・リストは、また解析されます。
そして、マクロ本体が解析されます。
各々のトークンのために、そのテキスト表現は、
lexGetText() を通して検索されます。そして、それはマクロ本体テキストに追加されます。
スペースは、保持されます(しかし、トリムされます);コメントは、無視されます; 複数行 #macro では、空白行は取り除かれます。
マクロに、パラメータがあれば、マクロ・トークンが、生成されます(マクロ展開の項を参照下さい)。
こうするために、マクロ・パラメータは、一時的なハッシュ・テーブルに加えられます。そして、一時的なハッシュ・テーブルは、パラメータのインデックスに、パラメータ名を結びつけます。
それから、マクロ本体の識別子は、調べられます。そして、パラメータが認められると、前の text() マクロトークンにトークンを追加する代わりに(またはそのために新しい text() を作成する代わりに)、パラメータ(インデックス)マクロトークンが、生成されます。
そのパラメータ(インデックス)の後に、再び他のテキストがあれば、新しい text() マクロトークンが生成されます。
パラメータで
# を使うと、stringify_parameter(index) マクロトークンを生成します。
PP 合併演算子
## は、単純にマクロ本体から除外されます。このため、
a##b は、text() マクロトークンでは、
ab になります。
すべての普通のテキスト before/after/between パラメータは、text() マクロトークンに入ります。
For example:
#define add(x, y) foo bar x + y
And the actions of the #define/#macro parser will be:
'add' - The macro's name
'(' following the name, without space in between: Parse the parameter list.
'x' - Parameter 0.
',' - Next parameter.
'y' - Parameter 1.
')' - End of parameter list.
Create the macro body in form of macro tokens.
' ' - Create new text(" ").
'foo' - Append "foo".
' ' - Append " ".
'bar' - Append "bar".
' ' - Append " ".
'x' - Is parameter 0, create new param(0).
' ' - Create new text(" ").
'+' - Append "+".
' ' - Append " ".
'y' - Is parameter 1, create new param(1).
EOL - End of macro body.
Resulting in this macro body:
text(" foo bar "), param(0), text(" + "), param(1)
本体が同じでなら、#define パーサーは、マクロが再定義されることを可能にします。
例 :
上は、繰り返された定義になりません。しかし、これは、下のようになります:
これらは、純粋なテキスト #define なので、本体の比較は、単純な文字列比較です。
この特徴は、現在、パラメータを持つマクロのために、実装されていません。
プリプロセッサーは、
#if/
#endif ブロックを管理するために単純なスタックを使います。
これらは入れ子にすることができます。また、
#if/
#endif ブロックの中に #includes を含める事ができます。しかし、
#if/
#endif ブロックは、ファイルをまたがることはできません。
間違ったブロック (#if 0、や #if 1 の #else) は、
lexSkipToken() に戻る前に、#if 0 や #else (
pp-cond.bas:ppSkip()) を解析するとき、直ちにスキップされます。
例えば:
#if 1 (push to stack: is_true = TRUE, #else not visited yet, return to lexSkipToken())
... (will be parsed)
#else 1) Set the #else visited flag for the current stack node,
so further #else's are not allowed.
2) Since the current stack node has is_true = TRUE,
that means the #else block must be skipped, -> call ppSkip().
... (skipped in ppSkip())
#endif (parsed from ppSkip(), skipping ends, ppSkip() returns to #else parser,
which returns to lexSkipToken())
PP 飛び越しに関して、少し巧妙なビットがあることに注意してください。
マクロは、PP 指示を含むことができるので、マクロ展開は、PP 飛び越しの間にさえ行われます。#else や #endif は、複数行マクロの内部にあるかもしれないからです。
さらに、複数行 #macro 宣言は、PP 飛び越しの間は扱われません。
これは、下のようになることを、意味します:
#if 0
#macro test()
#endif
#endmacro
は、下のように見えます:
#if 0
#macro test()
#endif
#endmacro
エラーになります。(#endmacro は #macro が無い)
このため、これは:
#if 0
#macro test()
#endif
#endmacro
#endif
インデントによって示されるようには、働きません。