content: ----------------------------------------------------------------------
  -- |
  -- Module      : PGFtoPython
  -- Maintainer  : Peter Ljunglöf
  --
  -- exports a GF grammar into a Python module
  -----------------------------------------------------------------------------
  
  {-# LANGUAGE FlexibleContexts #-}
  module GF.Compile.PGFtoPython (pgf2python) where
  
  import PGF(showCId)
  import PGF.Internal as M
  
  import GF.Data.Operations
  
  import qualified Data.Array.IArray as Array
  import qualified Data.Set as Set
  import qualified Data.Map as Map
  import qualified Data.IntMap as IntMap
  --import Data.List (intersperse)
  
  pgf2python :: PGF -> String
  pgf2python pgf = ("# -*- coding: utf-8 -*-" ++++
                    "# This file was automatically generated by GF" +++++
                    showCId name +++ "=" +++ 
                    pyDict 1 pyStr id [
                                ("flags", pyDict 2 pyCId pyLiteral (Map.assocs (gflags pgf))),
                                ("abstract", pyDict 2 pyStr id [
                                                ("name", pyCId name), 
                                                ("start", pyCId start), 
                                                ("flags", pyDict 3 pyCId pyLiteral (Map.assocs (aflags abs))),
                                                ("funs", pyDict 3 pyCId pyAbsdef (Map.assocs (funs abs)))
                                               ]),
                                ("concretes", pyDict 2 pyCId pyConcrete (Map.assocs cncs))
                               ] ++ "\n")
      where
        name  = absname pgf
        start = M.lookStartCat pgf
        abs = abstract pgf
        cncs = concretes pgf
  
  pyAbsdef :: (Type, Int, Maybe ([Equation], [[M.Instr]]), Double) -> String
  pyAbsdef (typ, _, _, _) = pyTuple 0 id [pyCId cat, pyList 0 pyCId args]
      where (args, cat) = M.catSkeleton typ 
  
  pyLiteral :: Literal -> String
  pyLiteral (LStr s) = pyStr s
  pyLiteral (LInt n) = show n
  pyLiteral (LFlt d) = show d
  
  pyConcrete :: Concr -> String
  pyConcrete cnc = pyDict 3 pyStr id [
                    ("flags", pyDict 0 pyCId pyLiteral (Map.assocs (cflags cnc))),
                    ("printnames", pyDict 4 pyCId pyStr (Map.assocs (printnames cnc))),
                    ("lindefs", pyDict 4 pyCat (pyList 0 pyFun) (IntMap.assocs (lindefs cnc))),
                    ("productions", pyDict 4 pyCat pyProds (IntMap.assocs (productions cnc))),
                    ("cncfuns", pyDict 4 pyFun pyCncFun (Array.assocs (cncfuns cnc))),
                    ("sequences",  pyDict 4 pySeq pySymbols (Array.assocs (sequences cnc))),
                    ("cnccats", pyDict 4 pyCId pyCncCat (Map.assocs (cnccats cnc))),
                    ("size",  show (totalCats cnc))
                   ]
      where pyProds prods = pyList 5 pyProduction (Set.toList prods)
            pyCncCat (CncCat start end _) = pyList 0 pyCat [start..end]
            pyCncFun (CncFun f lins) = pyTuple 0 id [pyList 0 pySeq (Array.elems lins), pyCId f]
            pySymbols syms = pyList 0 pySymbol (Array.elems syms)
  
  pyProduction :: Production -> String
  pyProduction (PCoerce arg)       = pyTuple 0 id [pyStr "", pyList 0 pyCat [arg]]
  pyProduction (PApply funid args) = pyTuple 0 id [pyFun funid, pyList 0 pyPArg args]
      where pyPArg (PArg [] fid) = pyCat fid
            pyPArg (PArg hypos fid) = pyTuple 0 pyCat (fid : map snd hypos)
  
  pySymbol :: Symbol -> String
  pySymbol (SymCat n l)    = pyTuple 0 show [n, l]
  pySymbol (SymLit n l)    = pyDict 0 pyStr id [("lit", pyTuple 0 show [n, l])]
  pySymbol (SymVar n l)    = pyDict 0 pyStr id [("var", pyTuple 0 show [n, l])]
  pySymbol (SymKS t)       = pyStr t
  pySymbol (SymKP ts alts) = pyDict 0 pyStr id [("pre", pyList 0 pySymbol ts), ("alts", pyList 0 alt2py alts)]
      where alt2py (ps,ts) = pyTuple 0 (pyList 0 pyStr) [map pySymbol ps, ts]
  pySymbol SymBIND         = pyStr "&+"
  pySymbol SymSOFT_BIND    = pyStr "&+"
  pySymbol SymSOFT_SPACE   = pyStr "&+"
  pySymbol SymCAPIT        = pyStr "&|"
  pySymbol SymALL_CAPIT    = pyStr "&|"
  pySymbol SymNE           = pyDict 0 pyStr id [("nonExist", pyTuple 0 id [])]
  
  ----------------------------------------------------------------------
  -- python helpers 
  
  pyDict :: Int -> (k -> String) -> (v -> String) -> [(k, v)] -> String
  pyDict n pk pv [] = "{}"
  pyDict n pk pv kvlist = prCurly (pyIndent n ++ prTList ("," ++ pyIndent n) (map pyKV kvlist) ++ pyIndent n)
      where pyKV (k, v) = pk k ++ ":" ++ pv v
  
  pyList :: Int -> (v -> String) -> [v] -> String
  pyList n pv [] = "[]"
  pyList n pv xs = prBracket (pyIndent n ++ prTList ("," ++ pyIndent n) (map pv xs) ++ pyIndent n)
  
  pyTuple :: Int -> (v -> String) -> [v] -> String
  pyTuple n pv [] = "()"
  pyTuple n pv [x] = prParenth (pyIndent n ++ pv x ++ "," ++ pyIndent n)
  pyTuple n pv xs = prParenth (pyIndent n ++ prTList ("," ++ pyIndent n) (map pv xs) ++ pyIndent n)
  
  pyCat :: Int -> String
  pyCat n = pyStr ('C' : show n)
  
  pyFun :: Int -> String
  pyFun n = pyStr ('F' : show n)
  
  pySeq :: Int -> String
  pySeq n = pyStr ('S' : show n)
  
  pyStr :: String -> String
  pyStr s = 'u' : prQuotedString s
  
  pyCId :: CId -> String
  pyCId = pyStr . showCId
  
  pyIndent :: Int -> String
  pyIndent n | n > 0 = "\n" ++ replicate n ' '
             | otherwise = ""
  