あまりにネタを思い付かないのでa24zのコンパイラをocamlで書き直してる次第。自分車輪再発明。
let rec fold_binary iop fop l r = match (fold_const(l),fold_const(r)) with (Int(l),Int(r)) -> Int( iop l r ) | (Float(l),Float(r)) -> Float( fop l r ) | (_,_) -> raise TypeUnmatch and convert_to_int e = match e with Int(i) -> i | Float(f) -> int_of_float f and convert_to_float e = match e with Int(i) -> float_of_int i | Float(f) -> f and fold_const = function IntConst(i) -> Int(i) | FloatConst(f) -> Float(f) | BinaryExpr(op,l,r) -> (match op with ADD -> fold_binary (+) (+.) l r | MINUS -> fold_binary (-) (-.) l r | MUL -> fold_binary ( * ) ( *.) l r | DIV-> fold_binary (/) (/.) l r) | CastExpr(t,e) -> (match t with Types.Builtin(Types.Int) -> Int(convert_to_int (fold_const e)) | Types.Builtin(Types.Float) -> Float(convert_to_float (fold_const e))) | _ -> raise NotConstant
定数計算のコードなんだけど、こういうのは関数型言語で書いたほうが楽。(untabifyのような文字列処理がよくないのかもしれない。だから文字列はリストであるべきだというHaskellの主張は正しい)
というより、もとのC++で書いたやつがよくなかったのか。あれは、構文木の各要素がオブジェクトになってる感じだった。
class CastExpr :public Expr { types::Type *to_type_; Expr *expr_; public: CastExpr ( types::Type *to, Expr *e ) : to_type_(to), expr_( e ) { } virtual void resolve( symtab::ResolveContext *ctxt ) { expr_->resolve( ctxt ); } virtual void emit_direct( intermediate::IntermediateCode *ctxt, enum target t, stackuse *su ); virtual int getHeight_impl() { return expr_->getHeight( ); } virtual types::Type *check_type_impl( types::TypeCheckContext *ctxt ); bool is_constant( ) { return expr_->is_constant(); } const_value fold() ; };
こんな感じのが一杯ある。んで、定数計算が、fold()で定義してあって、
common::const_value CastExpr::fold() { const_value v = expr_->fold(); types::Type *et = expr_->get_type(); if ( et == to_type_ ) { return v; } else if ( et == types::int_type && to_type_ ==types::float_type ) { v.f = (float) v.i; return v; } else if ( et == types::float_type && to_type_ ==types::int_type ) { v.i = (int)v.f; return v; } abort(); }
こんな感じ。ヤケクソで書いたabortとかがあるけど。
実際に書いた感想としては、こういうオブジェクト指向っぽい書きかたをすると、見通しが悪くなる気がする。
オブジェクト指向的に書くと、どうしても、
二項式の - 定数計算 - 型チェック - コード生成 キャスト式の - 定数計算 - 型チェック - コード生成 定数式の ...
こういう感じになってしまう。コードも、思考も。(syntax.cppがそれ)
確かに、こうすると、各式の処理が、他の式から隠蔽されて、新しい式を追加しやすくなる。
んだけど、まあ、実際の思考としては、こうなってほしいんだよな
定数計算 - 二項式の - キャスト式の - 定数式の 型チェック - 二項式の - キャスト式の - 定数式の コード生成 ...
理由は、特に無いけど、そういう気分になるんですよ。
隠蔽されるよりも、一気に全部を見渡せたほうが嬉しい気持ちになることもあるということで。
だからなんだという話なんだけど。オブジェクト指向的に書くよりもswitch-caseで書いたほうが嬉しいこともある…いや、switch-caseは弱すぎるので、ヴァリアント型とパターンマッチがあると良いっていうなんか普通の話。