GMachine on Haskell

-- 相変わらずParse Monadをぴゃぴゃっと書いてる
module Parser where

import Data.Char
import Control.Monad

data Result val rem = Parsed val rem
                    | NoParse
                      deriving Show

data Parser a = Parser (String -> Result a String)

ext_parser :: Parser a -> (String -> Result a String)
ext_parser (Parser p) = p

get_value :: Result val rem -> val
get_value (Parsed val rem) = val

instance Monad Parser where
    return a = Parser (\cs -> Parsed a cs)
                      
    p1 >>= f = Parser parse
        where parse cs = first ( (ext_parser p1) cs )
              first (Parsed val rem) =
                  let Parser p2 = f val
                  in second (p2 rem)
              first NoParse = NoParse

              second (Parsed val rem) = Parsed val rem
              second NoParse          = NoParse

instance MonadPlus Parser where
    mzero = Parser (\cs -> NoParse)

(Parser p) <|> (Parser q) = Parser (\cs -> case p cs of
                                             Parsed val rem -> Parsed val rem
                                             NoParse        -> q cs)

item :: Parser Char
item = Parser (\str -> case str of
                         ""     -> NoParse
                         (c:cs) -> Parsed c cs)

satisfy :: (Char -> Bool) -> Parser Char
satisfy p = do c <- item
               if p c
                 then return c
                 else mzero

char :: Char -> Parser Char
char c = satisfy (c ==)

string :: String -> Parser String
string ""     = return ""
string (c:cs) = do char c
                   string cs
                   return (c:cs)

many :: Parser a -> Parser [a]
many p = many1 p <|> return []

many1 :: Parser a -> Parser [a]
many1 p = do {a <- p;as <- many p; return (a:as)}

sepby :: Parser a -> Parser b -> Parser [a]
p `sepby` sep = (p `sepby1` sep) <|> return []

sepby1 :: Parser a -> Parser b -> Parser [a]
p `sepby1` sep = do a <- p
                    as <- many $ sep >> p -- セパレータの食い残しをpに食わせる
                    return (a:as)

white1 :: Parser String
white1 = many1 $ satisfy isSpace

word :: Parser String
word = many1 $ satisfy isAlpha

i_int :: Parser Int
i_int = do a <- many1 $ satisfy isDigit
           return $ read a

s_int :: Parser String
s_int = many1 $ satisfy isDigit
-- Version2
import Data.Char
import Data.List
import Parser

data Prog = Prog [SC] Exp deriving Show
data SC   = SC String [String] Exp deriving Show
data Exp  = Const Int | Var String | GFun String | App Exp Exp deriving Show
data Ope  = Pushint Int | Pushglobal String | Update Int | Push Int | Pop Int | Mkap | Unwind
          | Eval | Add | Les | Gre | Eq | Cond [Ope] [Ope] deriving Show

-- data HeapTree = NNum Int | NGlobal Int [Ope] | NAp Point Point | NInd Point
data HeapNode = NNum Int | NBool Bool | NGlobal Int [Ope] | NAp Point Point | NInd Point deriving Show

{-
NAp NIndは、ヒープ上のHeapNodeを指す為にインデックスを使っている。
-}
type Point  = Int -- インデックスを表す。
type Code   = [Ope]
type Stack  = [Point] -- ヒープ上のインデックス
type Dump   = [(Code,Stack)]
type Heap   = [HeapNode] -- tree作ってるから本当はHeapTreeの方が適当な気もする
type Global = [String] -- 関数名が入っている
type MachineState = (Code,Stack,Dump,Heap,Global)

int :: Int -> Exp
int x = Const x

var :: String -> Exp
var x = Var x

make_cenv :: [String] -> [(String,Int)]
make_cenv arg = zip arg [0..]

sc_scheme :: SC -> [Ope]
sc_scheme (SC name arg exp) = r_scheme exp (make_cenv arg) (length arg)

r_scheme :: Exp -> [(String,Int)] -> Int -> [Ope]
r_scheme exp cenv n = (c_scheme exp cenv) ++ [Update n,Pop n,Unwind]

lookup_var :: String -> [(String,Int)] -> Int
lookup_var var cenv = case lookup var cenv of
                        Just offset -> offset
                        Nothing     -> -1

c_scheme :: Exp -> [(String,Int)] -> [Ope]
c_scheme (GFun f)  p = [Pushglobal f]
c_scheme (Var x)   p = [Push $ lookup_var x p]
c_scheme (Const i) p = [Pushint i]
c_scheme (App e0 e1) p = (c_scheme e1 p) ++ (c_scheme e0 (p_n 1 p)) ++ [Mkap]
    where p_n n p = map (\(var,offset) -> (var,offset+n)) p

parse_term :: [String] -> Parser Exp
parse_term args = (parse_call args) <|> tfunc <|> tint
    where tfunc = do str <- fname
                     if elem str args
                       then return (Var str)
                       else return (GFun str)
          tint  = do int <- i_int
                     return (Const int)
          fname = do w  <- satisfy isAlpha <|> satisfy isSymbol
                     ws <- many (satisfy isAlpha <|> satisfy isNumber <|> satisfy isSymbol)
                     return (w:ws)

parse_call :: [String] -> Parser Exp
parse_call args = do char '('
                     fun_name <-  (parse_term args) `sepby1` white1
                     char ')'
                     return $ make_term fun_name
    where make_term :: [Exp] -> Exp
          make_term terms = foldl (App) (App (terms!!0) (terms!!1)) (drop 2 terms)

def_fun :: String -> [String] -> String -> SC
def_fun str args exp = def_fun' str args (get_value $ ext_parser (parse_term args) exp)
    where def_fun' :: String -> [String] -> Exp -> SC
          def_fun' f xn exp = SC f xn exp

parse_exp :: String -> Exp
parse_exp exp = get_value $ ext_parser (parse_term []) exp

eval :: [Ope] -> [Int] -> [([Ope], [Int])] -> [HeapNode] -> [String] -> [[String]] -> (Int, [[String]])
eval ((Pushint int):codes) stack dump heap global log = eval codes ((length heap):stack) dump (heap++[NNum int]) global log

eval ((Pushglobal f):codes) stack dump heap global log = eval codes ((find f global):stack) dump heap global log
    where find :: String -> [String] -> Int
          find f' global' = case elemIndex f' global' of
                              Just pos -> pos
                              Nothing  -> -1

eval ((Mkap):codes) (a1:a2:s) dump heap global log = eval codes ((length heap):s) dump (heap++[NAp a1 a2]) global log

eval ((Push n):codes) stack dump heap global log = eval codes ((stack!!n):stack) dump heap global log

eval ((Pop n):codes) stack dump heap global log = eval codes (drop n stack) dump heap global log

eval ((Update n):codes) stack dump heap global log = eval codes update_stack dump update_heap global log
    where update_heap = (take (stack!!(n+1)) heap) ++ [NInd (head stack)] ++ (drop ((stack!!(n+1))+1) heap)
          update_stack = tail stack

eval (Eval:codes) (a:s) dump heap global log = eval [Unwind] [a] ((codes,s):dump) heap global log

eval (Unwind:codes) stack dump heap global log = eval_unwind codes stack dump heap global log

{-
ユーザ定義(?)の関数群
-}
eval (Add:codes) (a0:a1:s) dump heap global log = eval codes ((length heap):s) dump (heap++[NNum (lval + rval)]) global log
    where (NNum lval) = heap!!a0
          (NNum rval) = heap!!a1
eval (Les:codes) (a0:a1:s) dump heap global log = eval codes ((length heap):s) dump (heap++[nnum (lval < rval)]) global log
    where (NNum lval) = heap!!a0
          (NNum rval) = heap!!a1
eval (Gre:codes) (a0:a1:s) dump heap global log = eval codes ((length heap):s) dump (heap++[nnum (lval > rval)]) global log
    where (NNum lval) = heap!!a0
          (NNum rval) = heap!!a1
eval (Eq:codes) (a0:a1:s) dump heap global log = eval codes ((length heap):s) dump (heap++[nnum (lval == rval)]) global log
    where (NNum lval) = heap!!a0
          (NNum rval) = heap!!a1
eval ((Cond i1 i2):codes) (a:s) dump heap global log = case heap!!a of
                                                         NNum 1  -> eval (i1++codes) s dump heap global log
                                                         NNum 0  -> eval (i2++codes) s dump heap global log
nnum :: Bool -> HeapNode
nnum True  = NNum 1
nnum False = NNum 0


eval_unwind :: [Ope] -> [Int] -> [([Ope], [Int])] -> [HeapNode] -> [String] -> [[String]] -> (Int, [[String]])
eval_unwind code stack@(top:lefts) dump heap global log = 
    case heap!!top of
      NNum n -> if null dump
                then (n,log)
                else eval (fst $ head dump) (top:(snd $ head dump)) (tail dump) heap global log
      NAp a1 a2 -> eval_unwind code (a1:stack) dump heap global log
      NInd a -> eval_unwind code (a:lefts) dump heap global log
      NGlobal n c -> if (length lefts) < n
                     then eval (fst $ head dump) ((head $ reverse lefts):(snd $ head dump)) (tail dump) heap global log
                     else eval new_code new_stack dump heap global
                              (log++[[show new_code,
                                      show $ get_heap $ take n new_stack,
                                      show $ heap]])
    where (new_code,new_stack) = goto_globalfunc (heap!!top)
          goto_globalfunc (NGlobal arity fcode) = (fcode,
                                                   (get_args $ take arity $ tail stack) ++ [stack!!arity])
          get_args points = map (\x -> let (NAp l r) = (heap!!x) in r) points
          get_heap points = map (\x -> heap!!x) points


make_heap_global :: [SC] -> ([HeapNode], [String])
make_heap_global scs = (make_heap scs,make_global scs)

make_heap :: [SC] -> [HeapNode]
make_heap []                      = []
make_heap ((SC name arg exp):scs) = (NGlobal (length arg) (sc_scheme (SC name arg exp))):(make_heap scs)

make_global :: [SC] -> [String]
make_global []                    = []
make_global ((SC name arg exp):scs) = name:(make_global scs)


main_exp str = (c_scheme (parse_exp str) []) ++ [Unwind]
(heap',global') = make_heap_global [def_fun "f1" ["x1","x2"] "x1",def_fun "f2" ["x1","x2"] "x2"]
(gm_add_heap,gm_add_global) =  ([NGlobal 2 [Push 1,Eval,Push 1,Eval,Add,Update 2,Pop 2,Unwind]],["+"])
(gm_les_heap,gm_les_global) =  ([NGlobal 2 [Push 1,Eval,Push 1,Eval,Les,Update 2,Pop 2,Unwind]],["<"])
(gm_gre_heap,gm_gre_global) =  ([NGlobal 2 [Push 1,Eval,Push 1,Eval,Gre,Update 2,Pop 2,Unwind]],[">"])
(gm_eq_heap,gm_eq_global) =  ([NGlobal 2 [Push 1,Eval,Push 1,Eval,Eq,Update 2,Pop 2,Unwind]],["=="])
(gm_if_heap,gm_if_global) =  ([NGlobal 3 [Push 0,Eval,Cond [Push 1] [Push 2],Update 3,Pop 3,Unwind]],["if"])
(gm_heap,gm_global) = (heap' ++ gm_add_heap ++ gm_les_heap ++ gm_gre_heap ++ gm_eq_heap ++ gm_if_heap,
                       global' ++ gm_add_global ++ gm_les_global ++ gm_gre_global ++ gm_eq_global ++ gm_if_global)
ope' = main_exp "(if (< 1 2) 1 2)"

main = print $ eval ope' [] [] gm_heap gm_global []

コードの殴り書き。

http://www.jaist.ac.jp/~kiyoshiy/writing/gmachine.pdf


ちょっと前に書いた似非は似非で動いたのですが、SECDを元(にしてるのかしらこれは??)な感じでgraph-reductionを適当な命令群だけで実行出来る様にしたmachineの事です。


ただコードを書き直しただけな気がしますが、これでも色々とピンと来ない所もあったので、自分でstackやらheapを紙に書きまくって色々と理解しました。


突っかかってたのはpushする所かなーとかそんな感じかもしれません。


超似非との違いは・・・まぁよりgraph弄ってるっていう感じがあるのと、call-by-needはheap上で見やすい形で展開されていくとかそんな感じかもしれません。


何か習得した気はしないのですが、1週間近くも無い頭で色々と考えていたので、多分何かしら力に成ってくれたとは思います。