packages feed

minizinc-process-0.1.4.0: src/Process/Minizinc/TH.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Template Haskell splices to generate the model Input/Output
-- datatype with introspection.
module Process.Minizinc.TH where

import Process.Minizinc.Inspect
import Language.Haskell.TH
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import qualified Data.Text as Text


-- | Generates some Input and Ouput data types by inspecting a minizinc file.
-- Supported types are int, bools, float and their nested arrays.
genModelData :: String -> FilePath -> Q [Dec]
genModelData prefix path = do
  miface <- runIO $ inspect path
  iface <- case miface of
               Nothing -> fail "no interface" 
               Just x -> pure x
  sequence [ genFromTypeDecls prefix "Input" (_input iface)
           , genFromTypeDecls prefix "Output" (_output iface)
           ]

genFromTypeDecls :: String -> String -> TypeDeclarations -> Q Dec
genFromTypeDecls prefix base typedecls = do
  let dataname = prefix <> base
  let pairs = List.sort $ Map.assocs typedecls
  let derivations = [ DerivClause Nothing [ ConT (mkName "Show")
                                          , ConT (mkName "Eq")
                                          , ConT (mkName "Ord")
                                          , ConT (mkName "Hashable")
                                          , ConT (mkName "Generic")
                                          , ConT (mkName "ToJSON")
                                          , ConT (mkName "FromJSON")
                                          ]
                    ]
  let bang = Bang NoSourceUnpackedness NoSourceStrictness
  pure $ DataD []
     (mkName dataname)
     []
     Nothing
     [RecC (mkName dataname) [ (mkName $ Text.unpack n, bang, typeFor typedecl) | (n,typedecl) <- pairs]]
     derivations
  where
     typeFor (TypeInfo "int" False Nothing)   = ConT (mkName "Int")
     typeFor (TypeInfo "bool" False Nothing)  = ConT (mkName "Bool")
     typeFor (TypeInfo "float" False Nothing) = ConT (mkName "Float")
     typeFor (TypeInfo "int" False (Just n))  = nestedlist n $ ConT (mkName "Int")
     typeFor typedecl = error $ "unsupported type info when generating Haskell code from MiniZinc files: " <> show typedecl

     nestedlist 0 ty = ty
     nestedlist n ty = AppT ListT (nestedlist (n-1) ty)