packages feed

x-dsp-0.2: tools/mkClass.hs

import           CsMaps
import           CsParser

import           Sound.Csound
import           Control.Arrow ((***))
import           Control.Applicative ((*>), (<*))
import           Control.Monad.Error
import           Control.Monad.State
import           Control.Monad.Writer
import qualified Data.Map as M
import           Data.Maybe
import qualified Data.Set as S
import           Text.Printf
import           Data.Char
import           Data.List (intercalate)

-- create a class description from OpcodeListEntry

-- | the opcodelist has duplicate entries for overloaded opcodes.
-- merge them to single polymorphic variants, collecting information
-- on errors
mergeOpcodeList :: [OpcodeListEntry] -> ([String], [OpcodeListEntry])
mergeOpcodeList = fmap M.elems . foldr fn ([],M.empty)
 where
  fn ocl (errs,mp) =
   let onm = opcodeName ocl
   in case M.lookup onm mp of
        Nothing   -> (errs,M.insert onm ocl mp)
        Just prev -> either (\err' -> (err':errs,mp))
                            (\n' -> (errs,M.insert onm n' mp))
                            (mixOcl ocl prev)


mixOcl :: OpcodeListEntry -> OpcodeListEntry -> Either String OpcodeListEntry
mixOcl a b | opcodeName a == opcodeName b = do
  los <- zipWithM mixLO (ouTypes a) (ouTypes b)
  return $ OpcodeListEntry (opcodeName a) los (zipWith mixL (inTypes a) (inTypes b))
mixOcl a b = throwError "mixOcl called with non-matching entries"

type SpCtx = String -- stub

cap :: String -> String
cap []     = []
cap (a:as) = toUpper a:as

unCap :: String -> String
unCap []     = []
unCap (a:as) = toLower a:as

mkClass :: SpCtx -> OpcodeListEntry -> Writer [String] String
mkClass ctx ole = case (runParseI (inTypes ole), fmap ctxtRepr $ parseOutarg (ouTypes ole)) of
  (Right i, Right o) -> return $ topline i o ++ mainline i o ++ oline i o
  (Left ie, Left oe) -> tell [opc,ie,oe] >> return ""
  (Left ie, _)       -> tell [opc,ie] >> return ""
  (_,       Left oe) -> tell [opc,oe] >> return ""
 where
  opc = let n' = unCap $ opcodeName ole
        in fromMaybe n' $ M.lookup n' invalidNameMap
  topline (invars,inargs,_,reqNum) (hasOut,outarg) =
    printf "class %s %s repr %s%s where\n"
           ("(" ++ intercalate ", " (ctx:addCtxt (opcodeName ole)) ++ ") =>")
           ("Cs" ++ cap opc)
           (getCtxt hasOut)
           (intercalate " " $ map (:[]) invars)
  mainline (invars,inargs,_,reqNum) (hasOut,outarg) =
    printf "  %s ::\n    %s\n"
           opc
           (intercalate "\n    -> " $ (maybe id take reqNum inargs) ++ [outarg])
  oline (invars,inargs,_,reqNum) (hasOut,outarg) =
    maybe "" (const $ printf "  %s' :: %s\n"
                             opc
                             (intercalate "\n    -> " $ inargs ++ [outarg]))
             reqNum

allOps = do
  ops <- runCsound (compile "-d foo.csd" >> newOpcodeList <* cleanup)
  return $ either (error . show) id ops

writeOut fn ctx ops = do
  writeFile fn . intercalate "\n\n" $
    filter (not .null) (exts:mkModule allModName:vals)
  writeFile ("err_" ++ fn) $ intercalate "\n\n" (errs ++ errs1)
 where
  (errs1, ops') = mergeOpcodeList $ filter (not . flip S.member noReuseSet . opcodeName) ops
  (vals,errs) = runWriter $ mapM (mkClass ctx) ops'

exts = "{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleContexts #-}"

mkModule modname =
  "module " ++ modname ++ " where\n\nimport Language.XDsp.Semantics.Core\nimport Language.XDsp.Semantics.CsoundExt.Core\nimport Language.XDsp.Semantics.Extras\nimport Language.XDsp.Semantics.BasicExtensions"

allModName = "Language.XDsp.Semantics.CsoundExt.All"

main = allOps >>= writeOut "allOps.txt" "CsoundClass repr"