summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbjorn <bjorn@bringert.net>2008-06-03 19:34:08 +0000
committerbjorn <bjorn@bringert.net>2008-06-03 19:34:08 +0000
commitd57a96a186683b39b57cea269935866d7e2200ad (patch)
treeee23693be494ab1f8ccd8d88cb17d47335754b76
parent8f6fc902283a4cb4d18c273e4c3b4858d13a8da4 (diff)
Added (still unchanged) GF.Speech.VoiceXML.
-rw-r--r--src-3.0/GF/Speech/VoiceXML.hs285
1 files changed, 285 insertions, 0 deletions
diff --git a/src-3.0/GF/Speech/VoiceXML.hs b/src-3.0/GF/Speech/VoiceXML.hs
new file mode 100644
index 000000000..ad7f25d1c
--- /dev/null
+++ b/src-3.0/GF/Speech/VoiceXML.hs
@@ -0,0 +1,285 @@
+----------------------------------------------------------------------
+-- |
+-- Module : GrammarToVoiceXML
+-- Maintainer : Bjorn Bringert
+-- Stability : (stable)
+-- Portability : (portable)
+--
+-- Create VoiceXML dialogue system from a GF grammar.
+-----------------------------------------------------------------------------
+
+module GF.Speech.GrammarToVoiceXML (grammar2vxml) where
+
+import GF.Canon.CanonToGFCC (canon2gfcc)
+import qualified GF.GFCC.CId as C
+import GF.GFCC.DataGFCC (GFCC(..), Abstr(..))
+import GF.GFCC.Macros
+import qualified GF.Canon.GFC as GFC
+import GF.Canon.AbsGFC (Term)
+import GF.Canon.PrintGFC (printTree)
+import GF.Canon.CMacros (noMark, strsFromTerm)
+import GF.Canon.Unlex (formatAsText)
+import GF.Data.Utilities
+import GF.CF.CFIdent (cfCat2Ident)
+import GF.Compile.ShellState (StateGrammar,stateGrammarST,cncId,grammar,
+ startCatStateOpts,stateOptions)
+import GF.Data.Str (sstrV)
+import GF.Grammar.Macros hiding (assign,strsFromTerm)
+import GF.Grammar.Grammar (Fun)
+import GF.Grammar.Values (Tree)
+import GF.Infra.Option (Options, addOptions, getOptVal, speechLanguage)
+import GF.UseGrammar.GetTree (string2treeErr)
+import GF.UseGrammar.Linear (linTree2strings)
+
+import GF.Infra.Ident
+import GF.Infra.Option (noOptions)
+import GF.Infra.Modules
+import GF.Data.Operations
+
+import GF.Data.XML
+
+import Control.Monad (liftM)
+import Data.List (isPrefixOf, find, intersperse)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+
+import Debug.Trace
+
+-- | the main function
+grammar2vxml :: Options -> StateGrammar -> String
+grammar2vxml opt s = showsXMLDoc (skel2vxml name language startcat gr' qs) ""
+ where (_, gr') = vSkeleton (stateGrammarST s)
+ name = prIdent (cncId s)
+ qs = catQuestions s (map fst gr')
+ opts = addOptions opt (stateOptions s)
+ language = fmap (replace '_' '-') $ getOptVal opts speechLanguage
+ startcat = C.CId $ prIdent $ cfCat2Ident $ startCatStateOpts opts s
+
+--
+-- * VSkeleton: a simple description of the abstract syntax.
+--
+
+type VSkeleton = [(VIdent, [(VIdent, [VIdent])])]
+type VIdent = C.CId
+
+prid :: VIdent -> String
+prid (C.CId x) = x
+
+vSkeleton :: GFC.CanonGrammar -> (VIdent,VSkeleton)
+vSkeleton = gfccSkeleton . canon2gfcc noOptions
+
+gfccSkeleton :: GFCC -> (VIdent,VSkeleton)
+gfccSkeleton gfcc = (absname gfcc, ts)
+ where a = abstract gfcc
+ ts = [(c,[(f,ft f) | f <- fs]) | (c,fs) <- Map.toList (catfuns a)]
+ ft f = case lookMap (error $ prid f) f (funs a) of
+ (ty,_) -> fst $ GF.GFCC.Macros.catSkeleton ty
+
+--
+-- * Questions to ask
+--
+
+type CatQuestions = [(VIdent,String)]
+
+catQuestions :: StateGrammar -> [VIdent] -> CatQuestions
+catQuestions gr cats = [(c,catQuestion gr c) | c <- cats]
+
+catQuestion :: StateGrammar -> VIdent -> String
+catQuestion gr cat = err errHandler id (getPrintname gr cat >>= term2string)
+ where -- FIXME: use some better warning facility
+ errHandler e = trace ("GrammarToVoiceXML: " ++ e) ("quest_"++prid cat)
+ term2string = liftM sstrV . strsFromTerm
+
+getPrintname :: StateGrammar -> VIdent -> Err Term
+getPrintname gr cat =
+ do m <- lookupModMod (grammar gr) (cncId gr)
+ i <- lookupInfo m (IC (prid cat))
+ case i of
+ GFC.CncCat _ _ p -> return p
+ _ -> fail $ "getPrintname " ++ prid cat
+ ++ ": Expected CncCat, got " ++ show i
+
+
+{-
+lin :: StateGrammar -> String -> Err String
+lin gr fun = do
+ tree <- string2treeErr gr fun
+ let ls = map unt $ linTree2strings noMark g c tree
+ case ls of
+ [] -> fail $ "No linearization of " ++ fun
+ l:_ -> return l
+ where c = cncId gr
+ g = stateGrammarST gr
+ unt = formatAsText
+-}
+
+getCatQuestion :: VIdent -> CatQuestions -> String
+getCatQuestion c qs =
+ fromMaybe (error "No question for category " ++ prid c) (lookup c qs)
+
+--
+-- * Generate VoiceXML
+--
+
+skel2vxml :: String -> Maybe String -> VIdent -> VSkeleton -> CatQuestions -> XML
+skel2vxml name language start skel qs =
+ vxml language ([startForm] ++ concatMap (uncurry (catForms gr qs)) skel)
+ where
+ gr = grammarURI name
+ startForm = Tag "form" [] [subdialog "sub" [("src", "#"++catFormId start)]
+ [param "old" "{ name : '?' }"]]
+
+grammarURI :: String -> String
+grammarURI name = name ++ ".grxml"
+
+
+catForms :: String -> CatQuestions -> VIdent -> [(VIdent, [VIdent])] -> [XML]
+catForms gr qs cat fs =
+ comments [prid cat ++ " category."]
+ ++ [cat2form gr qs cat fs]
+
+cat2form :: String -> CatQuestions -> VIdent -> [(VIdent, [VIdent])] -> XML
+cat2form gr qs cat fs =
+ form (catFormId cat) $
+ [var "old" Nothing,
+ blockCond "old.name != '?'" [assign "term" "old"],
+ field "term" []
+ [promptString (getCatQuestion cat qs),
+ vxmlGrammar (gr++"#"++catFormId cat)
+ ]
+ ]
+ ++ concatMap (uncurry (fun2sub gr cat)) fs
+ ++ [block [return_ ["term"]{-]-}]]
+
+fun2sub :: String -> VIdent -> VIdent -> [VIdent] -> [XML]
+fun2sub gr cat fun args =
+ comments [prid fun ++ " : ("
+ ++ concat (intersperse ", " (map prid args))
+ ++ ") " ++ prid cat] ++ ss
+ where
+ ss = zipWith mkSub [0..] args
+ mkSub n t = subdialog s [("src","#"++catFormId t),
+ ("cond","term.name == "++string (prid fun))]
+ [param "old" v,
+ filled [] [assign v (s++".term")]]
+ where s = prid fun ++ "_" ++ show n
+ v = "term.args["++show n++"]"
+
+catFormId :: VIdent -> String
+catFormId c = prid c ++ "_cat"
+
+
+--
+-- * VoiceXML stuff
+--
+
+vxml :: Maybe String -> [XML] -> XML
+vxml ml = Tag "vxml" $ [("version","2.0"),
+ ("xmlns","http://www.w3.org/2001/vxml")]
+ ++ maybe [] (\l -> [("xml:lang", l)]) ml
+
+form :: String -> [XML] -> XML
+form id xs = Tag "form" [("id", id)] xs
+
+field :: String -> [(String,String)] -> [XML] -> XML
+field name attrs = Tag "field" ([("name",name)]++attrs)
+
+subdialog :: String -> [(String,String)] -> [XML] -> XML
+subdialog name attrs = Tag "subdialog" ([("name",name)]++attrs)
+
+filled :: [(String,String)] -> [XML] -> XML
+filled = Tag "filled"
+
+vxmlGrammar :: String -> XML
+vxmlGrammar uri = ETag "grammar" [("src",uri)]
+
+prompt :: [XML] -> XML
+prompt = Tag "prompt" []
+
+promptString :: String -> XML
+promptString p = prompt [Data p]
+
+reprompt :: XML
+reprompt = ETag "reprompt" []
+
+assign :: String -> String -> XML
+assign n e = ETag "assign" [("name",n),("expr",e)]
+
+value :: String -> XML
+value expr = ETag "value" [("expr",expr)]
+
+if_ :: String -> [XML] -> XML
+if_ c b = if_else c b []
+
+if_else :: String -> [XML] -> [XML] -> XML
+if_else c t f = cond [(c,t)] f
+
+cond :: [(String,[XML])] -> [XML] -> XML
+cond ((c,b):rest) els = Tag "if" [("cond",c)] (b ++ es)
+ where es = [Tag "elseif" [("cond",c')] b' | (c',b') <- rest]
+ ++ if null els then [] else (Tag "else" [] []:els)
+
+goto_item :: String -> XML
+goto_item nextitem = ETag "goto" [("nextitem",nextitem)]
+
+return_ :: [String] -> XML
+return_ names = ETag "return" [("namelist", unwords names)]
+
+block :: [XML] -> XML
+block = Tag "block" []
+
+blockCond :: String -> [XML] -> XML
+blockCond cond = Tag "block" [("cond", cond)]
+
+throw :: String -> String -> XML
+throw event msg = Tag "throw" [("event",event),("message",msg)] []
+
+nomatch :: [XML] -> XML
+nomatch = Tag "nomatch" []
+
+help :: [XML] -> XML
+help = Tag "help" []
+
+param :: String -> String -> XML
+param name expr = ETag "param" [("name",name),("expr",expr)]
+
+var :: String -> Maybe String -> XML
+var name expr = ETag "var" ([("name",name)]++e)
+ where e = maybe [] ((:[]) . (,) "expr") expr
+
+script :: String -> XML
+script s = Tag "script" [] [CData s]
+
+scriptURI :: String -> XML
+scriptURI uri = Tag "script" [("uri", uri)] []
+
+--
+-- * ECMAScript stuff
+--
+
+string :: String -> String
+string s = "'" ++ concatMap esc s ++ "'"
+ where esc '\'' = "\\'"
+ esc c = [c]
+
+{-
+--
+-- * List stuff
+--
+
+isListCat :: (VIdent, [(VIdent, [VIdent])]) -> Bool
+isListCat (cat,rules) = "List" `isPrefixOf` prIdent cat && length rules == 2
+ && ("Base"++c) `elem` fs && ("Cons"++c) `elem` fs
+ where c = drop 4 (prIdent cat)
+ fs = map (prIdent . fst) rules
+
+isBaseFun :: VIdent -> Bool
+isBaseFun f = "Base" `isPrefixOf` prIdent f
+
+isConsFun :: VIdent -> Bool
+isConsFun f = "Cons" `isPrefixOf` prIdent f
+
+baseSize :: (VIdent, [(VIdent, [VIdent])]) -> Int
+baseSize (_,rules) = length bs
+ where Just (_,bs) = find (isBaseFun . fst) rules
+-}