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だけ・・・もうちょっとオペレータを増やして,それから共通部分を抽出して整理していきます.最低限,四則演算と剰余,それにスタック関係の基本的なオペレータをまずは準備しないと面白くありません.