C言語の伝統に則った言語には文 statement と式 expression の区別があり、+
や,
、&&
といった演算子は式を作り、if
やfor
やreturn
、;
といったキーワードは文を作ります。一方、Haxeには構文上、文と式の区別がが存在せず、Rubyのように、C言語では文だったような構文もすべて式です。
多くの式は型と値を持ちますが、Void型の式や、単独で型や値といった意味を持たない式もあります。そのような意味を持たない式もマクロで使うことができます。
イカしたエクスプレッション紹介
enum haxe.macro.ExprDef
のメンバーを紹介します。
定数式 EConst
リテラルや識別子。以下の種類があります。
- 整数
CInt
- 浮動小数点数
CFloat
- 文字列
CString
- 識別子
CIdent
- 正規表現
CRegexp
添字アクセス式 EArray
e1[e2]
の形をした、添字アクセスの式。
単項演算子式 EUnop
op e
またはe op
の形をした式。op
には次の種類があります。
-
OpIncrement
++
-
OpDecrement
--
-
OpNot
!
-
OpNeg
-
-
OpNegBits
~
二項演算子式 EBinop
e1 op e2
の形をした式。op
には以下の種類があります。
-
OpAdd
+
-
OpMult
*
-
OpDiv
/
-
OpSub
-
-
OpAssign
=
-
OpEq
==
-
OpNotEq
!=
-
OpGt
>
-
OpGte
>=
-
OpLt
<
-
OpLte
<=
-
OpAnd
&
-
OpOr
|
-
OpXor
^
-
OpBoolAnd
&&
-
OpBoolOr
||
-
OpShl
<<
-
OpShr
>>
-
OpUShr
>>>
-
OpMod
%
-
OpInterval
...
-
OpArrow
=>
-
OpAssignOp(op:Binop)
+=
-=
/=
*=
<<=
>>=
>>>=
|=
&=
^=
%=
フィールドアクセス式 EField
e.field
の形をした式。
括弧式 EParenthesis
(e)
の形をした式。
オブジェクト宣言式 EObjectDecl
{field: expr, ...}
の形をした、匿名構造体を宣言する式。
配列宣言式 EArrayDecl
[e, ...]
の形をした、配列を宣言する式。連想配列や内包記法も内部表現はこの式になります。
呼出し式 ECall
e1(e2, ...)
の形をした、関数呼出しの式。
new
式 ENew
new T(e, ...)
の形をした式。
var
式 EVar
var x = e, ...
の形をした、変数を宣言する式。値はVoid
。
function
式 EFunction
function f<T>(arg, ...) e
の形をした、関数を宣言する式。
ブロック式 EBlock
{e; ...}
の形をした式。ブロック式中一番最後の式の値が、ブロック式全体の値となります。
for
式 EFor
for (e1) e2
の形をした式。値はVoid
。内包記法にも使われます。
in
式 EIn
e1 in e2
の形をした式。基本的にfor式の中で使われます。
if
式 EIf
if (e1) e2 else e3
の形をした式。else
節がある場合、e1
の値に応じて、e2
またはe3
の値がif
式全体の値となります。
while
式 EWhile
while (e1) e2
またはdo e1 while(e2)
の形をした式。値はVoid
。
switch
式 ESwitch
switch e1 {case e2, ... if (e3): e4; ...}
の形をした式。e1
の値に応じて、各ケースいずれかがswitch
式全体の値となる。マクロ以外ではe1
の部分を括弧で括られた(e)
か配列の[e, ...]
の形のどちらかにしなければなりません。
try
式 ETry
try e1 catch (x: T) e2 ...
の形をした式。e1
または例外を捕捉したcatch
節の値を返します。
return
式 EReturn
return e
の形をした式。Dynamic
型として扱われます。
break
式 EBreak
, continue
式 EContinue
break
とcontinue
も式。return
式同様Dynamic
型として扱われます。
untyped
式 EUntyped
untyped e
の形をした式。e
の型を無視した型が付きます。
throw
式 EThrow
throw e
の形をした式。例外を投げます。
cast
式 ECast
cast e
またはcast(e, T)
の形をした式。型キャストを行います
三項演算子式 ETernary
e1 ? e2 : e3
の形をした式。これの代わりにif
式があるので必要ありません。
型検査式 ECheckType
e : T
の形をした式。e
の型がT
であることをコンパイル時に検査する。マクロ以外では全体を括弧で括って(e : T)
の形で使わなければなりません。
メタデータ式 EMeta
@m(p, ...) e
や@:m(p, ...) e
, $m{e}
の形をした式。マクロなどで使います。
macro
式
抽象構文木にmacro
式は存在しません。これはシンタックスシュガーで、macro
を使った表現は、内部で他の式を使った表現に置き換わります。
意味を持たない式
例えばin
式は単独では意味を持たず、普通はfor
式と組み合わせて使う必要があります。しかし、マクロに与える式の場合には、抽象構文木を書き換えてしまうわけで、式が意味を持たないことは問題となりません。意味を持たない式を使っている具体例として、hxparseのパーサーのswitch stream
を使った記法が挙げられます。
曖昧な式
空のオブジェクト宣言式と空のブロック式はどちらも{}
という表現で、区別が付きません。構文解析の段階では空のブロック式としてパースしておき、その後の処理で文脈を見て空のオブジェクト宣言か空のブロックかを判断しているようです。気をつけておかないと、マクロを書くときに引っかかりそうですね。
ASTを簡単に調べる方法
ihxを使うと簡単に抽象構文木を調べられます。
% haxelib run ihx
haxe interactive shell v0.3.4
type "help" for help
>> macro [for (x in a) if (p(x)) f(x)]
haxe.macro.Expr : { expr => EArrayDecl([{ expr => EFor({ expr => EIn({ expr => EConst(CIdent(x)), pos => { file => /tmp/IhxProgram_8249.hx, max => 235, min => 234 } },{ expr => EConst(CIdent(a)), pos => { file => /tmp/IhxProgram_8249.hx, max => 240, min => 239 } }), pos => { file => /tmp/IhxProgram_8249.hx, max => 240, min => 234 } },{ expr => EIf({ expr => ECall({ expr => EConst(CIdent(p)), pos => { file => /tmp/IhxProgram_8249.hx, max => 247, min => 246 } },[{ expr => EConst(CIdent(x)), pos => { file => /tmp/IhxProgram_8249.hx, max => 249, min => 248 } }]), pos => { file => /tmp/IhxProgram_8249.hx, max => 250, min => 246 } },{ expr => ECall({ expr => EConst(CIdent(f)), pos => { file => /tmp/IhxProgram_8249.hx, max => 253, min => 252 } },[{ expr => EConst(CIdent(x)), pos => { file => /tmp/IhxProgram_8249.hx, max => 255, min => 254 } }]), pos => { file => /tmp/IhxProgram_8249.hx, max => 256, min => 252 } },null), pos => { file => /tmp/IhxProgram_8249.hx, max => 256, min => 242 } }), pos => { file => /tmp/IhxProgram_8249.hx, max => 256, min => 229 } }]), pos => { file => /tmp/IhxProgram_8249.hx, max => 257, min => 228 } }
よきマクロ生活をお過ごしください。