packages feed

SableCC2Hs-0.0.0.2: haskell/Parser2.hs

{-
prototype of a Haskell backend for LR-parser-tables generated by SableCC
this parser imports the tables with a generated Module.
-}
{-# LANGUAGE RecordWildCards,BangPatterns #-}
module Parser2
where
import SableCC.ParserTypes as Parser
import ParserTables as Tables

import Data.Array as Array
import Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe

-- The Parser

type Stack = [(ParserStateID,DAst)]
type Input = [PToken]
type PState = (Stack,Input)

runParser :: Input -> DAst
runParser input
  = head $ parserLoop ([(ParserStateID 0, error "dummy CST of initial parserStack")], input)

parserLoop :: PState -> [DAst]
parserLoop pState@(stack,input) = case findAction pState of
    Shift newState    -> case input of
         [] -> error "shift action with empty input"
         (h:rest) -> parserLoop ((newState, DLeaf h) : stack, rest)
    Reduce reduction  -> parserLoop $ performReduction reduction pState
    Accept            -> map snd stack
    Error msg         -> error (Tables.errorTable ! msg)

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

performReduction :: Reduction -> PState -> PState
performReduction Reduction {..} (stack,input)
  = ((newState,astNode) : restStack, input)
  where
    restStack = drop popCount stack
    cl = map snd $ take popCount stack
    ! astNode = (reduceTable ! reductionID) cl
    newState = lookupGotoList gotoIndex $ fst $ head restStack

lookupGotoList :: GotoIndex -> ParserStateID -> ParserStateID
lookupGotoList index state
  = case Map.lookup state $ gotoMap gMap of
    Nothing -> defaultGoto gMap
    Just r  -> r
  where gMap = Tables.gotoTable ! index

trimTokens :: [SToken] -> [Parser.PToken]
trimTokens tokens = mapMaybe f tokens ++ [parserEof]
  where
    parserEof = (ParserTokenID Tables.eofTokenID ,"")
    f (LexTokenID tokID, tokenString)
      = case Map.lookup tokID tokenIDMap of
           Nothing -> Nothing
           Just parserID -> Just (ParserTokenID parserID, tokenString)

--- The Lexer

runLexer :: String -> [SToken]
runLexer input
  = reverse $ lexerLoop (initLexerState input) []

lexerLoop :: LexerState -> [SToken] -> [SToken]
lexerLoop lexerState@LexerState {..} acc
  = case runDFA (snd $ Tables.lexerModes ! lexerMode) lexerBuffer of
    Right (newBuff, tokenString,tokenID) -> if null newBuff
        then acc
        else lexerLoop newState (sToken : acc)
      where
         !newNestingCounter = case tokenID of
           LexTokenID 1 -> pred commentNestingCounter
           LexTokenID 0 -> succ commentNestingCounter
           _             -> commentNestingCounter
         newMode = if tokenID == LexTokenID 1 && newNestingCounter == 0
                     then ModeID 0
                     else Tables.modeTransitions ! (tokenID, lexerMode)
         sToken = (tokenID, tokenString)
         newState = lexerState {
                lexerBuffer = newBuff
               ,lexerMode = newMode
               ,commentNestingCounter = newNestingCounter
               }
    Left err -> do
      error "lexError"

runDFA :: Mode -> String -> 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
        -> Either String (String,String,LexTokenID)
    checkState lastAcceptedToken dfaState input acc
      = case (input, stateAcceptedToken dfaState) of
          ([],  Just tokenID)          -> Right ("",reverse acc, tokenID)
          ([],  Nothing)               -> 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
        -> 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 -> lastAcceptedToken
           Just (Epsilon newStateID)
             -> scanChar lastAcceptedToken (modeStates mode ! newStateID) input acc
           Just (NotEpsilon newStateID)
             -> checkState lastAcceptedToken (modeStates mode ! newStateID) rest (h:acc)