Morphing ASTs and scrapping boilerplate code

Folks, I'm transforming ASTs as part of my compiler from one language into another. The source AST is a list of statements whereas the target AST is a "class definition". type Object a = State Obj a data Obj = Object { objSym :: Integer -- starting # for gensym , objVars :: M.Map String VarDecl , objProps :: M.Map String PropDecl , objMeths :: M.Map String MethodDecl } deriving (Show, Eq) I'm using a state monad and the transformations are supposed to update the state (object) as needed to add variables, methods, etc. class Morpher a b | a -> b where morph :: a -> Object b I have a lot of boilerplate code like this and wonder how I can scrape it. instance Morpher Type C.Type where morph TyInt = return C.TyInt morph TyFloat = return C.TyFloat morph TyStr = return C.TyStr morph TyBool = return C.TyBool morph TyColor = return C.TyColor morph TyStyle = return C.TyStyle morph (TyList ty) = liftM C.TyList (morph ty) morph (TyArray ty) = liftM C.TyArray (morph ty) morph (TySeries ty) = liftM C.TySeries (morph ty) morph (TyInput ty) = liftM C.TyProp (morph ty) morph (TyRef ty) = liftM C.TyRef (morph ty) morph TyUnit = return C.TyUnit morph TyPrintDest = return C.TyPrintDest Notice that I'm calling a constructor of the same name in a different module ... unless it's an exception. instance Morpher BackRef C.Expr where morph Now = C.Int 0 morph (BarsBack e) = morph e instance Morpher DataRef C.Expr where morph (DS x) = C.Int x How can I reduce the boilerplate here? Thanks, Joel -- http://wagerlabs.com/

Just to clarify, I really liked the SYB solution to my previous issue with stripping token locations. It looked like this: strip :: (Data a) => a -> a strip = everywhere (mkT f) where f (TokenPos a _) = a f x = x In the general AST transformation case, my constructor name is the same and so is the number of arguments. So, yes, it's a different type but it's a derivative of Data and Typeable just like the original type. Conceptually, I want to "lookup" the new type from a given module, using the same constructor name as the original type. Then I want to apply morph to every argument of the source constructor and give the resulting values to the new constructor. Lastly, I would like to write pattern matches for special cases (see TokenPos above) and write out the transformation by hand. My ASTs are quite large and there aren't that many special cases. Thanks, Joel On Apr 19, 2007, at 5:11 PM, Joel Reymont wrote:
instance Morpher Type C.Type where ... morph (TyList ty) = liftM C.TyList (morph ty) morph (TyArray ty) = liftM C.TyArray (morph ty) morph (TySeries ty) = liftM C.TySeries (morph ty) morph (TyInput ty) = liftM C.TyProp (morph ty) morph (TyRef ty) = liftM C.TyRef (morph ty) morph TyUnit = return C.TyUnit morph TyPrintDest = return C.TyPrintDest

Joel Reymont wrote:
I have a lot of boilerplate code like this and wonder how I can scrape it.
instance Morpher Type C.Type where morph TyInt = return C.TyInt morph TyFloat = return C.TyFloat morph TyStr = return C.TyStr morph TyBool = return C.TyBool morph TyColor = return C.TyColor morph TyStyle = return C.TyStyle morph (TyList ty) = liftM C.TyList (morph ty) morph (TyArray ty) = liftM C.TyArray (morph ty) morph (TySeries ty) = liftM C.TySeries (morph ty) morph (TyInput ty) = liftM C.TyProp (morph ty) morph (TyRef ty) = liftM C.TyRef (morph ty) morph TyUnit = return C.TyUnit morph TyPrintDest = return C.TyPrintDest
One option is to change your data types. I would suggest something like this:
data Type = TyPrim TyPrim | TyCon TyCon Type data C.Type = C.TyPrim TyPrim | C.TyCon TyCon C.Type
where TyPrim and TyCon (primitves and constructors) can be shared:
data TyPrim = TyInt | TyString | TyFloat | TyBool | etc. data TyCon = TyList | TyArray | TySeries | TyInput | TyRef
Now the class instance can be:
instance Morpher Type C.Type where morph (TyPrim p) = return (C.TyPrim p) morph (TyCon c t) = C.TyCon c <$> morph t -- or liftM for people not so in love with Applicative :)
Twan
participants (2)
-
Joel Reymont
-
Twan van Laarhoven