packages feed

chs-deps-0.1.0.0: src/Language/Haskell/CHs/Deps.x

{
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
-- | This contains a partial lexer for @.chs@ files; enough to extract
-- information from @{\#import\#}@ declarations.
--
-- This is lenient in that it will not reject things like
-- {\# import const Data.Char \#}
module Language.Haskell.CHs.Deps ( getImports
                                 , getFileImports
                                 ) where

import Control.Applicative ((<$>))
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Lazy.Char8 as ASCII
}

%wrapper "monad-bytestring"

$module = [A-Za-z\.]

tokens :-

    $white+                      ;
    <0> "--".*                   ;

    <0> "{-"                     { \_ _ -> nested_comment }

    <chs> "import"               { \_ _ -> alex Import }
    <chs> "qualified"            ;

    <chs> "type"                 { \_ _ -> alex End }
    <chs> "typedef"              { \_ _ -> alex End }
    <chs> "enum"                 { \_ _ -> alex End }
    <chs> "fun"                  { \_ _ -> alex End }
    <chs> "sizeof"               { \_ _ -> alex End }
    <chs> "alignof"              { \_ _ -> alex End }
    <chs> "const"                { \_ _ -> alex End }
    <chs> "call"                 { \_ _ -> alex End }
    <chs> "get"                  { \_ _ -> alex End }
    <chs> "set"                  { \_ _ -> alex End }
    <chs> "offsetof"             { \_ _ -> alex End }

    <chs> "#}"                   { begin 0 }
    <0> "{#"                     { begin chs }
     
                                 -- this is safe because we only allow ASCII module names
    <chs> $module+               { tok (\_ s -> alex (Module $ ASCII.unpack s)) }

    <0> [^\{]+                   ;
    <0> $printable               ;
    <chs> [^\#$module]+          ;

{

data Token = Import
           | Module String
           | End

tok f (p,_,s,_) len = f p (BSL.take len s)

alex :: a -> Alex a
alex = pure

alexEOF :: Alex Token
alexEOF = pure End

-- | Given a 'BSL.ByteString' containing C2Hs, return a list of modules it @{\#import\#}@s.
getImports :: BSL.ByteString -> Either String [String]
getImports = fmap extractDeps . lexC

getFileImports :: FilePath -> IO (Either String [String])
getFileImports = fmap getImports . BSL.readFile

-- from: https://github.com/simonmar/alex/blob/master/examples/haskell.x#L128
nested_comment :: Alex Token
nested_comment = go 1 =<< alexGetInput

    where go :: Int -> AlexInput -> Alex Token
          go 0 input = alexSetInput input *> alexMonadScan
          go n input =
            case alexGetByte input of
                Nothing -> err input
                Just (c, input') ->
                    case c of
                        45 ->
                            case alexGetByte input' of
                                Nothing -> err input'
                                Just (125,input_) -> go (n-1) input_
                                Just (_,input_) -> go n input_
                        123 ->
                            case alexGetByte input' of
                                Nothing -> err input'
                                Just (c',input_) -> go (addLevel c' $ n) input_
                        _ -> go n input'

          addLevel c' = if c' == 45 then (+1) else id

          err (pos,_,_,_) =
            let (AlexPn _ line col) = pos in
                alexError ("Error in nested comment at line " ++ show line ++ ", column " ++ show col)

extractDeps :: [Token] -> [String]
extractDeps []                   = []
extractDeps (Import:Module s:xs) = s : extractDeps xs
extractDeps (_:xs)               = extractDeps xs

lexC :: BSL.ByteString -> Either String [Token]
lexC = flip runAlex loop

loop :: Alex [Token]
loop = do
    tok' <- alexMonadScan
    case tok' of
        End -> pure []
        _ -> (tok' :) <$> loop

}