packages feed

SableCC2Hs-0.0.0.1: src/SableCC/GenerateHaskell.hs

----------------------------------------------------------------------------
-- |
-- Module      :  SableCC.GenerateHaskell
-- Copyright   :  (c) Fontaine 2011
-- License     :  BSD3
--
-- Maintainer  :  fontaine@cs.uni-duesseldorf.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
--
-----------------------------------------------------------------------------

{-# Language RecordWildCards #-}
module SableCC.GenerateHaskell
(
generateHaskellTables
)
where

import SableCC.XML.ParserDefinitionRaw as Raw
import SableCC.XML.XML2Parser
import SableCC.ParserTypes as Parser
import SableCC.ParserTypes as Lexer

import SableCC.AstDefinition as Ast

import SableCC.ParserDefinition as Parser
import SableCC.LexerDefinition as Lexer

import Data.Array as Array
import Text.PrettyPrint.HughesPJ
import qualified Data.List as List
import qualified Data.Map as Map
import Data.Maybe
import Data.Char as Char

-- | Read the parser tables from an XML file and generate
-- Haskell modules which contain the tables as Haskell sourcecode.
generateHaskellTables :: FilePath -> IO ()
generateHaskellTables xmlFile = do
  putStrLn "reading Parser"
  rawParser <- readParser xmlFile
  let
     parserDefinition = makeParserDefinition rawParser
     astDefinition = makeAstDefinition rawParser
     lexerDefinition = makeLexerDefinition rawParser
  putStrLn "writing MakeUntypedAST.hs"
  writeFile "MakeUntypedAST.hs" $ render
    $ makeUntypedTransformation astDefinition

  putStrLn "writing ParserTables.hs"
  writeFile "ParserTables.hs" $ render $ vcat [
     text "-- autogenerated File do not edit!"
    ,text "module ParserTables where"
    ,text "import SableCC.ParserTypes"
    ,text "import Data.Array as Array"
    ,text "import qualified Data.Map as Map"
    ,text "import SableAst"
    ,text "import SableTokens"
    ,text "import Data.Array"
    ,text "import Data.Dynamic"
    ,text "import Data.Char (chr)"
    ,text "eofTokenID :: Int"
    ,text "eofTokenID =" <+> (int $ parser_parser_eof_index rawParser)
    ,makeLexerTables lexerDefinition
    ,makeParserTables parserDefinition
    ,makeDynamicTransformation astDefinition
    ]

  putStrLn "writing SableAST.hs"
  writeFile "SableAst.hs" $ render $ makeSableAst astDefinition


makeUntypedTransformation :: AstDefinition -> Doc
makeUntypedTransformation astDefinition = vcat [
   text "-- autogenerated File do not edit!"
  ,text "module MakeUntypedAST where"
  ,text "import ParserTypes (PToken, ReductionID (..), CST(..),AST(..)"
  ,text "import Data.Array"
  ,text "-- reduce actions that transform the CST to the AST"
  ,let len = unReductionID $ snd $ bounds $ reductionTable astDefinition
   in text "reduceTable = listArray (ReductionID 0,ReductionID" <+> int len <+> text ") $ reduceActions"
  ,text "cst2ast :: CST -> AST"
  ,text "cst2ast (CstLeaf t) = ASTLeaf t"
  ,text "cst2ast (CstNode reduction list)"
  ,text "  = (reduceTable ! reduction) $ map cst2ast list"
  ,text "reduceActions :: [[AST] -> AST]"
  ,text "reduceActions = ["
  ,nest 4 $ vcat $ punctuate comma $ map mkUntypedRule $ Array.elems $ reductionTable astDefinition
  ,nest 4 $ text "]"
  ]

mkUntypedRule :: Ast.Action -> Doc
mkUntypedRule Action {..} = vcat [
   text "-- rule :" <+> (int $ rule_index actionRule) <+> (text $ rule_ename actionRule)
  ,text "\\[" <> (hcat $ punctuate comma popArgs) <> text "] -> "
  ,nest 4 $ expr actionExpression
  ]
  where
    popArgs = map (text . action_result . unActionArgs)
               $ filter isPopAction $ rule_actions actionRule

    expr :: ActionExpression -> Doc
    expr term = case term of
      Var s -> text s
      EmptyList -> text "ASTList []"
      SingletonList e -> fun "ASTList" [text "[" <+> expr e <+> text "]"]
      AppendNode l n -> fun "ASTList" [fun "(++)" [fun "unASTList" [expr l] , text "[" <+> expr n <+> text "]"]]
      AppendList l1 l2 -> fun "ASTList" [fun "(++)" [fun "unASTList" [expr l1], fun "unASTList" [expr l2]]]
      NewNode prod alt l -> fun "ASTNode"
         [ text $ show $ alt_name alt
         , text "[" <+> (hsep $ punctuate comma $ map expr l) <+> text "]" ]
      FromNode e -> expr e
      FromList e -> fun "head" [fun "unASTList" [expr e]]
      NodeResult e -> expr e
      ListResult e -> fun "ASTList" [text "[" <+> expr e <+> text "]"]

fun :: String -> [Doc] -> Doc
fun name args = vcat [
        text "(" <+> text name
       ,nest 4 $ vcat $ map parens args
       ,text ")"
       ]

makeSableAst :: AstDefinition -> Doc
makeSableAst astDefinition = vcat [
   text "-- generated Module ! DO NOT EDIT"
  ,text "{-# Language DeriveDataTypeable #-}"
  ,text "module SableAst where"
  ,text "import SableTokens"
  ,text "import Data.Data"
  ,makeHaskellDataType $ productions astDefinition
  ]

makeHaskellDataType :: [Raw.Prod] -> Doc
makeHaskellDataType = vcat . map makeProd
  where
    makeProd prod =  vcat [
       text "--"
      ,text "data" <+> (text $ tail $ prod_ename prod)
      ,nest 4 $ vcat $ zipWith makeAlt (prod_alt prod) ("=" : repeat "|")
      ,nest 4 $ text "deriving (Typeable,Data,Show)"
      ]
    makeAlt alt seperator
      = text seperator <+> (text $ tail $ alt_ename alt) <+> (hsep $ map makeElem $ alt_elem alt)
    makeElem elem = if elem_is_list elem
       then text "[" <+> body <+> text "]"
       else body
     where body = text $ tail $ elem_etype elem


makeDynamicTransformation :: AstDefinition -> Doc
makeDynamicTransformation astDefinition = vcat [
   text "-- reduce actions that transform the CST to a AST data type"
  ,text "-- this works with data dynamic"
  ,text "data DAst"
  ,text "  = DLeaf PToken"
  ,text "  | DNode Dynamic"
  ,text "  | DList [DAst]"
  ,text "  deriving (Show)"

  ,text "fromDList :: DAst -> [DAst]"
  ,text "fromDList (DList l) = l"
  ,text "fromDList e = error \"fromDList : not DList\""

  ,text "fromDNode :: Typeable t => DAst -> t"
  ,text "fromDNode (DNode l) = fromDyn l (error \"fromDNode\")"
  ,text "fromDNode (DLeaf l) = fromToken l"
  ,text "fromDNode e = error \"fromDNode : not DLeaf or DNode\""

  ,text "toDNode :: Typeable t => t -> DAst"
  ,text "toDNode = DNode . toDyn"
  ,text "reduceTable = listArray (ReductionID 0,ReductionID $ (length reduceActions) -1) reduceActions"
  ,text "cst2ast :: CST -> DAst"
  ,text "cst2ast (CstLeaf t) = DLeaf t"
  ,text "cst2ast (CstNode reduction list)"
  ,text "  = (reduceTable ! reduction) $ map cst2ast list"
  ,text "reduceActions :: [[DAst] -> DAst]"
  ,text "reduceActions = ["
  ,nest 4 $ vcat $ punctuate comma $ map mkDynRule $ Array.elems $ reductionTable astDefinition
  ,nest 4 $ text "]"
  ]


mkDynRule :: Ast.Action -> Doc
mkDynRule Action {..} = vcat [
   text "-- rule :" <+> (int $ rule_index actionRule) <+> (text $ rule_ename actionRule)
  ,text "\\[" <> (hcat $ punctuate comma popArgs) <> text "] -> "
  ,nest 4 $ expr actionExpression
  ]
  where
    popArgs = map (text . action_result . unActionArgs)
               $ filter isPopAction $ rule_actions actionRule

    expr :: ActionExpression -> Doc
    expr term = case term of
      Var s -> text s
      EmptyList -> text "DList []"
      SingletonList e -> fun "DList" [text "[" <+> expr e <+> text "]"]
      AppendNode l n -> fun "DList" [fun "(++)" [fun "fromDList" [expr l] , text "[" <+> expr n <+> text "]"]]
      AppendList l1 l2 -> fun "DList" [fun "(++)" [fun "fromDList" [expr l1], fun "fromDList" [expr l2]]]
      NewNode prod alt l -> fun "toDNode" [ fun (tail $ alt_ename alt) $ zipWith mkChild l (alt_elem alt)]
      FromNode e -> expr e
      FromList e -> fun "head" [fun "fromDList" [expr e]]
      NodeResult e -> expr e
      ListResult e -> fun "DList" [text "[" <+> expr e <+> text "]"]
      where
        mkChild :: ActionExpression -> Raw.Elem -> Doc
        mkChild e elem = if elem_is_list elem
          then fun "map" [fun "fromDNode" [], fun "fromDList" [expr e]]
          else fun "fromDNode" [expr e]


makeLexerTables :: LexerDefinition -> Doc
makeLexerTables lexerDefinition = vcat [
   text "tokenIDMap :: Map.Map Int Int"
  ,text "tokenIDMap = Map.fromList ["
  ,nest 4 $ hcat $ punctuate comma $ flip mapMaybe (Array.assocs $ lexerTokens lexerDefinition)
             $ \(LexTokenID tokenID,token) -> do
                 parserID <- token_parser_index token
                 return $ text $ show (tokenID,parserID)
  ,nest 4 $ text "]"
  ,text "lexerModes = listArray" <+> modeBounds <+> hsList modeNames
  ,vcat $ map makeModeDef $ Array.assocs $ lexerModes lexerDefinition
  ,text "modeTransitions = listArray" <+> transitionBounds <+> (hsList $ map makeModeID $
      Array.elems $ modeTransitions lexerDefinition)
  ]
  where
    modeBounds = parens (makeModeID minMode <+> comma <+> makeModeID maxMode)
    (minMode,maxMode) = Array.bounds $ lexerModes lexerDefinition
    hsList x = text "[" <+> (hsep $ punctuate comma x) <+> text "]"
    modeNames = map (\(ModeID x) -> text "mode_"<> int x) $ Array.indices $ lexerModes lexerDefinition 

    makeModeDef :: (ModeID, (String, Lexer.Mode)) -> Doc
    makeModeDef (ModeID modeID,(modeName, Lexer.Mode mode)) = vcat [
      text "mode_" <> int modeID <+> text "= (,)" <+> (text $ show modeName) <+> text "$ Mode $ listArray"
        <+> stateBounds <+> text "["
        ,nest 4 $ vcat $ punctuate comma $ map makeState $ Array.elems mode
        ,nest 4 $ text "]"
        ]
      where
        (LexStateID minState, LexStateID maxState) = Array.bounds mode
        stateBounds = text "( LexStateID" <+> int minState <+> text ", LexStateID" <+> int maxState <+> text ")"

    makeStateID (LexStateID stateID) = hFun "LexStateID" [int stateID]
    makeModeID (ModeID modeID) = hFun "ModeID" [int modeID]

    makeState :: Lexer.State -> Doc
    makeState state = vcat [
       text "(State"
      ,nest 4 $ makeIntervalTree $ stateTransitions state
      ,nest 4 $ case stateAcceptedToken state of
         Nothing -> text "Nothing)"
         Just (LexTokenID tok) -> text "(Just (LexTokenID" <+> int tok <+> text ")))"
      ]
    makeIntervalTree IntervalLeaf = text "IntervalLeaf"
    makeIntervalTree (IntervalNode lowTree lowChar target highChar highTree) = hFun "IntervalNode" [
       makeIntervalTree lowTree
      ,makeChar lowChar
      ,case target of
        Epsilon (stateID) -> hFun "Epsilon" [makeStateID stateID]
        NotEpsilon (stateID) -> hFun "NotEpsilon" [makeStateID stateID]
      ,makeChar highChar
      ,makeIntervalTree highTree
      ]
      where
        makeChar x = hFun "chr" [int $ Char.ord x]

    transitionBounds = parens (transIndex minTransition <+> comma <+> transIndex maxTransition)
      where
        (minTransition,maxTransition) = Array.bounds $ modeTransitions lexerDefinition
        transIndex (LexTokenID tok, mode) = parens (
          fun "LexTokenID" [int tok] <+> comma <+> makeModeID mode)

hFun :: String -> [Doc] -> Doc
hFun name args = parens (text name <+> hsep args)


makeParserTables :: ParserDefinition -> Doc
makeParserTables parserDefinition = vcat [
   text "actionTable = listArray" <+> actionTableBounds <+> text "["
  ,nest 4 $ vcat $ punctuate comma $ map makeTransitionList $ Array.elems $ actionTable parserDefinition
  ,nest 4 $ text "]"
  ,text "gotoTable = listArray" <+> gotoTableBounds <+> text "["
  ,nest 4 $ vcat $ punctuate comma $ map makeGotoMap $ Array.elems $ gotoTable parserDefinition
  ,nest 4 $ text "]"
  ,text "errorTable = listArray" <+> errorTableBounds <+> text "["
  ,nest 4 $ vcat $ punctuate comma $ map (text . show) $ Array.elems $ errorTable parserDefinition
  ,nest 4 $ text "]"
  ]
  where
    actionTableBounds = parens (text "ParserStateID" <+> int minStateID
                            <+> comma <+> text "ParserStateID" <+> int maxStateID)
      where
         (ParserStateID minStateID, ParserStateID maxStateID) = Array.bounds $ actionTable parserDefinition
    gotoTableBounds = parens (text "GotoIndex" <+> int minGotoID <+> comma <+> text "GotoIndex" <+> int maxGotoID)
      where
         (GotoIndex minGotoID, GotoIndex maxGotoID) = Array.bounds $ gotoTable parserDefinition

    errorTableBounds = parens (text "ErrorID" <+> int minErrorID <+> comma <+> text "ErrorID" <+> int maxErrorID)
      where
         (ErrorID minErrorID, ErrorID maxErrorID) = Array.bounds $ errorTable parserDefinition

    makeTransitionList :: TransitionList -> Doc
    makeTransitionList (TransitionList {..})
      = fun "TransitionList" [makeAction defaultTransition
         , text "Map.fromList [" <+> (hsep $ punctuate comma $ map makeTrans $ Map.assocs transitionMap) <+> text "]"]

    makeAction :: Parser.Action -> Doc
    makeAction action = case action of
      (Shift (ParserStateID stateID)) -> hFun "Shift" [hFun "ParserStateID" [int stateID]]
      (Reduce (Reduction {..})) -> hFun "Reduce" [ fun "Reduction" [
          hFun "ReductionID" [int $ unReductionID reductionID]
         ,int popCount
         ,hFun "GotoIndex" [int $ unGotoIndex gotoIndex]
         ]]
      (Error (ErrorID errorID)) -> hFun "Error" [hFun "ErrorID" [int errorID]]
      Accept -> text "Accept"

    makeTrans :: (ParserTokenID, Parser.Action) -> Doc
    makeTrans (ParserTokenID tokenID, action)
      = parens (text "ParserTokenID" <+> int tokenID <+> comma <+> makeAction action)


    makeGotoMap :: GotoMap -> Doc
    makeGotoMap (GotoMap {..}) = fun "GotoMap" [
        fun "ParserStateID" [int $ Parser.fromParserStateID defaultGoto]
       ,fun "Map.fromList" [hsep [
           text "["
          ,hsep $ punctuate comma $ map stPair $ Map.assocs gotoMap
          ,text "]" ]]
      ]
      where stPair (ParserStateID a, ParserStateID b)
               = parens (text "ParserStateID" <+> int a <+> comma <+> text "ParserStateID" <+> int b)