phorth---(その1.4)オペレータ定義の仕様変更2
Evaluator
さらに,スタックも
type Stack = [Stackable]
から
type Stack = [Stackable] data Stackable = Pushed Literal | Opname Word | Code AST deriving Show
と変更します.つまり「新しいオペレータの名前」と「コードのブロック」もスタックに積めるように準備しておきます.Opname/CodeはAST内のName/Blockですが,同じ名前だとMultiple declarationsと怒られてしまいます.Literalにもデータコンストラクタが必要になるのも注意です.さらにShowのインスタンスにしておきます.
気持ち悪いのは。。ASTでのデータコンストラクタ名がそのままではないこと.これは混乱の元になりかねないので,さらに対処すべきTODOです.また,スタックにリテラルを積んだときにデータコンストラクタがついたのもいやです.少なくともスタックの内容を表示させるときにPushedはついて欲しくないのでここも要検討.
インタープリータの実装についてはほとんど変わりませんが,defオペレータが「引数をとる」オペレータになります.
interp:: Interp -> AST -> IO Interp interp i (x:xs) = do i' <- procExp i x interp i' xs interp i [] = return i procExp:: Interp -> Exp -> IO Interp procExp i@Interp {stack=xs} (Push lit) = return i{stack=(Pushed lit):xs} procExp i@Interp {stack=xs} (Name name) = return i{stack=(Opname name):xs} procExp i@Interp {stack=xs} (Block ast) = return i{stack=(Code ast):xs} procExp i@Interp {stack=x:xs} (Invoke "pop") = return i{stack=xs} procExp i@Interp {stack=(Code code):(Opname name):xs, dict = dict} (Invoke "def") = return i{stack=xs,dict = Map.insert name code dict } procExp i@Interp{dict=dict} (Invoke name) = interp i (dict Map.! name)
使えるのは,push/pop/defだけ・・・もうちょっとオペレータを増やして,それから共通部分を抽出して整理していきます.最低限,四則演算と剰余,それにスタック関係の基本的なオペレータをまずは準備しないと面白くありません.