libcspm-1.0.0: src/CSPM/Parser/Monad.hs
{-# LANGUAGE CPP #-}
module CSPM.Parser.Monad (
ParseMonad, ParserState(..),
FileParserState(..), movePos,
setParserState, getParserState,
FilePosition(..), filePositionToSrcLoc,
modifyTopFileParserState, getTopFileParserState,
runParser, pushFile, pushFileContents,
getTokenizerPos, getFileName, getInput,
getPreviousChar, getCurrentStartCode, setCurrentStartCode,
getSequenceStack, setSequenceStack
)
where
import Control.Exception
import Control.Monad.State
import GHC.IO.Encoding
#if __GLASGOW_HASKELL__ < 705
import Prelude hiding (catch)
#endif
import System.FilePath
import System.IO
import CSPM.Parser.Exceptions
import Util.Annotated
-- *************************************************************************
-- Parser Support Types
-- *************************************************************************
-- TAKEN FROM ALEX
-- `Posn' records the location of a token in the input text. It has three
-- fields: the address (number of chacaters preceding the token), line number
-- and column of a token within the file. `start_pos' gives the position of the
-- start of the file and `eof_pos' a standard encoding for the end of file.
-- `move_pos' calculates the new position after traversing a given character,
-- assuming the usual eight character tab stops.
data FilePosition = FilePosition !Int !Int !Int
deriving (Eq,Show)
startPos :: FilePosition
startPos = FilePosition 0 1 1
filePositionToSrcLoc :: String -> FilePosition -> SrcSpan
filePositionToSrcLoc filePath (FilePosition _ line col) =
SrcSpanPoint filePath line col
movePos :: FilePosition -> Char -> FilePosition
-- Don't treat tabs any differently (editors don't)
--movePos (FilePosition a l c) '\t' =
-- FilePosition (a+1) l (((c+7) `div` 8)*8+1)
movePos (FilePosition a l _) '\n' = FilePosition (a+1) (l+1) 1
movePos (FilePosition a l c) _ = FilePosition (a+1) l (c+1)
-- *************************************************************************
-- Parser Monad
-- *************************************************************************
data ParserState = ParserState {
rootDir :: !String,
fileStack :: ![FileParserState]
}
deriving Show
data FileParserState = FileParserState {
tokenizerPos :: !FilePosition,
fileName :: !String,
input :: String,
previousChar :: !Char,
currentStartCode :: !Int, -- current startcode
sequenceStack :: ![Int]
}
deriving Show
-- The parser monad takes a sequence of parser states, representing the
-- current stack of include files
type ParseMonad = StateT ParserState IO
runParser :: ParseMonad a -> String -> IO a
runParser prog dirname =
runStateT prog (ParserState dirname [])
>>= return . fst
getTopFileParserState :: ParseMonad FileParserState
getTopFileParserState = gets (head . fileStack)
getParserState :: ParseMonad ParserState
getParserState = gets id
setParserState :: ParserState -> ParseMonad ()
setParserState st = modify (\ _ -> st)
modifyTopFileParserState :: (FileParserState -> FileParserState) -> ParseMonad ()
modifyTopFileParserState stf =
modify (\ st -> let fs:fss = fileStack st in
st { fileStack = (stf fs):fss })
pushFile :: String -> ParseMonad a -> ParseMonad a
pushFile fname prog = do
dirname <- gets rootDir
let
filename = combine dirname fname
handle :: IOException -> a
handle e = throwSourceError [fileAccessErrorMessage filename]
str <- liftIO $ catch (do
handle <- openFile filename ReadMode
-- Decode the file using an ASCII codec (except in comments, all CSPM
-- characters should be ASCII - clearly in comments we can just ignore
-- anything non-ASCII).
hSetEncoding handle char8
hGetContents handle) handle
pushFileContents filename str
x <- prog
return x
pushFileContents :: String -> String -> ParseMonad ()
pushFileContents filename input =
modify (\ st -> let
-- We add an extra space on the end of the file contents to allow
-- the right contexts used in the lexer to still work even at the
-- end of the file.
fs = FileParserState startPos filename (input++" ") '\n' 0 [0]
in
st { fileStack = fs:(fileStack st) })
getTokenizerPos :: ParseMonad FilePosition
getTokenizerPos = getTopFileParserState >>= (return . tokenizerPos)
getFileName :: ParseMonad String
getFileName = getTopFileParserState >>= (return . fileName)
getInput :: ParseMonad String
getInput = getTopFileParserState >>= (return . input)
getPreviousChar :: ParseMonad Char
getPreviousChar = getTopFileParserState >>= (return . previousChar)
getCurrentStartCode :: ParseMonad Int
getCurrentStartCode = getTopFileParserState >>= (return . currentStartCode)
getSequenceStack :: ParseMonad [Int]
getSequenceStack = getTopFileParserState >>= (return . sequenceStack)
setCurrentStartCode :: Int -> ParseMonad ()
setCurrentStartCode sc =
modifyTopFileParserState (\ st -> st { currentStartCode = sc })
setSequenceStack :: [Int] -> ParseMonad ()
setSequenceStack st =
modifyTopFileParserState (\ s -> s { sequenceStack = st })