packages feed

SableCC2Hs-0.0.0.1: src/SableCC/Test/Parser.hs

----------------------------------------------------------------------------
-- |
-- Module      :  SableCC.Test.Parser
-- Copyright   :  (c) Fontaine 2011
-- License     :  BSD3
--
-- Maintainer  :  fontaine@cs.uni-duesseldorf.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
--
-- A lexer and a parser which takes the parser tables as a explicit argument
-- (as opposed to importing the tables as generated Haskell modules).
-----------------------------------------------------------------------------

{-# LANGUAGE GeneralizedNewtypeDeriving, RecordWildCards #-}
module SableCC.Test.Parser
where
import SableCC.ParserTypes
import SableCC.ParserDefinition
import SableCC.LexerDefinition
import SableCC.XML.ParserDefinitionRaw (Token(..))

import Data.Array as Array
import Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Control.Monad.Reader
import Control.Monad.IO.Class

-- the Parser
type Parser x = ReaderT ParserDefinition IO x

runParser :: ParserDefinition -> Input -> IO CST
runParser parserDef input
  = flip runReaderT parserDef $ do
     [cst,_] <- parserLoop (initialStack parserDef, input)
     return cst

parserLoop :: PState -> Parser [CST]
parserLoop pState@(stack,input) = do
  action <- findAction pState
  case action of
    Shift newState    -> case input of
         [] -> error "shift action with empty input"
         (h:rest) -> parserLoop ((newState, CstLeaf h) : stack, rest)
    Reduce reduction  -> performReduction reduction pState >>= parserLoop
    Accept            -> return (map snd stack)
    Error msg         -> do
      table <- asks errorTable
      error (table ! msg)


findAction :: (Stack,Input) -> Parser Action
findAction s = case s of
  ([],_) -> error "findAction: empty Stack"
  (_,[]) -> error "findAction: empty Input"
  (((state,_):_),((tokenID,_):_)) -> do
    table <- asks actionTable
    let list = table ! state
    case Map.lookup tokenID $ transitionMap list of
      Nothing -> return $ defaultTransition list
      Just r -> return r

performReduction :: Reduction -> PState -> Parser PState
performReduction Reduction {..} (stack,input) = do
  let
    restStack = drop popCount stack
    cstNode = CstNode reductionID $ map snd $ take popCount stack
  newState <- lookupGotoList gotoIndex $ fst $ head restStack
  return ((newState,cstNode) : restStack, input)


lookupGotoList :: GotoIndex -> ParserStateID -> Parser ParserStateID
lookupGotoList index state = do
  gTable <- asks gotoTable
  let gMap = gTable ! index
  case Map.lookup state $ gotoMap gMap of
    Nothing -> return $ defaultGoto gMap
    Just r  -> return r


-- the Lexer

type Lexer x = ReaderT LexerDefinition IO x

runLexer :: LexerDefinition -> String -> IO [SToken]
runLexer lexerDef input
  = flip runReaderT lexerDef $ do
      acc <- lexerLoop (initLexerState input) []
      return $ reverse acc

lexerLoop :: LexerState -> [SToken] -> Lexer [SToken]
lexerLoop lexerState@LexerState {..} acc = do
  LexerDefinition {..} <- ask
  scanToken <- runDFA (snd $ lexerModes ! lexerMode) lexerBuffer
  case scanToken of
    Right (newBuff, tokenString,tokenID) -> do
      let
         token = lexerTokens ! tokenID
         newNestingCounter = case token_ename token of
           "TCommentEnd" -> pred commentNestingCounter
           "TComment"    -> succ commentNestingCounter
           _             -> commentNestingCounter
         newMode = if token_ename token == "TCommentEnd" && newNestingCounter == 0
                     then ModeID 0
                     else modeTransitions ! (tokenID, lexerMode)
         sToken = (tokenID, tokenString)
         newState = lexerState {
                lexerBuffer = newBuff
               ,lexerMode = newMode
               ,commentNestingCounter = newNestingCounter
               }
      if null newBuff
         then return acc
         else lexerLoop newState (sToken : acc)
    Left err -> do
      liftIO $ putStrLn $ "lexError :" ++ err
      return $ error "lexError"

runDFA :: Mode -> String -> Lexer (Either String (String,String,LexTokenID))
runDFA mode input
   = checkState
       (Left "noToken recognised")
       (modeStates mode ! LexStateID 0)
       input
       []
  where
    checkState ::
           Either String (String,String,LexTokenID)
        -> State -> String -> String
        -> Lexer (Either String (String,String,LexTokenID))
    checkState lastAcceptedToken dfaState input acc = do
      case (input, stateAcceptedToken dfaState) of
       ([],  Just tokenID)          -> return $ Right ("",reverse acc, tokenID)
       ([],  Nothing)               -> return $ lastAcceptedToken
       (_, Just tokenID)          -> scanChar (Right (input, reverse acc, tokenID)) dfaState input acc
       (_, Nothing)               -> scanChar lastAcceptedToken dfaState input acc
    scanChar ::
           Either String (String,String,LexTokenID)
        -> State -> String -> String
        -> Lexer (Either String (String,String,LexTokenID))
    scanChar lastAcceptedToken dfaState [] acc = error "scanChar:unreachable"
    scanChar lastAcceptedToken dfaState input@(h:rest) acc
       = case lookupInterval h (stateTransitions dfaState) of
           Nothing -> return $ lastAcceptedToken
           Just (Epsilon newStateID)
             -> scanChar lastAcceptedToken (modeStates mode ! newStateID) input acc
           Just (NotEpsilon newStateID)
             -> checkState lastAcceptedToken (modeStates mode ! newStateID) rest (h:acc)