{-# LANGUAGE OverloadedStrings, LambdaCase, DeriveGeneric #-} {-# LANGUAGE RecordWildCards, DuplicateRecordFields #-} import Language.EFLINT.Spec hiding (Value(..)) import Language.EFLINT.State import Language.EFLINT.Util import Language.EFLINT.Options import Language.EFLINT.StaticEval import Language.EFLINT.Parse import Language.EFLINT.Print (ppProgram) import Language.EFLINT.Explorer hiding (Instruction(Revert), Response) import Language.EFLINT.Interpreter import Language.EFLINT.JSON(decode_json_file) import qualified Language.EFLINT.Explorer as Explorer import qualified Language.Explorer.Pure as EI import Control.Monad import Control.Applicative import Data.Function (on) import Data.Text (unpack) import Text.Read (readMaybe) import Data.List (isSuffixOf, sortBy, (\\)) import qualified Data.Set as S import qualified Data.Map as M import System.IO (hGetLine, hPutStrLn, hClose, IOMode(ReadWriteMode)) import System.IO.Error import System.Environment (getArgs) import System.Directory import System.FilePath import Network.Socket import qualified Data.ByteString.Lazy.Char8 (pack,unpack) import GHC.Generics import Data.Aeson hiding (String, Value(..),Options(..)) import qualified Data.Aeson as JSON -- determines which variant of execution graph to use init_explorer = init_tree_explorer main :: IO () main = do args <- getArgs cdir <- getCurrentDirectory case args of (f:p:opts) | ".eflint" `isSuffixOf` f, Just port_nr <- readMaybe p -> do fcont <- readFile f opts' <- run_options (["-i",takeDirectory f,"-i",cdir] ++ opts) case parse_component syn_directives_phrases fcont of Left err1 -> case parse_flint fcont of Left err2 -> do putStrLn "could not parse flint phrases:\n" >> putStrLn err1 putStrLn "could not parse flint spec:\n" >> putStrLn err2 Right (s, r, i, _) -> init_server opts' port_nr s r i Right ps -> init_with_phrases opts' port_nr ps (p:opts) | Just port_nr <- readMaybe p -> do opts <- run_options (["-i",cdir] ++ opts) init_with_phrases opts port_nr [] _ -> putStrLn "please provide: .eflint " init_with_phrases :: Options -> PortNumber -> [Either Directive Phrase] -> IO () init_with_phrases opts port_nr ps = do let explorer = init_explorer Nothing run_directives_phrases opts ps (start_server opts port_nr) explorer return () type Cont a = Explorer -> IO a run_directives_phrases :: Options -> [Either Directive Phrase] -> Cont a -> Cont a run_directives_phrases opts [] cont exp = cont exp run_directives_phrases opts (edp:ps) cont exp = let (_,_,(_,ctx)) = get_last_edge exp (EI.currRef exp) in case edp of Left d -> run_directive opts d (run_directives_phrases opts ps cont) exp Right p -> run_phrase opts p (run_directives_phrases opts ps cont) exp run_phrase :: Options -> Phrase -> Cont a -> Cont a run_phrase opts phrase cont exp = case run_ exp (Execute (convert_programs [phrase]) emptyInput) of ResultTrans exp _ _ _ -> cont exp Path _ -> putStrLn "Unexpected execution path encountered" >> cont exp Nodes _ -> putStrLn "Unexpected collection of nodes encountered" >> cont exp InvalidRevert -> putStrLn "invalid revert error" >> cont exp ExportExploration _ -> putStrLn "Unexpected export problem of execution graph" >> cont exp LoadExploration _ -> putStrLn "Unexpected load problem of execution graph" >> cont exp run_directive :: Options -> Directive -> Cont a -> Cont a run_directive opts (Require fp) cont exp | has_been_included fp opts = cont exp | otherwise = run_directive opts (Include fp) cont exp run_directive opts (Include fp) cont exp = do let dirs = find include_paths opts find_included_file dirs fp >>= \case [] -> putStrLn ("could not find " ++ fp ++ " in " ++ show dirs) >> cont exp (file:_) -> do add_include_path (takeDirectory file) opts add_include file opts case ".json" `isSuffixOf` file of True -> do mspec <- decode_json_file file case mspec of Left err -> putStrLn err >> cont exp Right spec -> putStrLn "including .json files is no longer supported" >> cont exp --repl_directive_phrases opts [Right (PFrames spec)] explorer False -> catchIOError (Right <$> readFile file) handler >>= \case Left err -> putStrLn err >> cont exp Right str -> case parse_component syn_directives_phrases str of Left err -> putStrLn err >> cont exp Right eps -> run_directives_phrases opts eps cont exp where handler :: IOError -> IO (Either String a) handler exc | isDoesNotExistError exc = return (Left ("unknown file: " ++ fp)) | isPermissionError exc = return (Left ("cannot read: " ++ fp)) | isAlreadyInUseError exc = return (Left ("in use: " ++ fp)) | otherwise = return (Left (show exc)) init_server :: Options -> PortNumber -> Spec -> Refiner -> Initialiser -> IO () init_server opts port_nr spec' ref' init' = do case compile_all spec' ref' init' [] of Left errs -> putStrLn "cannot compile specification" >> putStrLn (unlines errs) Right (spec', ref, init, _) -> do let spec = refine_specification spec' ref let state = make_initial_state spec init let explorer = init_explorer (Just (spec, state)) start_server opts port_nr explorer start_server :: Options -> PortNumber -> Cont () start_server opts port_nr explorer = do sock <- socket AF_INET Stream 0 setSocketOption sock ReuseAddr 1 Network.Socket.bind sock (SockAddrInet port_nr (tupleToHostAddress (127,0,0,1))) listen sock 2 server opts sock explorer server :: Options -> Socket -> Cont () server opts = continue where continue :: Socket -> Cont () continue sock exp = do putStrLn "--- AWAITING STATEMENT ---" conn <- accept sock handle <- socketToHandle (fst conn) ReadWriteMode string <- hGetLine handle let (_, _, (sid, ctx)) = get_last_edge exp (EI.currRef exp) putStrLn string let compile_and inpm program = report $ run_ exp (Execute [program] inpm) report res = case res of ResultTrans exp outs (old,oid) (new,sid) -> report_success sock outs oid old sid new exp Path path -> do hPutStrLn handle (json_encode (GivePath path)) hClose handle continue sock exp -- Explorer.ExecError err -> report_error (ExecError err) exp Nodes nodes -> do hPutStrLn handle (json_encode (GiveNodes nodes)) hClose handle continue sock exp ExportExploration exportGraph -> do putStrLn "--- Exporting ---" hPutStrLn handle (json_encode (GiveExportGraph exportGraph)) hClose handle continue sock exp LoadExploration exp -> do putStrLn "--- Loading execution graph ---" hPutStrLn handle (json_encode (GiveLoadGraph)) hClose handle continue sock exp InvalidRevert -> report_error InvalidState exp -- CompilationError err -> report_error (InvalidInput err) exp report_success sock outputs old_id c0 state_id ctx exp = do let facts_from = state_holds (cfg_state c0) let facts_to = state_holds (cfg_state ctx) let created_facts = facts_to \\ facts_from let terminated_facts = facts_from \\ facts_to let viols = S.fromList (violations outputs) let outs = S.fromList (ex_triggers outputs) let errs = S.fromList (errors outputs) let qress = query_ress outputs let transitions = S.fromList (rest_transitions ctx) let new_duties = S.fromList (rest_duties ctx) S.\\ S.fromList (rest_duties c0) let new_enabled = S.fromList (rest_enabled ctx) S.\\ S.fromList (rest_enabled c0) let new_disabled = S.fromList (rest_disabled ctx) S.\\ S.fromList (rest_disabled c0) let all_duties = S.fromList $ rest_duties ctx let response = case missing_inputs outputs of [] -> CommandSuccess old_id state_id facts_from facts_to created_facts terminated_facts viols outs errs qress new_duties all_duties new_enabled new_disabled transitions ms -> InputRequired ms hPutStrLn handle (json_encode response) hClose handle continue sock exp report_error err exp = do hPutStrLn handle (json_encode err) hClose handle continue sock exp let withCommand cmd = case cmd of CreateEvent term inpm -> compile_and inpm $ Program (Create [] term) TerminateEvent term inpm -> compile_and inpm $ Program (Terminate [] term) QueryCommand term inpm -> compile_and inpm $ Program (PQuery term) Revert new_state -> report $ run_ exp (Explorer.Revert new_state) Status mid -> report $ run_ exp (Display (maybe (EI.currRef exp) id mid)) History mid -> report $ run_ exp (DisplayFull (maybe (EI.currRef exp) id mid)) Heads -> report $ run_ exp ExplorationHeads CreateExport -> report $ run_ exp CreateExportExploration LoadExport graph -> report $ run_ exp (LoadExportExploration graph) GetFacts inpm -> do hPutStrLn handle (json_encode (GiveFacts (state_input_holds (cfg_state ctx) inpm))) hClose handle continue sock exp Kill -> hPutStrLn handle (json_encode ByeBye) >> hClose handle ActionCommand d a r os force inpm -> compile_and inpm $ Program (PTrigger [] term) where term = App d (Left (a : r : os)) CmdTrigger t b inpm -> compile_and inpm (Program (PTrigger [] t)) Phrase str inpm -> case parse_component syn_phrases str of Left err -> do hPutStrLn handle (json_encode (InvalidInput err)) hClose handle >> continue sock exp Right ps -> report $ run_ exp (Execute (convert_programs ps) inpm) Phrases str inpm -> case parse_component syn_phrases str of Left err -> do hPutStrLn handle (json_encode (InvalidInput err)) hClose handle >> continue sock exp Right ps -> report $ run_ exp (ExecuteOnce (collapse_programs (convert_programs ps)) inpm) case eitherDecode (Data.ByteString.Lazy.Char8.pack string) of Left err -> do when (find debug opts) (putStrLn err) case (find accept_phrases opts) of False -> do hPutStrLn handle (json_encode (InvalidCommand err)) hClose handle continue sock exp True -> withCommand (Phrase string emptyInput) Right cmd-> withCommand cmd json_encode r = Data.ByteString.Lazy.Char8.unpack (encode r) data Command = ActionCommand DomId Term Term [Term] Bool InputMap | CmdTrigger Term Bool InputMap | CreateEvent Term InputMap | TerminateEvent Term InputMap | QueryCommand Term InputMap | Revert Int | Status (Maybe Int) | Kill | GetFacts InputMap | History (Maybe Int) | Heads | CreateExport | LoadExport ExecutionGraph | Phrase String InputMap | Phrases String InputMap instance FromJSON Command where parseJSON = withObject "Command" $ \v -> do cmd <- v .: "command" case cmd::String of "create" -> CreateEvent . value_to_term <$> v .: "value" <*> maybe_input v "terminate" -> TerminateEvent . value_to_term <$> v .: "value" <*> maybe_input v "test-present"-> QueryCommand . value_to_term <$> v .: "value" <*> maybe_input v "test-absent" -> QueryCommand . Not . value_to_term <$> v .: "value" <*> maybe_input v "enabled" -> QueryCommand . Enabled . value_to_term <$> v .: "value" <*> maybe_input v "revert" -> Revert <$> v .: "value" "action" -> full_action <|> trigger_action where full_action = actionCommand <$> v .: "act-type" <*> v .: "actor" <*> v .: "recipient" <*> v .: "objects" <*> maybe_force v <*> maybe_input v trigger_action = CmdTrigger . value_to_term <$> v .: "value" <*> maybe_force v <*> maybe_input v actionCommand d a r os = ActionCommand d (to_term a) (to_term r) (map to_term os) "status" -> Status <$> v .: "state" <|> return (Status Nothing) "history" -> History <$> v .: "state" <|> return (History Nothing) "trace-heads" -> return Heads "create-export" -> return CreateExport "load-export" -> LoadExport <$> v .: "graph" "kill" -> return Kill "phrase" -> Phrase <$> v .: "text" <*> maybe_input v "phrases" -> Phrases <$> v .: "text" <*> maybe_input v "event" -> CmdTrigger . value_to_term <$> v .: "value" <*> maybe_force v <*> maybe_input v "facts" -> GetFacts <$> maybe_input v _ -> mzero maybe_force v = v .: "force" <|> return False maybe_input v = value_based_input <$> v .: "input" <|> return emptyInput value_based_input :: [AssTuple] -> InputMap value_based_input = M.fromList . map toTup where toTup at = (value_to_tagged $ (value :: AssTuple -> Value) at, assignment at) encode_store :: Store -> [AssTuple] encode_store = concatMap toTup . M.assocs where toTup (te, HoldsTrue) = return $ AssTuple (tagged_to_value te) True toTup (te, HoldsFalse) = return $ AssTuple (tagged_to_value te) False toTup (te, Unknown) = [] data AssTuple = AssTuple { value :: Value , assignment :: Bool } deriving (Generic) instance FromJSON AssTuple where instance ToJSON AssTuple data Value = Atom DomId (Either String Int) | Composite DomId [Value] data StringOrValue = ST String | VT Value instance FromJSON StringOrValue where parseJSON v = case v of JSON.String str -> return (ST (unpack str)) JSON.Object obj -> VT <$> parseJSON v _ -> fail ("looking for a string or a value, not a " ++ show v) to_term :: StringOrValue -> Term to_term (ST s) = StringLit s to_term (VT v) = value_to_term v tag_of :: Value -> DomId tag_of (Atom d _) = d tag_of (Composite d _) = d value_to_term :: Value -> Term value_to_term v = case v of Atom d (Left s) -> App d (Left [StringLit s]) Atom d (Right i) -> App d (Left [IntLit i]) Composite d vs -> App d (Right $ map value_to_modifier vs) value_to_modifier :: Value -> Modifier value_to_modifier v = Rename (no_decoration (tag_of v)) (value_to_term v) value_to_tagged :: Value -> Tagged value_to_tagged (Atom d (Left str)) = (String str, d) value_to_tagged (Atom d (Right i)) = (Int i, d) value_to_tagged (Composite d vs) = (Product (map value_to_tagged vs), d) tagged_to_value :: Tagged -> Value tagged_to_value (String s, d) = Atom d (Left s) tagged_to_value (Int i, d) = Atom d (Right i) tagged_to_value (Product args, d) = Composite d (map tagged_to_value args) instance FromJSON Value where parseJSON = withObject "Value" $ \v -> (\c i -> Atom c (Right i)) <$> v .: "fact-type" <*> v .: "value" <|> (\c s -> Atom c (Left s)) <$> v .: "fact-type" <*> v .: "value" <|> Composite <$> v .: "fact-type" <*> v .: "value" <|> Composite <$> v .: "fact-type" <*> v .: "arguments" instance ToJSON Value where toJSON v = case v of Atom d (Left str) -> object [ "fact-type" .= d, "value" .= toJSON str ] Atom d (Right i) -> object [ "fact-type" .= d, "value" .= toJSON i ] Composite d [v] -> object [ "fact-type" .= d, "value" .= toJSON v ] Composite d vs -> object [ "fact-type" .= d, "arguments" .= toJSON vs ] data Response = InvalidCommand String | InvalidInput String -- parse error | CommandSuccess Int -- Source node ID Int -- Target node ID [Tagged] -- source node facts [Tagged] -- target node facts [Tagged] -- created facts [Tagged] -- deleted facts (S.Set Violation) (S.Set TransInfo) (S.Set Error) -- errors: compilation + transition [QueryRes] -- query results (S.Set Tagged) -- new duties (S.Set Tagged) -- all duties in the current state (S.Set Tagged) -- newly enabled transitions (S.Set Tagged) -- newly disabled transitions (S.Set Transition) -- all transitions | InvalidState | InputRequired [Tagged] | GiveFacts [Tagged] | GivePath Path | GiveNodes [Node] | GiveExportGraph ExecutionGraph | GiveLoadGraph | ByeBye instance ToJSON Response where toJSON (InputRequired tes) = object [ "response" .= JSON.String "input required", "values" .= toJSON (map tagged_to_value tes)] toJSON (InvalidCommand err) = object [ "response" .= JSON.String "invalid command", "message" .= toJSON err ] toJSON (CommandSuccess sid_from i facts_from facts_to created_facts terminated_facts vs outs errs qress new_duties all_duties new_enabled new_disabled all_transitions) = object [ "response" .= JSON.String "success" , "old-state" .= toJSON sid_from , "new-state" .= toJSON i , "source_contents" .= toJSON (map TaggedJSON facts_from) , "target_contents" .= toJSON (map TaggedJSON facts_to) , "created_facts" .= toJSON (map TaggedJSON created_facts) , "terminated_facts" .= toJSON (map TaggedJSON terminated_facts) , "violations" .= toJSON vs , "output-events" .= toJSON outs , "errors" .= toJSON errs , "query-results" .= toJSON qress , "new-duties" .= toJSON (map TaggedJSON $ S.toList new_duties) , "new-enabled-transitions" .= toJSON (map TaggedJSON $ S.toList new_enabled) , "new-disabled-transitions" .= toJSON (map TaggedJSON $ S.toList new_disabled) , "all-duties" .= toJSON (map TaggedJSON $ S.toList all_duties) , "all-disabled-transitions" .= toJSON (map TaggedJSON dis_transitions) , "all-enabled-transitions" .= toJSON (map TaggedJSON en_transitions) ] where en_transitions = map fst $ filter snd $ map get_transition (S.toList all_transitions) dis_transitions = map fst $ filter (not . snd) $ map get_transition (S.toList all_transitions) toJSON InvalidState = object [ "response" .= JSON.String "invalid state" ] toJSON (InvalidInput err) = object [ "response" .= JSON.String "invalid input" , "error" .= toJSON err ] toJSON ByeBye = object [ "response" .= JSON.String "bye world.." ] toJSON (GiveFacts tes) = object [ "values" .= toJSON (map TaggedJSON tes) ] toJSON (GiveNodes nodes) = object [ "nodes" .= toJSON (map toJSONNode nodes) ] where toJSONNode (sid, cfg) = object [ "state_id" .= toJSON sid , "state_contents" .= toJSON (map TaggedJSON (state_contents)) , "duties" .= toJSON (map TaggedJSON $ S.toList all_duties) , "disabled-transitions" .= toJSON (map TaggedJSON dis_transitions) , "enabled-transitions" .= toJSON (map TaggedJSON en_transitions) ] where state_contents = state_holds (cfg_state cfg) all_duties = S.fromList $ rest_duties cfg all_transitions = S.fromList (map get_transition (rest_transitions cfg)) en_transitions = map fst $ filter snd $ S.toList all_transitions dis_transitions = map fst $ filter (not . snd) $ S.toList all_transitions toJSON (GivePath edges) = object [ "edges" .= toJSON (map toJSONEdge edges') ] where edges' = sortBy (on compare (\((sid,_),_,_) -> sid)) edges toJSONEdge ((sid_from,ctx_from), (phr, output), (sid_to, ctx_to)) = object [ "phrase" .= toJSON (ppProgram (snd phr)) , "input" .= toJSON (fst phr) , "source_id" .= toJSON sid_from , "target_id" .= toJSON sid_to , "source_contents" .= toJSON (map TaggedJSON facts_from) , "target_contents" .= toJSON (map TaggedJSON facts_to) , "created_facts" .= toJSON (map TaggedJSON created) , "terminated_facts" .= toJSON (map TaggedJSON terminated) , "violations" .= toJSON rep_viols , "output-events" .= toJSON outs , "errors" .= toJSON errs , "query-results" .= toJSON qress , "new-duties" .= toJSON (map TaggedJSON $ S.toList new_duties) , "new-enabled-transitions" .= toJSON (map TaggedJSON $ S.toList new_enabled) , "new-disabled-transitions" .= toJSON (map TaggedJSON $ S.toList new_disabled) , "all-duties" .= toJSON (map TaggedJSON $ S.toList all_duties) , "all-disabled-transitions" .= toJSON (map TaggedJSON dis_transitions) , "all-enabled-transitions" .= toJSON (map TaggedJSON en_transitions) ] where facts_from = state_holds (cfg_state ctx_from) facts_to = state_holds (cfg_state ctx_to) created = facts_to \\ facts_from terminated = facts_from \\ facts_to rep_viols = violations output outs = S.fromList (ex_triggers output) errs = S.fromList (errors output) qress = query_ress output all_transitions = S.fromList (rest_transitions ctx_to) en_transitions = map fst $ filter snd $ map get_transition (S.toList all_transitions) dis_transitions = map fst $ filter (not . snd) $ map get_transition (S.toList all_transitions) new_duties = S.fromList (rest_duties ctx_to) S.\\ S.fromList (rest_duties ctx_from) new_enabled = S.fromList (rest_enabled ctx_to) S.\\ S.fromList (rest_enabled ctx_from) new_disabled = S.fromList (rest_disabled ctx_to) S.\\ S.fromList (rest_disabled ctx_from) all_duties = S.fromList $ rest_duties ctx_to toJSON (GiveExportGraph graph) = toJSON graph toJSON GiveLoadGraph = object [ "response" .= JSON.String "success" ] instance ToJSON Violation where toJSON (TriggerViolation info) = object [ "violation" .= JSON.String "trigger" , "info" .= toJSON info ] toJSON (DutyViolation te) = object [ "violation" .= toJSON ("duty"::String) , "value" .= toJSON (TaggedJSON te) ] toJSON (InvariantViolation d) = object [ "violation" .= toJSON ("invariant"::String) , "invariant" .= toJSON d ] instance FromJSON Violation where parseJSON = withObject "trigger or duty or invariant violation" $ \o -> do violationtype <- o .: "violation" case violationtype of "trigger" -> TriggerViolation <$> o .: "info" "duty" -> DutyViolation <$> o .: "value" "invariant" -> InvariantViolation <$> o .: "invariant" _ -> fail ("unknown type: " ++ violationtype) instance ToJSON QueryRes where toJSON qres = case qres of QuerySuccess -> JSON.String "success" QueryFailure -> JSON.String "failure" instance FromJSON QueryRes where parseJSON o = case o of "success" -> return QuerySuccess "failure" -> return QueryFailure res -> fail ("unknown query-result " ++ show res) instance ToJSON Error where toJSON err = case err of NotTriggerable d -> object ["error-type" .= JSON.String "not triggerable" ,"value" .= d ] CompilationError err -> object ["error-type" .= JSON.String "compilation error" ,"error" .= toJSON err] RuntimeError (MissingInput te) -> object ["error-type" .= JSON.String "missing input" ,"error" .= JSON.toJSON (TaggedJSON te) ] RuntimeError (InternalError (EnumerateInfiniteDomain d dom)) -> object ["error-type" .= JSON.String "enumerating infinite domain" ,"error" .= toJSON (d, dom) ] RuntimeError (InternalError (MissingSubstitution var)) -> object ["error-type" .= JSON.String "missing substitution" ,"error" .= toJSON var ] RuntimeError (InternalError (PrimitiveApplication d)) -> object ["error-type" .= JSON.String "primitive application" ,"error" .= d ] RuntimeError (InternalError (UndeclaredType d)) -> object ["error-type" .= JSON.String "undeclared type" ,"error" .= d ] instance FromJSON Error where parseJSON = withObject "NotTriggerable or CompilationError" $ \o -> do errortype <- o .: "error-type" case errortype of "not triggerable" -> NotTriggerable <$> o .: "value" "compilation error" -> CompilationError <$> o .: "error" _ -> fail ("unknown type: " ++ errortype) instance ToJSON Output where toJSON (ExecutedTransition info) = object [ "output-type" .= JSON.String "executed-transition", "info" .= toJSON info] toJSON (Violation v) = object [ "output-type" .= JSON.String "violation", "output" .= v] toJSON (QueryRes r) = object [ "output-type" .= JSON.String "query-res", "output" .= r] toJSON (ErrorVal e) = object [ "output-type" .= JSON.String "error-val", "output" .= e] instance FromJSON Output where parseJSON = withObject "outputevent or violation or queryres or errorval" $ \o -> do outputtype <- o .: "output-type" case outputtype of "executed-transition" -> ExecutedTransition <$> o .: "info" "violation" -> Violation <$> o .: "output" "query-res" -> QueryRes <$> o .: "output" "error-val" -> ErrorVal <$> o .: "output" _ -> fail ("unknown output: " ++ outputtype) instance ToJSON Config where toJSON conf = object [ "spec" .= cfg_spec conf, "state" .= cfg_state conf, "rest_transitions" .= rest_transitions conf, "rest_duties" .= rest_duties conf ] instance FromJSON Config where parseJSON = withObject "config" $ \o -> do cfg_spec <- o .: "spec" cfg_state <- o .: "state" rest_transitions <- o .: "rest_transitions" rest_duties <- o .: "rest_duties" return Config{..} instance ToJSON Elem where toJSON (String el) = object ["elem-type" .= JSON.String "string", "elem" .= el] toJSON (Int el) = object ["elem-type" .= JSON.String "int", "elem" .= el] toJSON (Product el) = object ["elem-type" .= JSON.String "product", "elem" .= el] instance FromJSON Elem where parseJSON = withObject "string or int or product" $ \o -> do elemtype <- o .: "elem-type" case elemtype of "string" -> String <$> o .: "elem" "int" -> Int <$> o .: "elem" "product" -> Product <$> o .: "elem" _ -> fail ("unknown type: " ++ elemtype) instance ToJSON Spec where toJSON spec = object [ "decls" .= decls spec, "aliases" .= aliases spec ] instance FromJSON Spec where parseJSON = withObject "spec" $ \o -> do decls <- o .: "decls" aliases <- o .: "aliases" return Spec{..} -- TODO remove show and use ToJSON functions instance ToJSON State where toJSON state = object [ "contents" .= show (contents state), "time" .= (time state) ] -- TODO remove read and use FromJSON functions instance FromJSON State where parseJSON = withObject "state" $ \o -> do contents_show <- o .: "contents" let contents = read contents_show time <- o .: "time" return State{contents=contents, time=time} instance ToJSON Info where toJSON info = object [ "value" .= toJSON ((value :: Info -> Bool) info ), "from-sat" .= toJSON (from_sat info) ] instance FromJSON Info where parseJSON = withObject "info" $ \o -> do value <- o .: "value" from_sat <- o .: "from-sat" return Info{..} instance ToJSON TypeSpec where toJSON typespec = object [ "kind" .= kind typespec, "domain" .= domain typespec, "domain_constraint" .= domain_constraint typespec, "derivation" .= derivation typespec, "closed" .= closed typespec, "conditions" .= conditions typespec ] instance FromJSON TypeSpec where parseJSON = withObject "typespec" $ \o -> do kind <- o .: "kind" domain <- o .: "domain" domain_constraint <- o .: "domain_constraint" derivation <- o .: "derivation" closed <- o .: "closed" conditions <- o .: "conditions" return TypeSpec{..} instance ToJSON Kind where toJSON (Fact kind) = object ["kind-type" .= JSON.String "Fact", "fact" .= kind] toJSON (Act kind) = object ["kind-type" .= JSON.String "Act", "act" .= kind] toJSON (Duty kind) = object ["kind-type" .= JSON.String "Duty", "duty" .= kind] toJSON (Event kind) = object ["kind-type" .= JSON.String "Event", "event" .= kind] instance FromJSON Kind where parseJSON = withObject "fact or act or duty or event" $ \o -> do kindtype <- o .: "kind-type" case kindtype of "Fact" -> Fact <$> o .: "fact" "Act" -> Act <$> o .: "act" "Duty" -> Duty <$> o .: "duty" "Event" -> Event <$> o .: "event" _ -> fail ("unknown type: " ++ kindtype) instance ToJSON FactSpec where toJSON factspec = object [ "invariant" .= invariant factspec, "actor" .= actor factspec ] instance FromJSON FactSpec where parseJSON = withObject "factsspec" $ \o -> do invariant <- o .: "invariant" actor <- o .: "actor" return FactSpec{..} instance ToJSON ActSpec where toJSON actspec = object [ "effects" .= effects actspec, "syncs" .= syncs actspec ] instance FromJSON ActSpec where parseJSON = withObject "actspec" $ \o -> do effects <- o .: "effects" syncs <- o .: "syncs" return ActSpec{..} instance ToJSON Term where toJSON (Not t) = object ["term-type" .= JSON.String "Not", "t" .= t] toJSON (And t1 t2) = object ["term-type" .= JSON.String "And", "t1" .= t1, "t2" .= t2] toJSON (Or t1 t2) = object ["term-type" .= JSON.String "Or", "t1" .= t1, "t2" .= t2] toJSON (BoolLit b) = object ["term-type" .= JSON.String "BoolLit", "b" .= b] toJSON (Leq t1 t2) = object ["term-type" .= JSON.String "Leq", "t1" .= t1, "t2" .= t2] toJSON (Geq t1 t2) = object ["term-type" .= JSON.String "Geq", "t1" .= t1, "t2" .= t2] toJSON (Ge t1 t2) = object ["term-type" .= JSON.String "Ge", "t1" .= t1, "t2" .= t2] toJSON (Le t1 t2) = object ["term-type" .= JSON.String "Le", "t1" .= t1, "t2" .= t2] toJSON (Sub t1 t2) = object ["term-type" .= JSON.String "Sub", "t1" .= t1, "t2" .= t2] toJSON (Add t1 t2) = object ["term-type" .= JSON.String "Add", "t1" .= t1, "t2" .= t2] toJSON (Mult t1 t2) = object ["term-type" .= JSON.String "Mult", "t1" .= t1, "t2" .= t2] toJSON (Mod t1 t2) = object ["term-type" .= JSON.String "Mod", "t1" .= t1, "t2" .= t2] toJSON (Div t1 t2) = object ["term-type" .= JSON.String "Div", "t1" .= t1, "t2" .= t2] toJSON (IntLit i) = object ["term-type" .= JSON.String "IntLit", "int" .= i] toJSON (StringLit s) = object ["term-type" .= JSON.String "StringLit", "string" .= s] toJSON (Eq t1 t2) = object ["term-type" .= JSON.String "Eq", "t1" .= t1, "t2" .= t2] toJSON (Neq t1 t2) = object ["term-type" .= JSON.String "Neq", "t1" .= t1, "t2" .= t2] toJSON (Exists vars t) = object ["term-type" .= JSON.String "Exists", "vars" .= vars, "t" .= t] toJSON (Forall vars t) = object ["term-type" .= JSON.String "Forall", "vars" .= vars, "t" .= t] toJSON (Count vars t) = object ["term-type" .= JSON.String "Count", "vars" .= vars, "t" .= t] toJSON (Sum vars t) = object ["term-type" .= JSON.String "Sum", "vars" .= vars, "t" .= t] toJSON (Max vars t) = object ["term-type" .= JSON.String "Max", "vars" .= vars, "t" .= t] toJSON (Min vars t) = object ["term-type" .= JSON.String "Min", "vars" .= vars, "t" .= t] toJSON (When t1 t2) = object ["term-type" .= JSON.String "When", "t1" .= t1, "t2" .= t2] toJSON (Present t) = object ["term-type" .= JSON.String "Present", "t" .= t] toJSON (Violated t) = object ["term-type" .= JSON.String "Violated", "t" .= t] toJSON (Enabled t) = object ["term-type" .= JSON.String "Enabled", "t" .= t] toJSON (Project t var) = object ["term-type" .= JSON.String "Project", "t" .= t, "var" .= var] toJSON (Tag t domid) = object ["term-type" .= JSON.String "Tag", "t" .= t, "domID" .= domid] toJSON (Untag t) = object ["term-type" .= JSON.String "Untag", "t" .= t] toJSON (Ref var) = object ["term-type" .= JSON.String "Ref", "var" .= var] toJSON (App domid args) = object ["term-type" .= JSON.String "App", "domID" .= domid, "args" .= args] toJSON (CurrentTime) = object ["term-type" .= JSON.String "CurrentTime"] instance FromJSON Term where parseJSON = withObject "terms" $ \o -> do termtype <- o .: "term-type" case termtype of "Not" -> Not <$> o .: "t" "And" -> And <$> o .: "t1" <*> o .: "t2" "Or" -> Or <$> o .: "t1" <*> o .: "t2" "BoolLit" -> BoolLit <$> o .: "b" "Leq" -> Leq <$> o .: "t1" <*> o .: "t2" "Geq" -> Geq <$> o .: "t1" <*> o .: "t2" "Ge" -> Ge <$> o .: "t1" <*> o .: "t2" "Le" -> Le <$> o .: "t1" <*> o .: "t2" "Sub" -> Sub <$> o .: "t1" <*> o .: "t2" "Add" -> Add <$> o .: "t1" <*> o .: "t2" "Mult" -> Mult <$> o .: "t1" <*> o .: "t2" "Mod" -> Mod <$> o .: "t1" <*> o .: "t2" "Div" -> Div <$> o .: "t1" <*> o .: "t2" "IntLit" -> IntLit <$> o .: "int" "StringLit" -> StringLit <$> o .: "string" "Eq" -> Eq <$> o .: "t1" <*> o .: "t2" "Neq" -> Neq <$> o .: "t1" <*> o .: "t2" "Exists" -> Exists <$> o .: "vars" <*> o .: "t" "Forall" -> Forall <$> o .: "vars" <*> o .: "t" "Count" -> Count <$> o .: "vars" <*> o .: "t" "Sum" -> Sum <$> o .: "vars" <*> o .: "t" "Max" -> Max <$> o .: "vars" <*> o .: "t" "Min" -> Min <$> o .: "vars" <*> o .: "t" "When" -> When <$> o .: "t1" <*> o .: "t2" "Present" -> Present <$> o .: "t" "Violated" -> Violated <$> o .: "t" "Enabled" -> Enabled <$> o .: "t" "Project" -> Project <$> o .: "t" <*> o .: "var" "Tag" -> Tag <$> o .: "t" <*> o .: "domID" "Untag" -> Untag <$> o .: "t" "Ref" -> Ref <$> o .: "var" "App" -> App <$> o .: "domID" <*> o .: "args" "CurrentTime" -> return CurrentTime{} _ -> fail ("unknown type: " ++ termtype) instance ToJSON Modifier where toJSON (Rename var term) = object [ "var" .= var, "term" .= term ] instance FromJSON Modifier where parseJSON = withObject "modifier" $ \o -> do var <- o.: "var" term <- o.: "term" return (Rename var term) instance ToJSON Effect where toJSON (CAll vars t) = object ["effect-type" .= JSON.String "CAll", "vars" .= vars, "term" .= t] toJSON (TAll vars t) = object ["effect-type" .= JSON.String "TAll", "vars" .= vars, "term" .= t] toJSON (OAll vars t) = object ["effect-type" .= JSON.String "OAll", "vars" .= vars, "term" .= t] instance FromJSON Effect where parseJSON = withObject "CAll or Tall" $ \o -> do effecttype <- o .: "effect-type" case effecttype of "TAll" -> TAll <$> o .: "vars" <*> o .: "term" "CAll" -> CAll <$> o .: "vars" <*> o .: "term" _ -> fail ("unknown type: " ++ effecttype) instance ToJSON Sync where toJSON (Sync vars term) = object [ "vars" .= vars, "term" .= term ] instance FromJSON Sync where parseJSON = withObject "sync" $ \o -> do vars <- o.: "vars" term <- o.: "term" return (Sync vars term) instance ToJSON Var where toJSON (Var domid string) = object [ "domID" .= domid, "string" .= string ] instance FromJSON Var where parseJSON = withObject "var" $ \o -> do domid <- o.: "domID" string <- o.: "string" return (Var domid string) instance ToJSON DutySpec where toJSON dutyspec = object [ "enforcing_acts" .= enforcing_acts dutyspec, "violated_when" .= violated_when dutyspec ] instance FromJSON DutySpec where parseJSON = withObject "dutyspec" $ \o -> do enforcing_acts <- o.: "enforcing_acts" violated_when <- o.: "violated_when" return DutySpec{..} instance ToJSON EventSpec where toJSON eventspec = object [ "event_effects" .= event_effects eventspec ] instance FromJSON EventSpec where parseJSON = withObject "eventspec" $ \o -> do event_effects <- o.: "event_effects" return EventSpec{..} instance ToJSON Domain where toJSON (AnyString) = object ["domain-type" .= JSON.String "AnyString"] toJSON (AnyInt) = object ["domain-type" .= JSON.String "AnyInt"] toJSON (Strings strings) = object ["domain-type" .= JSON.String "Strings", "strings" .= strings] toJSON (Ints ints) = object ["domain-type" .= JSON.String "Ints", "ints" .= ints] toJSON (Products vars) = object ["domain-type" .= JSON.String "Products", "vars" .= vars] toJSON (Time) = object ["domain-type" .= JSON.String "Time"] instance FromJSON Domain where parseJSON = withObject "anystring or anyint or strings or ints or products or time or external" $ \o -> do domaintype <- o .: "domain-type" case domaintype of "AnyString" -> return AnyString{} "AnyInt" -> return AnyInt{} "Strings" -> Strings <$> o .: "strings" "Ints" -> Ints <$> o .: "ints" "Products" -> Products <$> o .: "vars" "Time" -> return Time{} _ -> fail ("unknown type: " ++ domaintype) instance ToJSON Derivation where toJSON (Dv vars term) = object ["derivation-type" .= JSON.String "Dv", "vars" .= vars, "term" .= term] toJSON (HoldsWhen term) = object ["derivation-type" .= JSON.String "HoldsWhen", "term" .= term] instance FromJSON Derivation where parseJSON = withObject "dv or holdswhen or externalvalue" $ \o -> do derivationtype <- o .: "derivation-type" case derivationtype of "Dv" -> Dv <$> o .: "vars" <*> o .: "term" "HoldsWhen" -> HoldsWhen <$> o .: "term" _ -> fail ("unknown type: " ++ derivationtype) instance ToJSON TransInfo where toJSON info = object [ "trans-tagged" .= toJSON (tagged_to_value (trans_tagged info)) , "trans-assignments" .= toJSON (encode_store (trans_assignments info)) , "trans-forced" .= toJSON (trans_forced info) , "trans-actor" .= toJSON (trans_actor info) , "trans-is-action" .= toJSON (trans_is_action info) , "trans-syncs" .= toJSON (trans_syncs info) ] instance ToJSON Assignment where toJSON HoldsTrue = JSON.String "true" toJSON HoldsFalse = JSON.String "false" toJSON Unknown = JSON.String "unknown" instance FromJSON Assignment where parseJSON o = case o of "true" -> return HoldsTrue "false" -> return HoldsFalse "unknown" -> return Unknown s -> fail ("unknown assignment " ++ show s) instance FromJSON TransInfo where parseJSON = withObject "TransInfo" $ \o -> do TransInfo <$> o .: "trans-tagged" <*> o .: "trans-assignments" <*> o .: "trans-forced" <*> o .: "trans-actor" <*> o .: "trans-syncs" -- TODO remove show and use ToJSON functions instance ToJSON Transition where toJSON transition = object [ "tagged" .= show (tagged transition), "present" .= exist transition ] -- TODO remove read and use FromJSON functions instance FromJSON Transition where parseJSON = withObject "transition" $ \o -> do tagged_show <- o .: "tagged" let tagged = read tagged_show exist <- o .: "present" return Transition{tagged=tagged, exist=exist} -- Converting to/from graph instance ToJSON ExecutionGraph where toJSON graph = object [ "current" .= current graph, "nodes" .= nodes graph, "edges" .= edges graph ] instance FromJSON ExecutionGraph where parseJSON = withObject "execution graph" $ \o -> do current <- o .: "current" nodes <- o .: "nodes" edges <- o .: "edges" return ExecutionGraph{..} instance ToJSON N where toJSON node = object [ "ref" .= ref node, "config" .= config node ] instance FromJSON N where parseJSON = withObject "node" $ \o -> do ref <- o .: "ref" config <- o .: "config" return N{..} instance ToJSON Edge where toJSON edge = object [ "source" .= source edge, "target" .= target edge, "po" .= po edge ] instance FromJSON Edge where parseJSON = withObject "edge" $ \o -> do source <- o .: "source" target <- o .: "target" po <- o .: "po" return Edge{..} -- TODO remove show and use ToJSON functions instance ToJSON PO where toJSON po = object [ "program" .= ppProgram (snd $ label po), "input" .= show (fst $ label po), "output" .= show (output po) ] -- TODO remove read and use FromJSON functions instance FromJSON PO where parseJSON = withObject "label output" $ \o -> do program_show <- o .: "label" output_show <- o .: "output" input_show <- o .: "input" let output = read output_show let input = read input_show case parse_component syn_phrases program_show of Left err -> error(err) Right ps -> return PO{label=(input, collapse_programs (convert_programs ps)),..}