packages feed

hgom-0.5.1: Gom/CodeGen/Common/Helpers.hs

-------------------------------------------------------------------
-- |
-- Module      : Gom.CodeGen.Common.Helpers
-- Copyright   : (c) Paul Brauner 2009
--               (c) Emilie Balland 2009
--               (c) INRIA 2009
-- Licence     : GPL (see COPYING)
--
-- Maintainer  : paul.brauner@inria.fr
-- Stability   : provisional
-- Portability : non-portable (requires generalized newtype deriving)
--
-- Some utility functions for generating
-- java and tom pieces of type 'Doc'.
--------------------------------------------------------------------

module Gom.CodeGen.Common.Helpers (
  -- * Java pretty-printing
  -- ** Generic
  ibraces, sbraces, encloseCommas, 
  encloseCommasNB, rBody, _u,
  -- ** Java keywords
  abstract, public, protected, private, this,
  jreturn, throw, new, void, instanceof, jif,
  while, jelse, true, false, final, static,
  -- ** Java Types
  jint, jStringBuilder, jboolean,
  jObject, jVisitable, jShared, jSharedId,
  jSCombinator, jStrategy,
  jVisitableArray, jStrategyArray,
  -- ** Classes
  rClass,
  -- ** Classes
  rInterface,
  -- ** Methods
  rMethodDef, rMethodCall, rWrapBuiltin, rConstructorCall,
  -- ** Control structures
  rIfThen, rIfThenElse, rWhile, rSwitch,
  -- * Tom pretty-printing
  -- ** Mappings
  rOp, rOpList, rTypeterm,
  rIsFsym, rMake, rGetSlot,
  -- ** Other
  inline
) where

import Text.PrettyPrint.Leijen

-- | Adds an underscore in front of an id
_u :: Doc -> Doc
_u = (text "_" <>)

-- | Encloses a document into { } and indents the body.
ibraces :: Doc -> Doc
ibraces d = group $ nest 2 (lbrace <$> d) <$> rbrace

-- | Encloses a document into { }
sbraces :: Doc -> Doc
sbraces d = lbrace <+> d <+> rbrace

abstract,public,protected,private :: Doc
this,jreturn,throw,new,void,final,static :: Doc
instanceof,jif,while,jelse,true,false :: Doc

abstract   = text "abstract"
public     = text "public"
protected  = text "protected"
private    = text "private"
this       = text "this"
jreturn    = text "return"
throw      = text "throw"
new        = text "new"
void       = text "void"
instanceof = text "instanceof"
jif        = text "if"
while      = text "while"
jelse      = text "else"
true       = text "true"
false      = text "false"
final      = text "final"
static     = text "static"

jint,jStringBuilder,jboolean,jObject :: Doc
jSCombinator,jStrategy, jVisitable :: Doc
jShared,jSharedId,jVisitableArray,jStrategyArray :: Doc
jint            = text "int"
jboolean        = text "boolean"
jStringBuilder  = text "java.lang.StringBuilder"
jObject         = text "Object"
jVisitable      = text "tom.library.sl.Visitable"
jStrategy       = text "tom.library.sl.Strategy"
jSCombinator    = text "tom.library.sl.AbstractStrategyCombinator"
jShared         = text "shared.SharedObject"
jSharedId       = text "shared.SharedObjectWithID"
jVisitableArray = jVisitable <> text "[]"
jStrategyArray  = jStrategy <> text "[]"

-- | Renders the list enclosed in parenthesis and
-- separated by commas. Breaks lines if the list is too long.
encloseCommas :: [Doc] -> Doc
encloseCommas [] = text "()"
encloseCommas l = parens $ nest 2 (softbreak <> fillSep (punctuate comma l))

-- | Renders the list enclosed in parenthesis and
-- separated by commas. Doesn't break lines (NB is for No Break).
encloseCommasNB :: [Doc] -> Doc
encloseCommasNB [] = text "()"
encloseCommasNB l = parens . hcat . punctuate comma $ l

-- | Renders the list punctuated by semicolons
rBody :: [Doc] -> Doc
rBody [] = empty
rBody l = align $ sep (punctuate semi l) <> semi

-- | Renders @modifier class name { body }@.
rClass
 :: Doc   -- ^ modifier (public, private..)
 -> Doc   -- ^ class name
 -> Maybe Doc   -- ^ extends
 -> [Doc] -- ^ implements
 -> Doc   -- ^ body
 -> Doc
rClass md cn ex im body = 
  md <+> text "class" <+> cn <+> r1 ex <+> r2 im <+> ibraces body
  where r1 Nothing  = empty
        r1 (Just d) = text "extends" <+> d
        r2 [] = empty
        r2 l  = text "implements" <+> hsep (punctuate comma l)


-- | Renders @modifier interface name { body }@.
rInterface
 :: Doc   -- ^ modifier (public, private..)
 -> Doc   -- ^ interface name
 -> Doc   -- ^ body
 -> Doc

rInterface md cn body = 
  md <+> text "interface" <+> cn <+> ibraces body

-- | Renders @modifier type name(arg_1,...,arg_n) { body }@.
rMethodDef
 :: Doc   -- ^ modifier
 -> Doc   -- ^ return type
 -> Doc   -- ^ method name
 -> [Doc] -- ^ arguments
 -> Doc   -- ^ body
 -> Doc
rMethodDef md ty mn args body = 
  md <+> ty <+> mn <> encloseCommas args <+> ibraces body

-- | Renders @object.method(arg_1,...,arg_n)@.
rMethodCall
 :: Doc   -- ^ object
 -> Doc   -- ^ method name
 -> [Doc] -- ^ arguments
 -> Doc
rMethodCall o f args = 
  o <> dot <> f <> encloseCommas args

-- | Renders @new className(arg_1,...,arg_n)@.
rConstructorCall
 :: Doc   -- ^ class name
 -> [Doc] -- ^ arguments
 -> Doc
rConstructorCall c args = 
  new <+> c <> encloseCommas args

-- | @rWrapBuiltin qt@ renders
--
-- > tom.library.sl.VisitableBuiltin<qt>
rWrapBuiltin
  :: Doc -- ^ qualified type
  -> Doc
rWrapBuiltin qt =
  text "tom.library.sl.VisitableBuiltin" <> angles qt

-- | Renders @if(cond) { then body }@.
rIfThen 
  :: Doc -- ^ condition
  -> Doc -- ^ then body
  -> Doc
rIfThen c b = 
  group $ jif <+> parens c <+> ibraces b

-- | Renders @if(cond) { then body } else { else body }@.
rIfThenElse 
  :: Doc -- ^ condition
  -> Doc -- ^ then body
  -> Doc -- ^ else body
  -> Doc
rIfThenElse c b1 b2 = 
  group $ jif <+> parens c <+> ibraces b1 <+> jelse <+> ibraces b2

-- | Renders @while(cond) { body }@
rWhile
  :: Doc -- ^ cond
  -> Doc -- ^ body
  -> Doc
rWhile c b = group $ while <+> parens c <+> ibraces b

-- | @rSwitch s [(l1,r1),..,(ln,rn)] d@ renders
--
-- > switch (s) {
-- >   l1: r1
-- >   ...
-- >   l2: r2
-- >   default: d
-- > }
rSwitch
 :: Doc -- ^ subject
 -> [(Doc,Doc)] -- ^ lhs, rhs
 -> Maybe Doc -- ^ default case
 -> Doc
rSwitch s l d = text "switch" <+> parens s <+> ibraces body
  where body    = vcat (map f l) <$> 
                  maybe empty ((text "default:" <+>) . align) d
        f (x,y) = text "case" <+> x <> colon <+> align y

-- | @rTypeterm s qs shr@ renders
--
-- > %typeterm s {
-- >   implement { qs }
-- >   is_sort(t) { ($t instanceof qs) }
-- >   equals(t1,t2) { eq }
-- > }
--
-- where @eq = $t1 == $t2@ if @shr@,
-- @$t1.equals($t2)@ otherwise.
rTypeterm
  :: Doc -- ^ sort
  -> Doc -- ^ qualified sort
  -> Bool -- ^ sharing ?
  -> Doc
rTypeterm s qs deep =
  text "%typeterm" <+> s <+> ibraces (vcat
    [text "implement" <+> sbraces qs,
     text "is_sort(t) { ($t instanceof" <+> qs <> text ") }",
     text "equals(t1,t2) { (" <> eq <> text ") }"])
  where eq = text $ if deep then "$t1 == $t2"
                            else "$t1.equals($t2)"

-- | Renders @is_fsym(t) { ($t instanceof sort) }@.
rIsFsym
  :: Doc -- ^ sort 
  -> Doc
rIsFsym s = 
  text "is_fsym(t) { ($t instanceof" <+> s <> text ") }"

-- | Renders @make(arg1,..,argn) { (m.types.co.make($arg1,..,$argn)) }@.
rMake
 :: Doc   -- ^ qualified constructor
 -> [Doc] -- ^ arguments
 -> Doc
rMake qc as =
  text "make" <> args <+> (sbraces . parens) (qc <> text ".make" <> iargs)
  where gen   = parens . hcat . punctuate comma
        args  = gen as
        iargs = gen (map inline as)


-- | Renders @get_slot(slot,t) { $t.getslot() }@.
rGetSlot
 :: Doc -- ^ slot
 -> Doc
rGetSlot x =
  text "get_slot(" <> x <> text ",t) { $t.get" <> x <> text "() }"

-- | Renders @%op sort c(field1,..,field2) { body }@.
rOp :: Doc   -- ^ sort
    -> Doc   -- ^ constructor
    -> [(Doc,Doc)] -- ^ fields
    -> Doc   -- ^ body
    -> Doc
rOp s c fs b = 
   text "%op" <+> s <+> c <> 
   (parens . hsep . punctuate comma $ map f fs) <+> ibraces b
  where f (x,t) = x <> colon <> t

-- | @rOpList c co dom cons empty@ renders
--
-- > %oplist co VC(dom*) {
-- >   is_fsym(t) { (($t instanceof cons) || ($t instanceof empty)) }
-- >   make_empty() { empty.make() }
-- >   make_insert(e,l) { cons.make($e,$l) }
-- >   get_head(l) { $l.getHeadc() }
-- >   get_tail(l) { $l.getTailc() }
-- >   is_empty(l) { $l.isEmptyc() }
-- > }
rOpList 
  :: Doc -- ^ the constructor
  -> Doc -- ^ codomain of the constructor
  -> Doc -- ^ domain of the constructor
  -> Doc -- ^ qualified Cons
  -> Doc -- ^ qualified Empty
  -> Doc
rOpList c co dom consc emptyc =
  text "%oplist" <+> co <+> c <> parens (dom <> text "*") <+> 
   ibraces (vcat
     [text "is_fsym(t) { (($t instanceof" <+> consc <> 
        text ") || ($t instanceof" <+> emptyc <> text ")) }",
      text "make_empty() {" <+> emptyc <> text ".make() }",
      text "make_insert(e,l) {" <+> consc <> text ".make($e,$l) }",
      text "get_head(l) { $l.getHead" <> c <> text "() }",
      text "get_tail(l) { $l.getTail" <> c <> text "() }",
      text "is_empty(l) { $l.isEmpty" <> c <> text "() }"])

-- | @inline d@ renders @$d@.
inline :: Doc -> Doc
inline = (text "$" <>)