packages feed

LslPlus-0.3.0: src/Language/Lsl/Internal/ExpressionHandler.hs

{-# OPTIONS_GHC -XFlexibleContexts #-}
module Language.Lsl.Internal.ExpressionHandler(validateExpression,evaluateExpression) where

import Control.Monad.Error
import Data.Bits
import IO

import Language.Lsl.Internal.Constants
import Language.Lsl.Internal.DOMProcessing
import Language.Lsl.Parse
import Language.Lsl.Syntax
import Language.Lsl.Internal.Type
import Language.Lsl.Internal.Util
import Language.Lsl.Internal.XmlCreate hiding (emit)
import qualified Language.Lsl.Internal.XmlCreate as E

emit s = E.emit s []

validPrimitiveCtxExpr (Ctx _ expr) = validPrimitiveExpr expr

validPrimitiveExpr (Get (Ctx _ name,All)) = findConstType name
validPrimitiveExpr (Neg expr) = 
    do t <- validPrimitiveCtxExpr expr
       when (t `notElem` [LLFloat,LLInteger,LLVector,LLRot]) $ fail "operator only valid for integer, float, vector, and rotation expressions"
       return t
validPrimitiveExpr (Inv expr) =
    do t <- validPrimitiveCtxExpr expr
       when (t /= LLInteger) $ fail "operator only valid for integer expressions"
       return LLInteger
validPrimitiveExpr (Not expr) =
    do t <- validPrimitiveCtxExpr expr
       when (t /= LLInteger) $ fail "operator only valid for integer expressions"
       return LLInteger
validPrimitiveExpr (IntLit i) = return LLInteger
validPrimitiveExpr (FloatLit f) = return LLFloat
validPrimitiveExpr (StringLit s) = return LLString
validPrimitiveExpr (KeyLit k) = return LLKey
validPrimitiveExpr (VecExpr x y z) = do
     t0 <- validPrimitiveCtxExpr x
     t1 <- validPrimitiveCtxExpr y
     t2 <- validPrimitiveCtxExpr z
     when (not (all (`elem` [LLInteger,LLFloat]) [t0,t1,t2])) $ fail "vector expression must contain only integer or float components"
     return LLVector
validPrimitiveExpr (RotExpr x y z s) = do
     t0 <- validPrimitiveCtxExpr x
     t1 <- validPrimitiveCtxExpr y
     t2 <- validPrimitiveCtxExpr z
     t3 <- validPrimitiveCtxExpr s
     when (not (all (`elem` [LLInteger,LLFloat]) [t0,t1,t2,t3])) $ fail "vector expression must contain only integer or float components"
     return LLRot
validPrimitiveExpr (ListExpr l) = do
    ts <- mapM validPrimitiveCtxExpr l
    when (LLList `elem` ts) $ fail "lists can't contain lists"
    return LLList
validPrimitiveExpr (Add e0 e1) =
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLFloat) -> return LLFloat
           (LLFloat,LLInteger) -> return LLFloat
           (LLFloat,LLFloat) -> return LLFloat
           (LLInteger,LLInteger) -> return LLInteger
           (LLVector,LLVector) -> return LLVector
           (LLRot,LLRot) -> return LLVector
           (LLString,LLString) -> return LLString
           _ -> fail "incompatible operands"
validPrimitiveExpr (Sub e0 e1) = 
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLFloat) -> return LLFloat
           (LLInteger,LLInteger) -> return LLInteger
           (LLFloat,LLInteger) -> return LLFloat
           (LLFloat,LLFloat) -> return LLFloat
           (LLVector,LLVector) -> return LLVector
           (LLRot,LLRot) -> return LLRot
           _ -> fail "incompatible operands"
validPrimitiveExpr (Mul e0 e1) = 
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLFloat) -> return LLFloat
           (LLFloat,LLFloat) -> return LLFloat
           (LLInteger,LLInteger) -> return LLInteger
           (LLFloat,LLInteger) -> return LLFloat
           (LLFloat,LLVector) -> return LLVector
           (LLVector,LLFloat) -> return LLVector
           (LLInteger,LLVector) -> return LLVector
           (LLVector,LLInteger) -> return LLVector
           (LLVector,LLRot) -> return LLVector
           (LLRot,LLRot) -> return LLRot
           _ -> fail "incompatible operands"
validPrimitiveExpr (Div e0 e1) =
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLInteger) -> return LLInteger
           (LLInteger,LLFloat) -> return LLFloat
           (LLFloat,LLInteger) -> return LLFloat
           (LLFloat,LLFloat) -> return LLFloat
           (LLVector,LLInteger) -> return LLVector
           (LLVector,LLFloat) -> return LLVector
           (LLVector,LLRot) -> return LLVector
           (LLRot,LLRot) -> return LLRot
           _ -> fail "incompatible operands"
validPrimitiveExpr (Mod e0 e1) =
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLInteger) -> return LLInteger
           (LLVector,LLVector) -> return LLVector
           _ -> fail "incompatible operands"
validPrimitiveExpr (BAnd e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (BOr e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (Xor e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (ShiftL e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (ShiftR e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (And e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (Or e0 e1) = validIntegerExpr e0 e1
validPrimitiveExpr (Equal e0 e1) = validEqExpr e0 e1
validPrimitiveExpr (NotEqual e0 e1) = validEqExpr e0 e1
validPrimitiveExpr (Lt e0 e1) = validRelExpr e0 e1
validPrimitiveExpr (Le e0 e1) = validRelExpr e0 e1
validPrimitiveExpr (Gt e0 e1) = validRelExpr e0 e1
validPrimitiveExpr (Ge e0 e1) = validRelExpr e0 e1
validPrimitiveExpr (Cast t e) = do
    t' <- validPrimitiveCtxExpr e
    when (not $ isCastValid t' t) $ fail "invalid cast"
    return t
validPrimitiveExpr expr = fail "expression not valid in this context"

validPrimEach e0 e1 =
    do t0 <- validPrimitiveCtxExpr e0
       t1 <- validPrimitiveCtxExpr e1
       return (t0,t1)
       
validIntegerExpr e0 e1 =
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLInteger) -> return LLInteger
           _ -> fail "incompatible operands"
           
validRelExpr e0 e1 =
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLFloat) -> return LLInteger
           (LLFloat,LLInteger) -> return LLInteger
           (LLFloat,LLFloat) -> return LLInteger
           (LLInteger,LLInteger) -> return LLInteger
           _ -> fail "incompatible operands"

validEqExpr e0 e1 =           
    do (t0,t1) <- validPrimEach e0 e1
       case (t0,t1) of
           (LLInteger,LLFloat) -> return LLInteger
           (LLInteger,LLInteger) -> return LLInteger
           (LLFloat,LLFloat) -> return LLInteger
           (LLFloat,LLInteger) -> return LLInteger
           (LLString,LLString) -> return LLInteger
           (LLKey,LLKey) -> return LLInteger
           _ -> fail "incompatible operands"

checkExpr t text =
   case exprParser text of
       Left _ -> fail "syntax error"
       Right expr ->
           case validPrimitiveCtxExpr expr of
               Right t' -> 
                   case (t,t') of
                       (LLString,LLKey) -> return expr
                       (LLKey,LLString) -> return expr
                       (LLFloat,LLInteger) -> return expr
                       (t,t') | t == t' -> return expr
                              | otherwise -> fail (lslTypeString t ++ " expected")
               Left s -> fail s
               
evaluateExpression t text =
    case checkExpr t text of
        Left s -> fail s
        Right expr -> 
            do v <- evalCtxExpr expr
               case (t,v) of
                   (LLKey,SVal s) -> return $ KVal s
                   (LLString,KVal k) -> return $ SVal k
                   (LLFloat,IVal i) -> return $ FVal (fromInt i)
                   _ -> return v
            
unexpectedValue :: Monad m => m a
unexpectedValue = fail "unexpected value"

evalCtxExpr (Ctx _ expr) = evalExpr expr

evalExpr (Get (Ctx _ name,All)) = findConstVal name
evalExpr (Neg expr) = 
    do v <- evalCtxExpr expr
       case v of
           (IVal i) -> return (IVal (-i))
           (FVal f) -> return (FVal (-f))
           _ -> unexpectedValue
evalExpr (Inv expr) =
    do t <- evalCtxExpr expr
       case t of
           (IVal i) -> return (IVal $ complement i)
           _ -> unexpectedValue
evalExpr (Not expr) =
    do t <- evalCtxExpr expr
       case t of
           (IVal i) -> return $ IVal (if i == 0 then 1 else 0)
evalExpr (IntLit i) = return (IVal i)
evalExpr (FloatLit f) = return (FVal $ realToFrac f)
evalExpr (StringLit s) = return (SVal s)
evalExpr (KeyLit k) = return (KVal k)
evalExpr (VecExpr xExpr yExpr zExpr) = 
    do x <- evalCtxExpr xExpr
       y <- evalCtxExpr yExpr
       z <- evalCtxExpr zExpr
       return (VVal (toFloat x) (toFloat y) (toFloat z))
evalExpr (RotExpr xExpr yExpr zExpr sExpr) =
    do x <- evalCtxExpr xExpr
       y <- evalCtxExpr yExpr
       z <- evalCtxExpr zExpr
       s <- evalCtxExpr sExpr
       return (RVal (toFloat x) (toFloat y) (toFloat z) (toFloat s))
evalExpr (ListExpr l) =
    do vals <- mapM evalCtxExpr l
       return (LVal vals)
evalExpr (Add e0 e1) =
    do (v0,v1) <- evalEach e0 e1
       case (v0,v1) of
           (IVal i,FVal f) -> return $ FVal (f + fromInt i)
           (FVal f,IVal i) -> return $ FVal (f + fromInt i)
           (FVal f0,FVal f1) -> return $ FVal (f0 + f1)
           (IVal i0,IVal i1) -> return $ IVal (i0 + i1)
           (SVal s0,SVal s1) -> return $ SVal (s0 ++ s1)
           (VVal x y z,VVal x' y' z') -> return $ VVal (x + x') (y + y') (z + z')
           (RVal x1 y1 z1 s1,RVal x2 y2 z2 s2) -> return $ RVal (x1 + x2) (y1 + y2) (z1 + z2) (s1 + s2)
           _ -> fail "incompatible operands"
evalExpr (Sub e0 e1) = 
    do (v0,v1) <- evalEach e0 e1
       case (v0,v1) of
           (IVal i1,IVal i2) -> return $ IVal (i1 - i2)
           (IVal i1,FVal f2) -> return $ FVal (fromInt i1 - f2)
           (FVal f1,IVal i2) -> return $ FVal (f1 - fromInt i2)
           (FVal f1,FVal f2) -> return $ FVal (f1 - f2)
           (VVal x1 y1 z1,VVal x2 y2 z2) -> return $ VVal (x1 - x2) (y1 - y2) (z1 - z2)
           (RVal x1 y1 z1 s1,RVal x2 y2 z2 s2) -> return $ RVal (x1 - x2) (y1 - y2) (z1 - z2) (s1 - s2)
           _ -> fail "incompatible operands"
evalExpr (Mul e0 e1) =
    do (v0,v1) <- evalEach e0 e1
       case (v0,v1) of
            (IVal i1,IVal i2) -> return $ IVal (i1*i2)
            (IVal i1,FVal f2) -> return $ FVal (fromInt i1 * f2)
            (FVal f1,IVal i2) -> return $ FVal (f1 * fromInt i2)
            (FVal f1,FVal f2) -> return $ FVal (f1 * f2)
            (v@(VVal _ _ _),IVal i) -> let f = fromInt i in return $ vecMulScalar v f
            (v@(VVal _ _ _),FVal f) -> return $ vecMulScalar v f
            ((VVal x1 y1 z1),(VVal x2 y2 z2)) -> return $ FVal $ x1 * x2 + y1 * y2 + z1 * z2
            (v@(VVal _ _ _),r@(RVal _ _ _ _)) -> return $ rotMulVec r v
            (r1@(RVal _ _ _ _),r2@(RVal _ _ _ _)) -> return $ rotMul r1 r2
            _ -> fail "incompatible operands"
evalExpr (Div e0 e1) =
    do (v0,v1) <- evalEach e0 e1
       case (v0,v1) of
            (IVal i1,IVal i2) -> return $ IVal (i1 `div` i2) -- TODO: how does SL handle divide by zero?
            (IVal i1,FVal f2) -> return $ FVal (fromInt i1 / f2)
            (FVal f1,IVal i2) -> return $ FVal (f1 / fromInt i2)
            (FVal f1,FVal f2) -> return $ FVal (f1/f2)
            (v@(VVal _ _ _),IVal i) -> let f = 1.0 / fromInt i in return $ vecMulScalar v f
            (v@(VVal _ _ _),FVal f) -> return $ vecMulScalar v f
            (v@(VVal _ _ _),r@(RVal _ _ _ _)) -> return $ rotMulVec (invRot r) v
            (r1@(RVal _ _ _ _),r2@(RVal _ _ _ _)) -> return $ rotMul r1 $ invRot r2
            _ -> fail "incompatible operands"
evalExpr (Mod e0 e1) = 
    do (v0,v1) <- evalEach e0 e1
       case (v0,v1) of
           (IVal i1,IVal i2) -> return $ IVal (i1 `mod` i2)
           (v1@(VVal _ _ _),v2@(VVal _ _ _)) ->return $ v1 `vcross` v2
           _ -> fail "incompatible operands"
evalExpr (BAnd e0 e1) = evalIntExpr (.&.) e0 e1
evalExpr (BOr e0 e1) = evalIntExpr (.|.) e0 e1
evalExpr (Xor e0 e1) = evalIntExpr xor e0 e1
evalExpr (ShiftL e0 e1) = evalIntExpr shiftL e0 e1
evalExpr (ShiftR e0 e1) = evalIntExpr shiftR e0 e1
evalExpr (And e0 e1) = evalIntExpr (\ x y -> if x /= 0 && y /= 0 then 1 else 0) e0 e1
evalExpr (Or e0 e1) = evalIntExpr (\ x y -> if x /= 0 || y /= 0 then 1 else 0) e0 e1
evalExpr (Equal e0 e1) = evalEqExpr (==) e0 e1
evalExpr (NotEqual e0 e1) = evalEqExpr (/=) e0 e1
evalExpr (Lt e0 e1) = evalRelExpr (<) (<) e0 e1
evalExpr (Le e0 e1) = evalRelExpr (<=) (<=) e0 e1
evalExpr (Gt e0 e1) = evalRelExpr (>) (>) e0 e1
evalExpr (Ge e0 e1) = evalRelExpr (>=) (>=) e0 e1
evalExpr (Cast t e) = do
    v <- evalCtxExpr e
    case (t,v) of
       (LLInteger,IVal i) -> return $ IVal i
       (LLInteger,FVal f) -> return $ IVal (truncate f)
       (LLInteger,SVal s) -> return $ IVal (parseInt s)
       -- TODO: can you cast a key to an int?
       (LLFloat,FVal f) -> return $ FVal f
       (LLFloat,IVal i) -> return $ FVal (fromInteger $ toInteger i)
       (LLFloat,SVal s) -> return $ FVal (parseFloat s)
       -- TODO: can you cast a key to a float?
       (LLString,v) -> return $ toSVal v
       -- TODO: can you cast anything but a string to a key?
       (LLVector,SVal s) -> return $ parseVector s
       (LLRot,SVal s) -> return $ parseRotation s
       (LLList,SVal s) -> return $ LVal [SVal s]
       (LLKey,SVal s) -> return $ KVal s
       (LLKey,KVal s) -> return $ KVal s
       (LLVector, (VVal _ _ _)) -> return $ v
       (LLRot, (RVal _ _ _ _)) -> return $ v
       (LLList, LVal l) -> return $ v
       _ -> fail "invalid cast!"
       
evalExpr expr = fail "expression not valid in this context"

evalArithExpr fi ff e0 e1 =
    do (v0,v1) <- evalEach e0 e1
       case (v0,v1) of
           (IVal i0, IVal i1) -> return $ IVal (fi i0 i1)
           (FVal f0, IVal i1) -> return $ FVal (ff f0 $ fromInt i1)
           (FVal f0, FVal f1) -> return $ FVal (ff f0 f1)
           (IVal i0, FVal f1) -> return $ FVal (ff (fromInt i0) f1)

evalEqExpr f e0 e1 =
    do (v0,v1) <- evalEach e0 e1
       let b = case (v0,v1) of
               (FVal f0, IVal i1) -> f (FVal f0) (FVal $ fromInt i1)
               (IVal i0, FVal f1) -> f (FVal $ fromInt i0) (FVal f1)
               (SVal s, KVal k) -> f (SVal s) (SVal k)
               (KVal k, SVal s) -> f (SVal s) (SVal k)
               (x,y) -> f x y
       return $ IVal $ if b then 1 else 0
           
evalRelExpr fi ff e0 e1 =
    do (v0,v1) <- evalEach e0 e1
       let b = case (v0,v1) of
               (FVal f, IVal i) -> ff f (fromInt i)
               (IVal i, FVal f) -> ff (fromInt i) f
               (FVal f0, FVal f1) -> ff f0 f1
               (IVal i0, IVal i1) -> fi i0 i1
       return $ IVal $ if b then 1 else 0

evalIntExpr f e0 e1 =
    do (IVal i0, IVal i1) <- evalEach e0 e1
       return $ IVal $ f i0 i1 
       
evalEach e0 e1 =
    do v0 <- evalCtxExpr e0
       v1 <- evalCtxExpr e1
       return (v0,v1)
       
extractExpressionFromXML s =
    let doc = xmlParse "" s in processDOMExpr doc
       
processDOMExpr (Document _ _ root _) = 
    case match expressionElementAcceptor root of
        Left s -> error s
        Right v -> v

expressionElementAcceptor :: MonadError String m => ElemAcceptor m (String,String)
expressionElementAcceptor =
    let f (Elem _ _ contents) = do
            (t,contents1) <- findElement (ElemAcceptor "type" simple) (elementsOnly contents)
            (text,[]) <- findElement (ElemAcceptor "text" simple) contents1
            return (t,text)
    in ElemAcceptor "expression" f

expressionValidatorEmitter (Left s) =
    (emit "result" [ emit "ok" [showString "false"], emit "msg" [showString $ xmlEscape s]]) ""
expressionValidatorEmitter (Right _) =
    (emit "result" [ emit "ok" [showString "true"]]) ""

validateExpression hIn hOut =
    do input <- hGetContents hIn
       let (tstring,text) = extractExpressionFromXML input
       let t = case parseType tstring of
                   Right v -> v
                   Left s -> error (show s)
       putStr $ expressionValidatorEmitter (checkExpr t text)