packages feed

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
}