packages feed

protobuf-simple-0.1.0.0: 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
import Parser.EnumDesc (EnumDesc)
import Parser.EnumGenerator (getEnumCode)
import Parser.FileDesc (FileDesc)
import Parser.GeneratorUtils
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 = case FileDesc.getPackage fd of
    Just val -> val ++ "." ++ name
    Nothing  -> name
  where name = EnumDesc.getName ed


getFullMessageName :: FileDesc -> MessageDesc -> String
getFullMessageName fd md = case FileDesc.getPackage fd of
    Just val -> val ++ "." ++ name
    Nothing  -> name
  where name = MessageDesc.getName md