module Language.Sifflet.SiffML
(
ToXml(..)
, produceSiffMLFile
, consumeSiffMLFile
, xmlToFunctions
, testFromXml
)
where
import Text.XML.HXT.Core
import Data.Number.Sifflet
import Language.Sifflet.Expr
import Language.Sifflet.Util
class ToXml a where
toXml :: a -> XMLProducer
type XMLProducer = IOSLA (XIOState ()) XmlTree XmlTree
type XMLConsumer a b = IOSLA (XIOState ()) a b
defaultOptions :: SysConfigList
defaultOptions = [withIndent yes, withValidate no ]
produceSiffMLFile :: (ToXml a) => a -> FilePath -> IO ()
produceSiffMLFile src path =
let arrow :: XMLProducer
arrow = toXml src
options = defaultOptions
in do
{
putStrLn ""
; [rc] <- runX (root [] [arrow] >>>
writeDocument options path >>>
getErrStatus)
; putStrLn (case rc of
0 -> "Okay"
_ -> "Failed")
}
consumeSiffMLFile :: XMLConsumer XmlTree a -> FilePath -> IO [a]
consumeSiffMLFile fromXml filePath =
let options = defaultOptions
in do
doc <- readFile filePath
runX (readString options doc >>> fromXml)
instance ToXml Symbol where
toXml = symbolToXml
symbolToXml :: Symbol -> XMLProducer
symbolToXml (Symbol name) =
selem "symbol" [txt name]
instance ToXml Expr where
toXml = exprToXml
exprToXml :: Expr -> XMLProducer
exprToXml expr =
let literal label text =
selem "literal" [selem label [txt text]]
in case expr of
EUndefined ->
eelem "undefined"
ESymbol (Symbol name) ->
selem "symbol" [txt name]
EBool b ->
selem "literal" [selem "bool" [eelem (show b)]]
EChar c ->
literal "char" [c]
ENumber (Exact i) ->
literal "int" (show i)
ENumber (Inexact x) ->
literal "float" (show x)
EString s ->
literal "string" s
EIf e1 e2 e3 ->
selem "if" [toXml e1, toXml e2, toXml e3]
EList xs ->
selem "literal" [selem "list"
(map (toXml . literalToValue) xs)]
ELambda _ _ -> error "exprToXml: not implemented for lambda expr"
ECall (Symbol name) xs ->
selem "call"
(selem "symbol" [txt name] :
map toXml xs)
_ -> errcats ["exprToXml: extended expr:", show expr]
literalToValue :: Expr -> Value
literalToValue e =
if exprIsLiteral e
then case e of
EBool b -> VBool b
EChar c -> VChar c
ENumber n -> VNumber n
EString s -> VString s
EList es -> VList (map literalToValue es)
EGroup e' -> literalToValue e'
_ -> error "literalToValue: expr is literal, but not literal?"
else error ("literalToValue: expr is not a literal: " ++ show e)
xmlToExpr :: XMLConsumer XmlTree Expr
xmlToExpr =
isElem >>>
(
(hasName "undefined" >>> constA EUndefined) <+>
(hasName "symbol" >>> getChildren >>> isText >>> getText >>>
arr (ESymbol . Symbol)) <+>
(hasName "literal" >>> getChildren >>> xmlToExpr) <+>
(hasName "True" >>> constA (EBool True)) <+>
(hasName "False" >>> constA (EBool False)) <+>
(hasName "char" >>> getChildren >>> isText >>> getText >>>
arr (EChar . head)) <+>
(hasName "int" >>> getChildren >>> isText >>> getText >>>
arr (ENumber . Exact . read)) <+>
(hasName "float" >>> getChildren >>> isText >>> getText >>>
arr (ENumber . Inexact . read)) <+>
(hasName "string" >>> getChildren >>> isText >>> getText >>>
arr EString) <+>
(hasName "if" >>> listA (getChildren >>> xmlToExpr) >>>
arr (\ [a, b, c] -> EIf a b c)) <+>
(hasName "list" >>>
listA (getChildren >>> xmlToExpr) >>>
arr EList) <+>
(hasName "call" >>> listA (getChildren >>> xmlToExpr) >>>
arr (\ (ESymbol symf : args) -> ECall symf args))
)
instance ToXml Value where
toXml = valueToXml
valueToXml :: Value -> XMLProducer
valueToXml value =
case value of
VBool b ->
eelem (show b)
VChar c ->
selem "char" [txt [c]]
VString s ->
selem "string" [txt s]
VNumber (Exact i) ->
selem "int" [txt (show i)]
VNumber (Inexact x) ->
selem "float" [txt (show x)]
VFun f ->
selem "function" [toXml f]
VList vs ->
selem "list" (map toXml vs)
xmlToValue :: XMLConsumer XmlTree Value
xmlToValue =
isElem >>>
((hasName "True" >>> constA (VBool True)) <+>
(hasName "False" >>> constA (VBool False)) <+>
(hasName "char" >>> getChildren >>> isText >>> getText >>>
arr (VChar . head)) <+>
(hasName "string" >>> getChildren >>> isText >>> getText >>>
arr VString) <+>
(hasName "int" >>> getChildren >>> isText >>> getText >>>
arr (VNumber . Exact . read))
<+>
(hasName "float" >>> getChildren >>> isText >>> getText >>>
arr (VNumber . Inexact . read))
<+>
(hasName "function" >>> getChildren >>> xmlToFunction >>> arr VFun)
<+>
(hasName "list" >>> listA (getChildren >>> xmlToValue) >>>
arr VList)
)
instance ToXml Type where
toXml = typeToXml
typeToXml :: Type -> XMLProducer
typeToXml vtype =
case vtype of
TypeVar typeVarName -> selem "type-variable" [txt typeVarName]
TypeCons "String" [] -> eelem "string-type"
TypeCons "Char" [] -> eelem "char-type"
TypeCons "Num" [] -> eelem "num-type"
TypeCons "Bool" [] -> eelem "bool-type"
TypeCons "List" [eltType] -> selem "list-type" [typeToXml eltType]
TypeCons "Function" [argT, resultT] ->
selem "function-type" [typeToXml argT, typeToXml resultT]
TypeCons _ _ ->
errcats ["typeToXml:", show vtype, "cannot be converted to XML"]
xmlToType :: XMLConsumer XmlTree Type
xmlToType =
isElem >>>
((hasName "string-type" >>> constA typeString) <+>
(hasName "char-type" >>> constA typeChar) <+>
(hasName "num-type" >>> constA typeNum) <+>
(hasName "bool-type" >>> constA typeBool) <+>
(hasName "list-type" >>> getChildren >>> xmlToType >>> arr typeList) <+>
(hasName "function-type" >>>
listA (getChildren >>> xmlToType) >>>
arr (\ ts -> TypeCons "Function" ts)) <+>
(hasName "type-variable" >>> getChildren >>>
isText >>> getText >>> arr TypeVar)
)
instance ToXml Function where
toXml = functionToXml
functionToXml :: Function -> XMLProducer
functionToXml (Function mName argTypes retType impl) =
case impl of
Primitive _ ->
errcats ["functionToXml:",
"primitive functions cannot be exported to XML",
show (mName, argTypes, retType)]
Compound argNames body ->
selem "compound-function"
(let name s = selem "name" [txt s]
rest =
[selem "return-type" [typeToXml retType],
selem "arg-types" (map typeToXml argTypes),
selem "arg-names" (map name argNames),
selem "body" [toXml body]]
in case mName of
Nothing -> rest
Just fName -> name fName : rest
)
xmlToFunction :: XMLConsumer XmlTree Function
xmlToFunction =
let getChildElem :: XMLConsumer XmlTree XmlTree
getChildElem = getChildren >>> isElem
getFuncName :: XMLConsumer XmlTree String
getFuncName = hasName "name" >>> getChildren >>> isText >>> getText
getReturnType :: XMLConsumer XmlTree Type
getReturnType = hasName "return-type" >>> getChildren >>> xmlToType
getArgTypes :: XMLConsumer XmlTree [Type]
getArgTypes = hasName "arg-types" >>>
listA (getChildren >>> xmlToType)
getArgNames :: XMLConsumer XmlTree [String]
getArgNames = hasName "arg-names" >>>
listA (getChildElem >>> getFuncName)
getBody :: XMLConsumer XmlTree Expr
getBody = hasName "body" >>> getChildren >>> xmlToExpr
in
isElem >>> hasName "compound-function" >>>
(
(
listA (getChildElem >>> getFuncName)) &&&
(getChildElem >>> getReturnType) &&&
(getChildElem >>> getArgTypes) &&&
(getChildElem >>> getArgNames) &&&
(getChildElem >>> getBody)
)
>>>
(arr (\ (names, (returnType, (argTypes, (argNames, body)))) ->
Function (case names of
[] -> Nothing
(fname : _) -> (Just fname)
)
argTypes
returnType (Compound argNames body)))
functionsToXml :: Functions -> XMLProducer
functionsToXml (Functions fs) =
selem "functions" (map toXml fs)
xmlToFunctions :: XMLConsumer XmlTree Functions
xmlToFunctions =
isElem >>>
getChildren >>>
hasName "functions" >>>
listA (getChildren >>> xmlToFunction) >>>
arr Functions
instance ToXml Functions where
toXml = functionsToXml
testFromXml :: (ToXml a, Show b) => Int -> a -> XMLConsumer XmlTree b -> IO ()
testFromXml traceLevel src consumer = do
{
produceSiffMLFile src "test.xml"
; results <- runX (readDocument
(defaultOptions ++ [withTrace traceLevel])
"test.xml" >>>
isElem >>>
getChildren >>>
consumer)
; case results of
[] -> putStrLn "Failed"
result : _ -> print result
}