monadic stack to register machine translator

William Lee Irwin III wli@holomorphy.com
Wed, 13 Nov 2002 03:58:22 -0800


module GT where
import Monad
import Monoid
import MonadState
import MonadWriter
import MonadRWS

-- Just a quick exercise in using monads.
-- Thought it'd be nice to share with the class.

data GOp
	= PushVal Integer
	| Push Integer
	| Pop Integer
	| Slide Integer
	| Update Integer
	| GAdd | GSub | GMul | GDiv | GMod | GPow
	| GNeg | GAbs
	deriving (Eq, Ord, Read, Show)

type Tmp = Integer

data ROp
	= LoadImm Tmp Integer
	| RAdd Tmp Tmp Tmp
	| RSub Tmp Tmp Tmp
	| RMul Tmp Tmp Tmp
	| RDiv Tmp Tmp Tmp
	| RMod Tmp Tmp Tmp
	| RPow Tmp Tmp Tmp
	| RNeg Tmp Tmp
	| RAbs Tmp Tmp
	deriving (Eq, Ord, Read, Show)

type CounterT m t = StateT Integer m t
type StackT t = State [Integer] t

type GST t = RWS () [ROp] (Integer, [Integer]) t

class Stack f where
	pushVal, push, pop, update, slide :: Integral t => t -> f ()
	popVal :: Integral t => f t

instance Integral t => Stack (RWS () [ROp] (t, [t])) where
	pushVal n = do
					(ctr, stk) <- get
					put (ctr, fromIntegral n : stk)
	popVal = do
					(ctr, top:stk) <- get
					put (ctr, stk)
					return (fromIntegral top)
	push n = do
				(ctr, stk) <- get
				put (ctr, stk!!fromIntegral n : stk)
	pop n = do
				(ctr, stk) <- get
				put (ctr, drop (fromIntegral n) stk)
	slide n = do
				(ctr, top:stk) <- get
				put (ctr, top : drop (fromIntegral n) stk)
	update n = do
				(ctr, top:stk) <- get
				let (front, _:back) = splitAt (fromIntegral n) stk
				put (ctr, front ++ [top] ++ back)

class Counter f where
	gen :: Enum t => f t

instance Integral t => Counter (RWS () [ROp] (t, [t])) where
	gen = do
				(ctr, stk) <- get
				put (ctr + 1, stk)
				return . toEnum . fromIntegral $ ctr + 1

instance (Enum t, Monad m) => Counter (StateT t m) where
	gen = do
				ctr <- get
				put $ succ ctr
				ctr <- get
				return . toEnum $ fromEnum ctr
				
translate gOps = snd $ evalRWS (mapM trans gOps) () (0,[])

trans :: GOp -> GST ()
trans i = case i of
	PushVal n	->
		do
			reg <- gen
			tell [LoadImm reg n]
			pushVal reg
	Push n		-> push n
	Pop n		-> pop n
	Slide n		-> slide n
	Update n	-> update n
	GAdd		-> doBinOp RAdd
	GSub		-> doBinOp RSub
	GMul		-> doBinOp RMul
	GDiv		-> doBinOp RDiv
	GMod		-> doBinOp RMod
	GPow		-> doBinOp RPow
	GNeg		-> doUnOp RNeg
	GAbs		-> doUnOp RAbs
	where
		doUnOp op =
			do
				x <- popVal
				y <- gen
				tell [op y x]
				pushVal y
		doBinOp op =
			do
				x <- popVal
				y <- popVal
				z <- gen
				tell [op z x y]
				pushVal z