packages feed

SableCC2Hs-0.0.0.1: src/SableCC/GenerateProlog.hs

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

{-# LANGUAGE RecordWildCards #-}
module SableCC.GenerateProlog
(
generatePrologTables
)
where

import SableCC.XML.ParserDefinitionRaw as Raw
import SableCC.XML.XML2Parser
import SableCC.ParserDefinition as Parser
import SableCC.LexerDefinition as Lexer
import SableCC.AstDefinition as Ast
import SableCC.ParserTypes as Parser
import Data.Array as Array
import Data.Char (ord)
import qualified Data.Map as Map

import Text.PrettyPrint.HughesPJ hiding (Mode)

-- | Read the parser tables from an XML file and generate
-- modules which contain the tables as Prolog clauses

generatePrologTables :: FilePath -> IO ()
generatePrologTables xmlFile = do
  putStrLn "reading Parser"
  rawParser <- readParser xmlFile
  let
    lexerDefinition = makeLexerDefinition rawParser
    parserDefinition = makeParserDefinition rawParser
    astDefinition = makeAstDefinition rawParser
  putStrLn "writing parser_tables.pl"
  writeFile "parser_tables.pl" $ render $ vcat [
     text "% Generated File DO NOT EDIT !!"
    ,text ":- style_check(-singleton)."
    ,text ":- discontiguous transition/3, accepts/3."
    ,text ":- discontiguous action/3, defaultAction/2."
    ,text ":- discontiguous goto/3, defaultGoto/2."
    ,breakLine
    ,text "% definition of the token classes"
    ,breakLine
    ,vcat $ map makeToken $ Array.assocs $ lexerTokens lexerDefinition
    ,breakLine
    ,text "% definition of the transition table"
    ,breakLine
    ,vcat $ map makeMode $ Array.assocs $ lexerModes lexerDefinition
    ,breakLine
    ,text "% definition of the mode transitions"
    ,breakLine
    ,vcat $ map makeModeTransition $ Array.assocs $ modeTransitions lexerDefinition
    ,breakLine
    ,breakLine
    ,text "% special EOF token for Parser"
    ,predicate "eof_token_id" [text "ptoken_" <> (int $ parser_parser_eof_index rawParser)]
    ,breakLine
    ,text "% definition of the LR action table"
    ,vcat $ map prologActionTable $ Array.assocs $ actionTable parserDefinition
    ,breakLine
    ,text "% parser error messages"
    ,vcat $ map parserErrorMessage $ Array.assocs $ errorTable parserDefinition
    ,text "% definition of the LR goto table"
    ,vcat $ map prologGotoTable $ Array.assocs $ gotoTable parserDefinition
    ,breakLine
    ,text "% definition of the CST -> AST reductions"
    ,vcat $ map makeReductionRule $ Array.elems $ reductionTable astDefinition
    ]

breakLine :: Doc
breakLine = text $ "% " ++ replicate 60 '-'

term :: String -> [Doc] -> Doc
term p l = text p <> (parens $ hsep $ punctuate comma l)

predicate :: String -> [Doc] -> Doc
predicate p l = term p l <> char '.'

pString :: String -> Doc
pString s = char '\'' <> text s <> char '\''

makeTokenID :: LexTokenID -> Doc
makeTokenID (LexTokenID x) = text "token_" <> int x

makeStateID :: LexStateID -> Doc
makeStateID (LexStateID x) = text "state_" <> int x

makeModeID :: ModeID -> Doc
makeModeID (ModeID x) = text "mode_" <> int x

makeToken :: (LexTokenID, Raw.Token) -> Doc
makeToken (x, Raw.Token {..})
  = predicate "token" [
     makeTokenID x
    ,pString token_ename
    ,case token_parser_index of
       Nothing -> text "nothing"
       Just i  -> term "just" [text "ptoken_" <> int i]
    ]

makeMode :: (ModeID,(ModeName, Mode)) -> Doc
makeMode (modeId,(modeName, mode)) = vcat [
   breakLine
  ,text "% lexer mode : " <+> text modeName
  ,breakLine
  ,vcat $ map (makeTransition $ makeModeID modeId) $ Array.assocs $ modeStates mode
  ]


makeTransition :: Doc -> (LexStateID, State) -> Doc
makeTransition mode (state, State {..}) = vcat [
   predicate "transition" [makeStateID state, mode, makeIntervalTree stateTransitions]
  ,predicate "accepts" [makeStateID state, mode, case stateAcceptedToken of
     Nothing -> text "nothing"
     Just i -> term "just" [makeTokenID i]
     ]
  ]

makeIntervalTree :: IntervalTree -> Doc
makeIntervalTree IntervalLeaf = text "leaf"
makeIntervalTree (IntervalNode lowTree lowChar target highChar highTree)
  = term "node" [ makeIntervalTree lowTree
                , int $ ord lowChar
                , case target of
                    Epsilon s -> term "epsilon" [makeStateID s]
                    NotEpsilon s -> term "notEpsilon" [makeStateID s]
                , int $ ord highChar
                , makeIntervalTree highTree
                ]

makeModeTransition :: ((LexTokenID,ModeID),ModeID) -> Doc
makeModeTransition ((token,oldMode),newMode)
  = predicate "modeTransition"
       [ makeTokenID token
       , makeModeID oldMode
       , makeModeID newMode]

mkLRState :: ParserStateID -> Doc
mkLRState (ParserStateID x) = text "pstate_" <> int x

prologActionTable :: (ParserStateID, TransitionList) -> Doc
prologActionTable (pState, TransitionList {..}) = vcat [
   breakLine
  ,text "% ParserState" <+> (text $ show pState)
  ,breakLine
  ,vcat $ map (mkTransition pState) $ Map.assocs transitionMap
  ,predicate "defaultAction" [mkLRState pState, makeAction defaultTransition]
  ,breakLine
  ]
  where
    mkTransition :: ParserStateID -> (ParserTokenID, Parser.Action) -> Doc
    mkTransition pState (ParserTokenID pToken, action)
      = predicate "action" [
            mkLRState pState
          , text "ptoken_" <> int pToken
          , makeAction action]
    makeAction :: Parser.Action -> Doc
    makeAction x = case x of
       Shift state -> term "shift" [mkLRState state]
       Reduce (Reduction {..}) -> term "reduce" [
           text "reduction_" <> (int $ unReductionID reductionID)
         , int popCount
         , text "goto_" <> (int $ unGotoIndex gotoIndex)]
       Accept -> text "accept"
       Error (ErrorID err) -> term "error" [text "error_" <> int err]

parserErrorMessage :: (ErrorID, String) -> Doc
parserErrorMessage (ErrorID i, msg)
  = predicate "errorMessage" [text "error_" <> int i, text $ show msg]

prologGotoTable :: (GotoIndex, GotoMap) -> Doc
prologGotoTable (GotoIndex i, GotoMap {..}) = vcat [
   breakLine
  ,text "% Goto Table " <+> int i
  ,breakLine
  ,vcat $ map (mkGoto gotoIndex) $ Map.assocs gotoMap
  ,predicate "defaultGoto" [gotoIndex, mkLRState defaultGoto]
  ,breakLine
  ]
  where
    gotoIndex = text "goto_" <> int i
    mkGoto :: Doc -> (ParserStateID, ParserStateID) -> Doc
    mkGoto gi (from,to) = predicate "goto" [gi, mkLRState from, mkLRState to]


makeReductionRule :: Ast.Action -> Doc
makeReductionRule Ast.Action {..} = vcat [
   text "% rule :" <+> (int $ rule_index actionRule) <+> (text $ rule_ename actionRule)
   ,predicate "reductionRule" [
      text "reduction_" <> (int $ rule_index actionRule)
     ,plList popArgs
     ,expr $ actionExpression
     ]
   ]
  where
    popArgs = map (text . (++) "_" . action_result . unActionArgs)
               $ filter isPopAction $ rule_actions actionRule

    plList :: [Doc] -> Doc
    plList l = text "[" <+> (hsep $ punctuate (text " , ") l ) <+> text "]"

    plAtom :: String -> Doc
    plAtom s = char '\'' <> text s <> char '\''

    expr :: ActionExpression -> Doc
    expr reduction = case reduction of
      Var s -> term "var" [text "_" <> text s]
      EmptyList -> text "emptyList"
      SingletonList e -> term "singletonList" [expr e]
      AppendNode l n -> term "appendNode" [expr l, expr n]
      AppendList l1 l2 -> term "appendList" [expr l1,expr l2]
      NewNode _prod alt l -> term "newNode"
        [ plAtom $ alt_name alt
        , plAtom $ alt_ename alt
        ,plList $ map expr l]
      FromNode e -> expr e
      FromList e -> term "head" [expr e]
      NodeResult e -> expr e
      ListResult e -> term "singletonList" [expr e]