-- Time-stamp: -- -- Basic SCSCP operations such as readSCSCPMsg and writeSCSCPMsg. -- Builds on HaXmL for realising OpenMath encoding. ----------------------------------------------------------------------------- module Math.ComputerAlgebra.Cash.HS2SCSCP where import Math.ComputerAlgebra.Cash.SCSCP_DTD -- parser support import Math.ComputerAlgebra.Cash.HS_SCSCP -- types import Text.XML.HaXml.Parse import Text.XML.HaXml.Xml2Haskell -- import Text.XML.HaXml.XmlContent import Text.XML.HaXml.Types import Text.XML.HaXml.Verbatim import Text.XML.HaXml.OneOfN import Text.XML.HaXml.Pretty import Text.PrettyPrint import Data.Maybe import Data.Char -- debugging only import Debug.Trace -- getContent :: Document -> Content getTopElement (Document _ _ e _) = CElem e cleanUpStr xs = reverse (cleanUpStrAcc [] xs) cleanUpStrAcc acc ('\t':xs) = cleanUpStrAcc acc xs cleanUpStrAcc acc ('\n':xs) = cleanUpStrAcc acc xs cleanUpStrAcc acc (c:xs) = cleanUpStrAcc (c:acc) xs cleanUpStrAcc acc [] = acc -- interface: read a message readSCSCPMsg :: String -> SCSCPMsg readSCSCPMsg str'= let str = cleanUpStr str' parsed = xmlParse "" str (interim,_) = fromElem [getTopElement parsed] (OMOBJ _ (OMATTR atps oma)) = fromJust interim -- processing attributes: -- pairing on atps pairs :: [(String,String)] pairs = case atps of Nothing -> [] Just (OMATP xs) -> mkPairs xs mkPairs [] = [] mkPairs (OneOf2 (oms,OMSTR str):rest) = ( oMSName oms,str):mkPairs rest mkPairs (TwoOf2 (oms, OMI str):rest) = (oMSName oms,str):mkPairs rest -- always there (we hope so...) callId = fromJust (lookup "call_id" pairs) -- HWL: WAS: "call_ID" -- CATime and CAMem (aliased Int) not always there maybeInt key = case lookup key pairs of Nothing -> Nothing Just s -> Just ((read s)::Int) -- attributes for result and error: t = maybeInt "info_runtime" mem = maybeInt "info_memory" -- attributes for call: pct = maybeInt nMaxTime pcmaxmem= maybeInt nMaxMem pcminmem= maybeInt nMinMem pcdebug = maybeInt nDebug pcResult= Nothing -- for now -- processing call/result/error data (tipe,result) = case oma of OMAOMS_OMR (x,OMR ref) -> (oMSName x,toOM (CARef ref)) OMAOMS_OME (x,OME oms (OMSTR err)) -> (oMSName x, -- pass errors to HS land error (oMSName oms ++ ": " ++ err)) OMAOMS_EATALL (x,EAT (Elem n attrs c)) -> (oMSName x, -- OM n attrs (concatMap verbatim c)) caError = case oma of OMAOMS_OME (_,OME oms (OMSTR err)) -> case oMSName oms of -- scscp1 sez: "error_memory" -> CAMemExhausted -- "error_CAS" in SCSCP-1.2.pdf "error_system_specific" -> CAMsg err -- runtime exhausted? Spec.unclear "error_runtime" -> CATimeExhausted -- callName = Left ("Why decode PCall?", -- "I want to be a client anyway.") -- The tricky thing here is, the OMA is parsed -- by EATALL. We do not have the nice HaXml -- structures and field names from above. (callName,callRest) = case oma of OMAOMS_EATALL (x, EAT (Elem n attrs (oms:rest))) -> (case fromElem [oms] of (Just ok,_) -> let cd = oMSCd ok in if cd == "scscp2" -- all special op.s -- are defined there then Right (decodeOp (oMSName ok)) else Left (cd,oMSName ok) other -> Left ("sys error", "cannot extract call name: found\n"++(show oma)) , -- FIXME: debug code only, all strings map contentToOM rest ) other -> (Left ("sys error","invalid PCall format") , []) in -- trace ("!! raw string read is: \n"++str) $ case tipe of "procedure_completed" -> PResult result callId t mem "procedure_terminated" -> PTerminated callId caError t mem "procedure_call" -> PCall callId callName callRest (PCOpts pcResult pct pcminmem pcmaxmem pcdebug) -- error ("Why should I decode PCall?" ++ -- " I am a client, not a server!") other -> error "unexpected SCSCP message type" scscpPrefix = "" -- then the options. we assume call_ID to be always present. scscpInfix = "" -- then the OMS indicating the type, then the included data, which can -- be OMA, OME, OMR, or any other result type scscpSuffix = "" ----------------------------------------------------------------------------- -- Main routine -- construct OM encoding of a message writeSCSCPMsg :: SCSCPMsg -> String -- XML writeSCSCPMsg (PCall id name args opts) = scscpPrefix ++ writeOpts id opts -- see HS_SCSCP ++ scscpInfix ++ "" ++ "" ++ (if null ref then concatMap writeOMObj args else ref) ++ "" ++ scscpSuffix where (cd,op,ref) = case name of Left (cd,n) -> (cd,n,"") -- Right UnbindObj -- -> ("scscp2","unbind", -- writeOMObj (toOM r)) -- Right RetrieveObj -- -> ("scscp2","retrieve", -- writeOMObj (toOM r)) Right special -> ("scscp2", encodeOp special, bonzo special) -- HWL: CHECK: whether scscp1 or scscp2 -- decode: see HS_SCSCP bonzo GetSignature | length args < 2 = error $ "bonzo: length of arguments to GetSignature too short; should be 2 but is" ++(show (length args)) | otherwise = "" -- HWL: HACK: encode arguments to GetSignature in an OMS (BROKEN) bonzo _ = "" getContentFromOMObj (OM tag attList []) = error "getContentFromOMObj: empty content in OMObj" getContentFromOMObj (OM tag attList nonemptyContent) = nonemptyContent writeSCSCPMsg (PTerminated id err t mem) = scscpPrefix ++ writeInfos id t mem ++ scscpInfix ++ "" ++ "" ++ errText err ++ "" ++ "" ++ scscpSuffix writeSCSCPMsg (PResult res id t mem) = scscpPrefix ++ writeInfos id t mem ++ scscpInfix ++ "" ++ writeOMObj res ++ scscpSuffix -- my own pretty-printer, simple indentation only indentXml :: String -> String -- for now hand-rolled: separate each tag in a new line -- entry, when starting like xml should: indentXml ('<':rest) = '<':indent 1 rest where indent :: Int -> String -> String -- end indent lvl "" = "" -- tag immediately closed: indent lvl ('/':'>':rest) = "/>" ++ indent (lvl-1) rest -- closing tag: indent lvl ('<':'/':rest) = '\n':replicate (lvl-1) ' ' ++ "':rest) = '>':indent l rest endTag l (s:rest) = s:endTag l rest endTag l [] = "" -- should not happen, though indentXml other = other -- in fact, not xml -- later: try using HaXml. Seems to introduce some overhead, though. -- indentXml = render . document