packages feed

protobuf-simple-0.1.1.1: src/Parser/Generator.hs

-- |
-- Module:      Parser.Generator
-- Copyright:   (c) 2015-2016 Martijn Rijkeboer <mrr@sru-systems.com>
-- License:     MIT
-- Maintainer:  Martijn Rijkeboer <mrr@sru-systems.com>
--
-- Generator for the data types.

module Parser.Generator
    ( generate
    ) where


import Control.Monad.State (State, execState, get, put)
import Data.List (foldl')
import Data.List.Split (splitOn)
import Parser.CodeInfo (CodeInfo(CodeInfo))
import Parser.EnumDesc (EnumDesc)
import Parser.EnumGenerator (getEnumCode)
import Parser.FileDesc (FileDesc)
import Parser.GeneratorUtils
    ( MessageSet,
      EnumSet,
      GenState(GenState, codeInfos),
      getNamespace
    )
import Parser.MessageDesc (MessageDesc)
import Parser.MessageGenerator (getMessageCode)
import System.FilePath (joinPath)

import qualified Data.Set            as Set
import qualified Parser.EnumDesc     as EnumDesc
import qualified Parser.FileDesc     as FileDesc
import qualified Parser.MessageDesc  as MessageDesc


generate :: [FileDesc] -> [CodeInfo]
generate fs = codeInfos $ execState (mapM_ addFileCodeInfos fs) state
  where
    state = GenState fs [] mset eset
    mset  = getMessageSet fs
    eset  = getEnumSet fs


addFileCodeInfos :: FileDesc -> State GenState ()
addFileCodeInfos f = do
    mapM_ (addMessageCodeInfo f) $ getMessageDescs f
    mapM_ (addEnumCodeInfo f) $ getEnumDescs f


addMessageCodeInfo :: FileDesc -> MessageDesc -> State GenState ()
addMessageCodeInfo fd md = do
    state <- get
    fdirs <- getDirectories fd
    fname <- getMessageFilename md
    fcode <- getMessageCode fd md
    put $ state{codeInfos = CodeInfo fdirs fname fcode : codeInfos state}


addEnumCodeInfo :: FileDesc -> EnumDesc -> State GenState ()
addEnumCodeInfo fd ed = do
    state <- get
    fdirs <- getDirectories fd
    fname <- getEnumFilename ed
    fcode <- getEnumCode fd ed
    put $ state{codeInfos = CodeInfo fdirs fname fcode : codeInfos state}


getMessageDescs :: FileDesc -> [MessageDesc]
getMessageDescs f = addMessageDescs [] $ FileDesc.getMessageDescs f


addMessageDescs :: [MessageDesc] -> [MessageDesc] -> [MessageDesc]
addMessageDescs = foldl' addMessageDesc


addMessageDesc :: [MessageDesc] -> MessageDesc -> [MessageDesc]
addMessageDesc mlist m = case MessageDesc.getMessageDescs m of
    [] -> m : mlist
    ns -> addMessageDescs (m : mlist) ns


getEnumDescs :: FileDesc -> [EnumDesc]
getEnumDescs f = insertSubEnums (insertEnums [] f) f
  where
    insertEnums    elist fd = addEnumDescs elist (FileDesc.getEnumDescs fd)
    insertSubEnums elist fd = addSubEnumDescs elist (FileDesc.getMessageDescs fd)


addEnumDescs :: [EnumDesc] -> [EnumDesc] -> [EnumDesc]
addEnumDescs = foldl' addEnumDesc


addEnumDesc :: [EnumDesc] -> EnumDesc -> [EnumDesc]
addEnumDesc elist e = e : elist


addSubEnumDescs :: [EnumDesc] -> [MessageDesc] -> [EnumDesc]
addSubEnumDescs = foldl' addSubEnumDesc


addSubEnumDesc :: [EnumDesc] -> MessageDesc -> [EnumDesc]
addSubEnumDesc elist m = case MessageDesc.getMessageDescs m of
    [] -> case MessageDesc.getEnumDescs m of
        [] -> elist
        es -> addEnumDescs elist es
    ms -> case MessageDesc.getEnumDescs m of
        [] -> addSubEnumDescs elist ms
        es -> addSubEnumDescs (addEnumDescs elist es) ms


getMessageSet :: [FileDesc] -> MessageSet
getMessageSet = foldl' insertMany Set.empty
  where
    insertMany mset fd = foldl' (insertOne fd) mset $ getMessageDescs fd
    insertOne fd mset md = Set.insert (getFullMessageName fd md) mset


getEnumSet :: [FileDesc] -> EnumSet
getEnumSet = foldl' insertMany Set.empty
  where
    insertMany eset fd = foldl' (insertOne fd) eset $ getEnumDescs fd
    insertOne fd eset ed = Set.insert (getFullEnumName fd ed) eset


getDirectories :: FileDesc -> State GenState FilePath
getDirectories fd = case FileDesc.getPackage fd of
    Just val -> return $ joinPath $ splitOn "." val
    Nothing  -> return $ FileDesc.getName fd


getMessageFilename :: MessageDesc -> State GenState FilePath
getMessageFilename md = return $ MessageDesc.getName md ++ ".hs"


getEnumFilename :: EnumDesc -> State GenState FilePath
getEnumFilename ed = return $ EnumDesc.getName ed ++ ".hs"


getFullEnumName :: FileDesc -> EnumDesc -> String
getFullEnumName fd ed = getNamespace fd ++ "." ++ EnumDesc.getName ed


getFullMessageName :: FileDesc -> MessageDesc -> String
getFullMessageName fd md = getNamespace fd ++ "." ++ MessageDesc.getName md