packages feed

eflint-3.1.0.0: src/Language/EFLINT/JSON.hs

{-# LANGUAGE DeriveGeneric, DuplicateRecordFields, TupleSections #-}

module Language.EFLINT.JSON where

import Language.EFLINT.Spec hiding (FactSpec(actor))
import qualified Language.EFLINT.Spec as Spec

import Data.Aeson hiding (object)
import Data.Maybe
import Data.Char (toLower, isUpper, isSpace)
import qualified Data.Map as M

import GHC.Generics

decode_json_file :: String -> IO (Either String Spec)
decode_json_file f = fmap tModel <$> eitherDecodeFileStrict f

tExpr :: Expression -> Term
tExpr e = case e of 
  RefExpr v -> Ref (tVariable v) 
  MultiExpr m -> case (expression :: Multi -> String) m of
    "AND" -> foldr And (BoolLit True) (map tExpr (fromJust (operands m)))
    "OR"  -> foldr Or  (BoolLit False) (map tExpr (fromJust (operands m)))
    "NOT" -> Not (tExpr (fromJust (operand m)))
    "LIST"-> tExpr (fromJust (items m))
    cons  -> error ("JSON2SPEC: unknown expression type: " ++ cons)

tAct :: Act -> (DomId, TypeSpec)
tAct a = (act a,) $ TypeSpec {
    kind = Spec.Act aspec
  , domain = Products args 
  , domain_constraint = BoolLit True
  , restriction = Nothing
  , derivation = [HoldsWhen condition_term]
  , closed = True 
  , conditions = []  
  }
  where condition_term = foldr And (BoolLit True) $ 
                          [ tExpr (preconditions a) 
                          , Ref (no_decoration (actor a))
                          , Ref (no_decoration (interested_party a))] ++
                          (map Ref objects)
        args = [no_decoration (actor a), no_decoration (interested_party a)] ++ objects
        aspec = ActSpec { effects = map mkT ((terminate :: Act -> [OptBinder]) a) 
                                 ++ map mkC ((create :: Act -> [OptBinder]) a)
                        , syncs = []
                        , physical = False }
          where mkT ob = case ob of ImplicitDV expr -> tAll [] expr
                                    ExplicitDV b    -> tAll (vars b) (binder_expression b)
                  where tAll vs expr = TAll (map tVariable vs) (tExpr expr) 
                mkC ob = case ob of ImplicitDV expr -> cAll [] expr
                                    ExplicitDV b    -> cAll [] (binder_expression b)
                  where cAll vs expr = CAll (map tVariable vs) (tExpr expr) 
        objects | all isSpace (object a) = []
                | otherwise              = [no_decoration (object a)]


tDuty :: Duty -> (DomId, TypeSpec)
tDuty d = (duty d,) $ TypeSpec {
    kind = Spec.Duty dspec
  , domain = Products $ [no_decoration (duty_holder d)
                        ,no_decoration (claimant d)] ++ objects
  , domain_constraint = BoolLit True
  , restriction = Nothing
  , derivation = case (derivation :: Duty -> Maybe Binder) d of 
      Nothing -> [] 
      Just b  -> [dv (duty d) (vars b) (binder_expression b)]
  , closed = True
  , conditions = []
  }
  where dspec = DutySpec {enforcing_acts = maybe [] (:[]) (enforcing d)
                         ,violated_when = []
                         ,terminating_acts = []
                         ,creating_acts = []}
        objects | all isSpace (duty_components d) = []
                | otherwise                       = [no_decoration (duty_components d)]

tFact :: Fact -> (DomId, TypeSpec)
tFact f = (fact f,) $ TypeSpec {
    kind = Spec.Fact fspec 
  , domain = case (domain :: Fact -> Maybe Language.EFLINT.JSON.Domain) f of 
                Nothing  -> case function f of 
                              ImplicitDV (RefExpr (BaseVar "[]")) -> AnyString 
                              _                                   -> Products []
                Just dom -> tDomain dom
  , domain_constraint = BoolLit True
  , restriction = Nothing
  , derivation = case function f of
      ImplicitDV expr  -> case expr of 
          RefExpr (BaseVar "[]")    -> [] --Just (Dv [] (When (App (fact f) (Right [])) (BoolLit True)))
          RefExpr (BaseVar "<<>>")  -> [] 
          expr                      -> [dv (fact f) [] expr]
      ExplicitDV d                  -> [dv (fact f) (vars d) (binder_expression d)]
  , closed = False
  , conditions = []
  }
  where fspec = FactSpec { invariant = False, Spec.actor = False }

dv :: String -> [Variable] -> Expression -> Spec.Derivation
dv cons vars expr = Dv (map tVariable vars) (When (App cons (Right [])) (tExpr expr))

tDomain :: Language.EFLINT.JSON.Domain -> Spec.Domain
tDomain d = case type_constructor d of 
  "ANY-STRING"    -> AnyString
  "ANY-INT"       -> AnyInt
  "STRING"        -> Strings (fromJust (strings d))
  "INT"           -> Ints (fromJust (ints d))
  "PRODUCT"       -> Products (map tVariable (fromJust (arguments d)))
  cons            -> error ("unknown type-constructor: " ++ cons)

tVariable :: Variable -> Spec.Var
tVariable (BaseVar var) = Var var ""
tVariable (DecVar (DecoratedVariable x d)) = Var x d 

tModel :: Model -> Spec
tModel m = Spec { decls = M.fromList $  
                            map tAct (acts m) ++ 
                            map tDuty (duties m) ++   
                            map tFact (facts m)
                , aliases = M.empty}

unhyphen :: String -> String
unhyphen = map op
  where op '-' = '_'
        op c   = c

hyphen :: String -> String
hyphen = map op
  where op '_' = '-'
        op c   = c

process_string :: String -> String
process_string = id
{-
process_string ('<':'<':s) = reverse (drop 2 (reverse (concatMap replace s)))
process_string ('<':s) = reverse (drop 1 (reverse (concatMap replace s)))
process_string ('[':s) = reverse (drop 1 (reverse (concatMap replace s)))
process_string s = s
-}

replace :: Char -> String
replace ',' = ""
replace '-' = ""
replace c | isUpper c = [toLower c]
replace c = [c]

customOptions = defaultOptions { fieldLabelModifier = hyphen, sumEncoding = UntaggedValue}

type BaseVariable = String
data Variable = BaseVar BaseVariable
              | DecVar DecoratedVariable
              deriving (Show, Generic)
instance ToJSON Variable where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Variable where
  parseJSON = genericParseJSON customOptions 

data DecoratedVariable = DecoratedVariable {
        base      :: BaseVariable
      , modifier  :: String
      } deriving (Show, Generic)
instance ToJSON DecoratedVariable where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON DecoratedVariable where
  parseJSON = genericParseJSON customOptions 

data Model = Model {
    acts    :: [Act]
  , facts   :: [Fact]
  , duties  :: [Duty]
  } deriving (Show, Generic) 
instance ToJSON Model where
  toEncoding = genericToEncoding (defaultOptions { constructorTagModifier = unhyphen, sumEncoding = UntaggedValue} )
instance FromJSON Model where

data Act = Act {
    act               :: String
  , actor             :: String
  , action            :: String
  , object            :: String
  , interested_party  :: String
  , preconditions     :: Expression 
  , create            :: [OptBinder]
  , terminate         :: [OptBinder]
  , derivation        :: Maybe Binder
  , sources           :: Maybe [Source]
  , explanation       :: Maybe String
  , version           :: Maybe String
  , reference         :: Maybe References
  , juriconnect       :: Maybe String
  , sourcetext        :: Maybe String
  } deriving (Show, Generic)
instance ToJSON Act where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Act where
  parseJSON = genericParseJSON customOptions 

data Fact = Fact {
    fact        :: String
  , function    :: OptBinder
  , domain      :: Maybe Language.EFLINT.JSON.Domain
  , sources     :: Maybe [Source]
  , explanation :: Maybe String
  , version     :: Maybe String
  , reference   :: Maybe References 
  , juriconnect :: Maybe String
  , sourcetext  :: Maybe String
  } deriving (Show, Generic)
instance ToJSON Fact where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Fact where
  parseJSON = genericParseJSON customOptions 

data Domain = Domain {
    type_constructor  :: String
  , arguments         :: Maybe [Variable]
  , strings           :: Maybe [String]
  , ints              :: Maybe [Int]
  } deriving (Show, Generic)
instance ToJSON Language.EFLINT.JSON.Domain where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Language.EFLINT.JSON.Domain where
  parseJSON = genericParseJSON customOptions 

data Duty = Duty {
    duty            :: String
  , duty_components :: String
  , duty_holder     :: String
  , claimant        :: String
  , create          :: String
  , terminate       :: String
  , enforcing       :: Maybe String
  , derivation      :: Maybe Binder
  , sources         :: Maybe [Source]
  , explanation     :: Maybe String
  , version         :: Maybe String
  , reference       :: Maybe References
  , juriconnect     :: Maybe String
  , sourcetext      :: Maybe String
  }   deriving (Show, Generic)
instance ToJSON Duty where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Duty where
  parseJSON = genericParseJSON customOptions 

data OptBinder = ExplicitDV Binder
               | ImplicitDV Expression 
               deriving (Show, Generic)
instance ToJSON OptBinder where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON OptBinder where
  parseJSON = genericParseJSON customOptions 

data Binder = Binder {
        vars               :: [Variable]
      , binder_expression  :: Expression
      } deriving (Show, Generic)
instance ToJSON Binder where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Binder where
  parseJSON = genericParseJSON customOptions 

data Source = Source {
    validFrom       :: String
  , validTo         :: Maybe String
  , citation        :: String
  , juriconnect     :: String
  , text            :: String
  } deriving (Show, Generic)
instance ToJSON Source where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Source where
  parseJSON = genericParseJSON customOptions

data Expression = RefExpr Variable 
                | MultiExpr Multi 
                deriving (Show, Generic)
instance ToJSON Expression where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions 
instance FromJSON Expression where
  parseJSON = genericParseJSON customOptions

data Multi = Multi {
    expression  :: String
  , operands    :: Maybe [Expression]
  , operand     :: Maybe Expression
  , items       :: Maybe Expression
  } deriving (Show, Generic)
instance ToJSON Multi where
instance FromJSON Multi where

data References = RSingle String
                | RMulti  [String]
                deriving (Show, Generic)
instance ToJSON References where
  toEncoding = genericToEncoding customOptions
  toJSON = genericToJSON customOptions 
instance FromJSON References where
  parseJSON = genericParseJSON customOptions