packages feed

deka-0.6.0.0: dectest/Parse.hs

{-# LANGUAGE OverloadedStrings #-}
module Parse where

import Parse.Tokens
import qualified Data.ByteString.Char8 as BS8

-- | Remove the comments from a line of tokens.  Any unquoted token that
-- starts with two dashes is removed.  Also, any token that comes
-- after such a token is also removed.
removeComments :: [Token] -> [Token]
removeComments toks = go toks
  where
    go [] = []
    go (t:ts)
      | quoted t = t : go ts
      | BS8.take 2 (unToken t) == "--" = []
      | otherwise = t : go ts

newtype Keyword = Keyword { unKeyword :: BS8.ByteString }
  deriving (Eq, Show)

newtype Value = Value { unValue :: BS8.ByteString }
  deriving (Eq, Show)

data TestSpec = TestSpec
  { testId :: BS8.ByteString
  , testOperation :: BS8.ByteString
  , testOperands :: [BS8.ByteString]
  , testResult :: BS8.ByteString
  , testConditions :: [BS8.ByteString]
  } deriving Show

data Instruction
  = Blank
  | Directive Keyword Value
  | Test TestSpec
  deriving Show

data File = File
  { fileName :: BS8.ByteString
  , fileContents :: [Either File Instruction]
  } deriving Show

directive :: [Token] -> (Keyword, Value)
directive ts = case ts of
  x:y:[] -> ( Keyword . BS8.init . unToken $ x,
              Value (unToken y) )
  _ -> error "directive: bad token count"

lineToContent :: [Token] -> IO (Either File Instruction)
lineToContent ts
  | null ts = return . Right $ Blank
  | length ts == 2 && kw == "dectest" = fmap Left $ parseFile fn
  | length ts == 2 = return . Right $ Directive akw avl
  | otherwise = return . Right . Test . mkTestSpec $ ts
  where
    (akw@(Keyword kw), avl@(Value val)) = directive ts
    fn = val `BS8.append` ".decTest"
  
mkTestSpec :: [Token] -> TestSpec
mkTestSpec ts
  | length ts < 5 = error "mkTestSpec: list too short"
  | otherwise = TestSpec
      { testId = unToken . head $ ts
      , testOperation = unToken . head . drop 1 $ ts
      , testOperands = map unToken
          . takeWhile (not . resultsIn)
          . drop 2
          $ ts
      , testResult = unToken
          . safeHead
          . drop 1
          . dropWhile (not . resultsIn)
          . drop 2
          $ ts
      , testConditions = map unToken
          . drop 2
          . dropWhile (not . resultsIn)
          . drop 2
          $ ts
      }
  where
    resultsIn t = unToken t == "->" && not (quoted t)
    safeHead x = case x of
      [] -> error "mkTestSpec: list too short"
      y:_ -> y

rawToContent :: Raw -> IO [Either File Instruction]
rawToContent
  = mapM lineToContent
  . map removeComments
  . map processLine
  . splitLines

parseFile :: BS8.ByteString -> IO File
parseFile fn = do
  rw <- raw (BS8.unpack fn)
  ctnt <- rawToContent rw
  return $ File fn ctnt