parsestar-1.4: Data/STAR/Parser.y
{
{-# LANGUAGE OverloadedStrings, BangPatterns, NoMonomorphismRestriction, ScopedTypeVariables #-}
module Data.STAR.Parser(parse, parseFile,
parsePlainFile,
parseCompressedFile) where
import qualified Data.STAR.Tokens as Tokens
import Control.Monad(liftM, liftM2)
import Control.Monad.State.Strict
import qualified Data.STAR.Type as Type
import Prelude hiding (String, getContents, drop, take, (++))
import Data.ByteString.Char8 as BSC
import Control.DeepSeq
import qualified Control.Exception as Exc -- (SomeException, catch)
import qualified GHC.Exts as Happy_GHC_Exts
import Data.STAR.StringUtil
import qualified Data.List(isSuffixOf, (++))
}
%name parseSTAR star
%tokentype { Tokens.Token }
%monad { Tokens.ParserM } { Tokens.parseThen } { Tokens.parseReturn }
%lexer { Tokens.getToken } { Tokens.EOF }
%error { failToken }
%token
Name { Tokens.Name _ }
Text { Tokens.Text _ }
Save { Tokens.Save _ }
Endsave { Tokens.EndSave }
Loop { Tokens.Loop }
EndLoop { Tokens.EndLoop }
Data { Tokens.Data _ }
Global { Tokens.Global }
Ref { Tokens.Ref _ }
SemiStart { Tokens.SemiStart _ }
SemiEnd { Tokens.SemiEnd _ }
-- Err { Tokens.Err }
%%
list_(a) : list_(a) a { $2:$1 }
| { [] }
list(a) : list_(a) { Prelude.reverse $1 }
list1(a) : a list(a) { $1 `seq` $1:$2 }
star :: { [Type.STARBlock] }
star : list1(block) { $1 }
block :: { Type.STARBlock }
block : Global list1(flatData) { Type.Global $2 }
| Data list1(entry ) { Type.Data (Tokens.tokenValue $1) $2 }
entry :: { Type.STAREntry }
entry : flatData { $1 }
| Save list1(entry) Endsave { Type.Frame (Tokens.tokenValue $1) $2 }
-- should there be flatEntry and entry (with ref allowed or not)
flatData :: { Type.STAREntry }
flatEntry : item { $1 }
| topLoop { $1 }
item :: { Type.STAREntry }
item : Name value { let t = Tokens.tokenValue $1 in t `seq` $2 (Tokens.tokenValue $1) }
value :: { Type.STARKey -> Type.STAREntry }
value : Text { let t = Tokens.tokenValue $1 in t `seq` \k -> Type.Entry k t }
| Ref { let t = Tokens.tokenValue $1 in t `seq` \k -> Type.Ref k t }
| semilist { $1 `seq` \k -> Type.Entry k $1 }
--{% deref (Tokens.tokenValue $1) >>= \f -> case f of Type.Frame _ es -> return (\k -> Frame k es) }
semilist :: { Type.String }
semilist : SemiStart SemiEnd { cheatConcat (Tokens.tokenValue $1) (Tokens.tokenValue $2) }
topLoop :: { Type.STAREntry }
topLoop : Loop list1(structure) list1(valueListEntry) {% matchTypesValues $2 $3 }
structureList :: { [STARType] }
structureList : list1(structure) { $1 }
structure :: { STARType }
structure : Name { TSimple (Tokens.tokenValue $1) }
| Loop list1(structure) EndLoop { TComplex $2 }
valueList :: { [STARStruct] }
valueList : list1(valueListEntry) { $1 }
valueListEntry :: { STARStruct }
valueListEntry : Text {% addPos $ \p -> SText p $ Tokens.tokenValue $1 }
| Ref {% addPos $ \p -> SRef p $ Tokens.tokenValue $1 }
| EndLoop {% addPos $ \p -> SStop p }
| semilist {% addPos $ \p -> SText p $1 }
{
data STARType = TSimple Type.STARKey
| TComplex [STARType]
deriving (Show, Eq)
data STARStruct = SText Tokens.AlexPosn Type.String -- keep position for matchTypesValues error reporting!
| SRef Tokens.AlexPosn Type.String -- TODO: implement!!!
| SStop Tokens.AlexPosn
deriving (Show,Eq)
addPos e = do pos <- Tokens.getPos
return $! e pos
matchTypesValues :: [STARType] -> [STARStruct] -> Tokens.ParserM Type.STAREntry
matchTypesValues !ts !ss = matchTypesValues' ts ts ss [] [] finish reportError
where
finish :: [[Type.STAREntry]] -> [STARStruct] -> Tokens.ParserM Type.STAREntry
finish entries [] = return $ Type.Loop entries
reportError msg = Tokens.parseError $ BSC.concat [msg, bshow $ Prelude.zip (infiniteConcat ts) (Prelude.concatMap unText ss)]
bshow = BSC.pack . show
unText (SText _ s) = [s]
unText (SRef _ s) = [s]
unText (SStop _ ) = [ ]
infiniteConcat ss = ss Data.List.++ infiniteConcat ss
--matchTypesValues' :: [STARType] -> [STARType] -> [STARStruct] -> [[Type.STAREntry]] -> [Type.STAREntry] -> ([[Type.STAREntry]] -> [STARStruct] -> a) -> a
matchTypesValues' (TSimple t :ts) tts (SText p s:ss) !acc1 !acc2 !cont !errCont = matchTypesValues' ts tts ss acc1 (Type.Entry t s:acc2) cont errCont
matchTypesValues' (TSimple t :ts) tts (SRef p s:ss) !acc1 !acc2 !cont !errCont = matchTypesValues' ts tts ss acc1 (Type.Ref t s:acc2) cont errCont
matchTypesValues' (TComplex tc:ts) tts ss !acc1 !acc2 !cont !errCont = matchTypesValues' tc tc ss [] [] loopCont errCont
where
--loopCont :: [Type.STAREntry] -> [STARStruct] -> a
loopCont es sn = matchTypesValues' ts tts sn acc1 (Type.Loop es:acc2) cont errCont
matchTypesValues' [] tts (SStop p :ss) !acc1 !acc2 !cont !errCont = cont (Prelude.reverse (Prelude.reverse acc2:acc1)) ss
matchTypesValues' [] tts ss !acc1 !acc2 !cont !errCont = matchTypesValues' tts tts ss (Prelude.reverse acc2:acc1) [] cont errCont
matchTypesValues' (t :_ ) _ (s :ss) !acc1 !acc2 !cont !errCont = errCont $ BSC.pack $ Prelude.concat ["Can't match declared ",
show t,
" and actual ",
show s]
failToken tok = Tokens.parseError . BSC.concat $ ["parse error on ", BSC.pack $ show tok]
parse = Tokens.runParser parseSTAR
parsePlainFile fname = parseFileCompressed False fname
parseFileCompressed isCompressed fname = (do r <- reader fname
case parse r of
Left (Tokens.ParseError l c st s) -> return $ Left $ Prelude.concat ["Parse error in line ", show l,
" column ", show c,
":", BSC.unpack s,
"(lexer state is ", show st, ")"]
Right result -> return $ Right $ Type.STAR result
) `Exc.catch` handler
where reader = if isCompressed
then compressedRead
else simpleRead
handler (e :: Exc.SomeException) = return . Left . Prelude.concat $ ["Error in ", fname, ": ", show e]
parseCompressedFile fname = parseFileCompressed True fname
parseFile fname = parseFileCompressed (".gz" `Data.List.isSuffixOf` fname) fname
}