├── .gitignore
├── TODO.md
├── modalagents.cabal
├── src
├── Modal
│ ├── Programs.hs
│ ├── Programming.hs
│ ├── Utilities.hs
│ ├── Display.hs
│ ├── Agents.hs
│ ├── Games.hs
│ ├── GameTools.hs
│ ├── Parser.hs
│ ├── Competition.hs
│ ├── Statement.hs
│ ├── Def.hs
│ ├── Formulas.hs
│ ├── Code.hs
│ ├── CompilerBase.hs
│ └── Combat.hs
├── ModalCombat.hs
├── WebInterface.hs
└── WebInterface.jsexe
│ └── index.html
├── LICENSE
├── agents
└── target.ua
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.o
3 | *.hi
4 | /dist
5 | /.cabal-sandbox
6 | /cabal.sandbox.config
7 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | 1. Write a commented examples file that explains how things work.
2 | 2. Play with the API for loading up a file and fiddling with the objects
3 | manually. We probably don't want *all* the interaction to go through the
4 | new language compiler, as it's probably still buggy (and it's not as
5 | powerful as manipulating the formula maps manually).
6 | 3. Remove or upgrade all the "dying files" discussed in the README.
7 | 4. Grep around for TODOs
8 |
--------------------------------------------------------------------------------
/modalagents.cabal:
--------------------------------------------------------------------------------
1 | name: ModalCombat
2 | version: 0.0.1.0
3 | synopsis: Evaluates modal agents.
4 | license: MIT
5 | license-file: LICENSE
6 | author: Nate Soares
7 | maintainer: nate@intelligence.org
8 | build-type: Simple
9 | cabal-version: >=1.10
10 |
11 | executable modalcombat
12 | main-is: ModalCombat.hs
13 | hs-source-dirs: src
14 | default-language: Haskell2010
15 | default-extensions: OverloadedStrings
16 | build-depends:
17 | base >= 4.7,
18 | bytestring >= 0.10,
19 | containers >= 0.5,
20 | mtl >= 2.2.1,
21 | optparse-applicative >=0.10,
22 | parsec >= 3.1.7,
23 | text >= 1.1,
24 | transformers >= 0.4.1
25 |
--------------------------------------------------------------------------------
/src/Modal/Programs.hs:
--------------------------------------------------------------------------------
1 | module Modal.Programs where
2 | import Modal.Formulas
3 | import Modal.GameTools
4 | import Modal.Programming
5 | import Modal.Utilities
6 |
7 | generalUDT :: Eq a => [Int] -> [u] -> [a] -> a -> ModalProgram a (U1 u a)
8 | generalUDT levels uorder aorder dflt = completeProgram dflt mainLoop where
9 | mainLoop = mFor (zip levels uaPairs) (uncurry checkUApair)
10 | uaPairs = [(u, a) | u <- uorder, a <- aorder]
11 | checkUApair n (u, a) = mIf (boxk n (Var (U1A a) %> Var (U1 u))) (mReturn a)
12 |
13 | escalatingUDT :: (Eq a, Enum a, Enum u) => [Int] -> a -> ModalProgram a (U1 u a)
14 | escalatingUDT levels = generalUDT levels enumerate enumerate
15 |
16 | udtN :: (Eq a, Enum a, Enum u) => Int -> a -> ModalProgram a (U1 u a)
17 | udtN level = generalUDT (repeat level) enumerate enumerate
18 |
19 | udt' :: Eq a => [u] -> [a] -> a -> ModalProgram a (U1 u a)
20 | udt' = generalUDT (repeat 0)
21 |
22 | udt :: (Eq a, Enum a, Enum u) => a -> ModalProgram a (U1 u a)
23 | udt = udtN 0
24 |
--------------------------------------------------------------------------------
/src/ModalCombat.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 | import Control.Monad (foldM)
3 | import Data.Monoid
4 | import Options.Applicative
5 | import System.Environment (getProgName)
6 | import Modal.Combat
7 | import Modal.Utilities
8 |
9 | programDescription :: String
10 | programDescription =
11 | "Modal combat checker. Figures out how modal agents will behave " <>
12 | "when pitted against each other."
13 |
14 | data Options = Options
15 | { optEnvs :: [FilePath]
16 | , optFile :: FilePath
17 | , optUtf8 :: Bool
18 | } deriving Show
19 |
20 | optionParser :: Parser Options
21 | optionParser = Options
22 | <$> many (option str
23 | ( long "env"
24 | <> short 'e'
25 | <> metavar "FILE"
26 | <> help "An environment file defining other agents." ))
27 | <*> argument str
28 | ( metavar "FILE" )
29 | <*> switch
30 | ( long "utf8"
31 | <> short 'u'
32 | <> help "Interpret input files in UTF-8." )
33 |
34 | options :: String -> ParserInfo Options
35 | options name = info (helper <*> optionParser)
36 | ( fullDesc
37 | <> progDesc programDescription
38 | <> header (name ++ " - MODAL COMBAT!" ) )
39 |
40 | main :: IO ()
41 | main = do
42 | name <- getProgName
43 | opts <- execParser $ options name
44 | let useUtf8 = optUtf8 opts
45 | file = optFile opts
46 | settings <- mapM (compileFile useUtf8) (optEnvs opts)
47 | case settings of
48 | [] -> playFile useUtf8 file
49 | (x:xs) -> foldM (run .: mergeSettingsR) x xs >>= playFile' useUtf8 file
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Machine Intelligence Research Institute
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of the Machine Intelligence Research Institute, nor
17 | the names of other contributors may be used to endorse or promote
18 | products derived from this software without specific prior written
19 | permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 |
--------------------------------------------------------------------------------
/agents/target.ua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------
2 | -- This is our standard modal version of UDT.
3 | -- (Parameterized by default action, staring proof level, and proof escalation
4 | -- amount.)
5 |
6 | theory UDT(action default, number level=0, number step=0)
7 | let &n = &level
8 | for outcome &o in outcomes
9 | for action &a in actions
10 | if [&n]⌜A()=&a → U()=&o⌝
11 | return &a
12 | let &n = &n + &step
13 | return &default
14 |
15 | --------------------------------------------------------------------------
16 | -- Here are some simple decision problems.
17 |
18 | problem FiveAndTen [$10, $5] [10, 5]
19 | if A()=5
20 | return $5
21 | return $10
22 |
23 | problem Newcomb(number level=0) [$1001000, $1000000, $1000, $0] [1, 2]
24 | if A()=2 and [&level]⌜A()=1⌝
25 | return $1001000
26 | if A()=1 and [&level]⌜A()=1⌝
27 | return $1000000
28 | if A()=2
29 | return $1000
30 | return $0
31 |
32 | problem map 5and10 [$10, $5] [5, 10]
33 | $5 ↔ A()=5
34 | $10 ↔ A()=10
35 |
36 | problem map PD [$DC, $CC, $DD, $CD] [C, D]
37 | $DC ↔ A1()=D and A2()=C
38 | $CC ↔ A1()=C and A2()=C
39 | $DD ↔ A1()=D and A2()=D
40 | $CD ↔ A1()=C and A2()=D
41 |
42 | bot CooperateBot = ⊤
43 |
44 | bot DefectBot = ⊥
45 |
46 | agent FairBot
47 | if □⌜Them(Me)=C⌝
48 | return C
49 | return D
50 |
51 | agent PrudentBot
52 | if [1]⌜Them(DefectBot)=D⌝ and □⌜Them(Me)=C⌝
53 | return C
54 | return D
55 |
56 | combat with frames! PrudentBot vs FairBot
57 |
58 | compete: PrudentBot vs FairBot
59 |
60 | play with map with frames: FiveAndTen, UDT(5).
61 | play: Newcomb, UDT(2, step=1).
62 | play: Newcomb(1), UDT(2, level=10, step=1).
63 | play: 5and10, UDT(5).
64 | play: 5and10, UDT(10).
65 | play: PD,
66 | UDT(D, step=1)[...][$DC, $CC, $DD, $CD],
67 | UDT(D, step=1)[...][$CD, $CC, $DD, $DC].
68 |
--------------------------------------------------------------------------------
/src/Modal/Programming.hs:
--------------------------------------------------------------------------------
1 | module Modal.Programming where
2 | import Prelude hiding ((.), id)
3 | import Control.Arrow
4 | import Control.Category
5 | import Data.Maybe
6 | import qualified Data.List as List
7 | import Modal.Display
8 | import Modal.Formulas
9 | import Modal.Parser
10 | import Text.Parsec (sepEndBy)
11 | import Text.Parsec.Text
12 |
13 | type ModalProgram a v = a -> ModalFormula v
14 |
15 | showProgram :: (Show a, Show v) => [a] -> ModalProgram a v -> String
16 | showProgram as p = renderTable $ tuplesToTable [(a, p a) | a <- as]
17 |
18 | programParser :: (Eq a, Read v) => Parser a -> Parser (ModalProgram a v)
19 | programParser p = makeProgram <$> (line `sepEndBy` w) where
20 | line = (,) <$> (p <* symbol ":") <*> parser
21 | makeProgram kvs = fromJust . flip List.lookup kvs
22 |
23 | -------------------------------------------------------------------------------
24 |
25 | -- The type of partial programs. You must tell them "what to do next" in order
26 | -- to generate the completed ModalProgram.
27 | type PartialProgram a v = ModalProgram a v -> ModalProgram a v
28 |
29 | -- Completes a program by adding a default action.
30 | completeProgram :: Eq a => a -> PartialProgram a v -> ModalProgram a v
31 | completeProgram dflt f = f $ Val . (dflt ==)
32 |
33 | -- Partial program that ignores the continuation and returns a.
34 | mReturn :: Eq a => a -> PartialProgram a v
35 | mReturn a _ = Val . (a ==)
36 |
37 | -- Partial program that either does t or e before the continuation.
38 | mIfElse :: ModalFormula v -> PartialProgram a v -> PartialProgram a v -> PartialProgram a v
39 | mIfElse cond t e next a = Or (cond %^ t next a) (Neg cond %^ e next a) where
40 | -- Alternatively: And (Imp cond (tnext a)) (Imp (Neg cond) (enext a))
41 |
42 | -- Partial program that may or may not do t.
43 | mIf :: ModalFormula v -> PartialProgram a v-> PartialProgram a v
44 | mIf cond t = mIfElse cond t id
45 |
46 | -- Partial program that iterates over the list of cs.
47 | mFor :: [c] -> (c -> PartialProgram a v) -> PartialProgram a v
48 | mFor [] _ = id
49 | mFor (c:cs) f = f c <<< mFor cs f
50 |
--------------------------------------------------------------------------------
/src/WebInterface.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP, TemplateHaskell, QuasiQuotes, ScopedTypeVariables, NoMonomorphismRestriction, Rank2Types, DeriveDataTypeable #-}
2 | module Main (
3 | main
4 | ) where
5 |
6 | import Prelude hiding ((!!))
7 | import Control.Applicative ((<$>))
8 | import Control.Monad.Trans (liftIO)
9 | import GHCJS.DOM
10 | import GHCJS.DOM.CSSStyleDeclaration
11 | import GHCJS.DOM.Document
12 | import GHCJS.DOM.HTMLElement
13 | import GHCJS.DOM.Element
14 | import GHCJS.DOM.HTMLTextAreaElement
15 | import GHCJS.DOM.Node
16 | import GHCJS.DOM.EventM
17 | import GHCJS.DOM.Types
18 | import Data.Text.Lazy (Text, unpack)
19 | import Text.Blaze.Html.Renderer.Text (renderHtml)
20 | import Text.Hamlet (shamlet)
21 | import Text.Blaze.Html (Html)
22 | import Text.Parsec hiding ((<|>), optional, many, State)
23 | import qualified Data.Text as T (unpack, pack)
24 |
25 | import Modal.Combat
26 | import Modal.Utilities
27 |
28 | runGame source = do
29 | game <- run $ parse gameParser "input" $ T.pack source
30 | playGame "game" $ game
31 |
32 | main = runWebGUI $ \ webView -> do
33 | enableInspector webView
34 | Just doc <- webViewGetDomDocument webView
35 | Just body <- documentGetBody doc
36 | Just div <- fmap castToHTMLDivElement <$> documentCreateElement doc "div"
37 | mbTerminal <- fmap castToHTMLDivElement <$> documentGetElementById doc "terminal"
38 | case mbTerminal of
39 | Just terminal -> do
40 | Just style <- elementGetStyle terminal
41 | cssStyleDeclarationSetProperty style "height" "200px" ""
42 | cssStyleDeclarationSetProperty style "position" "absolute" ""
43 | cssStyleDeclarationSetProperty style "bottom" "0" ""
44 | nodeInsertBefore body (Just div) (Just terminal)
45 | _ -> do
46 | nodeAppendChild body (Just div)
47 | Just input <- fmap castToHTMLTextAreaElement <$> documentGetElementById doc "input"
48 | Just btn <- fmap castToHTMLInputElement <$> documentGetElementById doc "btn"
49 | Just output <- fmap castToHTMLPreElement <$> documentGetElementById doc "output"
50 | elementOnclick btn $ liftIO $ do
51 | source <- htmlTextAreaElementGetValue input
52 | htmlElementSetInnerHTML output ""
53 | runGame $ unpack source
54 | return ()
55 |
--------------------------------------------------------------------------------
/src/Modal/Utilities.hs:
--------------------------------------------------------------------------------
1 | module Modal.Utilities
2 | ( (.:)
3 | , (.::)
4 | , (.:::)
5 | , ($>)
6 | , (<$$>)
7 | , Name
8 | , enumerate
9 | , alter
10 | , every
11 | , swap
12 | , firstDup
13 | , die
14 | , wrapError
15 | , force
16 | , run
17 | , runFile
18 | ) where
19 | import Prelude hiding (readFile)
20 | import Control.Monad.Except hiding (mapM, sequence)
21 | import qualified Data.ByteString as BS
22 | import Data.Text (Text)
23 | import Data.Text.Encoding (decodeUtf8)
24 | import Data.Text.IO (readFile)
25 | import System.IO (stderr, hPutStrLn)
26 | import System.Exit hiding (die)
27 | import Text.Printf (printf)
28 | import qualified Data.Set as Set
29 |
30 | (.:) :: (c -> x) -> (a -> b -> c) -> a -> b -> x
31 | (.:) = (.) . (.)
32 |
33 | (.::) :: (d -> x) -> (a -> b -> c -> d) -> a -> b -> c -> x
34 | (.::) = (.) . (.) . (.)
35 |
36 | (.:::) :: (e -> x) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> x
37 | (.:::) = (.) . (.) . (.) . (.)
38 |
39 | infixl 4 $>, <$$>
40 |
41 | ($>) :: Applicative f => f a -> b -> f b
42 | x $> y = x *> pure y
43 |
44 | (<$$>) :: Functor f => f a -> (a -> b) -> f b
45 | (<$$>) = flip (<$>)
46 |
47 | type Name = String
48 |
49 | enumerate :: Enum a => [a]
50 | enumerate = enumFrom (toEnum 0)
51 |
52 | alter :: [a] -> Int -> (a -> a) -> [a]
53 | alter [] _ _ = error "empty list"
54 | alter (x:xs) 0 f = f x : xs
55 | alter (x:xs) n f = x : alter xs (pred n) f
56 |
57 | every :: Int -> [a] -> [a]
58 | every n (x : xs) = x : every n (drop (pred n) xs)
59 | every _ [] = []
60 |
61 | swap :: (a, b) -> (b, a)
62 | swap = uncurry $ flip (,)
63 |
64 | firstDup :: Ord a => [a] -> Maybe a
65 | firstDup = either Just (const Nothing) . foldM addToSet Set.empty where
66 | addToSet s x = if x `Set.member` s then Left x else Right (Set.insert x s)
67 |
68 | die :: Show a => a -> IO b
69 | die x = hPutStrLn stderr ("Failure: " ++ show x) >> exitFailure
70 |
71 | wrapError :: MonadError b m => (a -> b) -> Except a c -> m c
72 | wrapError wrap = either (throwError . wrap) return . runExcept
73 |
74 | force :: Show l => Either l r -> r
75 | force = either (error . printf "Forcing failed: %s" . show) id
76 |
77 | run :: Show x => Either x a -> IO a
78 | run = either die return
79 |
80 | readFileEnc :: Bool -> FilePath -> IO Text
81 | readFileEnc useUtf8 = if useUtf8 then readFileUtf8 else readFile
82 | where
83 | readFileUtf8 fn = decodeUtf8 <$> BS.readFile fn
84 |
85 | runFile :: Show x => (Text -> Either x a) -> Bool -> FilePath -> IO a
86 | runFile f useUtf8 path = run . f =<< readFileEnc useUtf8 path
87 |
--------------------------------------------------------------------------------
/src/Modal/Display.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Modal.Display where
3 | import Control.Arrow (first)
4 | import Data.List (transpose, intercalate)
5 | import Data.Map hiding (map, foldr)
6 | import Data.Monoid ((<>))
7 | import Data.Text (Text)
8 | import qualified Data.Text as Text
9 | import qualified Data.Text.IO as Text.IO
10 | import Modal.Utilities
11 | import Text.Printf (printf)
12 |
13 | type Table = [[String]]
14 |
15 | padr :: a -> Int -> [a] -> [a]
16 | padr x n xs = xs ++ replicate (n - length xs) x
17 |
18 | padl :: a -> Int -> [a] -> [a]
19 | padl x n xs = replicate (n - length xs) x ++ xs
20 |
21 | listmapToTable :: (Show k, Ord k) => [k] -> Map k [String] -> Table
22 | listmapToTable [] _ = []
23 | listmapToTable ks m = header : rows where
24 | header = "" : " │" : map (printf " %s" . show) ks
25 | unpaddedCols = map (m !) ks
26 | cols = map (padr "" $ maximum $ map length unpaddedCols) unpaddedCols
27 | rows = zipWith addNum [0 :: Int ..] (transpose cols)
28 | addNum n row = show n : " │" : map (printf " %s") row
29 |
30 | tuplesToTable :: (Show k, Show v) => [(k, v)] -> Table
31 | tuplesToTable kvs = [row k v | (k, v) <- kvs] where
32 | row k v = [padr ' ' maxwidth (show k), " : ", show v]
33 | maxwidth = foldr (\(k, _) n -> max (length $ show k) n) 0 kvs
34 |
35 | displayMap :: (Ord k, Show k, Show v) => Map k v -> IO ()
36 | displayMap = putStrLn . unlines . map concat . tuplesToTable . toAscList
37 |
38 | squareUp' :: String -> String -> Table -> [[String]]
39 | squareUp' l r rows = map normalizeRow paddedRows where
40 | paddedRows = map (padr "" $ maxlen rows) rows
41 | maxlen = foldr (max . length) 0
42 | normalizeRow = zipWith normalizeCell [0..] where
43 | normalizeCell i c = l ++ padr ' ' (colwidth i) c ++ r
44 | colwidth i = maximum [length $ row !! i | row <- paddedRows]
45 |
46 | squareUp :: Table -> [[String]]
47 | squareUp = squareUp' "" ""
48 |
49 | renderTable :: Table -> String
50 | renderTable table = unlines $ map concat (squareUp table)
51 |
52 | indentTable :: String -> Table -> Table
53 | indentTable indent = map (indent:)
54 |
55 | displayTable :: Table -> IO ()
56 | displayTable = putStrLn . renderTable
57 |
58 | class Blockable a where
59 | blockLines :: a -> [(Int, Text)]
60 |
61 | increaseIndent :: [(Int, Text)] -> [(Int, Text)]
62 | increaseIndent = map (first succ)
63 |
64 | renderBlock' :: Blockable a => Text -> a -> Text
65 | renderBlock' indent = Text.unlines . map (uncurry indented) . blockLines where
66 | indented n = (mconcat (replicate n indent) <>)
67 |
68 | renderBlock :: Blockable a => a -> Text
69 | renderBlock = renderBlock' " "
70 |
71 | displayBlock' :: Blockable a => Text -> a -> IO ()
72 | displayBlock' = Text.IO.putStrLn .: renderBlock'
73 |
74 | displayBlock :: Blockable a => a -> IO ()
75 | displayBlock = Text.IO.putStrLn . renderBlock
76 |
77 | -- TODO: Replace as many List.intercalates as you can with this.
78 | renderArgs :: (a -> String) -> [a] -> String
79 | renderArgs f = intercalate ", " . map f
80 |
--------------------------------------------------------------------------------
/src/WebInterface.jsexe/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 | Modal Kombat!
14 |
80 |
81 |
82 |
83 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/src/Modal/Agents.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE FlexibleInstances #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Modal.Agents where
4 | import Control.Applicative
5 | import Data.Monoid
6 | import Modal.Code (Agent, ModalVar(..), Program, agent, ContextError, forceCompile)
7 | import Modal.Formulas
8 | import Modal.Environment
9 | import Modal.Parser
10 | import qualified Data.Text as Text
11 | import Text.Parsec (oneOf, ParseError)
12 | import Modal.Display
13 |
14 | data CorD = C | D deriving (Eq, Ord, Enum, Read, Show)
15 | instance Parsable CorD where parser = read . pure <$> oneOf "CD"
16 |
17 | instance Read (ModalVar CorD CorD) where
18 | readsPrec _ str = [(from name, rest) | not $ null name] where
19 | name = takeWhile (`elem` '_' : ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9']) str
20 | rest = dropWhile (`elem` '_' : ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9']) str
21 | from "a" = MeVsThemIs C
22 | from "b" = ThemVsMeIs C
23 | from n = ThemVsOtherIs (Text.pack n) C
24 |
25 | type AgentVar = ModalVar CorD CorD
26 |
27 | layeredBot :: Int -> ModalFormula AgentVar -> ModalFormula AgentVar
28 | layeredBot n base = foldl1 Or (map level [0..n]) where
29 | cond k = Neg (boxk k base) %^ diak k base
30 | level 0 = Box base
31 | level k = foldl1 And (map cond [0..pred k]) %^ boxk k base
32 |
33 | masqueBot :: Int -> ModalFormula AgentVar
34 | masqueBot n = dbLoop $ tbfLoop disjunct where
35 | dbLoop = loopBreakDBot (read "¬DB") (read "DB")
36 | tbfLoop = loopBreakDBot (read "TbF_2") (read "¬TbF_2")
37 | disjunct = foldl1 Or (map (\k -> boxk k $ read "b") [0..n])
38 | loopBreakDBot fbreak fdefect cont = breakOut n %^ cont where
39 | cond m = foldl1 And $ map (\k -> Neg (boxk k fbreak) %^ Neg (boxk k fdefect)) [0..m]
40 | breakOut 0 = Box fbreak
41 | breakOut m = breakOut (pred m) %| (cond (pred m) %^ boxk m fbreak)
42 |
43 | modalUDT :: (Agent CorD CorD)
44 | Right modalUDT = agent $
45 | "def UDT\n" <>
46 | " let $level = 0\n" <>
47 | " for outcome $o in ...\n" <>
48 | " for action $a in ...\n" <>
49 | " if [] $level [Me(Them)=$a -> Them(Me)=$o]\n" <>
50 | " return $a\n" <>
51 | " end\n" <>
52 | " let $level = $level + 1\n" <>
53 | " end\n" <>
54 | " end\n" <>
55 | " return\n"
56 |
57 | prg :: ModalFormula AgentVar -> Program CorD CorD
58 | prg f C = f
59 | prg f D = Neg f
60 |
61 | bot :: String -> Program CorD CorD
62 | bot = prg . read
63 |
64 | env :: Env CorD CorD
65 | Right env = nobody @<
66 | -- Rank 0 bots
67 | ("CB", bot "⊤") @+
68 | ("DB", bot "⊥") @+
69 | ("FB", bot "□ b") @+
70 | ("FB_1", bot "[1] b") @+
71 | ("FB_2", bot "[2] b") @+
72 | ("FB_3", bot "[3] b") @+
73 | ("FB_4", bot "[4] b") @+
74 | ("FB_5", bot "[5] b") @+
75 | ("TbF_1", prg $ layeredBot 1 $ read "b") @+
76 | ("TbF_2", prg $ layeredBot 2 $ read "b") @+
77 | ("TbF_3", prg $ layeredBot 3 $ read "b") @+
78 | ("FB_r", bot "¬□¬b ∧ □b") @+
79 | ("MB", bot "[1](□a → b)") @+
80 | ("WB", bot "¬□⊥ ∧ [1]b") @+
81 | ("WB_2", bot "¬□a ∧ (□¬a ∨ [1]b)") @+
82 | ("aMB", bot "¬[1](□¬a → b) ∧ [2](¬[1](□¬a → b) → b)") @+
83 | ("sMB", bot "□(◇a → b)") @+
84 | ("IB", bot "¬(□(a → ¬b))") @+
85 | -- Rank 1 bots
86 | ("PB", bot "□b ∧ [1]¬DB") @+
87 | ("PB_2", prg $ layeredBot 2 $ read "¬DB ∧ b") @+
88 | ("PB_3", prg $ layeredBot 3 $ read "¬DB ∧ b") @+
89 | ("NB", bot "□CB") @+
90 | ("JB", bot "□FB") @+
91 | ("TB", bot "□DB") @+
92 | ("QB_1", prg $ masqueBot 1) @+
93 | ("QB_2", prg $ masqueBot 2) @+
94 | ("QB_3", prg $ masqueBot 3)
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Lightning overview:
2 |
3 | `Modal/Formulas.hs` is the starting point. That's where the modal formula data
4 | structure is defined, and that's where the fixed point solver lives.
5 |
6 | The other files are as follows: (Read in order to understand the code)
7 |
8 | `Modal/CompilerBase.hs`: Contains basic types used throughout the compiler,
9 | such as Ref, Relation, and all the error types.
10 |
11 | `Modal/Statement.hs`: Contains the definition and parser for "statements",
12 | which are like modal formulas but can refer to variables at the meta level.
13 | They can be turned into a formula given a context. (For example, "A()=&a
14 | → U()=&u" can be turned into a formula given a context containing both a and
15 | u.)
16 |
17 | `Modal/Code.hs`: Contains the definition and parser for "code", including if
18 | blocks, for blocks, etc. Can compile them down to a map of modal formulas: give
19 | it an action it gives you a formula saying true iff the code returns that
20 | action.
21 |
22 | `Modal/Def.hs`: Contains the definition and parser for "defs", which are the
23 | full agents including a name, parameters, code, etc. This file also contains
24 | the logic for applying the parameters from a call to create the code context,
25 | and then compiling the def into a map of modal formulas.
26 |
27 | `Modal/Competition.hs`: This basically takes a bunch of agent maps and combines
28 | them into the competition map evaluated by the fixpoint evaluator. It can
29 | currently either play two modalized agents againts each other, or it can play
30 | many modalized agents against a single unmodalized universe.
31 |
32 | `Modal/Combat.hs`: Contains the definition of and logic for handilng files such
33 | as `agents/test.mc` (which *totally exists* (TODO)), which consist of a series
34 | of agent definitions and execution commands used to play agents againt seach
35 | other or inspect their behavior.
36 |
37 | `ModalCombat.hs`: The file implementing the `modalcombat` executable, which
38 | runs on files such as the one found in `agents/test.mc`. (See that file for
39 | an example of what sort of syntax is supported.)
40 |
41 | Dying files
42 | -----------
43 |
44 | `Modal/Programming.hs`: This file provides some tools for making modal agents
45 | in Haskell (rather than in the still-rough-around-the-edges modal program
46 | language). Eventually the modal programming language may supersede the tools in
47 | this file, but we don't necessarily want to delete it altogether. (We might
48 | decide to add better tools for manipulating modal agents in ghci or something.)
49 |
50 | `Modal/GameTools.hs`: A file that makes it easier to evaluate agents in
51 | multi-player games. This file is slowly being deprecated by upgrades to
52 | `Combat.hs`, but it will be a little while before we're comfortable enough with
53 | multi-agent games in the language parser to justify removing this code.
54 |
55 | `Modal/Agents.hs`: A dead file that has a bunch of agents defined in it using
56 | an old format. They need to be evacuated to a file of their own, and then this
57 | file can be removed.
58 |
59 | `Modal/Games.hs`: Various haskell implementations of modal games
60 | (hand-specified), including the 5 and 10 problem, the Newcomb problem, and so
61 | on. This file is dying; the games need to be evacuated to a file of their own
62 | and then this file can be removed.
63 |
64 | Helpers
65 | -------
66 |
67 | `Modal/Utilities.hs`: contains some haskell convenience tools (such as
68 | functions which turn Either x a into IO a, etc).
69 |
70 | `Modal/Parser.hs`: contains some parsec convenience tools (such as a Parsable
71 | typeclass, etc).
72 |
73 | `Modal/Display.hs`: contains some display convenience tools (such as code for
74 | rendering maps, etc).
75 |
--------------------------------------------------------------------------------
/src/Modal/Games.hs:
--------------------------------------------------------------------------------
1 | module Modal.Games where
2 | import Data.Map hiding (map)
3 | import Modal.Display
4 | import Modal.Formulas
5 | import Modal.GameTools
6 | import Modal.Programming
7 | import Modal.Programs
8 |
9 | data FiveOrTen = Ten | Five deriving (Eq, Ord, Read, Enum)
10 | instance Show FiveOrTen where
11 | show Ten = "10"
12 | show Five = "5"
13 |
14 | fiveAndTen :: ModalProgram FiveOrTen FiveOrTen
15 | fiveAndTen Five = Var Five
16 | fiveAndTen Ten = Var Ten
17 |
18 |
19 | data OneOrTwo = OneBox | TwoBox deriving (Eq, Ord, Read, Enum)
20 | instance Show OneOrTwo where
21 | show OneBox = "1"
22 | show TwoBox = "2"
23 |
24 | data NewcombOutcome = MillionThousand | Million | Thousand | Naught
25 | deriving (Eq, Ord, Read, Enum)
26 | instance Show NewcombOutcome where
27 | show MillionThousand = "$1001000"
28 | show Million = "$1000000"
29 | show Thousand = "$1000"
30 | show Naught = "$0"
31 |
32 | oneboxes, twoboxes :: ModalFormula OneOrTwo
33 | oneboxes = Var OneBox
34 | twoboxes = Neg oneboxes
35 |
36 | newcomb :: Int -> ModalProgram NewcombOutcome OneOrTwo
37 | newcomb k MillionThousand = twoboxes %^ boxk k oneboxes
38 | newcomb k Million = oneboxes %^ boxk k oneboxes
39 | newcomb k Thousand = twoboxes %^ Neg (boxk k oneboxes)
40 | newcomb k Naught = oneboxes %^ Neg (boxk k oneboxes)
41 |
42 |
43 | data AorB = A| B deriving (Eq, Ord, Read, Enum)
44 | instance Show AorB where
45 | show A = "A"
46 | show B = "B"
47 | data GoodOrBad = Good | Bad deriving (Eq, Ord, Show, Read, Enum)
48 |
49 | doesA, doesB :: ModalFormula AorB
50 | doesA = Var A
51 | doesB = Neg doesA
52 |
53 | aGame :: Int -> ModalProgram GoodOrBad AorB
54 | aGame k Good = boxk k doesA
55 | aGame k Bad = Neg (boxk k doesA)
56 |
57 | bGame :: Int -> ModalProgram GoodOrBad AorB
58 | bGame k Good = boxk k doesB
59 | bGame k Bad = Neg (boxk k doesB)
60 |
61 | abAgent :: ModalProgram GoodOrBad AorB -> ModalProgram AorB AorB
62 | abAgent univ A = Box $ Var A %> univ Good
63 | abAgent univ B = Neg $ Box $ Var A %> univ Good
64 |
65 | data Strangeverse = Three | Two | One deriving (Eq, Ord, Read, Enum)
66 | instance Show Strangeverse where
67 | show Three = "3"
68 | show Two = "2"
69 | show One = "1"
70 | data Strangeact = Alpha | Beta deriving (Eq, Ord, Read, Enum)
71 | instance Show Strangeact where
72 | show Alpha = "α"
73 | show Beta = "β"
74 |
75 | doesAlpha, doesBeta :: ModalFormula Strangeact
76 | doesAlpha = Var Alpha
77 | doesBeta = Neg doesAlpha
78 |
79 | strangeverse :: Int -> ModalProgram Strangeverse Strangeact
80 | strangeverse k Three = doesAlpha %^ boxk k doesBeta
81 | strangeverse k Two = doesBeta
82 | strangeverse k One = doesAlpha %^ Neg (boxk k doesBeta)
83 |
84 |
85 | data PD = DC | CC | DD | CD deriving (Eq, Ord, Read, Enum)
86 | instance Show PD where
87 | show DC = "[D₁C₂]"
88 | show CC = "[C₁C₂]"
89 | show DD = "[D₁D₁]"
90 | show CD = "[C₁D₂]"
91 | data CorD = C | D deriving (Eq, Ord, Read, Enum)
92 | instance Show CorD where
93 | show C = "C"
94 | show D = "D"
95 |
96 | prisonersDilemma :: ModalProgram PD (Either CorD CorD)
97 | prisonersDilemma DC = And (Var $ Left D) (Var $ Right C)
98 | prisonersDilemma CC = And (Var $ Left C) (Var $ Right C)
99 | prisonersDilemma DD = And (Var $ Left D) (Var $ Right D)
100 | prisonersDilemma CD = And (Var $ Left C) (Var $ Right D)
101 |
102 | pdGameMap :: Map (U2 PD CorD CorD) (ModalFormula (U2 PD CorD CorD))
103 | pdGameMap = gameMap2 prisonersDilemma udtA udtB where
104 | udtA = udt' [DC, CC, DD, CD] [D, C] D
105 | udtB = udt' [CD, CC, DD, DC] [D, C] D
106 |
107 |
108 | main :: IO ()
109 | main = do
110 | putStrLn "In Newcomb's problem, if the predictor uses a box to predict"
111 | putStrLn "the agent's action, UDT takes whatever its default action was:"
112 | displayGame (newcomb 0) (udt OneBox)
113 | displayGame (newcomb 0) (udt TwoBox)
114 | putStrLn ""
115 | putStrLn "These are the modal formulas for UDT in the newcomb problem:"
116 | displayMap $ gameMap (newcomb 0) (udt TwoBox)
117 | putStrLn ""
118 | putStrLn "Time for the five and ten game!"
119 | displayGame fiveAndTen (udt Five)
120 | putStrLn ""
121 | putStrLn "These are the modal formulas for UDT in the 5-and-10 game:"
122 | displayMap $ gameMap fiveAndTen (udt Five)
123 |
--------------------------------------------------------------------------------
/src/Modal/GameTools.hs:
--------------------------------------------------------------------------------
1 | module Modal.GameTools where
2 | import Control.Applicative
3 | import Modal.Formulas
4 | import Data.Map hiding (map)
5 | import qualified Data.Map as Map
6 | import Modal.Competition
7 | import Modal.Programming
8 | import Modal.Utilities
9 | import Text.Printf (printf)
10 |
11 | data U1 u a = U1 u | U1A a | Q1 String deriving (Eq, Ord, Read)
12 | instance (Show a, Show u) => Show (U1 u a) where
13 | show (U1 u) = show u ++ "ₒ"
14 | show (U1A a) = show a ++ "ₐ"
15 | show (Q1 q) = q ++ "?"
16 | instance Functor (U1 u) where
17 | fmap f (U1A a) = U1A (f a)
18 | fmap _ (U1 u) = U1 u
19 | fmap _ (Q1 s) = Q1 s
20 |
21 | u1IsU :: U1 u a -> Bool
22 | u1IsU (U1 _) = True
23 | u1IsU _ = False
24 |
25 | u1IsA :: U1 u a -> Bool
26 | u1IsA (U1A _) = True
27 | u1IsA _ = False
28 |
29 | u1ExtractU :: Map (U1 u a) Bool -> u
30 | u1ExtractU m = let U1 u = extractPMEEkey u1IsU m in u
31 |
32 | u1ExtractA :: Map (U1 u a) Bool -> a
33 | u1ExtractA m = let U1A a = extractPMEEkey u1IsA m in a
34 |
35 | gameMap :: (Ord u, Enum u, Ord a, Enum a) =>
36 | ModalProgram u a ->
37 | ModalProgram a (U1 u a) ->
38 | Map (U1 u a) (ModalFormula (U1 u a))
39 | gameMap universe agent = Map.fromList $ us ++ as where
40 | us = [(U1 u, U1A <$> universe u) | u <- enumerate]
41 | as = [(U1A a, agent a) | a <- enumerate]
42 |
43 | resolveGame :: (Show a, Show u, Ord u, Enum u, Ord a, Enum a) =>
44 | Map (U1 u a) (ModalFormula (U1 u a)) ->
45 | (u, a)
46 | resolveGame game = (u1ExtractU fixpt, u1ExtractA fixpt) where
47 | fixpt = findGeneralGLFixpoint game
48 |
49 | playGame :: (Show a, Show u, Ord u, Enum u, Ord a, Enum a) =>
50 | ModalProgram u a ->
51 | ModalProgram a (U1 u a) ->
52 | (u, a)
53 | playGame universe agent = resolveGame $ gameMap universe agent
54 |
55 | showGame :: (Show u, Show a) => (u, a) -> IO ()
56 | showGame (u, a) =
57 | printf "U=%s, A=%s\n" (show u) (show a)
58 |
59 | displayGame :: (Ord u, Enum u, Show u, Ord a, Enum a, Show a) =>
60 | ModalProgram u a ->
61 | ModalProgram a (U1 u a) ->
62 | IO ()
63 | displayGame universe agent = showGame $ playGame universe agent
64 |
65 |
66 | data U2 u a1 a2 = U2 u | U2A1 a1 | U2A2 a2 | Q2 String deriving (Eq, Ord, Read)
67 | instance (Show u, Show a1, Show a2) => Show (U2 u a1 a2) where
68 | show (U2 u) = show u ++ "ₒ"
69 | show (U2A1 a) = show a ++ "₁"
70 | show (U2A2 a) = show a ++ "₂"
71 | show (Q2 q) = q ++ "?"
72 |
73 | u2IsU :: U2 u a1 a2 -> Bool
74 | u2IsU (U2 _) = True
75 | u2IsU _ = False
76 |
77 | u2IsA1 :: U2 u a1 a2 -> Bool
78 | u2IsA1 (U2A1 _) = True
79 | u2IsA1 _ = False
80 |
81 | u2IsA2 :: U2 u a1 a2 -> Bool
82 | u2IsA2 (U2A2 _) = True
83 | u2IsA2 _ = False
84 |
85 | u2ExtractU :: Map (U2 u a1 a2) Bool -> u
86 | u2ExtractU m = let U2 u = extractPMEEkey u2IsU m in u
87 |
88 | u2ExtractA1 :: Map (U2 u a1 a2) Bool -> a1
89 | u2ExtractA1 m = let U2A1 a = extractPMEEkey u2IsA1 m in a
90 |
91 | u2ExtractA2 :: Map (U2 u a1 a2) Bool -> a2
92 | u2ExtractA2 m = let U2A2 a = extractPMEEkey u2IsA2 m in a
93 |
94 | gameMap2 :: (Ord u, Enum u, Ord a1, Enum a1, Ord a2, Enum a2) =>
95 | ModalProgram u (Either a1 a2) ->
96 | ModalProgram a1 (U1 u a1) ->
97 | ModalProgram a2 (U1 u a2) ->
98 | Map (U2 u a1 a2) (ModalFormula (U2 u a1 a2))
99 | gameMap2 universe agent1 agent2 = Map.fromList $ us ++ a1s ++ a2s where
100 | us = [(U2 u, promoteToA <$> universe u) | u <- enumerate]
101 | a1s = [(U2A1 a1, promoteTo1 <$> agent1 a1) | a1 <- enumerate]
102 | a2s = [(U2A2 a2, promoteTo2 <$> agent2 a2) | a2 <- enumerate]
103 | promoteToA (Left a) = U2A1 a
104 | promoteToA (Right a) = U2A2 a
105 | promoteTo1 (U1A a) = U2A1 a
106 | promoteTo1 (U1 u) = U2 u
107 | promoteTo1 (Q1 s) = Q2 s
108 | promoteTo2 (U1A a) = U2A2 a
109 | promoteTo2 (U1 u) = U2 u
110 | promoteTo2 (Q1 s) = Q2 s
111 |
112 | resolveGame2 :: (Show u, Show a1, Show a2, Ord u, Enum u, Ord a1, Enum a1, Ord a2, Enum a2) =>
113 | Map (U2 u a1 a2) (ModalFormula (U2 u a1 a2)) ->
114 | (u, a1, a2)
115 | resolveGame2 game = (u2ExtractU fp, u2ExtractA1 fp, u2ExtractA2 fp) where
116 | fp = findGeneralGLFixpoint game
117 |
118 | playGame2 :: (Show u, Show a1, Show a2, Ord u, Enum u, Ord a1, Enum a1, Ord a2, Enum a2) =>
119 | ModalProgram u (Either a1 a2) ->
120 | ModalProgram a1 (U1 u a1) ->
121 | ModalProgram a2 (U1 u a2) ->
122 | (u, a1, a2)
123 | playGame2 u a1 a2 = resolveGame2 $ gameMap2 u a1 a2
124 |
125 | showGame2 :: (Show u, Show a1, Show a2) => (u, a1, a2) -> IO ()
126 | showGame2 (u, a1, a2) =
127 | printf "U=%s, A₁=%s, A₂=%s\n" (show u) (show a1) (show a2)
128 |
129 | displayGame2 ::
130 | (Ord u, Enum u, Show u,
131 | Ord a1, Enum a1, Show a1,
132 | Ord a2, Enum a2, Show a2) =>
133 | ModalProgram u (Either a1 a2) ->
134 | ModalProgram a1 (U1 u a1) ->
135 | ModalProgram a2 (U1 u a2) ->
136 | IO ()
137 | displayGame2 u a1 a2= showGame2 $ playGame2 u a1 a2
138 |
--------------------------------------------------------------------------------
/src/Modal/Parser.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Modal.Parser where
3 | import Control.Applicative
4 | import Control.Monad (void)
5 | import Data.Char
6 | import Data.Functor.Identity
7 | import Data.Set (Set)
8 | import Data.Text (Text)
9 | import Modal.Utilities
10 | import Text.Parsec hiding ((<|>), optional, many)
11 | import Text.Parsec.Text (Parser)
12 | import Text.Printf (printf)
13 | import qualified Data.Set as Set
14 | import qualified Data.Text as Text
15 |
16 | class Parsable a where
17 | parser :: Parser a
18 |
19 | instance Parsable Int where
20 | parser = read <$> many1 digit
21 | instance Parsable x => Parsable [x] where
22 | parser = listParser parser
23 | instance (Ord x, Parsable x) => Parsable (Set x) where
24 | parser = setParser parser
25 | instance Parsable a => Parsable (Identity a) where
26 | parser = Identity <$> parser
27 |
28 | listParser :: Parser x -> Parser [x]
29 | listParser p = brackets $ sepEndBy p comma
30 |
31 | setParser :: Ord x => Parser x -> Parser (Set x)
32 | setParser p = Set.fromList <$> braces (sepEndBy p comma)
33 |
34 | keyword :: String -> Parser ()
35 | keyword s = void $ try w *> try (string s) <* lookAhead ok <* w where
36 | ok = try eof <|> void (satisfy isOk)
37 | isOk c = not (isLetter c) && not (isNumber c) && c `notElem` ("-_" :: String)
38 |
39 | symbol :: String -> Parser ()
40 | symbol s = void $ w *> string s <* w
41 |
42 | eol :: Parser ()
43 | eol = try (void endOfLine) <|> try eof > "a line ending"
44 |
45 | eols :: Parser ()
46 | eols = void $ many1 (w *> endOfLine)
47 |
48 | ignoredToken :: Parser ()
49 | ignoredToken
50 | = try _blockComment
51 | <|> try _lineComment
52 | <|> void (char ' ')
53 | > "whitespace"
54 |
55 | _blockComment :: Parser ()
56 | _blockComment = void (o *> many innards *> c) > "a block comment" where
57 | o = string "{-"
58 | c = string "-}"
59 | safeMark = char '-' *> notFollowedBy (char '}')
60 | innards = _blockComment <|> void (noneOf "-") <|> try safeMark
61 |
62 | _lineComment :: Parser ()
63 | _lineComment = void ((string "--" *> many (noneOf "\n")) > "a line comment")
64 |
65 | ignoredLine :: Parser ()
66 | ignoredLine = many (void (char '\t') <|> ignoredToken) *> void endOfLine
67 |
68 | w :: Parser ()
69 | w = void $ many ignoredToken
70 |
71 | w1 :: Parser ()
72 | w1 = void $ many1 ignoredToken
73 |
74 | identifier :: Parser Char -> Parser Char -> Parser Name
75 | identifier h t = (:) <$> h <*> many t
76 |
77 | parens :: Parser a -> Parser a
78 | parens = between (symbol "(") (symbol ")")
79 |
80 | comma :: Parser ()
81 | comma = symbol ","
82 |
83 | powerComma :: Parser () -- can span newlines and eat tabs etc. Ugh.
84 | powerComma = void $ wN *> string "," <* wN where
85 | wN = void $ many (ignoredToken <|> void (char '\t') <|> void endOfLine)
86 |
87 | brackets :: Parser a -> Parser a
88 | brackets = between (symbol "[") (symbol "]")
89 |
90 | braces :: Parser a -> Parser a
91 | braces = between (symbol "{") (symbol "}")
92 |
93 | name :: Parser Name
94 | name = identifier (satisfy isNameFirstChar) (satisfy isNameChar)
95 |
96 | valueStr :: Parser String
97 | valueStr = many1 (satisfy isValueChar)
98 |
99 | isNameFirstChar, isNameChar, isValueChar :: Char -> Bool
100 | isNameFirstChar = (||) <$> isLetter <*> (`elem` ("-_'" :: String))
101 | isNameChar = (||) <$> isNameFirstChar <*> isNumber
102 | -- TODO: we may safely relax this, but it's not clear how far.
103 | isValueChar c = c `elem` ("$@" :: String) || isNameChar c
104 |
105 | anyComboOf :: Parser x -> Parser y -> Parser (Maybe x, Maybe y)
106 | anyComboOf x y = try xThenMaybeY <|> try yThenMaybeX <|> pure (Nothing, Nothing) where
107 | xThenMaybeY = (,) <$> (Just <$> x) <*> optional y
108 | yThenMaybeX = flip (,) <$> (Just <$> y) <*> optional x
109 |
110 | -------------------------------------------------------------------------------
111 | -- Testing
112 | verifyParseResult :: Show a => (a -> Maybe String) -> Parser a -> Text -> IO ()
113 | verifyParseResult check p input = either parseErr doCheck parsed where
114 | parsed = parse (p <* eof) "verifying parser" input
115 | doCheck result = maybe (putStr ".") checkErr (check result)
116 | parseErr = printf "\nError parsing \"%s\" in %s\n" (Text.unpack input) . show
117 | checkErr = printf "\nFailure parsing \"%s\"!\n%s\n" (Text.unpack input)
118 |
119 | verifyParser :: (Show a, Eq a) => Parser a -> Text -> a -> IO ()
120 | verifyParser p input target = verifyParseResult isTarget p input where
121 | isTarget result = if result == target
122 | then Nothing
123 | else Just $ printf "Expected %s, got %s" (show target) (show result)
124 |
125 | verifyParserFails :: Show a => Parser a -> Text -> IO ()
126 | verifyParserFails p input = either succeed err parsed where
127 | parsed = parse (p <* eof) "verifying parser failure" input
128 | succeed _ = putStr "."
129 | err result = printf "\nError! Expected a failure parsing \"%s\", but parsed %s.\n"
130 | (Text.unpack input) (show result)
131 |
132 | verifyParsable :: (Show a, Eq a, Parsable a) => Text -> a -> IO ()
133 | verifyParsable = verifyParser parser
134 |
135 | main :: IO ()
136 | main = do
137 | verifyParser w " {- IGNORE THIS COMMENT -} {- this one too. -}" ()
138 | verifyParser w " {- {- nested -} woo -} " ()
139 | verifyParser w " -- anything goes now! \t \t" ()
140 | verifyParserFails w " {- {- nested woo -} "
141 |
--------------------------------------------------------------------------------
/src/Modal/Competition.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE Rank2Types #-}
2 | {-# LANGUAGE MultiParamTypeClasses #-}
3 | {-# LANGUAGE ConstraintKinds #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | module Modal.Competition where
6 | import Prelude hiding (mapM, sequence)
7 | import Control.Arrow ((***))
8 | import Control.Monad.Except hiding (mapM, sequence)
9 | import Data.Traversable (sequence)
10 | import Data.Map (Map)
11 | import Data.Maybe (fromMaybe)
12 | import Data.Set (Set)
13 | import Modal.CompilerBase
14 | import Modal.Formulas hiding (left)
15 | import Text.Printf (printf)
16 | import qualified Data.Map as Map
17 | import qualified Data.Set as Set
18 |
19 | extractPMEEkey :: (k -> Bool) -> Map k Bool -> k
20 | extractPMEEkey p = extract . Map.keys . Map.filterWithKey matchKey where
21 | matchKey k v = p k && v
22 | extract [ ] = error "No true formula! Map was not P.M.E.E."
23 | extract [x] = x
24 | extract _ = error "Multiple true formulas! Map was not P.M.E.E."
25 |
26 | --------------------------------------------------------------------------------
27 | -- The type of variables that actually makes it into the full competition map.
28 | -- This can be thought as the type of "canonicalized names" of various
29 | -- statements, such as "FairBot(PrudentBot)=C."
30 |
31 | data VsVar a o
32 | = Vs1 Call Call a
33 | | Vs2 Call Call o
34 | deriving (Eq, Ord)
35 |
36 | instance (Show a, Show o) => Show (VsVar a o) where
37 | show (Vs1 call1 call2 a) = printf "%s(%s)=%s" (show call1) (show call2) (show a)
38 | show (Vs2 call1 call2 o) = printf "%s(%s)=%s" (show call1) (show call2) (show o)
39 |
40 | is1 :: Call -> Call -> VsVar a o -> Bool
41 | is1 n m (Vs1 x y _) = x == n && y == m
42 | is1 _ _ _ = False
43 |
44 | is2 :: Call -> Call -> VsVar a o -> Bool
45 | is2 n m (Vs2 x y _) = x == n && y == m
46 | is2 _ _ _ = False
47 |
48 | class ModalCombatVar v where
49 | subagentsIn :: v a o -> Set Call
50 | makeModalVar :: (a-> x) -> (Maybe Call -> o-> x) -> v a o -> x
51 |
52 | subagents :: ModalCombatVar v => ModalAgent v a o -> Set Call
53 | subagents = Set.unions . map fSubagents . Map.elems where
54 | fSubagents = Set.unions . map subagentsIn . allVars
55 |
56 | --------------------------------------------------------------------------------
57 | -- Competitions, by default, allow agents with different action types to play
58 | -- against each other. This introduces a bit of extra complexity to the types;
59 | -- some helper functions (without 2s in their names) exist below to handle this
60 | -- simpler case.
61 |
62 | type ModalAgent v a o = Map a (ModalFormula (v a o))
63 | type Env m v a o = Call -> m (ModalAgent v a o)
64 | type Competition a o = Map (VsVar a o) (ModalFormula (VsVar a o))
65 | type IsCompetition v1 v2 a o =
66 | ( Ord a, Ord o, ModalCombatVar v1, ModalCombatVar v2 )
67 |
68 | -- Attempts to build a map of modal formulas describing the competition, given
69 | -- two environments and two names.
70 | modalCombatMap :: (IsCompetition v1 v2 a o, MonadError RuntimeError m) =>
71 | Env m v1 a o -> Env m v2 o a -> Call -> Call -> m (Competition a o)
72 | modalCombatMap env1 env2 call1 call2 = do
73 | agent1 <- env1 call1
74 | agent2 <- env2 call2
75 | let agent1map = Map.toList agent1
76 | let agent2map = Map.toList agent2
77 | let expand1 = fmap $ makeModalVar (Vs1 call1 call2) (Vs2 call2 . fromMaybe call1)
78 | let expand2 = fmap $ makeModalVar (Vs2 call2 call1) (Vs1 call1 . fromMaybe call2)
79 | let top1 = map (Vs1 call1 call2 *** expand1) agent1map
80 | let top2 = map (Vs2 call2 call1 *** expand2) agent2map
81 | let recurse = modalCombatMap env1 env2
82 | lefts <- sequence [recurse x call2 | x <- Set.toList $ subagents agent1]
83 | rights <- sequence [recurse call1 x | x <- Set.toList $ subagents agent2]
84 | return $ Map.unions $ Map.fromList top1 : Map.fromList top2 : lefts ++ rights
85 |
86 | -- TODO: Add error handling.
87 | -- (Right now, the fixpoint evaluator just errors when bad things happen.)
88 | modalCombatResolve :: (Show x, Show y, Ord x, Ord y) =>
89 | Call -> Call -> Competition x y -> (x, y)
90 | modalCombatResolve call1 call2 cmap = (result1, result2) where
91 | fixpt = findGeneralGLFixpoint cmap
92 | Vs1 _ _ result1 = extractPMEEkey (is1 call1 call2) fixpt
93 | Vs2 _ _ result2 = extractPMEEkey (is2 call2 call1) fixpt
94 |
95 | -- Attempts to figure out how the two named agents behave against each other.
96 | -- WARNING: This function may error if the modal formulas in the competition
97 | -- map are not P.M.E.E. (provably mutally exclusive and extensional).
98 | modalCombat :: (Show x, Show y, IsCompetition v1 v2 x y, MonadError RuntimeError m) =>
99 | Env m v1 x y -> Env m v2 y x -> Call -> Call -> m (x, y)
100 | modalCombat env1 env2 call1 call2 =
101 | modalCombatResolve call1 call2 <$> modalCombatMap env1 env2 call1 call2
102 |
103 | --------------------------------------------------------------------------------
104 | -- Simplified versions of the above functions for the scenario where both
105 | -- agents have the same action type.
106 |
107 | modalCombatMap1 :: (Ord a, ModalCombatVar v, MonadError RuntimeError m) =>
108 | Env m v a a -> Call -> Call -> m (Competition a a)
109 | modalCombatMap1 env = modalCombatMap env env
110 |
111 | modalCombat1 :: (Show a, Ord a, ModalCombatVar v, MonadError RuntimeError m) =>
112 | Env m v a a -> Call -> Call -> m (a, a)
113 | modalCombat1 env = modalCombat env env
114 |
115 | --------------------------------------------------------------------------------
116 | -- Competitions, by default, allow agents with different action types to play
117 | -- against each other. This introduces a bit of extra complexity to the types;
118 | -- some helper functions (without 2s in their names) exist below to handle this
119 | -- simpler case.
120 |
121 | type IsMultiCompetition vu va u a = (Ord u, Ord a, MultiVarU vu, MultiVarA va)
122 | type MultiCompetition u a = Map (MultiVsVar u a) (ModalFormula (MultiVsVar u a))
123 |
124 | class MultiVarA v where
125 | promoteA :: Int -> v a u -> MultiVsVar u a
126 |
127 | class MultiVarU v where
128 | promoteU :: v u a -> MultiVsVar u a
129 |
130 | data MultiVsVar u a = UniversePlays u | PlayerNPlays Int a deriving (Eq, Ord)
131 |
132 | instance (Show u, Show a) => Show (MultiVsVar u a) where
133 | show (UniversePlays x) = printf "U()=%s" (show x)
134 | show (PlayerNPlays i x) = printf "A%d()=%s" i (show x)
135 |
136 | isEntryFor :: Int -> MultiVsVar u a -> Bool
137 | isEntryFor 0 (UniversePlays _) = True
138 | isEntryFor _ (UniversePlays _) = False
139 | isEntryFor n (PlayerNPlays i _) = n == i
140 |
141 | multiCompetition :: IsMultiCompetition vu va u a =>
142 | Map u (ModalFormula (vu u a)) ->
143 | [Map a (ModalFormula (va a u))] ->
144 | MultiCompetition u a
145 | multiCompetition univ agents = Map.unions (uMap : pMaps) where
146 | uMap = Map.fromList $ map (uncurry createUEntry) $ Map.toList univ
147 | createUEntry outcome uFormula = (UniversePlays outcome, promoteU <$> uFormula)
148 | pMaps = zipWith createMap [1..] agents where
149 | createEntry n = PlayerNPlays n *** fmap (promoteA n)
150 | createMap n = Map.fromList . map (createEntry n) . Map.toList
151 |
152 | -- TODO: Add error handling.
153 | -- (Right now, the fixpoint evaluator just errors when bad things happen.)
154 | multiResolve :: (Show a, Show u, Ord a, Ord u) =>
155 | Int -> MultiCompetition u a -> (u, [a])
156 | multiResolve n cmap = (u, as) where
157 | fixpt = findGeneralGLFixpoint cmap
158 | UniversePlays u = extractPMEEkey (isEntryFor 0) fixpt
159 | as = [let PlayerNPlays _ a = extractPMEEkey (isEntryFor i) fixpt
160 | in a | i <- [1 .. n]]
161 |
162 | multiCompete :: (Show a, Show u, IsMultiCompetition vu va u a) =>
163 | Map u (ModalFormula (vu u a)) ->
164 | [Map a (ModalFormula (va a u))] ->
165 | (u, [a])
166 | multiCompete univ agents =
167 | multiResolve (length agents) (multiCompetition univ agents)
168 |
--------------------------------------------------------------------------------
/src/Modal/Statement.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ConstraintKinds #-}
2 | {-# LANGUAGE FlexibleContexts #-}
3 | {-# LANGUAGE FlexibleInstances #-}
4 | {-# LANGUAGE OverloadedStrings #-}
5 | {-# LANGUAGE ScopedTypeVariables #-}
6 | {-# LANGUAGE TypeFamilies #-}
7 | module Modal.Statement where
8 | import Prelude hiding (readFile, sequence, mapM, foldr1, concat, concatMap)
9 | import Control.Applicative
10 | import Modal.CompilerBase hiding (main)
11 | import Modal.Formulas (ModalFormula)
12 | import Modal.Parser hiding (main)
13 | import Modal.Utilities
14 | import Text.Parsec hiding ((<|>), optional, many, State)
15 | import Text.Parsec.Expr
16 | import Text.Parsec.Text (Parser)
17 | import Text.Printf (printf)
18 | import qualified Data.Map as M
19 | import qualified Data.Text as T
20 | import qualified Modal.Formulas as F
21 |
22 | -------------------------------------------------------------------------------
23 |
24 | data Statement
25 | = Val Bool
26 | | Var ParsedClaim
27 | | Neg Statement
28 | | And Statement Statement
29 | | Or Statement Statement
30 | | Imp Statement Statement
31 | | Iff Statement Statement
32 | | Consistent (Ref Int)
33 | | Provable (Ref Int) Statement
34 | | Possible (Ref Int) Statement
35 | deriving Eq
36 |
37 | data ShowStatement = ShowStatement {
38 | topSymbol :: String,
39 | botSymbol :: String,
40 | notSymbol :: String,
41 | andSymbol :: String,
42 | orSymbol :: String,
43 | impSymbol :: String,
44 | iffSymbol :: String,
45 | conSign :: String -> String,
46 | boxSign :: String-> String,
47 | diaSign :: String -> String,
48 | quotes :: (String, String) }
49 |
50 | showsStatement :: ShowStatement -> Int -> Statement -> ShowS
51 | showsStatement sf p statement = result where
52 | result = case statement of
53 | Val l -> showString $ if l then topSymbol sf else botSymbol sf
54 | Var v -> showString $ show v
55 | Neg x -> showParen (p > 8) $ showString (notSymbol sf) . showsStatement sf 8 x
56 | And x y -> showParen (p > 7) $ showBinary (andSymbol sf) 7 x 8 y
57 | Or x y -> showParen (p > 6) $ showBinary (orSymbol sf) 6 x 7 y
58 | Imp x y -> showParen (p > 5) $ showBinary (impSymbol sf) 6 x 5 y
59 | Iff x y -> showParen (p > 4) $ showBinary (iffSymbol sf) 5 x 4 y
60 | Consistent v -> showString $ conSign sf (show v)
61 | Provable v x -> showParen (p > 8) $ showInner boxSign v 8 x
62 | Possible v x -> showParen (p > 8) $ showInner diaSign v 8 x
63 | padded o = showString " " . showString o . showString " "
64 | showBinary o l x r y = showsStatement sf l x . padded o . showsStatement sf r y
65 | showInner sig v i x = showString (sig sf $ show v) . quote (showsStatement sf i x)
66 | quote s = let (l, r) = quotes sf in showString l . s . showString r
67 |
68 | instance Show Statement where
69 | showsPrec = showsStatement (ShowStatement "⊤" "⊥" "¬" "∧" "∨" "→" "↔"
70 | (printf "Con(%s)")
71 | (\var -> if var == "0" then "□" else printf "[%s]" var)
72 | (\var -> if var == "0" then "◇" else printf "<%s>" var)
73 | ("⌜", "⌝"))
74 |
75 | instance Parsable Statement where
76 | parser = buildExpressionParser lTable term where
77 | lTable =
78 | [ [Prefix lNeg]
79 | , [ Infix lAnd AssocRight ]
80 | , [ Infix lOr AssocRight ]
81 | , [ Infix lImp AssocRight ]
82 | , [ Infix lIff AssocRight ] ]
83 | term
84 | = parens parser
85 | <|> try cConsistent
86 | <|> try (fProvable <*> quoted parser)
87 | <|> try (fPossible <*> quoted parser)
88 | <|> try (Var <$> parser)
89 | <|> try (Val <$> val)
90 | > "a statement term"
91 | val = try sTop <|> try sBot > "a boolean value" where
92 | sTop = sym $> True where
93 | sym = try (symbol "⊤")
94 | <|> try (keyword "top")
95 | <|> try (keyword "true")
96 | <|> try (keyword "True")
97 | > "truth"
98 | sBot = sym $> False where
99 | sym = try (symbol "⊥")
100 | <|> try (keyword "bot")
101 | <|> try (keyword "bottom")
102 | <|> try (keyword "false")
103 | <|> try (keyword "False")
104 | > "falsehood"
105 | lNeg = sym $> Neg where
106 | sym = try (symbol "¬")
107 | <|> try (symbol "~")
108 | <|> try (keyword "not")
109 | > "a negation"
110 | lAnd = sym $> And where
111 | sym = try (symbol "∧")
112 | <|> try (symbol "/\\")
113 | <|> try (symbol "&")
114 | <|> try (symbol "&&")
115 | <|> try (keyword "and")
116 | > "an and"
117 | lOr = sym $> Or where
118 | sym = try (symbol "∨")
119 | <|> try (symbol "\\/")
120 | <|> try (symbol "|")
121 | <|> try (symbol "||")
122 | <|> try (keyword "and")
123 | > "an or"
124 | lImp = sym $> Imp where
125 | sym = try (symbol "→")
126 | <|> try (symbol "->")
127 | <|> try (keyword "implies")
128 | > "an implication"
129 | lIff = sym $> Iff where
130 | sym = try (symbol "↔")
131 | <|> try (symbol "<->")
132 | <|> try (keyword "iff")
133 | > "a biconditional"
134 | cConsistent = (symbol "Con" $> Consistent) <*> option (Lit 0) (parens parser)
135 | quoted x
136 | = try (between (symbol "⌜") (symbol "⌝") x)
137 | <|> try (between (symbol "[") (symbol "]") x)
138 | fProvable = try inSym <|> choice (map (try . afterSym) syms) > "a box" where
139 | inSym = Provable <$> (char '[' *> option (Lit 0) parser <* char ']')
140 | afterSym s = Provable <$> (symbol s *> option (Lit 0) (parens parser))
141 | syms = ["□", "Provable", "Box"]
142 | fPossible = try inSym <|> choice (map (try . afterSym) syms) > "a diamond" where
143 | inSym = Possible <$> (char '<' *> option (Lit 0) parser <* char '>')
144 | afterSym s = Possible <$> (symbol s *> option (Lit 0) (parens parser))
145 | syms = ["◇", "Possible", "Dia", "Diamond"]
146 |
147 | claimsParsed :: Statement -> [ParsedClaim]
148 | claimsParsed statement = case statement of
149 | Val _ -> []
150 | Var a -> [a]
151 | Neg s -> claimsParsed s
152 | And x y -> claimsParsed x ++ claimsParsed y
153 | Or x y -> claimsParsed x ++ claimsParsed y
154 | Imp x y -> claimsParsed x ++ claimsParsed y
155 | Iff x y -> claimsParsed x ++ claimsParsed y
156 | Consistent _ -> []
157 | Provable _ s -> claimsParsed s
158 | Possible _ s -> claimsParsed s
159 |
160 | type HandleVar v m = ParsedClaim -> m (ModalFormula v)
161 |
162 | compileStatement :: MonadCompile m => HandleVar v m -> Statement -> m (ModalFormula v)
163 | compileStatement handleVar stm = case stm of
164 | Val v -> return $ F.Val v
165 | Var v -> handleVar v
166 | Neg x -> F.Neg <$> rec x
167 | And x y -> F.And <$> rec x <*> rec y
168 | Or x y -> F.Or <$> rec x <*> rec y
169 | Imp x y -> F.Imp <$> rec x <*> rec y
170 | Iff x y -> F.Iff <$> rec x <*> rec y
171 | Consistent v -> F.incon <$> lookupN v
172 | Provable r x -> F.boxk <$> lookupN r <*> rec x
173 | Possible r x -> F.diak <$> lookupN r <*> rec x
174 | where rec = compileStatement handleVar
175 |
176 | -------------------------------------------------------------------------------
177 | -- Testing
178 |
179 | main :: IO ()
180 | main = do
181 | let fails = verifyParserFails (parser :: Parser Statement)
182 | let simpleClaim = ParsedClaim "A" Nothing (Equals $ Lit "a")
183 | verifyParsable "⊤" (Val True)
184 | verifyParsable "⊥" (Val False)
185 | fails "a"
186 | verifyParsable "A()=a" (Var simpleClaim)
187 | verifyParsable "¬A()=a" (Neg $ Var simpleClaim)
188 | verifyParsable "A()=a ∧ A()=a" (And (Var simpleClaim) (Var simpleClaim))
189 | verifyParsable "A()=a∨A()=a" (Or (Var simpleClaim) (Var simpleClaim))
190 | verifyParsable "A()=a → A()=a" (Imp (Var simpleClaim) (Var simpleClaim))
191 | verifyParsable "(⊤ ∨ ⊥) ∧ (⊤ ∨ ⊥)" (And
192 | (Or (Val True) (Val False)) (Or (Val True) (Val False)))
193 | verifyParsable "⊤ ∧ ⊤ ∨ ⊥ ∧ ⊥" (Or
194 | (And (Val True) (Val True)) (And (Val False) (Val False)))
195 | verifyParsable "⊤ → ⊥ ∧ ⊥" (Imp (Val True) (And (Val False) (Val False)))
196 | verifyParsable "⊤↔⊥" (Iff (Val True) (Val False))
197 | fails "x ∧ ∨ y"
198 | verifyParsable "Con(1)" (Consistent (Lit 1))
199 | verifyParsable "Con(&n)" (Consistent (Ref "n"))
200 | fails "Con 1"
201 | verifyParsable "□⌜⊤⌝" (Provable (Lit 0) (Val True))
202 | verifyParsable "[][top]" (Provable (Lit 0) (Val True))
203 | verifyParsable "[1][top]" (Provable (Lit 1) (Val True))
204 | verifyParsable "Provable[top]" (Provable (Lit 0) (Val True))
205 | verifyParsable "Provable(1)[top]" (Provable (Lit 1) (Val True))
206 | verifyParsable "◇⌜⊤⌝" (Possible (Lit 0) (Val True))
207 | verifyParsable "<>[top]" (Possible (Lit 0) (Val True))
208 | verifyParsable "<1>[top]" (Possible (Lit 1) (Val True))
209 | verifyParsable "Possible[top]" (Possible (Lit 0) (Val True))
210 | verifyParsable "Possible(1)[top]" (Possible (Lit 1) (Val True))
211 | verifyParsable "Con(1) implies ¬□⌜⊥⌝" (Imp
212 | (Consistent (Lit 1)) (Neg (Provable (Lit 0) (Val False))))
213 | verifyParsable "Con(1) implies ~[][bottom]" (Imp
214 | (Consistent (Lit 1)) (Neg (Provable (Lit 0) (Val False))))
215 | putStrLn ""
216 |
--------------------------------------------------------------------------------
/src/Modal/Def.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE Rank2Types #-}
2 | {-# LANGUAGE ConstraintKinds #-}
3 | {-# LANGUAGE TypeFamilies #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | {-# LANGUAGE ScopedTypeVariables #-}
6 | {-# LANGUAGE UndecidableInstances #-}
7 | {-# LANGUAGE NoMonomorphismRestriction #-}
8 | module Modal.Def where
9 | import Prelude hiding (readFile, sequence, mapM, foldr1, concat)
10 | import Control.Applicative
11 | import Control.Monad.Except hiding (mapM, sequence)
12 | import Data.Either (partitionEithers)
13 | import Data.Map (Map)
14 | import Data.Set ((\\))
15 | import Data.Traversable
16 | import Modal.Code
17 | import Modal.CompilerBase
18 | import Modal.Formulas (ModalFormula)
19 | import Modal.Parser
20 | import Modal.Utilities
21 | import Text.Parsec hiding ((<|>), optional, many, State)
22 | import Text.Parsec.Text (Parser)
23 | import Text.Read (readMaybe)
24 | import qualified Data.Map as Map
25 | import qualified Data.Set as Set
26 |
27 | -------------------------------------------------------------------------------
28 |
29 | data DefConfig = DefConfig
30 | { defKw :: Name
31 | , defAllowsOrderings :: Bool
32 | , defActionKw :: Name
33 | , defActionsKw :: Name
34 | , defOutcomeKw :: Name
35 | , defOutcomesKw :: Name
36 | } deriving (Eq, Ord, Read, Show)
37 |
38 | makeCodeConfig :: DefConfig -> CodeConfig
39 | makeCodeConfig dconf = CodeConfig
40 | { actionKw = defActionKw dconf
41 | , actionsKw = defActionsKw dconf
42 | , outcomeKw = defOutcomeKw dconf
43 | , outcomesKw = defOutcomesKw dconf }
44 |
45 | -------------------------------------------------------------------------------
46 |
47 | data Def = Def
48 | { defArgs :: [(Name, VarType, Maybe VarVal)]
49 | , defName :: Name
50 | , defCode :: Code
51 | } deriving Eq
52 |
53 | instance Show Def where show = defName
54 |
55 | defHeadParser :: DefConfig -> Parser (Code -> Def)
56 | defHeadParser conf = makeDef where
57 | makeDef = flip Def <$> valueStr <*> option [] (try argsParser)
58 | argsParser = parens (arg `sepBy` comma) where
59 | arg = try num <|> try act <|> try out
60 | param kwd t p = (,,) <$>
61 | (keyword kwd *> name) <*>
62 | return t <*>
63 | optional (symbol "=" *> p)
64 | num = param "number" NumberT (Number <$> parser)
65 | act = param (defActionKw conf) (ClaimT ActionT) (Action <$> parser)
66 | out = param (defOutcomeKw conf) (ClaimT OutcomeT) (Outcome <$> parser)
67 |
68 | defParser :: DefConfig -> Parser Def
69 | defParser = fmap fst . defParserWithExtras (pure ())
70 |
71 | defParserWithExtras :: Parser x -> DefConfig -> Parser (Def, x)
72 | defParserWithExtras px conf = keyword (defKw conf) *> (try mapDef <|> codeDef) where
73 | mapDef = do
74 | keyword "map"
75 | h <- defHeadParser conf
76 | x <- px
77 | c <- codeMapParser
78 | return (h c, x)
79 | codeDef = do
80 | h <- defHeadParser conf
81 | x <- px
82 | c <- codeParser (makeCodeConfig conf)
83 | return (h c, x)
84 |
85 | -------------------------------------------------------------------------------
86 |
87 | -- Errors unless the left list is a subset of the right one.
88 | ensureEnumContains :: MonadError EnumError m => [Value] -> [Value] -> m ()
89 | ensureEnumContains xs enum =
90 | let missing = Set.fromList xs \\ Set.fromList enum
91 | in unless (Set.null missing) (throwError $ EnumExcludes enum missing)
92 |
93 | -- Checks that the first list and the second list are equivalent up to
94 | -- ordering, where an empty list is treated as missing (and ignored). Returns
95 | -- the non-missing list if any, preferring the ordering of the right list.
96 | matchEnumsR :: MonadError EnumError m => [Value] -> [Value] -> m [Value]
97 | matchEnumsR xs [] = return xs
98 | matchEnumsR [] ys = return ys
99 | matchEnumsR xs ys
100 | | Set.fromList xs == Set.fromList ys = return ys
101 | | otherwise = throwError $ EnumMismatch xs ys
102 |
103 | -------------------------------------------------------------------------------
104 |
105 | data CompileConfig a v = CompileConfig
106 | { availableActions :: [Value]
107 | , availableOutcomes :: [Value]
108 | , claimValues :: ParsedClaim -> [(ClaimType, Value)]
109 | , handleClaim :: forall m. MonadError DefError m => CompiledClaim -> m v
110 | , compileAction :: forall m. MonadError DefError m => Value -> m a
111 | , finalizeFormula :: forall m. MonadError DefError m => ModalFormula v -> m (ModalFormula v) }
112 |
113 | -- Finds all actions & outcomes mentioned in the code.
114 | codeAOs :: (ParsedClaim -> [(ClaimType, Value)]) -> Code -> ([Value], [Value])
115 | codeAOs valsIn code = (aMentions ++ asInClaims, oMentions ++ osInClaims) where
116 | aMentions = actionsMentioned code
117 | oMentions = outcomesMentioned code
118 | aLoR (t, v) = if t == ActionT then Left v else Right v
119 | aoList = concatMap valsIn (claimsMade code)
120 | (asInClaims, osInClaims) = partitionEithers (map aLoR aoList)
121 |
122 | -- Ensures that all actions (outcomes) mentioned in the code appear in the
123 | -- available action (outcome) list.
124 | reconfigureWithCode :: MonadError CompileError m =>
125 | Name -> Code -> CompileConfig a v -> m (CompileConfig a v)
126 | reconfigureWithCode defname code conf = do
127 | let (as, os) = codeAOs (claimValues conf) code
128 | let ensureA = ensureEnumContains as $ availableActions conf
129 | let ensureO = ensureEnumContains os $ availableOutcomes conf
130 | either (throwError . AListErr defname) return ensureA
131 | either (throwError . OListErr defname) return ensureO
132 | return conf
133 |
134 | -- Allows the call to reorder the available action (outcome) lists.
135 | reconfigureWithCall :: MonadError CompileError m =>
136 | Name -> Call -> CompileConfig a v -> m (CompileConfig a v)
137 | reconfigureWithCall defname call conf = do
138 | let (lAs, lOs) = (availableActions conf, availableOutcomes conf)
139 | let (rAs, rOs) = (callActions call, callOutcomes call)
140 | as <- wrapError (AListErr defname) (matchEnumsR lAs rAs)
141 | os <- wrapError (OListErr defname) (matchEnumsR lOs rOs)
142 | return conf{availableActions=as, availableOutcomes=os}
143 |
144 | effectiveAOs ::
145 | (ParsedClaim -> [(ClaimType, Value)]) -> Code -> Call -> ([Value], [Value])
146 | effectiveAOs valsIn code call = (preferR codeAs callAs, preferR codeOs callOs) where
147 | (callAs, callOs) = (callActions call, callOutcomes call)
148 | (codeAs, codeOs) = codeAOs valsIn code
149 | preferR xs ys = if null ys then xs else ys
150 |
151 | -------------------------------------------------------------------------------
152 |
153 | initialVariables :: MonadError CompileError m =>
154 | Name ->
155 | ([Value], [Value]) ->
156 | [(Name, VarType, Maybe VarVal)] ->
157 | [Value] ->
158 | Map Name Value ->
159 | m (Map Name VarVal)
160 | initialVariables defname (as, os) vars args kwargs = updateVars where
161 | updateVars = do
162 | when (length args > length vars)
163 | (throwError $ ArgErr defname $ TooManyArgs (length vars) (length args))
164 | unless (Set.null unknowns)
165 | (throwError $ ArgErr defname $ UnknownArgs unknowns)
166 | varsWithArgs <- zipWithM applyArg vars extendedArgs
167 | updatedVars <- mapM applyKwarg varsWithArgs
168 | return $ Map.fromList updatedVars
169 | fst3 (x, _, _) = x
170 | unknowns = Set.fromList (Map.keys kwargs) \\ Set.fromList (map fst3 vars)
171 | extendedArgs = map Just args ++ repeat Nothing
172 | applyArg (varname, t, mdflt) Nothing = return (varname, t, mdflt)
173 | applyArg (varname, t, _) (Just val) = (,,) varname t . Just <$> cast varname t val
174 | applyKwarg (varname, t, mval) = case (mval, Map.lookup varname kwargs) of
175 | (Nothing, Nothing) -> throwError $ ArgErr defname $ ArgMissing varname t
176 | (Just dflt, Nothing) -> return (varname, dflt)
177 | (_, Just val) -> (,) varname <$> cast varname t val
178 | cast vname NumberT v = maybe
179 | (throwError $ ArgErr defname $ ArgIsNotNum vname v)
180 | (return . Number)
181 | (readMaybe $ show v)
182 | cast vname (ClaimT ActionT) v = if v `notElem` as
183 | then throwError $ ArgErr defname $ ArgIsNotIn vname v as
184 | else return $ Action v
185 | cast vname (ClaimT OutcomeT) v = if v `notElem` os
186 | then throwError $ ArgErr defname $ ArgIsNotIn vname v os
187 | else return $ Outcome v
188 |
189 | makeContext :: MonadError CompileError m =>
190 | CompileConfig a v -> Call -> Def -> m CompileContext
191 | makeContext conf call def = do
192 | let n = defName def
193 | reconf <- reconfigureWithCall n call =<< reconfigureWithCode n (defCode def) conf
194 | let (as, os) = (availableActions reconf, availableOutcomes reconf)
195 | when (null as) (throwError $ AListErr n EnumMissing)
196 | when (null os) (throwError $ OListErr n EnumMissing)
197 | vars <- initialVariables n (as, os) (defArgs def) (callArgs call) (callKwargs call)
198 | return $ CompileContext vars as os n
199 |
200 | compile :: (MonadError CompileError m, Ord a) =>
201 | CompileConfig a v -> Call -> Def -> m (Map a (ModalFormula v))
202 | compile conf call def = do
203 | context <- makeContext conf call def
204 | program <- codeToProgram context (defCode def)
205 | let wrapDErr = wrapError $ DefErr $ defName def
206 | Map.fromList <$> mapM (wrapDErr . uncurry finalize) (Map.toList program)
207 | where finalize val formula = do
208 | action <- compileAction conf val
209 | varified <- traverse (handleClaim conf) formula
210 | finalized <- finalizeFormula conf varified
211 | return (action, finalized)
212 |
--------------------------------------------------------------------------------
/src/Modal/Formulas.hs:
--------------------------------------------------------------------------------
1 | module Modal.Formulas where
2 | import Control.Applicative hiding ((<|>))
3 | import Control.Arrow ((***))
4 | import Control.Monad (ap)
5 | import Data.List
6 | import Data.Monoid
7 | import Data.Maybe
8 | import Data.Map (Map)
9 | import qualified Data.Map as M
10 | import Data.Text (Text)
11 | import qualified Data.Text as T
12 | import Modal.Display
13 | import Modal.Parser hiding (parens, braces, identifier)
14 | import Text.Parsec
15 | import Text.Parsec.Expr
16 | import Text.Parsec.Language
17 | import Text.Parsec.Token
18 |
19 | -- Example usage:
20 | -- findGeneralGLFixpoint $ M.fromList [("a",read "~ [] b"), ("b", read "[] (a -> [] ~ b)")]
21 | -- Alternatively:
22 | -- findGeneralGLFixpoint $ makeEquivs [("a", "~ [] b"), ("b", "[] (a -> [] ~ b)")]
23 |
24 |
25 | -- Modal Logic Formula data structure
26 | data ModalFormula v = Val {value :: Bool}
27 | | Var {variable :: v}
28 | | Neg {contents :: ModalFormula v}
29 | | And {left, right :: ModalFormula v}
30 | | Or {left, right :: ModalFormula v}
31 | | Imp {left, right :: ModalFormula v}
32 | | Iff {left, right :: ModalFormula v}
33 | | Box {contents :: ModalFormula v}
34 | | Dia {contents :: ModalFormula v}
35 | deriving (Eq, Ord)
36 |
37 | instance Monad ModalFormula where
38 | return = Var
39 | m >>= f = modalEval ModalEvaluator{
40 | handleVal = Val, handleVar = f, handleNeg = Neg,
41 | handleAnd = And, handleOr = Or, handleImp = Imp, handleIff = Iff,
42 | handleBox = Box, handleDia = Dia } m
43 |
44 | instance Applicative ModalFormula where
45 | pure = return
46 | (<*>) = ap
47 |
48 | instance Functor ModalFormula where
49 | fmap f m = m >>= (Var . f)
50 |
51 | instance Foldable ModalFormula where
52 | foldMap acc = modalEval ModalEvaluator{
53 | handleVal = const mempty, handleVar = acc, handleNeg = id,
54 | handleAnd = (<>), handleOr = (<>), handleImp = (<>), handleIff = (<>),
55 | handleBox = id, handleDia = id }
56 |
57 | instance Traversable ModalFormula where
58 | traverse f = modalEval mevaler where
59 | mevaler = ModalEvaluator {
60 | handleVal = pure . Val, handleVar = fmap Var . f, handleNeg = fmap Neg,
61 | handleAnd = liftA2 And, handleOr = liftA2 Or,
62 | handleImp = liftA2 Imp, handleIff = liftA2 Iff,
63 | handleBox = fmap Box, handleDia = fmap Dia }
64 |
65 | -- Syntactic Conveniences:
66 | infixr 4 %=
67 | (%=) :: ModalFormula v -> ModalFormula v -> ModalFormula v
68 | (%=) = Iff
69 |
70 | infixr 5 %>
71 | (%>) :: ModalFormula v -> ModalFormula v -> ModalFormula v
72 | (%>) = Imp
73 |
74 | infixl 6 %|
75 | (%|) :: ModalFormula v -> ModalFormula v -> ModalFormula v
76 | (%|) = Or
77 |
78 | infixl 7 %^
79 | (%^) :: ModalFormula v -> ModalFormula v -> ModalFormula v
80 | (%^) = And
81 |
82 | ff :: ModalFormula v
83 | ff = Val False
84 |
85 | tt :: ModalFormula v
86 | tt = Val True
87 |
88 | incon :: Int -> ModalFormula v
89 | incon 0 = ff
90 | incon n = Box $ incon (n-1)
91 |
92 | holdsk :: Int -> ModalFormula v -> ModalFormula v
93 | holdsk 0 phi = phi
94 | holdsk k phi = Neg (incon k) `Imp` phi
95 |
96 | -- Operator like function that encodes "provable in S+Con^k(S)", where
97 | -- "S" is the original system.
98 | boxk :: Int -> ModalFormula v -> ModalFormula v
99 | boxk k phi = Box (holdsk k phi)
100 |
101 | diak :: Int -> ModalFormula v -> ModalFormula v
102 | diak k phi = Neg $ Box (holdsk k $ Neg phi)
103 |
104 | -- Data structure to be mapped across a formula.
105 | data ModalEvaluator v a = ModalEvaluator {
106 | handleVal :: Bool -> a,
107 | handleVar :: v -> a,
108 | handleNeg :: a -> a,
109 | handleAnd :: a -> a -> a,
110 | handleOr :: a -> a -> a,
111 | handleImp :: a -> a -> a,
112 | handleIff :: a -> a -> a,
113 | handleBox :: a -> a,
114 | handleDia :: a -> a}
115 |
116 | -- And how to use it to map:
117 | modalEval :: ModalEvaluator v a -> ModalFormula v -> a
118 | modalEval m = f where
119 | f (Val v) = handleVal m v
120 | f (Var v) = handleVar m v
121 | f (Neg x) = handleNeg m (f x)
122 | f (And x y) = handleAnd m (f x) (f y)
123 | f (Or x y) = handleOr m (f x) (f y)
124 | f (Imp x y) = handleImp m (f x) (f y)
125 | f (Iff x y) = handleIff m (f x) (f y)
126 | f (Box x) = handleBox m (f x)
127 | f (Dia x) = handleDia m (f x)
128 |
129 | allVars :: ModalFormula v -> [v]
130 | allVars = modalEval ModalEvaluator {
131 | handleVal = const [], handleVar = pure, handleNeg = id,
132 | handleAnd = (++), handleOr = (++), handleImp = (++), handleIff = (++),
133 | handleBox = id, handleDia = id }
134 |
135 | data ShowFormula = ShowFormula {
136 | topSymbol :: String,
137 | botSymbol :: String,
138 | negSymbol :: String,
139 | andSymbol :: String,
140 | orSymbol :: String,
141 | impSymbol :: String,
142 | iffSymbol :: String,
143 | boxSymbol :: String,
144 | diaSymbol :: String
145 | } deriving (Eq, Ord, Read, Show)
146 |
147 | showFormula :: Show v => ShowFormula -> Int -> ModalFormula v -> ShowS
148 | showFormula sf = showsFormula where
149 | showsFormula p f = case f of
150 | Val l -> showString $ if l then topSymbol sf else botSymbol sf
151 | Var v -> showString $ show v
152 | Neg x -> showParen (p > 8) $ showUnary (negSymbol sf) 8 x
153 | And x y -> showParen (p > 7) $ showBinary (andSymbol sf) 7 x 8 y
154 | Or x y -> showParen (p > 6) $ showBinary (orSymbol sf) 6 x 7 y
155 | Imp x y -> showParen (p > 5) $ showBinary (impSymbol sf) 6 x 5 y
156 | Iff x y -> showParen (p > 4) $ showBinary (iffSymbol sf) 5 x 4 y
157 | Box x -> showParen (p > 8) $ showUnary (boxSymbol sf) 8 x
158 | Dia x -> showParen (p > 8) $ showUnary (diaSymbol sf) 8 x
159 | padded o = showString " " . showString o . showString " "
160 | showUnary o i x = showString o . showsFormula i x
161 | showBinary o l x r y = showsFormula l x . padded o . showsFormula r y
162 |
163 | instance Show v => Show (ModalFormula v) where
164 | showsPrec = showFormula (ShowFormula "⊤" "⊥" "¬" "∧" "∨" "→" "↔" "□" "◇")
165 |
166 | --------------------------------------------------------------------------------
167 |
168 | instance Read v => Parsable (ModalFormula v) where parser = mformulaParser read
169 |
170 | mformulaParser :: (String -> v) -> Parsec Text s (ModalFormula v)
171 | mformulaParser reader = buildExpressionParser table term > "ModalFormula" where
172 | table = [
173 | [ prefix $ choice
174 | [ m_reservedOp "¬" >> return Neg
175 | , m_reservedOp "~" >> return Neg
176 | , m_reservedOp "□" >> return Box
177 | , m_reservedOp "[]" >> return Box
178 | , m_reservedOp "[0]" >> return Box
179 | , m_reservedOp "[1]" >> return (boxk 1)
180 | , m_reservedOp "[2]" >> return (boxk 2)
181 | , m_reservedOp "[3]" >> return (boxk 3)
182 | , m_reservedOp "[4]" >> return (boxk 4)
183 | , m_reservedOp "[5]" >> return (boxk 5)
184 | , m_reservedOp "[6]" >> return (boxk 6)
185 | , m_reservedOp "[7]" >> return (boxk 7)
186 | , m_reservedOp "[8]" >> return (boxk 8)
187 | , m_reservedOp "[9]" >> return (boxk 9)
188 | , m_reservedOp "◇" >> return Dia
189 | , m_reservedOp "<>" >> return Dia
190 | , m_reservedOp "<0>" >> return Dia
191 | , m_reservedOp "<1>" >> return (diak 1)
192 | , m_reservedOp "<2>" >> return (diak 2)
193 | , m_reservedOp "<3>" >> return (diak 3)
194 | , m_reservedOp "<4>" >> return (diak 4)
195 | , m_reservedOp "<5>" >> return (diak 5)
196 | , m_reservedOp "<6>" >> return (diak 6)
197 | , m_reservedOp "<7>" >> return (diak 7)
198 | , m_reservedOp "<8>" >> return (diak 8)
199 | , m_reservedOp "<9>" >> return (diak 9) ] ]
200 | , [ Infix (m_reservedOp "∧" >> return And) AssocLeft
201 | , Infix (m_reservedOp "&&" >> return And) AssocLeft ]
202 | , [ Infix (m_reservedOp "∨" >> return Or) AssocLeft
203 | , Infix (m_reservedOp "||" >> return Or) AssocLeft ]
204 | , [ Infix (m_reservedOp "→" >> return Imp) AssocRight
205 | , Infix (m_reservedOp "->" >> return Imp) AssocRight ]
206 | , [ Infix (m_reservedOp "↔" >> return Iff) AssocRight
207 | , Infix (m_reservedOp "<->" >> return Iff) AssocRight ] ]
208 |
209 | term = m_parens (mformulaParser reader)
210 | <|> m_braces (mformulaParser reader)
211 | <|> (m_reserved "⊤" >> return (Val True))
212 | <|> (m_reserved "T" >> return (Val True))
213 | <|> (m_reserved "⊥" >> return (Val False))
214 | <|> (m_reserved "F" >> return (Val False))
215 | <|> fmap (Var . reader) m_identifier
216 |
217 | -- To work-around Parsec's limitation for prefix operators:
218 | prefix p = Prefix . chainl1 p $ return (.)
219 |
220 | TokenParser
221 | { parens = m_parens
222 | , braces = m_braces
223 | , identifier = m_identifier
224 | , reservedOp = m_reservedOp
225 | , reserved = m_reserved
226 | , semiSep1 = _
227 | , whiteSpace = _ } =
228 | makeTokenParser emptyDef
229 | { commentStart = "{-"
230 | , commentEnd = "-}"
231 | , identStart = satisfy isNameFirstChar
232 | , identLetter = satisfy isNameChar
233 | , opStart = oneOf "~-<[&|¬□◇→↔∨∧"
234 | , opLetter = oneOf "->]&|123456789"
235 | , reservedOpNames =
236 | [ "¬", "∧", "∨", "→", "↔", "□", "◇"
237 | , "~", "&&", "||", "->", "<->", "[]", "<>"
238 | , "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]"
239 | , "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" ]
240 | , reservedNames = ["T", "F", "⊤", "⊥"]
241 | , caseSensitive = False }
242 |
243 | instance Read v => Read (ModalFormula v) where
244 | readsPrec _ s = case parse (parser <* eof) "reading formula" (T.pack s) of
245 | Right result -> [(result,"")]
246 | -- We could just return the remaining string, but Parsec gives
247 | -- much nicer errors. So we ask it to consume the whole input and
248 | -- fail if it fails.
249 | Left err -> error $ show err
250 |
251 | --------------------------------------------------------------------------------
252 |
253 | -- Note: Code not dead; just not yet used.
254 | isModalized :: ModalFormula v -> Bool
255 | isModalized = modalEval ModalEvaluator {
256 | handleVar = const False, handleVal = const True, handleNeg = id,
257 | handleAnd = (&&), handleOr = (&&), handleImp = (&&), handleIff = (&&),
258 | handleBox = const True, handleDia = const True }
259 |
260 | -- Nesting Depth of Modal Operators
261 | maxModalDepthHandler :: ModalEvaluator v Int
262 | maxModalDepthHandler = ModalEvaluator {
263 | handleVal = const 0, handleVar = const 0,
264 | handleNeg = id,
265 | handleAnd = max, handleOr = max, handleImp = max, handleIff = max,
266 | handleBox = (1+), handleDia = (1+)}
267 | maxModalDepth :: ModalFormula v -> Int
268 | maxModalDepth = modalEval maxModalDepthHandler
269 |
270 | -- How to simplify modal formulas:
271 | mapFormulaOutput :: (Bool -> Bool) -> ModalFormula v -> ModalFormula v
272 | mapFormulaOutput f formula = g (f False) (f True)
273 | where
274 | g True True = Val True
275 | g False False = Val False
276 | g False True = formula
277 | g True False = Neg formula
278 |
279 | simplifyBinaryOperator :: (ModalFormula v -> ModalFormula v -> ModalFormula v) ->
280 | (Bool -> Bool -> Bool) ->
281 | ModalFormula v -> ModalFormula v ->
282 | ModalFormula v
283 | simplifyBinaryOperator _ behavior (Val a) (Val b) = Val (behavior a b)
284 | simplifyBinaryOperator _ behavior (Val a) formula =
285 | mapFormulaOutput (behavior a) formula
286 | simplifyBinaryOperator _ behavior formula (Val b) =
287 | mapFormulaOutput (`behavior` b) formula
288 | simplifyBinaryOperator op _ f1 f2 = op f1 f2
289 |
290 | simplifyNeg :: ModalFormula v -> ModalFormula v
291 | simplifyNeg (Val v) = Val (not v)
292 | simplifyNeg (Neg x) = x
293 | simplifyNeg x = Neg x
294 |
295 | simplifyBox :: ModalFormula v -> ModalFormula v
296 | simplifyBox t@(Val True) = t
297 | simplifyBox x = Box x
298 |
299 | simplifyDia :: ModalFormula v -> ModalFormula v
300 | simplifyDia f@(Val False) = f
301 | simplifyDia x = Dia x
302 |
303 | simplifyHandler :: ModalEvaluator v (ModalFormula v)
304 | simplifyHandler = ModalEvaluator {
305 | handleVal = Val,
306 | handleVar = Var,
307 | handleNeg = simplifyNeg,
308 | handleAnd = simplifyBinaryOperator And (&&),
309 | handleOr = simplifyBinaryOperator Or (||),
310 | handleImp = simplifyBinaryOperator Imp (<=),
311 | handleIff = simplifyBinaryOperator Iff (==),
312 | handleBox = simplifyBox,
313 | handleDia = simplifyDia }
314 |
315 | simplify :: ModalFormula v -> ModalFormula v
316 | simplify = modalEval simplifyHandler
317 |
318 | -- GL Eval in standard model
319 | glEvalHandler :: ModalEvaluator v [Bool]
320 | glEvalHandler = ModalEvaluator {
321 | handleVal = repeat,
322 | handleVar = error "Variables are not supported in GLEval",
323 | handleNeg = fmap not,
324 | handleAnd = zipWith (&&),
325 | handleOr = zipWith (||),
326 | handleImp = zipWith (<=),
327 | handleIff = zipWith (==),
328 | handleBox = scanl (&&) True,
329 | handleDia = scanl (||) False }
330 |
331 | -- The reason we don't combine this with the above is because that would induce
332 | -- an Ord constraint on v unnecessarily.
333 | glEvalHandlerWithVars :: (Show v, Ord v) => Map v [Bool] -> ModalEvaluator v [Bool]
334 | glEvalHandlerWithVars m = glEvalHandler{
335 | handleVar = \var -> fromMaybe (unmapped var) (var `M.lookup` m)}
336 | where unmapped var = error $ "Unmapped variable in GLEval: " ++ show var
337 |
338 | glEvalWithVars :: (Show v, Ord v) => Map v [Bool] -> ModalFormula v -> [Bool]
339 | glEvalWithVars = modalEval . glEvalHandlerWithVars
340 |
341 | glEvalWithVarsStandard :: (Show v, Ord v) => Map v [Bool] -> ModalFormula v -> Bool
342 | glEvalWithVarsStandard m f = glEvalWithVars m f !! maxModalDepth f
343 |
344 | glEval :: ModalFormula v -> [Bool]
345 | glEval = modalEval glEvalHandler
346 |
347 | glEvalStandard :: ModalFormula v -> Bool
348 | glEvalStandard f = glEval f !! maxModalDepth f
349 |
350 | simplifiedMaxDepth :: ModalFormula v -> Int
351 | simplifiedMaxDepth formula =
352 | depth - length (head $ group $ reverse results) + 1 where
353 | results = take (depth+1) (glEval formula)
354 | depth = maxModalDepth formula
355 |
356 | fixpointGLEval :: (Show v, Eq v) => v -> ModalFormula v -> [Bool]
357 | fixpointGLEval var fi = result
358 | where
359 | unmapped = error . ("Non-fixpoint-variable used in fixpointGLEval: " ++) . show
360 | evalHandler = glEvalHandler{handleVar = \var' ->
361 | if var == var' then result else unmapped var'}
362 | result = modalEval evalHandler fi
363 |
364 | generalFixpointGLEval :: (Show v, Ord v) => Map v (ModalFormula v) -> Map v [Bool]
365 | generalFixpointGLEval formulaMap = evalMap
366 | where
367 | unmapped var = error $ "Unmapped variable in generalFixpointGLEval: " ++ show var
368 | evalHandler = glEvalHandler{handleVar = \var ->
369 | fromMaybe (unmapped var) (M.lookup var evalMap)}
370 | evalMap = M.map (modalEval evalHandler) formulaMap
371 |
372 | -- Finding the fixedpoints
373 |
374 | -- Check whether the length of a list is at least n without infinite looping.
375 | lengthAtLeast :: Int -> [a] -> Bool
376 | lengthAtLeast 0 _ = True
377 | lengthAtLeast _ [] = False
378 | lengthAtLeast n (_:xs) = lengthAtLeast (n-1) xs
379 |
380 | -- TODO: infinite loop
381 | fixpointDepth :: (Eq a) => Int -> [a] -> Int
382 | fixpointDepth n xs = 1 + countSkipped 0 (group xs) where
383 | countSkipped acc [] = acc
384 | countSkipped acc (g:gs)
385 | | lengthAtLeast n g = acc
386 | | otherwise = countSkipped (acc + length g) gs
387 |
388 | -- Find the fixpoint of a list, given a length of run after which we should conclude we found it.
389 | findFixpoint :: (Eq a) => Int -> [a] -> a
390 | findFixpoint n xs = (!!0) $ fromJust $ find (lengthAtLeast n) $ group xs
391 |
392 | -- Find the Fixpoint for a Modal formula
393 | findGLFixpoint :: (Show v, Eq v) => v -> ModalFormula v -> Bool
394 | findGLFixpoint var formula = findFixpoint
395 | (1 + maxModalDepth formula)
396 | (fixpointGLEval var formula)
397 |
398 | -- Find the Fixpoint for a collection of Modal formulas
399 | makeEquivs :: (Ord v, Read v) => [(String, String)] -> Map v (ModalFormula v)
400 | makeEquivs = M.fromList . map (read *** read)
401 |
402 | generalGLEvalSeq :: (Show v, Ord v) => Map v (ModalFormula v)-> [Map v Bool]
403 | generalGLEvalSeq formulaMap = map level [0..]
404 | where
405 | level n = M.map (!!n) result
406 | result = generalFixpointGLEval formulaMap
407 |
408 | findGeneralGLFixpoint :: (Eq v, Show v, Ord v) => Map v (ModalFormula v) -> Map v Bool
409 | findGeneralGLFixpoint formulaMap = findFixpoint (1 + maxFormulaDepth) results where
410 | results = generalGLEvalSeq formulaMap
411 | maxFormulaDepth = maximum $ map maxModalDepth $ M.elems formulaMap
412 |
413 | -- Display code to help visualize the kripke frames
414 | kripkeFrames :: (Eq v, Show v, Ord v) => Map v (ModalFormula v) -> Map v [Bool]
415 | kripkeFrames formulaMap = M.map (take (2 + fixPointer)) results where
416 | results = generalFixpointGLEval formulaMap
417 | mapList = generalGLEvalSeq formulaMap
418 | maxFormulaDepth = maximum $ map maxModalDepth $ M.elems formulaMap
419 | fixPointer = fixpointDepth (1 + maxFormulaDepth) mapList
420 |
421 | kripkeTable' :: (Show k, Ord k) => [k] -> Map k (ModalFormula k) -> Table
422 | kripkeTable' ks = toTable . kripkeFrames where
423 | toTable m = listmapToTable ks $ M.map (map boolify) m
424 | boolify = show . (Val :: Bool -> ModalFormula ())
425 |
426 | kripkeTable :: (Show k, Ord k) => Map k (ModalFormula k) -> Table
427 | kripkeTable m = kripkeTable' (M.keys m) m
428 |
429 | kripkeTableFiltered :: (Show k, Ord k) => (k -> Bool) -> Map k (ModalFormula k) -> Table
430 | kripkeTableFiltered f m = kripkeTable' (filter f $ M.keys m) m
431 |
432 | displayKripkeFrames :: (Show k, Ord k) => Map k (ModalFormula k) -> IO ()
433 | displayKripkeFrames = displayTable . kripkeTable
434 |
--------------------------------------------------------------------------------
/src/Modal/Code.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | {-# LANGUAGE ConstraintKinds #-}
3 | {-# LANGUAGE FlexibleContexts #-}
4 | module Modal.Code where
5 | import Prelude hiding (readFile, sequence, mapM, foldr1, concat, concatMap)
6 | import Control.Applicative
7 | import Control.Monad.Except hiding (mapM, sequence)
8 | import Control.Monad.State hiding (mapM, sequence, state)
9 | import Data.Map (Map)
10 | import Data.Maybe (mapMaybe, maybeToList)
11 | import Data.Monoid ((<>))
12 | import Data.Foldable
13 | import Data.Traversable
14 | import Modal.CompilerBase hiding (main)
15 | import Modal.Display
16 | import Modal.Formulas (ModalFormula, (%^), (%|))
17 | import Modal.Parser hiding (main)
18 | import Modal.Programming
19 | import Modal.Statement hiding (main)
20 | import Modal.Utilities
21 | import Text.Parsec hiding ((<|>), optional, many, State)
22 | import Text.Parsec.Expr
23 | import Text.Parsec.Text (Parser)
24 | import Text.Printf (printf)
25 | import qualified Data.List as List
26 | import qualified Data.Map as Map
27 | import qualified Data.Text as Text
28 | import qualified Modal.Formulas as F
29 |
30 | -------------------------------------------------------------------------------
31 |
32 | data CodeConfig = CodeConfig
33 | { actionKw :: String
34 | , actionsKw :: String
35 | , outcomeKw :: String
36 | , outcomesKw :: String
37 | } deriving (Eq, Ord, Read, Show)
38 |
39 | -------------------------------------------------------------------------------
40 |
41 | data SimpleExpr
42 | = Num (Ref Int)
43 | | Add SimpleExpr SimpleExpr
44 | | Sub SimpleExpr SimpleExpr
45 | | Mul SimpleExpr SimpleExpr
46 | | Exp SimpleExpr SimpleExpr
47 | deriving Eq
48 |
49 | instance Show SimpleExpr where
50 | show (Num v) = show v
51 | show (Add x y) = show x ++ "+" ++ show y
52 | show (Sub x y) = show x ++ "-" ++ show y
53 | show (Mul x y) = show x ++ "*" ++ show y
54 | show (Exp x y) = show x ++ "^" ++ show y
55 |
56 | instance Parsable SimpleExpr where
57 | parser = buildExpressionParser lTable term where
58 | lTable =
59 | [ [Infix (try $ symbol "+" $> Add) AssocRight]
60 | , [Infix (try $ symbol "-" $> Sub) AssocRight]
61 | , [Infix (try $ symbol "*" $> Mul) AssocRight]
62 | , [Infix (try $ symbol "^" $> Exp) AssocRight] ]
63 | term
64 | = parens parser
65 | <|> try (Num <$> (parser :: Parser (Ref Int)))
66 | > "a math expression"
67 |
68 | compileExpr :: MonadCompile m => SimpleExpr -> m Int
69 | compileExpr (Num v) = lookupN v
70 | compileExpr (Add x y) = (+) <$> compileExpr x <*> compileExpr y
71 | compileExpr (Sub x y) = (-) <$> compileExpr x <*> compileExpr y
72 | compileExpr (Mul x y) = (*) <$> compileExpr x <*> compileExpr y
73 | compileExpr (Exp x y) = (^) <$> compileExpr x <*> compileExpr y
74 |
75 | -------------------------------------------------------------------------------
76 |
77 | data Range x
78 | = EnumRange (Ref x) (Maybe (Ref x)) (Maybe (Ref Int))
79 | | ListRange [Ref x]
80 | | TotalRange
81 | deriving Eq
82 |
83 | instance Show x => Show (Range x) where
84 | show (EnumRange sta msto mste) = printf "%s..%s%s" (show sta) x y where
85 | x = maybe ("" :: String) show msto
86 | y = maybe ("" :: String) (printf " by %s" . show) mste
87 | show (ListRange xs) = printf "[%s]" (List.intercalate ", " $ map show xs)
88 | show TotalRange = "[...]"
89 |
90 | instance Parsable x => Parsable (Range x) where
91 | parser = rangeParser "[...]" parser
92 |
93 | rangeParser :: String -> Parser x -> Parser (Range x)
94 | rangeParser allname x = try rEnum <|> try rList <|> try rAll > "a range" where
95 | rEnum = EnumRange <$>
96 | (symbol "[" *> refParser x <* symbol "..") <*>
97 | (optional (refParser x) <* symbol "]") <*>
98 | optional (try $ keyword "by" *> parser)
99 | rList = ListRange <$> listParser (refParser x)
100 | rAll = keyword allname $> TotalRange
101 |
102 | _testRangeParser :: IO ()
103 | _testRangeParser = do
104 | let succeeds = verifyParser (parser :: Parser (Range Int))
105 | let fails = verifyParserFails (parser :: Parser (Range Int))
106 | succeeds "[1..]" (EnumRange (Lit 1) Nothing Nothing)
107 | succeeds "[ 1 ..]" (EnumRange (Lit 1) Nothing Nothing)
108 | succeeds "[ 1 .. 2 ]" (EnumRange (Lit 1) (Just (Lit 2)) Nothing)
109 | succeeds "[&n..]" (EnumRange (Ref "n") Nothing Nothing)
110 | succeeds "[&n..3]" (EnumRange (Ref "n") (Just (Lit 3)) Nothing)
111 | succeeds "[&n..3] by 2" (EnumRange (Ref "n") (Just (Lit 3)) (Just (Lit 2)))
112 | fails "[1..2..3]"
113 | succeeds "[1, 2, &three]" (ListRange [Lit 1, Lit 2, Ref "three"])
114 | succeeds "[...]" TotalRange
115 | succeeds "[ ]" (ListRange [])
116 | fails "[ "
117 |
118 | boundedRange :: Parsable x => Parser (Range x)
119 | boundedRange = try rBoundedEnum <|> try rList > "a bounded range" where
120 | rBoundedEnum = EnumRange <$>
121 | (symbol "[" *> parser <* symbol "..") <*>
122 | (Just <$> parser <* symbol "]") <*>
123 | optional (try $ keyword "by" *> parser)
124 | rList = ListRange <$> parser
125 |
126 | _testBoundedRangeParser :: IO ()
127 | _testBoundedRangeParser = do
128 | let succeeds = verifyParser (boundedRange :: Parser (Range Int))
129 | let fails = verifyParserFails (boundedRange :: Parser (Range Int))
130 | fails "[1..]"
131 | succeeds "[1 .. 2]" (EnumRange (Lit 1) (Just (Lit 2)) Nothing)
132 | succeeds "[&n .. 2] by 10" (EnumRange (Ref "n") (Just (Lit 2)) (Just (Lit 10)))
133 | succeeds "[1, 2, &three]" (ListRange [Lit 1, Lit 2, Ref "three"])
134 | fails "[...]"
135 |
136 | rangeLitValues :: Range x -> [x]
137 | rangeLitValues (EnumRange sta sto _) =
138 | maybeToList (lit sta) ++ maybe [] (maybeToList . lit) sto
139 | rangeLitValues (ListRange refs) = mapMaybe lit refs
140 | rangeLitValues _ = []
141 |
142 | compileRange :: (Eq x, MonadCompile m) => m [x] -> (Ref x -> m x) -> Range x -> m [x]
143 | compileRange getXs _ TotalRange = getXs
144 | compileRange _ getX (ListRange xs) = mapM getX xs
145 | compileRange getXs getX (EnumRange sta msto mste) = renum msto mste where
146 | renum Nothing Nothing = dropWhile . (/=) <$> getX sta <*> getXs
147 | renum (Just sto) Nothing = takeWhile . (/=) <$> getX sto <*> renum Nothing Nothing
148 | renum _ (Just ste) = every <$> lookupN ste <*> renum msto Nothing
149 |
150 | -------------------------------------------------------------------------------
151 |
152 | data CodeFragment
153 | = For ClaimType Name (Range Value) [CodeFragment]
154 | | ForN Name (Range Int) [CodeFragment]
155 | | LetN Name SimpleExpr
156 | | If Statement [CodeFragment]
157 | | IfElse Statement [CodeFragment] [CodeFragment]
158 | | Return (Maybe (Ref Value))
159 | | Pass
160 | deriving Eq
161 |
162 | instance Blockable CodeFragment where
163 | blockLines (For t n r cs) =
164 | [(0, Text.pack $ printf "for %s %s in %s" (show t) n (show r))] <>
165 | increaseIndent (concatMap blockLines cs)
166 | blockLines (ForN n r cs) =
167 | [(0, Text.pack $ printf "for number %s in %s" n (show r))] <>
168 | increaseIndent (concatMap blockLines cs)
169 | blockLines (LetN n x) =
170 | [(0, Text.pack $ printf "let %s = %s" n (show x))]
171 | blockLines (If s xs) =
172 | [(0, Text.pack $ printf "if %s" $ show s)] <>
173 | increaseIndent (concatMap blockLines xs)
174 | blockLines (IfElse s xs ys) =
175 | [(0, Text.pack $ printf "if %s" $ show s)] <>
176 | increaseIndent (concatMap blockLines xs) <>
177 | [(0, "else")] <>
178 | increaseIndent (concatMap blockLines ys)
179 | blockLines (Return Nothing) = [(0, "return")]
180 | blockLines (Return (Just x)) = [(0, Text.pack $ printf "return %s" (show x))]
181 | blockLines (Pass) = [(0, "pass")]
182 |
183 | instance Show CodeFragment where
184 | show = Text.unpack . renderBlock
185 |
186 | data CodeFragConfig = CodeFragConfig
187 | { indentLevel :: Int
188 | , codeConfig :: CodeConfig
189 | } deriving (Eq, Ord, Read, Show)
190 |
191 | eatIndent :: CodeFragConfig -> Parser ()
192 | eatIndent conf = void (count (indentLevel conf) (char '\t'))
193 | > printf "%d tabs" (indentLevel conf)
194 |
195 | codeFragmentParser :: CodeFragConfig -> Parser CodeFragment
196 | codeFragmentParser conf = try indent *> pFrag where
197 | indent = (many $ try ignoredLine) *> eatIndent conf
198 | pFrag = try pForA
199 | <|> try pForO
200 | <|> try pForN
201 | <|> try pLetN
202 | <|> try pIfElse
203 | <|> try pIf
204 | <|> try pReturn
205 | <|> try pPass
206 | pForA = pFor ActionT action actions
207 | pForO = pFor OutcomeT outcome outcomes
208 | pFor t x xs = For t
209 | <$> (keyword "for" *> keyword x *> varname)
210 | <*> (keyword "in" *> rangeParser xs parser <* w <* endOfLine)
211 | <*> pBlock
212 | pForN = ForN
213 | <$> (keyword "for" *> keyword "number" *> varname)
214 | <*> (keyword "in" *> boundedRange <* w <* endOfLine)
215 | <*> pBlock
216 | pLetN = LetN
217 | <$> (keyword "let" *> varname <* symbol "=")
218 | <*> parser <* eols
219 | pIf = If
220 | <$> (keyword "if" *> parser <* w <* endOfLine)
221 | <*> pBlock
222 | pIfElse = IfElse
223 | <$> (keyword "if" *> parser <* w <* endOfLine)
224 | <*> pBlock
225 | <*> (indent *> keyword "else" *> w *> endOfLine *> pBlock)
226 | pBlock = many1 $ try $ codeFragmentParser conf{indentLevel=succ $ indentLevel conf}
227 | pPass = symbol "pass" $> Pass <* w <* eol
228 | pReturn = try returnThing <|> returnNothing > "a return statement"
229 | returnNothing :: Parser CodeFragment
230 | returnThing = symbol "return " *> (Return . Just <$> parser) <* w <* eol
231 | returnNothing = symbol "return" $> Return Nothing <* w <* eol
232 | action = actionKw $ codeConfig conf
233 | outcome = outcomeKw $ codeConfig conf
234 | actions = actionsKw $ codeConfig conf
235 | outcomes = outcomesKw $ codeConfig conf
236 | varname = char '&' *> name
237 |
238 | compileCodeFragment :: MonadCompile m =>
239 | CodeFragment -> m (PartialProgram Value CompiledClaim)
240 | compileCodeFragment code = case code of
241 | For ActionT n r x -> loop (withA n) x =<< compileRange (gets actionList) lookupA r
242 | For OutcomeT n r x -> loop (withO n) x =<< compileRange (gets outcomeList) lookupO r
243 | ForN n r x -> loop (withN n) x =<< compileRange (return [0..]) lookupN r
244 | LetN n x -> compileExpr x >>= modify . withN n >> return id
245 | If s block -> compileCodeFragment (IfElse s block [Pass])
246 | IfElse s tblock eblock -> do
247 | cond <- compileStatement compileClaim s
248 | thens <- mapM compileCodeFragment tblock
249 | elses <- mapM compileCodeFragment eblock
250 | let yes = foldr1 (.) thens
251 | let no = foldr1 (.) elses
252 | return (\continue act ->
253 | (cond %^ yes continue act) %| (F.Neg cond %^ no continue act))
254 | Return (Just v) -> (\a -> const $ F.Val . (a ==)) <$> lookupA v
255 | Return Nothing -> (\a -> const $ F.Val . (a ==)) <$> defaultAction
256 | Pass -> return id
257 | where loop update block xs
258 | | null xs = return id
259 | | otherwise = foldr1 (.) . concat <$> mapM doFragment xs
260 | where doFragment x = modify (update x) >> mapM compileCodeFragment block
261 |
262 | -------------------------------------------------------------------------------
263 |
264 | data Code
265 | = Code [CodeFragment]
266 | | ActionMap (Map Value Statement)
267 | deriving Eq
268 |
269 | instance Blockable Code where
270 | blockLines (Code frags) = concatMap blockLines frags
271 | blockLines (ActionMap a2s) = [
272 | (0, Text.pack $ printf "%s ↔ %s" (show a) (show s)) | (a, s) <- Map.toList a2s]
273 |
274 | instance Show Code where
275 | show = Text.unpack . renderBlock
276 |
277 | codeParser :: CodeConfig -> Parser Code
278 | codeParser conf = Code <$> many1 (codeFragmentParser $ CodeFragConfig 1 conf)
279 |
280 | _testCodeParser :: IO ()
281 | _testCodeParser = testAllSamples where
282 | sample1 = Text.unlines
283 | ["\tlet &step = 0"
284 | ,"\tfor action &a in actions"
285 | ,"\t\t-- This is a comment about the inner loop."
286 | ,"\t\tfor outcome &u in utilities"
287 | ,"\t\t\tif [&step][A()=&a -> U()=&u]"
288 | ,"\t\t\t\treturn &a"
289 | ,"\t\t\tlet &step = &step + 1"
290 | ,"\treturn"]
291 | sample2 = Text.unlines
292 | ["\tif {- IGNORE THIS COMMENT -} [][Them(Me)=C]"
293 | ,"\t\treturn C -- Ignore this one too."
294 | ,""
295 | ,"\telse"
296 | ,"\t\treturn D"]
297 | sample3 = Text.unlines
298 | [" -- Sample 3:"
299 | ,"\tfor number &n in [0, 1, 2, 3]"
300 | ,"\t\tif Possible(&n)[Them(Me)=C]"
301 | ,"\t\t\treturn C"
302 | ," \t "
303 | ,""
304 | ,"\treturn D"]
305 | sample4 = Text.unlines
306 | ["\tfor number &n in [...]"
307 | ,"\t\treturn &n"]
308 | sample5 = Text.unlines
309 | ["\tif ⊤"
310 | ,"\treturn 0"]
311 | sample6 = Text.unlines
312 | ["\tif ⊤"
313 | ,"\t return 0"]
314 | sample7 = Text.unlines
315 | ["\tif ⊤"
316 | ,"\t\t\treturn 0"]
317 | conf = CodeConfig "action" "actions" "outcome" "utilities"
318 | testAllSamples = do
319 | verifyParser (codeParser conf) sample1 (Code
320 | [ LetN "step" (Num (Lit 0))
321 | , For ActionT "a" TotalRange
322 | [ For OutcomeT "u" TotalRange
323 | [ If (Provable (Ref "step")
324 | (Imp
325 | (Var $ ParsedClaim "A" Nothing (Equals (Ref "a")))
326 | (Var $ ParsedClaim "U" Nothing (Equals (Ref "u")))))
327 | [ Return (Just (Ref "a")) ]
328 | , LetN "step" (Add (Num $ Ref "step") (Num $ Lit 1)) ] ]
329 | , Return Nothing ])
330 | verifyParser (codeParser conf) sample2 (Code
331 | [ IfElse (Provable (Lit 0)
332 | (Var $ ParsedClaim "Them"
333 | (Just $ Call "Me" [] Map.empty [] [])
334 | (Equals $ Lit "C")))
335 | [ Return (Just (Lit "C")) ]
336 | [ Return (Just (Lit "D")) ] ])
337 | verifyParser (codeParser conf) sample3 (Code
338 | [ ForN "n" (ListRange [Lit 0, Lit 1, Lit 2, Lit 3])
339 | [ If (Possible (Ref "n")
340 | (Var $ ParsedClaim "Them"
341 | (Just $ Call "Me" [] Map.empty [] [])
342 | (Equals $ Lit "C")))
343 | [ Return (Just (Lit "C")) ] ]
344 | , Return (Just (Lit "D")) ])
345 | verifyParserFails (codeParser conf) sample4
346 | verifyParserFails (codeParser conf) sample5
347 | verifyParserFails (codeParser conf) sample6
348 | verifyParserFails (codeParser conf) sample7
349 |
350 | codeMapParser :: Parser Code
351 | codeMapParser = ActionMap . Map.fromList <$> many1 assignment where
352 | indent = (many (w *> endOfLine)) *> char '\t'
353 | iffParsers = [symbol "↔", symbol "<->", keyword "iff"]
354 | pIff = void $ choice $ map try iffParsers
355 | assignment = (,) <$> (indent *> parser <* pIff) <*> (parser <* eols)
356 |
357 | _testCodeMapParser :: IO ()
358 | _testCodeMapParser = testAllSamples where
359 | sample1 = Text.unlines
360 | ["\tC ↔ [][Them(Me)=C]"
361 | ,"\tD ↔ ~[][Them(Me)=C]"]
362 | sample2 = Text.unlines
363 | ["\tCD iff A1()=C and A2()=D"
364 | ,"\tCC iff A1()=C and A2()=C"
365 | ,"\tDD iff A1()=D and A2()=D"
366 | ,"\tDC iff A1()=D and A2()=C"]
367 | sample3 = Text.unlines
368 | ["\tC ↔ [][Them(Me)=C]"
369 | ,"\t\tD ↔ ~[][Them(Me)=C]"]
370 | sample4 = Text.unlines
371 | ["\tC ↔ [][Them(Me)=C]"
372 | ," D ↔ ~[][Them(Me)=C]"]
373 | testAllSamples = do
374 | verifyParser codeMapParser sample1 (ActionMap $ Map.fromList
375 | [ ("C", Provable (Lit 0) (Var $ ParsedClaim "Them"
376 | (Just $ Call "Me" [] Map.empty [] [])
377 | (Equals $ Lit "C")))
378 | , ("D", Neg $ Provable (Lit 0) (Var $ ParsedClaim "Them"
379 | (Just $ Call "Me" [] Map.empty [] [])
380 | (Equals $ Lit "C"))) ])
381 | verifyParser codeMapParser sample2 (ActionMap $ Map.fromList
382 | [ ("CD", (And
383 | (Var $ ParsedClaim "A1" Nothing (Equals $ Lit "C"))
384 | (Var $ ParsedClaim "A2" Nothing (Equals $ Lit "D"))))
385 | , ("CC", (And
386 | (Var $ ParsedClaim "A1" Nothing (Equals $ Lit "C"))
387 | (Var $ ParsedClaim "A2" Nothing (Equals $ Lit "C"))))
388 | , ("DD", (And
389 | (Var $ ParsedClaim "A1" Nothing (Equals $ Lit "D"))
390 | (Var $ ParsedClaim "A2" Nothing (Equals $ Lit "D"))))
391 | , ("DC", (And
392 | (Var $ ParsedClaim "A1" Nothing (Equals $ Lit "D"))
393 | (Var $ ParsedClaim "A2" Nothing (Equals $ Lit "C")))) ])
394 | verifyParserFails codeMapParser sample3
395 | verifyParserFails codeMapParser sample4
396 |
397 | compileCode :: MonadCompile m => Code -> m (ModalProgram Value CompiledClaim)
398 | compileCode (Code frags) = do
399 | prog <- foldM (\f c -> (f .) <$> compileCodeFragment c) id frags
400 | dflt <- defaultAction
401 | return $ prog (F.Val . (dflt ==))
402 | compileCode (ActionMap a2smap) = do
403 | let a2slist = Map.toList a2smap
404 | formulas <- mapM (compileStatement compileClaim . snd) a2slist
405 | let a2flist = zip (map fst a2slist) formulas
406 | return $ \a -> let Just f = List.lookup a a2flist in f
407 |
408 | -- Note: Code not dead; just not yet used.
409 | actionsMentioned :: Code -> [Value]
410 | actionsMentioned (ActionMap m) = Map.keys m
411 | actionsMentioned (Code frags) = concatMap fragRets frags where
412 | fragRets (For ActionT _ range fs) = rangeLitValues range ++ concatMap fragRets fs
413 | fragRets (For OutcomeT _ _ fs) = concatMap fragRets fs
414 | fragRets (ForN _ _ fs) = concatMap fragRets fs
415 | fragRets (If _ fs) = concatMap fragRets fs
416 | fragRets (IfElse _ fs gs) = concatMap fragRets fs ++ concatMap fragRets gs
417 | fragRets (Return (Just v)) = maybeToList $ lit v
418 | fragRets (Return _) = []
419 | fragRets (LetN _ _) = []
420 | fragRets Pass = []
421 |
422 | -- Note: Code not dead; just not yet used.
423 | outcomesMentioned :: Code -> [Value]
424 | outcomesMentioned (ActionMap _) = []
425 | outcomesMentioned (Code frags) = concatMap fragRets frags where
426 | fragRets (For ActionT _ _ fs) = concatMap fragRets fs
427 | fragRets (For OutcomeT _ range fs) = rangeLitValues range ++ concatMap fragRets fs
428 | fragRets (ForN _ _ fs) = concatMap fragRets fs
429 | fragRets (If _ fs) = concatMap fragRets fs
430 | fragRets (IfElse _ fs gs) = concatMap fragRets fs ++ concatMap fragRets gs
431 | fragRets (Return _) = []
432 | fragRets (LetN _ _) = []
433 | fragRets Pass = []
434 |
435 | -- Note: Code not dead; just not yet used.
436 | claimsMade :: Code -> [ParsedClaim]
437 | claimsMade (ActionMap m) = concatMap claimsParsed $ Map.elems m
438 | claimsMade (Code frags) = concatMap fragClaims frags where
439 | fragClaims (For _ _ _ fs) = concatMap fragClaims fs
440 | fragClaims (ForN _ _ fs) = concatMap fragClaims fs
441 | fragClaims (If s fs) = claimsParsed s ++ concatMap fragClaims fs
442 | fragClaims (IfElse s fs gs) =
443 | claimsParsed s ++ concatMap fragClaims fs ++ concatMap fragClaims gs
444 | fragClaims (LetN _ _) = []
445 | fragClaims (Return _) = []
446 | fragClaims Pass = []
447 |
448 | -------------------------------------------------------------------------------
449 |
450 | type CompiledAgent = Map Value (ModalFormula CompiledClaim)
451 |
452 | codeToProgram :: MonadError CompileError m =>
453 | CompileContext -> Code -> m CompiledAgent
454 | codeToProgram context code = do
455 | (prog, state) <- runStateT (compileCode code) context
456 | return $ Map.fromList [(a, prog a) | a <- actionList state]
457 |
458 | -------------------------------------------------------------------------------
459 | -- Testing
460 |
461 | main :: IO ()
462 | main = do
463 | _testRangeParser
464 | _testBoundedRangeParser
465 | _testCodeParser
466 | _testCodeMapParser
467 | putStrLn ""
468 |
--------------------------------------------------------------------------------
/src/Modal/CompilerBase.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveFunctor #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE FlexibleContexts #-}
4 | {-# LANGUAGE ConstraintKinds #-}
5 | module Modal.CompilerBase where
6 | import Prelude hiding (readFile, sequence, mapM, foldr1, concat, concatMap)
7 | import Control.Arrow (first)
8 | import Control.Applicative
9 | import Control.Monad.Except hiding (mapM, sequence)
10 | import Control.Monad.State hiding (mapM, sequence, state)
11 | import Data.Either (partitionEithers)
12 | import Data.Foldable
13 | import Data.Map (Map)
14 | import Data.Set (Set)
15 | import Data.String
16 | import Data.Traversable
17 | import Modal.Display (renderArgs)
18 | import Modal.Formulas (ModalFormula)
19 | import Modal.Parser hiding (main)
20 | import Modal.Utilities
21 | import Text.Parsec hiding ((<|>), optional, many, State)
22 | import Text.Parsec.Text (Parser)
23 | import Text.Printf (printf)
24 | import qualified Data.List as List
25 | import qualified Data.Map as Map
26 | import qualified Data.Set as Set
27 | import qualified Data.Text as Text
28 | import qualified Modal.Formulas as F
29 |
30 | -------------------------------------------------------------------------------
31 |
32 | newtype Value = V String deriving (Eq, Ord)
33 |
34 | instance Read Value where
35 | readsPrec _ s = case parse (parser <* eof) "reading value" (Text.pack s) of
36 | Right result -> [(result, "")]
37 | Left err -> []
38 |
39 | instance IsString Value where
40 | fromString = V
41 |
42 | instance Show Value where
43 | show (V val) = val
44 |
45 | instance Parsable Value where
46 | parser = V <$> valueStr
47 |
48 | -------------------------------------------------------------------------------
49 |
50 | data VarVal = Number Int | Action Value | Outcome Value deriving (Eq, Ord, Read)
51 |
52 | instance Show VarVal where
53 | show (Number n) = '#' : show n
54 | show (Action v) = '@' : show v
55 | show (Outcome v) = '$' : show v
56 |
57 | asN :: MonadCompile m => Name -> VarVal -> m Int
58 | asN _ (Number i) = return i
59 | asN n (Action _) = refError $ ExpectingNum n ActionT
60 | asN n (Outcome _) = refError $ ExpectingNum n OutcomeT
61 |
62 | asClaim :: MonadCompile m => Name -> VarVal -> m (ClaimType, Value)
63 | asClaim _ (Action v) = return (ActionT, v)
64 | asClaim _ (Outcome v) = return (OutcomeT, v)
65 | asClaim n _ = refError $ ExpectingClaim n
66 |
67 | -------------------------------------------------------------------------------
68 |
69 | data ClaimType = ActionT | OutcomeT deriving (Eq, Ord, Read, Enum)
70 |
71 | -- TODO: naming schema kinda sucks.
72 | -- This is especially evident when the universe has an universe-output
73 | -- enumeration that excludes values seen in the code, in which case the error
74 | -- message refers to this as an "action enumeration." (This is confusing
75 | -- because we think of the universe as outputing outcomes rather than actions,
76 | -- but vise versa for agents.)
77 | instance Show ClaimType where
78 | show ActionT = "action"
79 | show OutcomeT = "outcome"
80 |
81 | -------------------------------------------------------------------------------
82 |
83 | data VarType = NumberT | ClaimT ClaimType deriving (Eq, Read)
84 |
85 | instance Show VarType where
86 | show NumberT = "number"
87 | show (ClaimT t) = show t
88 |
89 | -------------------------------------------------------------------------------
90 |
91 | data DefType = AgentT | TheoryT | ProblemT deriving (Eq, Ord, Read, Enum)
92 |
93 | instance Show DefType where
94 | show AgentT = "agent"
95 | show TheoryT = "theory"
96 | show ProblemT = "problem"
97 |
98 | -------------------------------------------------------------------------------
99 |
100 | data Ref a = Ref Name | Lit a deriving (Eq, Ord, Read)
101 |
102 | instance Show a => Show (Ref a) where
103 | show (Ref n) = '&' : n
104 | show (Lit x) = show x
105 |
106 | instance Parsable a => Parsable (Ref a) where
107 | parser = refParser parser
108 |
109 | _testRefParser :: IO ()
110 | _testRefParser = do
111 | let succeeds = verifyParser (parser :: Parser (Ref Int))
112 | let fails = verifyParserFails (parser :: Parser (Ref Int))
113 | succeeds "&reference" (Ref "reference")
114 | fails "&123"
115 | succeeds "&_123" (Ref "_123")
116 | succeeds "3" (Lit 3)
117 | fails "hello"
118 | fails "3four5"
119 |
120 | lit :: Ref a -> Maybe a
121 | lit (Lit a) = Just a
122 | lit (Ref _) = Nothing
123 |
124 | refParser :: Parser x -> Parser (Ref x)
125 | refParser p = try (Lit <$> p)
126 | <|> try (Ref <$> (char '&' *> name))
127 | > "a variable"
128 |
129 | -------------------------------------------------------------------------------
130 |
131 | data Relation a
132 | = Equals a
133 | | In [a]
134 | | NotEquals a
135 | | NotIn [a]
136 | deriving (Eq, Ord, Read, Functor)
137 |
138 | instance Show a => Show (Relation a) where
139 | show (Equals v) = printf "=%s" (show v)
140 | show (In vs) = printf "∈{%s}" (renderArgs show vs)
141 | show (NotEquals v) = printf "≠%s" (show v)
142 | show (NotIn vs) = printf "∉{%s}" (renderArgs show vs)
143 |
144 | instance Foldable Relation where
145 | foldMap addM (Equals a) = addM a
146 | foldMap addM (In as) = foldMap addM as
147 | foldMap addM (NotEquals a) = addM a
148 | foldMap addM (NotIn as) = foldMap addM as
149 |
150 | instance Traversable Relation where
151 | traverse f (Equals a) = Equals <$> f a
152 | traverse f (In as) = In <$> sequenceA (map f as)
153 | traverse f (NotEquals a) = NotEquals <$> f a
154 | traverse f (NotIn as) = NotIn <$> sequenceA (map f as)
155 |
156 | instance (Ord a, Parsable a) => Parsable (Relation a) where
157 | parser = relationParser parser
158 |
159 | _testRelationParser :: IO ()
160 | _testRelationParser = do
161 | let succeeds = verifyParser (parser :: Parser (Relation Int))
162 | let fails = verifyParserFails (parser :: Parser (Relation Int))
163 | succeeds "=3" (Equals 3)
164 | fails "=A"
165 | succeeds "≠0" (NotEquals 0)
166 | succeeds "in {1, 2, 3}" (In [1, 2, 3])
167 | fails "in {1,"
168 | succeeds "not in {1, 2, 3}" (NotIn [1, 2, 3])
169 | succeeds " ∈ {1,}" (In [1])
170 |
171 | relationParser :: Parser x -> Parser (Relation x)
172 | relationParser p = go where
173 | go = try (Equals <$> (sEquals *> p))
174 | <|> try (NotEquals <$> (sNotEquals *> p))
175 | <|> try (In <$> (sIn *> set))
176 | <|> NotIn <$> (sNotIn *> set)
177 | sEquals = void sym where
178 | sym = try (symbol "=")
179 | <|> try (symbol "==")
180 | <|> try (keyword "is")
181 | > "an equality"
182 | sNotEquals = void sym where
183 | sym = try (symbol "≠")
184 | <|> try (symbol "!=")
185 | <|> try (symbol "/=")
186 | <|> try (keyword "isnt")
187 | > "a disequality"
188 | sIn = void sym where
189 | sym = try (symbol "∈")
190 | <|> try (keyword "in")
191 | > "a membership test"
192 | sNotIn = void sym where
193 | sym = try (symbol "∉")
194 | <|> try (keyword "not" *> keyword "in")
195 | > "an absence test"
196 | set = braces $ sepEndBy p comma
197 |
198 | relToMentions :: Relation a -> [a]
199 | relToMentions (Equals a) = [a]
200 | relToMentions (In as) = as
201 | relToMentions (NotEquals a) = [a]
202 | relToMentions (NotIn as) = as
203 |
204 | -------------------------------------------------------------------------------
205 |
206 | data Call = Call
207 | { callName :: Name
208 | , callArgs :: [Value]
209 | , callKwargs :: Map Name Value
210 | , callActions :: [Value]
211 | , callOutcomes :: [Value]
212 | } deriving (Eq, Ord)
213 |
214 | instance Show Call where
215 | show (Call n args kwargs as os) = n ++ controlstr where
216 | controlstr = printf "%s%s%s" paramstr actsstr outsstr
217 | paramstr = if null args && Map.null kwargs then ("" :: String)
218 | else printf "(%s%s%s)" argstr mid kwargstr
219 | argstr = renderArgs show args
220 | mid = if List.null args || Map.null kwargs then "" else "," :: String
221 | kwargstr = renderArgs (\(k, v) -> printf "%s=%s" k (show v)) (Map.toAscList kwargs)
222 | actsstr = case (as, os) of
223 | ([], []) -> "" :: String
224 | ([], _) -> "[...]"
225 | (_, _) -> printf "[%s]" (renderArgs show as)
226 | outsstr = if null os then "" :: String else printf "[%s]" (renderArgs show os)
227 |
228 | instance Parsable Call where
229 | parser = do
230 | n <- valueStr
231 | (args, kwargs) <- option ([], Map.empty) (try argsParser)
232 | as <- option [] (try valuesParser)
233 | os <- option [] (try valuesParser)
234 | return $ Call n args kwargs as os
235 | where
236 | valuesParser = try (brackets (string "...") $> []) <|> listParser parser
237 | argsParser = parens argsAndKwargs where
238 | argOrKwarg = try (Right <$> kwarg) <|> (Left <$> parser)
239 | kwarg = (,) <$> name <*> (symbol "=" *> parser)
240 | argsAndKwargs = do
241 | (args, kwargs) <- partitionEithers <$> (argOrKwarg `sepEndBy` comma)
242 | return (args, Map.fromList kwargs)
243 |
244 | _testCallParser :: IO ()
245 | _testCallParser = do
246 | let succeeds = verifyParser (parser :: Parser Call)
247 | let fails = verifyParserFails (parser :: Parser Call)
248 | succeeds "Name" (Call "Name" [] Map.empty [] [])
249 | succeeds "A-B()" (Call "A-B" [] Map.empty [] [])
250 | fails "Name!#-"
251 | succeeds "A(x, y, z)" (Call "A" ["x", "y", "z"] Map.empty [] [])
252 | succeeds "A(arg, kwarg=1)" (Call "A" ["arg"] (Map.fromList [("kwarg", "1")]) [] [])
253 | succeeds "A(x=1, a, y=2, b,)" (Call "A" ["a", "b"] (Map.fromList
254 | [("x", "1"), ("y", "2")]) [] [])
255 | fails "A(a a a )"
256 | succeeds "A()[x, y]" (Call "A" [] Map.empty ["x", "y"] [])
257 | succeeds "A()[x, y][a, b]" (Call "A" [] Map.empty ["x", "y"] ["a", "b"])
258 | succeeds "A()[][a, b]" (Call "A" [] Map.empty [] ["a", "b"])
259 | succeeds "A()[...][a, b]" (Call "A" [] Map.empty [] ["a", "b"])
260 | succeeds "A()[...][...]" (Call "A" [] Map.empty [] [])
261 | succeeds "A()[x, y][...]" (Call "A" [] Map.empty ["x", "y"] [])
262 | fails "A()[][][]"
263 |
264 | instance Read Call where
265 | readsPrec _ s = case parse (parser <* eof) "reading call" (Text.pack s) of
266 | Right result -> [(result,"")]
267 | Left err -> error $ show err
268 |
269 | simpleCall :: Name -> Call
270 | simpleCall n = Call n [] Map.empty [] []
271 |
272 | -------------------------------------------------------------------------------
273 |
274 | data ParsedClaim = ParsedClaim Name (Maybe Call) (Relation (Ref Value))
275 | deriving (Eq, Ord, Read)
276 |
277 | instance Show ParsedClaim where
278 | show (ParsedClaim n o r) = printf "%s(%s)%s" n (maybe "" show o) (show r)
279 |
280 | instance Parsable ParsedClaim where
281 | parser = try pclaim > "a claim about an agent" where
282 | pclaim = ParsedClaim <$>
283 | name <*>
284 | maybeCall <*>
285 | relationParser (refParser parser)
286 | maybeCall = try (symbol "()" $> Nothing)
287 | <|> try (Just <$> parens parser)
288 | <|> pure Nothing
289 |
290 | _testParsedClaimParser :: IO ()
291 | _testParsedClaimParser = do
292 | let succeeds = verifyParser (parser :: Parser ParsedClaim)
293 | let fails = verifyParserFails (parser :: Parser ParsedClaim)
294 | succeeds "A=val" (ParsedClaim "A" Nothing (Equals $ Lit $ "val"))
295 | fails "X=y=z"
296 | succeeds "A=&a" (ParsedClaim "A" Nothing (Equals $ Ref "a"))
297 | succeeds "A()=&a" (ParsedClaim "A" Nothing (Equals $ Ref "a"))
298 | succeeds "A(B(a))=&a" (ParsedClaim "A"
299 | (Just (Call "B" ["a"] Map.empty [] [])) (Equals $ Ref "a"))
300 | succeeds "Xxx(B(a)[...][x]) in {&a, b, &c}" (ParsedClaim "Xxx"
301 | (Just (Call "B" ["a"] Map.empty [] ["x"])) (In [Ref "a", Lit $ "b", Ref "c"]))
302 | fails "X(f(g(h(x))))=value"
303 |
304 | -------------------------------------------------------------------------------
305 |
306 | data CompiledClaim = CompiledClaim
307 | { claimNameIs :: Name
308 | , claimPlayedVs :: Maybe Call
309 | , claimType :: Maybe ClaimType
310 | , claimAgentPlays :: Value
311 | } deriving (Eq, Ord, Read)
312 |
313 | instance Show CompiledClaim where
314 | show (CompiledClaim n o t v) = printf "%s(%s)=%s%s" n showo showt (show v) where
315 | showo = maybe "" show o
316 | showt = maybe ("" :: String) (printf "%c" . tSymbol) t
317 | tSymbol ActionT = '@'
318 | tSymbol OutcomeT = '$'
319 |
320 | compileClaim :: MonadCompile m => ParsedClaim -> m (ModalFormula CompiledClaim)
321 | compileClaim (ParsedClaim n o rel) = mapM makeClaim (toF rel) where
322 | makeClaim :: MonadCompile m => Ref Value -> m CompiledClaim
323 | makeClaim ref = uncurry (CompiledClaim n o) <$> lookupClaim ref
324 | toF (Equals a) = F.Var a
325 | toF (In []) = F.Val False
326 | toF (In as) = foldr1 F.Or $ map F.Var as
327 | toF (NotEquals a) = F.Neg $ toF (Equals a)
328 | toF (NotIn []) = F.Val True
329 | toF (NotIn as) = F.Neg $ toF (In as)
330 |
331 | -------------------------------------------------------------------------------
332 |
333 | data CompileContext = CompileContext
334 | { variables :: Map Name VarVal
335 | , actionList :: [Value]
336 | , outcomeList :: [Value]
337 | , agentName :: Name
338 | } deriving (Eq, Show)
339 |
340 | withN :: Name -> Int -> CompileContext -> CompileContext
341 | withN n i c = c{variables=Map.insert n (Number i) $ variables c}
342 |
343 | withA :: Name -> Value -> CompileContext -> CompileContext
344 | withA n a c = c{variables=Map.insert n (Action a) $ variables c}
345 |
346 | withO :: Name -> Value -> CompileContext -> CompileContext
347 | withO n o c = c{variables=Map.insert n (Outcome o) $ variables c}
348 |
349 | lookupN :: MonadCompile m => Ref Int -> m Int
350 | lookupN (Ref n) = gets variables >>= toN where
351 | toN = maybe unknown (asN n) . Map.lookup n
352 | unknown = refError $ UnknownNumVar n
353 | lookupN (Lit i) = return i
354 |
355 | lookupClaim :: MonadCompile m => Ref Value -> m (Maybe ClaimType, Value)
356 | lookupClaim (Ref n) = gets variables >>= toClaim where
357 | toClaim = maybe unknown (fmap (first Just) . asClaim n) . Map.lookup n
358 | unknown = refError $ UnknownClaimVar n
359 | lookupClaim (Lit v) = return (Nothing, v)
360 |
361 | lookupA :: MonadCompile m => Ref Value -> m Value
362 | lookupA ref = lookupClaim ref >>= forceA where
363 | forceA (Just OutcomeT, _) = let Ref n = ref in refError $ ExpectingA n
364 | forceA (_, v) = return v
365 |
366 | lookupO :: MonadCompile m => Ref Value -> m Value
367 | lookupO ref = lookupClaim ref >>= forceO where
368 | forceO (Just ActionT, _) = let Ref n = ref in refError $ ExpectingO n
369 | forceO (_, v) = return v
370 |
371 | defaultAction :: MonadCompile m => m Value
372 | defaultAction = gets actionList >>= getFirst where
373 | getFirst [] = actionListError EnumMissing
374 | getFirst (a:_) = return a
375 |
376 | argumentError :: MonadCompile m => ArgumentError -> m a
377 | argumentError err = gets agentName >>= throwError . flip ArgErr err
378 |
379 | actionListError :: MonadCompile m => EnumError -> m a
380 | actionListError err = gets agentName >>= throwError . flip AListErr err
381 |
382 | outcomeListError :: MonadCompile m => EnumError -> m a
383 | outcomeListError err = gets agentName >>= throwError . flip OListErr err
384 |
385 | refError :: MonadCompile m => RefError -> m a
386 | refError err = gets agentName >>= throwError . flip RefErr err
387 |
388 | defError :: MonadCompile m => DefError -> m a
389 | defError err = gets agentName >>= throwError . flip DefErr err
390 |
391 | type MonadCompile m = (MonadError CompileError m, MonadState CompileContext m)
392 |
393 | -------------------------------------------------------------------------------
394 |
395 | data ArgumentError
396 | = UnknownArgs (Set Name)
397 | | TooManyArgs Int Int
398 | | ArgMissing Name VarType
399 | | ArgIsNotNum Name Value
400 | | ArgIsNotIn Name Value [Value]
401 | deriving (Eq, Read)
402 |
403 | instance Show ArgumentError where
404 | show (UnknownArgs ns) =
405 | printf "unknown keyword arguments: {%s}" (renderArgs id $ Set.toList ns)
406 | show (TooManyArgs x y) =
407 | printf "too many arguments: expected %d, got %d" x y
408 | show (ArgMissing n t) =
409 | printf "%s argument %s not given" (show t) n
410 | show (ArgIsNotNum n v) =
411 | printf "argument type mismatch for %s: expected a number, got %s" n (show v)
412 | show (ArgIsNotIn n v vs) =
413 | printf "argument type mismatch for %s: expected one of {%s}, got %s"
414 | n (renderArgs show vs) (show v)
415 |
416 | data EnumError
417 | = EnumMissing
418 | | EnumExcludes [Value] (Set Value)
419 | | EnumMismatch [Value] [Value]
420 | deriving (Eq, Ord, Read)
421 |
422 | instance Show EnumError where
423 | show EnumMissing = "enumeration missing."
424 | show (EnumExcludes xs vs) =
425 | printf "enumeration {%s} excludes {%s}, used in the code"
426 | (renderArgs show xs)
427 | (renderArgs show $ Set.toList vs)
428 | show (EnumMismatch vs ws) =
429 | printf "enumeration mismatch: [%s] / [%s]" (renderArgs show vs) (renderArgs show ws)
430 |
431 | data RefError
432 | = UnknownNumVar Name
433 | | UnknownClaimVar Name
434 | | ExpectingNum Name ClaimType
435 | | ExpectingClaim Name
436 | | ExpectingA Name
437 | | ExpectingO Name
438 | deriving (Eq, Read)
439 |
440 | instance Show RefError where
441 | show (UnknownNumVar n) = printf "unknown number var %s" n
442 | show (UnknownClaimVar n) = printf "unknown var %s used in action claim" n
443 | show (ExpectingNum n t) = printf "%s variable %s used as a number" (show t) n
444 | show (ExpectingClaim n) = printf "number variable %s used in an action claim" n
445 | show (ExpectingA n) = printf "outcome variable %s used as an action" n
446 | show (ExpectingO n) = printf "action variable %s used as an outcome" n
447 |
448 | data DefError
449 | = IsUnmodalized (ModalFormula String)
450 | | InvalidValue ClaimType Value [Value]
451 | | InvalidClaim CompiledClaim String
452 | deriving (Eq, Read)
453 |
454 | instance Show DefError where
455 | show (IsUnmodalized s) = printf "unmodalized statement: %s" (show s)
456 | show (InvalidValue t v vs) = printf "invalid %s %s: expected one of [%s]"
457 | (show t) (show v) (renderArgs show vs)
458 | show (InvalidClaim c s) = printf "invalid claim: %s (%s)" (show c) s
459 |
460 | data CompileError
461 | = ArgErr Name ArgumentError
462 | | AListErr Name EnumError
463 | | OListErr Name EnumError
464 | | RefErr Name RefError
465 | | DefErr Name DefError
466 | deriving (Eq, Read)
467 |
468 | instance Show CompileError where
469 | show (ArgErr n e) = printf "error while compiling %s: %s" n (show e)
470 | show (AListErr n e) = printf "error while compiling %s: action %s" n (show e)
471 | show (OListErr n e) = printf "error while compiling %s: outcome %s" n (show e)
472 | show (RefErr n e) = printf "error while compiling %s: %s" n (show e)
473 | show (DefErr n e) = printf "error while compiling %s: %s" n (show e)
474 |
475 | -- TODO: Not yet in use.
476 | data ExecutionError
477 | = FixpointError String
478 | deriving (Eq, Read)
479 |
480 | instance Show ExecutionError where
481 | show (FixpointError v) = printf "unknown variable when finding fixpoint: %s" v
482 |
483 | data FileError
484 | = UnknownDef DefType Name
485 | | NameCollision DefType Name
486 | deriving (Eq, Read)
487 |
488 | instance Show FileError where
489 | show (UnknownDef t n) = printf "unknown %s: %s" (show t) n
490 | show (NameCollision t n) = printf "name collision: %s %s is already defined" (show t) n
491 |
492 | data RuntimeError
493 | = FileErr FilePath FileError
494 | | ExecErr FilePath String ExecutionError
495 | | CompileErr FilePath CompileError
496 | deriving (Eq, Read)
497 |
498 | instance Show RuntimeError where
499 | show (FileErr f g) = printf "error in %s: %s" f (show g)
500 | show (ExecErr f x r) = printf "error running %s in %s: %s" x f (show r)
501 | show (CompileErr f c) = printf "error running %s: %s" f (show c)
502 |
503 | -------------------------------------------------------------------------------
504 | -- Testing
505 |
506 | main :: IO ()
507 | main = do
508 | _testRefParser
509 | _testRelationParser
510 | _testCallParser
511 | _testParsedClaimParser
512 | putStrLn ""
513 |
--------------------------------------------------------------------------------
/src/Modal/Combat.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ConstraintKinds #-}
2 | {-# LANGUAGE FlexibleContexts #-}
3 | {-# LANGUAGE OverloadedStrings #-}
4 | {-# LANGUAGE TupleSections #-}
5 | {-# LANGUAGE TypeFamilies #-}
6 | module Modal.Combat where
7 | import Prelude hiding (mapM, sequence, foldr)
8 | import Control.Applicative
9 | import Control.Monad.Except hiding (mapM, mapM_, sequence)
10 | import Data.Maybe
11 | import Data.Map (Map)
12 | import Data.Traversable
13 | import Modal.CompilerBase
14 | import Modal.Code
15 | import Modal.Def
16 | import Modal.Competition
17 | import Modal.Display
18 | import Modal.Formulas
19 | import Modal.Parser (Parsable, parser)
20 | import Modal.Utilities
21 | import Text.Parsec hiding ((<|>), optional, many, State)
22 | import Text.Parsec.Text
23 | import Text.Printf (printf)
24 | import Text.Read (readMaybe)
25 | import qualified Data.List as List
26 | import qualified Data.Map as Map
27 | import qualified Data.Set as Set
28 | import qualified Data.Text as Text
29 | import qualified Modal.Parser as P
30 | import qualified Modal.Statement as S
31 |
32 | -------------------------------------------------------------------------------
33 |
34 | data CorD = C | D deriving (Eq, Ord, Enum, Read, Show)
35 |
36 | -------------------------------------------------------------------------------
37 |
38 | data CombatVar m t = MeVsThemIs m | ThemVsIs (Maybe Call) t deriving (Eq, Ord)
39 |
40 | instance (Show a, Show o) => Show (CombatVar a o) where
41 | show (MeVsThemIs m) = printf "Me(Them)=%s" (show m)
42 | show (ThemVsIs Nothing t) = printf "Them(Me)=%s" (show t)
43 | show (ThemVsIs (Just o) t) = printf "Them(%s)%s" (show o) (show t)
44 |
45 | instance (Parsable m, Parsable t) => Parsable (CombatVar m t) where
46 | parser = try mvt <|> try tvm <|> try tvo > "a variable" where
47 | mvt = MeVsThemIs . snd <$> vs (string "Me") (nilOr "Them") parser
48 | tvm = ThemVsIs Nothing . snd <$> vs (string "Them") (nilOr "Me") parser
49 | tvo = uncurry (ThemVsIs . Just) <$> vs (string "Them") parser parser
50 | vs x y z = (,) <$> (x *> P.parens y) <*> z
51 | nilOr = option () . void . string
52 |
53 | instance (Parsable u, Parsable a) => Read (CombatVar u a) where
54 | readsPrec _ s = case parse (parser <* eof) "reading CombatVar" (Text.pack s) of
55 | Right result -> [(result,"")]
56 | Left err -> error $ show err
57 |
58 | instance ModalCombatVar CombatVar where
59 | subagentsIn (ThemVsIs (Just x) _) = Set.singleton x
60 | subagentsIn _ = Set.empty
61 |
62 | makeModalVar v1 _ (MeVsThemIs m) = v1 m
63 | makeModalVar _ v2 (ThemVsIs o t) = v2 o t
64 |
65 | -------------------------------------------------------------------------------
66 |
67 | data AVar a u = AMe a | AThem u deriving (Eq, Ord)
68 |
69 | instance (Show a, Show u) => Show (AVar a u) where
70 | show (AMe a) = printf "A()%s" (show a)
71 | show (AThem u) = printf "U()%s" (show u)
72 |
73 | instance MultiVarA AVar where
74 | promoteA i (AMe a) = PlayerNPlays i a
75 | promoteA _ (AThem u) = UniversePlays u
76 |
77 | -------------------------------------------------------------------------------
78 |
79 | data UVar u a = UMe u | UThem Int a deriving (Eq, Ord)
80 |
81 | instance (Show a, Show u) => Show (UVar a u) where
82 | show (UMe u) = printf "U()%s" (show u)
83 | show (UThem n a) = printf "A%d()%s" n (show a)
84 |
85 | instance MultiVarU UVar where
86 | promoteU (UMe u) = UniversePlays u
87 | promoteU (UThem n a) = PlayerNPlays n a
88 |
89 | -------------------------------------------------------------------------------
90 |
91 | data Controls = Controls
92 | { ctrlShowFrames :: Bool
93 | , ctrlShowMap :: Bool
94 | , ctrlHidden :: Bool
95 | } deriving (Eq, Ord, Read)
96 |
97 | instance Show Controls where
98 | show (Controls f m h) = "<" ++
99 | (if f then "F" else "") ++
100 | (if m then "M" else "") ++
101 | (if h then "H" else "") ++ ">"
102 |
103 | instance Parsable Controls where
104 | parser = do
105 | let pWith kw = P.keyword "with" *> P.keyword kw
106 | (f, m) <- P.anyComboOf (pWith "frames") (pWith "map")
107 | h <- optional (P.keyword "hidden")
108 | return $ Controls (isJust f) (isJust m) (isJust h)
109 |
110 | -------------------------------------------------------------------------------
111 |
112 | data GameObject
113 | = Agent Def
114 | | Theory Def
115 | | Problem Def [Value] [[Value]]
116 | | Execute Action
117 | deriving Show
118 |
119 | instance Parsable GameObject where
120 | parser
121 | = pProblem
122 | <|> (Theory <$> defParser theoryDConf)
123 | <|> (Agent <$> defParser agentDConf)
124 | <|> (Agent <$> pOldschoolBot)
125 | <|> (Execute <$> parser)
126 | where
127 | problemDConf = DefConfig "problem" True "outcome" "outcomes" "action" "actions"
128 | theoryDConf = DefConfig "theory" False "action" "actions" "outcome" "outcomes"
129 | agentDConf = DefConfig "agent" False "action" "actions" "response" "responses"
130 | pProblem = (\(d, (v, vs)) -> Problem d v vs) <$>
131 | defParserWithExtras pValues problemDConf
132 | pValues = (,) <$> valuesParser <*> many1 valuesParser
133 | valuesParser = try vals <|> try dunno where
134 | vals = P.brackets (parser `sepEndBy` P.comma)
135 | dunno = P.brackets (string "...") $> []
136 | pOldschoolBot = do
137 | P.keyword "bot"
138 | name <- P.valueStr
139 | P.symbol "="
140 | rawStatement <- parser
141 | let actTable = [("C", rawStatement), ("D", S.Neg rawStatement)]
142 | return $ Def [] name (ActionMap $ Map.fromList actTable)
143 |
144 | -------------------------------------------------------------------------------
145 |
146 | data Action
147 | = Combat Controls Call Call
148 | | Compete Controls Call Call
149 | | Play Controls Call [Call]
150 | deriving Show
151 |
152 | instance Parsable Action where
153 | parser = combatParser <|> competeParser <|> playParser where
154 | combatParser = Combat
155 | <$> (P.keyword "combat" *> parser <* P.symbol "!")
156 | <*> (parser <* P.keyword "vs")
157 | <*> parser
158 | competeParser = Compete
159 | <$> (P.keyword "compete" *> parser <* P.symbol ":")
160 | <*> (parser <* P.keyword "vs")
161 | <*> parser
162 | playParser = Play
163 | <$> (P.keyword "play" *> parser <* P.symbol ":")
164 | <*> (parser <* P.powerComma)
165 | <*> ((parser `sepEndBy` P.powerComma) <* P.symbol ".")
166 |
167 | -------------------------------------------------------------------------------
168 |
169 | data Setting = Setting
170 | { settingName :: Name
171 | , agents :: Map Name Def
172 | , theories :: Map Name Def
173 | , problems :: Map Name (Def, [Value], [[Value]])
174 | } deriving Show
175 |
176 | instance Blockable Setting where
177 | blockLines setting =
178 | [ (0, Text.pack $ printf "%s:" $ settingName setting)
179 | , (1, Text.pack $ line "Agents" agents)
180 | , (1, Text.pack $ line "Theories" theories)
181 | , (1, Text.pack $ line "Problems" problems) ]
182 | where line :: String -> (Setting -> Map Name x) -> String
183 | line x m = printf "%s: %s" x $ renderArgs id $ Map.keys $ m setting
184 |
185 | mergeSettingsR :: MonadError FileError m => Setting -> Setting -> m Setting
186 | mergeSettingsR x y = do
187 | as <- mergeMap (NameCollision AgentT) (agents x) (agents y)
188 | ts <- mergeMap (NameCollision TheoryT) (theories x) (theories y)
189 | ps <- mergeMap (NameCollision ProblemT) (problems x) (problems y)
190 | return $ Setting (settingName y) as ts ps
191 | where mergeMap err a b = case firstDup (Map.keys a ++ Map.keys b) of
192 | Nothing -> return $ a `Map.union` b
193 | Just dup -> throwError (err dup)
194 |
195 | -------------------------------------------------------------------------------
196 |
197 | lookupDef :: MonadError FileError m => DefType -> Map Name x -> Name -> m x
198 | lookupDef t defs name = maybe errUnknown return (Map.lookup name defs) where
199 | errUnknown = throwError $ UnknownDef t name
200 |
201 | lookupVal :: MonadError DefError m => ClaimType -> [(Value, x)] -> Value -> m x
202 | lookupVal t table val = maybe errInvalid return (List.lookup val table) where
203 | errInvalid = throwError $ InvalidValue t val (map fst table)
204 |
205 | lookupAndCompile :: (Ord a, MonadError RuntimeError m) =>
206 | Setting -> CompileConfig a (v a o) -> Call -> m (ModalAgent v a o)
207 | lookupAndCompile setting conf call = do
208 | let compileErr = CompileErr $ settingName setting
209 | let fileErr = FileErr $ settingName setting
210 | def <- wrapError fileErr (lookupDef AgentT (agents setting) (callName call))
211 | wrapError compileErr (compile conf call def)
212 |
213 | -------------------------------------------------------------------------------
214 |
215 | printVsHeader :: Controls -> Call -> Call -> IO ()
216 | printVsHeader ctrls call1 call2 =
217 | (unless $ ctrlHidden ctrls)
218 | (printf "%s vs %s:\n" (show call1) (show call2))
219 |
220 | printMultiHeader :: Controls -> Call -> [Call] -> IO ()
221 | printMultiHeader ctrls call0 calls = unless (ctrlHidden ctrls) doDisplay where
222 | doDisplay =
223 | (printf "%s as U vs\n %s:\n" (show call0)
224 | (List.intercalate ",\n " $ zipWith addAliases calls [1..]))
225 | addAliases :: Call -> Int -> String
226 | addAliases c d = printf "%s as A%s" (show c) (anum d)
227 | anum d = if length calls == 1 then "" else show d
228 |
229 | printCompetitionTable :: Show v => Controls -> Map v (ModalFormula v) -> IO ()
230 | printCompetitionTable ctrls cmap =
231 | (when $ ctrlShowMap ctrls && not (ctrlHidden ctrls))
232 | (putStrLn "\n Full combat map:" >>
233 | (displayTable $ indentTable " " $ tuplesToTable $ Map.toAscList cmap))
234 |
235 | printKripkeTable :: (Ord v, Show v)
236 | => Controls -> (v -> Bool) -> Map v (ModalFormula v) -> IO ()
237 | printKripkeTable ctrls varFilter cmap = do
238 | when (ctrlShowFrames ctrls && not (ctrlHidden ctrls)) $ do
239 | putStrLn "\n Kripke frames:"
240 | displayTable $ indentTable " " $ kripkeTableFiltered varFilter cmap
241 |
242 | printVsResults :: (Show a, Show b) => Controls -> Call -> a -> Call -> b -> IO ()
243 | printVsResults ctrls call1 r1 call2 r2 =
244 | (unless $ ctrlHidden ctrls)
245 | (printf " Result: %s=%s, %s=%s\n\n"
246 | (show call1) (show r1)
247 | (show call2) (show r2))
248 |
249 | printMultiResults :: (Show a, Show b) =>
250 | Controls -> Call -> a -> [Call] -> [b] -> IO ()
251 | printMultiResults ctrls call0 r0 calls rs = unless (ctrlHidden ctrls) doDisplay where
252 | doDisplay =
253 | (printf " Result: U()=%s, %s\n\n" (show r0)
254 | (renderArgs id $ zipWith showAresult rs [1..]))
255 | showAresult :: Show b => b -> Int -> String
256 | showAresult r d = if length rs == 1
257 | then printf "A()=%s" (show r)
258 | else printf "A%d()=%s" d (show r)
259 |
260 | -------------------------------------------------------------------------------
261 |
262 | modalClaimValues :: ParsedClaim -> [(ClaimType, Value)]
263 | modalClaimValues (ParsedClaim name _ rel) = case name of
264 | "Me" -> map (ActionT,) (mapMaybe lit $ relToMentions rel)
265 | "Them" -> map (OutcomeT,) (mapMaybe lit $ relToMentions rel)
266 | _ -> []
267 |
268 | handleModalClaim :: MonadError DefError m =>
269 | [(Value, a)] -> [(Value, o)] -> CompiledClaim -> m (CombatVar a o)
270 | handleModalClaim as os claim@(CompiledClaim name mcall mtype val) = handler where
271 | err = throwError . InvalidClaim claim
272 | mcall' = if mcall == Just (simpleCall "Me") then Nothing else mcall
273 | getAVal = lookupVal ActionT as val
274 | getOVal = lookupVal OutcomeT os val
275 | handler = do
276 | when (name `notElem` ["Me", "Them"])
277 | (err "claim must be about 'Me' or 'Them'")
278 | when (name == "Me" && mtype == Just OutcomeT)
279 | (err "'Me' returns actions, not responses")
280 | when (name == "Me" && isJust mcall && mcall /= Just (simpleCall "Them"))
281 | (err "cannot reason about what 'Me' would do against another")
282 | when (name == "Them" && mtype == Just ActionT)
283 | (err "'Them' returns responses, not actions")
284 | if name == "Me" then MeVsThemIs <$> getAVal else ThemVsIs mcall' <$> getOVal
285 |
286 | modalCombatCConf :: (Show a, Show o) =>
287 | [(Value, a)] -> [(Value, o)] -> CompileConfig a (CombatVar a o)
288 | modalCombatCConf aTable oTable = CompileConfig
289 | { availableActions = map fst aTable
290 | , availableOutcomes = map fst oTable
291 | , compileAction = lookupVal ActionT aTable
292 | , claimValues = modalClaimValues
293 | , handleClaim = handleModalClaim aTable oTable
294 | , finalizeFormula = \f -> do
295 | unless (isModalized f) (throwError $ IsUnmodalized $ show <$> f)
296 | return $ simplify f }
297 |
298 | -------------------------------------------------------------------------------
299 |
300 | problemClaimValues :: ParsedClaim -> [(ClaimType, Value)]
301 | problemClaimValues (ParsedClaim name _ rel) = case name of
302 | "U" -> map (ActionT,) (mapMaybe lit $ relToMentions rel)
303 | _ -> map (OutcomeT,) (mapMaybe lit $ relToMentions rel)
304 |
305 | handleProblemClaim :: MonadError DefError m =>
306 | [(Value, u)] -> [(Value, a)] -> CompiledClaim -> m (UVar u a)
307 | handleProblemClaim us as claim@(CompiledClaim name mcall mtype val) = handler where
308 | err :: MonadError DefError m => String -> m a
309 | err = throwError . InvalidClaim claim
310 | getUVal = lookupVal ActionT us val
311 | getAVal = lookupVal OutcomeT as val
312 | notAnAgent = err "not a valid agent (use A1, A2, ...)"
313 | getPNum = case name of
314 | "U" -> return (0 :: Int)
315 | "A" -> return 1
316 | ('A':num) -> maybe notAnAgent return (readMaybe num)
317 | _ -> notAnAgent
318 | handler = do
319 | pnum <- getPNum
320 | when (name /= "U" && pnum < 1) (err "player numbers must be at least 1")
321 | when (name == "U" && isJust mcall)
322 | (err "cannot reason about self vs other agents")
323 | when (name /= "U" && isJust mcall)
324 | (err "universe cannot reason about agents vs other universes")
325 | -- This is correct. Don't be confused: problem "outcomes" are ActionT,
326 | -- and vice versa. (theory "outcomes" are OutcomeT. Someone has to be
327 | -- reversed. Or we could find better names for ActionT/OutcomeT.)
328 | when (name == "U" && mtype == Just OutcomeT)
329 | (err "'U' returns outcomes, not actions")
330 | when (name /= "U" && mtype == Just ActionT)
331 | (err "players returns actions, not outcomes")
332 | if pnum == 0 then UMe <$> getUVal else UThem pnum <$> getAVal
333 |
334 | problemCConf :: [(Value, u)] -> [(Value, a)] -> CompileConfig u (UVar u a)
335 | problemCConf uTable aTable = CompileConfig
336 | { availableActions = map fst uTable
337 | , availableOutcomes = map fst aTable
338 | , compileAction = lookupVal ActionT uTable
339 | , claimValues = problemClaimValues
340 | , handleClaim = handleProblemClaim uTable aTable
341 | , finalizeFormula = return . simplify }
342 |
343 | -------------------------------------------------------------------------------
344 |
345 | theoryClaimValues :: ParsedClaim -> [(ClaimType, Value)]
346 | theoryClaimValues (ParsedClaim name _ rel) = case name of
347 | "A" -> map (ActionT,) (mapMaybe lit $ relToMentions rel)
348 | "U" -> map (OutcomeT,) (mapMaybe lit $ relToMentions rel)
349 | _ -> []
350 |
351 | handleTheoryClaim :: MonadError DefError m =>
352 | [(Value, a)] -> [(Value, u)] -> CompiledClaim -> m (AVar a u)
353 | handleTheoryClaim as us claim@(CompiledClaim name mcall mtype val) = handler where
354 | err = throwError . InvalidClaim claim
355 | getAVal = lookupVal ActionT as val
356 | getUVal = lookupVal OutcomeT us val
357 | handler = do
358 | when (name `notElem` ["A", "U"]) (err "invalid player (use A or U)")
359 | when (name == "A" && isJust mcall)
360 | (err "cannot reason about self in other universes")
361 | when (name == "U" && isJust mcall)
362 | (err "cannot reason about universe vs other agents")
363 | when (name == "A" && mtype == Just OutcomeT)
364 | (err "'A' returns actions, not outcomes")
365 | when (name == "U" && mtype == Just ActionT)
366 | (err "'U' returns outcomes, not actions")
367 | if name == "A" then AMe <$> getAVal else AThem <$> getUVal
368 |
369 | theoryCConf :: (Show a, Show u) =>
370 | [(Value, a)] -> [(Value, u)] -> CompileConfig a (AVar a u)
371 | theoryCConf aTable uTable = CompileConfig
372 | { availableActions = map fst aTable
373 | , availableOutcomes = map fst uTable
374 | , compileAction = lookupVal ActionT aTable
375 | , claimValues = theoryClaimValues
376 | , handleClaim = handleTheoryClaim aTable uTable
377 | , finalizeFormula = \f -> do
378 | unless (isModalized f) (throwError $ IsUnmodalized $ show <$> f)
379 | return $ simplify f }
380 |
381 | -------------------------------------------------------------------------------
382 |
383 | runModalCombat :: (Eq x, Eq y, Ord x, Ord y, Show x, Show y) =>
384 | Controls -> Call -> Call -> (VsVar x y -> Bool) -> Competition x y -> IO ()
385 | runModalCombat ctrls call1 call2 varFilter cmap = do
386 | let (r1, r2) = modalCombatResolve call1 call2 cmap
387 | printCompetitionTable ctrls cmap
388 | printKripkeTable ctrls varFilter cmap
389 | printVsResults ctrls call1 r1 call2 r2
390 |
391 | executeAction :: Setting -> Action -> IO ()
392 | executeAction setting = execute where
393 | -- Prisoner's dilemma competition
394 | execute (Combat ctrls call1 call2) = do
395 | printVsHeader ctrls call1 call2
396 | cmap <- run $ modalCombatMap1 (lookupAndCompile setting pdconf) call1 call2
397 | runModalCombat ctrls call1 call2 varIsC cmap
398 | -- Modal agents referring to each other
399 | execute (Compete ctrls call1 call2) = do
400 | printVsHeader ctrls call1 call2
401 | def1 <- run $ findDef AgentT (agents setting) call1
402 | def2 <- run $ findDef AgentT (agents setting) call2
403 | let (as1, os1) = effectiveAOs modalClaimValues (defCode def1) call1
404 | let (as2, os2) = effectiveAOs modalClaimValues (defCode def2) call2
405 | let conf1 = modalCombatCConf (makeTable as1) (makeTable $ List.nub $ as2 ++ os1)
406 | let conf2 = modalCombatCConf (makeTable as2) (makeTable $ List.nub $ as1 ++ os2)
407 | let env1 = lookupAndCompile setting conf1
408 | let env2 = lookupAndCompile setting conf2
409 | cmap <- run $ modalCombatMap env1 env2 call1 call2
410 | runModalCombat ctrls call1 call2 (const True) cmap
411 | -- Unmodalized universe vs modalized agents
412 | execute (Play ctrls pCall tCalls) = do
413 | printMultiHeader ctrls pCall tCalls
414 | (pDef, oList, aLists) <- run $ findDef ProblemT (problems setting) pCall
415 | tDefs <- run $ mapM (findDef TheoryT (theories setting)) tCalls
416 | let (pAms, pOms) = effectiveAOs problemClaimValues (defCode pDef) pCall
417 | let pATable = makeTable $ if null oList then pAms else oList
418 | let pOTable = makeTable $ if all null aLists then pOms else concat aLists
419 | let pConf = problemCConf pATable pOTable
420 | let makeTableR xs ys = makeTable (if null xs then ys else xs)
421 | let { makeTConf def call aList =
422 | let (tAms, tOms) = effectiveAOs theoryClaimValues (defCode def) call
423 | in theoryCConf (makeTableR tAms aList) (makeTableR tOms oList) }
424 | let aLists' = if length aLists == 1 then repeat (head aLists) else aLists
425 | let tConfs = zipWith3 makeTConf tDefs tCalls aLists'
426 | let tCompile (conf, call, def) = wrapCErr $ compile conf call def
427 | p <- run $ wrapCErr $ compile pConf pCall pDef
428 | ts <- run $ mapM tCompile (zip3 tConfs tCalls tDefs)
429 | let cmap = multiCompetition p ts
430 | printCompetitionTable ctrls cmap
431 | printKripkeTable ctrls (const True) cmap
432 | let (pResult, tResults) = multiCompete p ts
433 | printMultiResults ctrls pCall pResult tCalls tResults
434 | -- Error wrappers
435 | wrapFErr :: MonadError RuntimeError m => Except FileError a -> m a
436 | wrapFErr = wrapError (FileErr $ settingName setting)
437 | wrapCErr :: MonadError RuntimeError m => Except CompileError a -> m a
438 | wrapCErr = wrapError (CompileErr $ settingName setting)
439 | -- Generic helper functions
440 | makeTable = map (\v -> (v, v))
441 | findDef t defs = wrapFErr . lookupDef t defs . callName
442 | -- Helpers specific to the prisoner's dilemma
443 | cdTable = [("C", C), ("D", D)]
444 | pdconf = modalCombatCConf cdTable cdTable
445 | varIsC (Vs1 _ _ C) = True
446 | varIsC (Vs2 _ _ C) = True
447 | varIsC _ = False
448 |
449 | -------------------------------------------------------------------------------
450 |
451 | actions :: [GameObject] -> [Action]
452 | actions objects = [x | Execute x <- objects]
453 |
454 | gameToSetting :: MonadError FileError m => Name -> [GameObject] -> m Setting
455 | gameToSetting name = foldM addToSetting emptySetting where
456 | emptySetting = Setting name Map.empty Map.empty Map.empty
457 | addToSetting setting (Agent a) =
458 | (\x -> setting{agents=x}) <$> addToMap AgentT (defName a) a (agents setting)
459 | addToSetting setting (Theory t) =
460 | (\x -> setting{theories=x}) <$> addToMap TheoryT (defName t) t (theories setting)
461 | addToSetting setting (Problem p v vs) =
462 | (\x -> setting{problems=x}) <$>
463 | addToMap ProblemT (defName p) (p, v, vs) (problems setting)
464 | addToSetting setting (Execute _) = return setting
465 | addToMap :: MonadError FileError m =>
466 | DefType -> Name -> x -> Map Name x -> m (Map Name x)
467 | addToMap t n val m = do
468 | when (Map.member n m) (throwError $ NameCollision t n)
469 | return $ Map.insert n val m
470 |
471 | gameParser :: Parser [GameObject]
472 | gameParser = many P.ignoredLine *> (parser `sepEndBy` many P.ignoredLine) <* end where
473 | end = many (void (char '\t') <|> P.ignoredToken) *> eof -- Ugh.
474 |
475 | -------------------------------------------------------------------------------
476 |
477 | parseFile :: Bool -> FilePath -> IO [GameObject]
478 | parseFile useUtf8 path = runFile (parse gameParser path) useUtf8 path
479 |
480 | compileFile :: Bool -> FilePath -> IO Setting
481 | compileFile useUtf8 path = run . gameToSetting path =<< parseFile useUtf8 path
482 |
483 | playGame :: Name -> [GameObject] -> IO ()
484 | playGame name game = do
485 | setting <- run $ gameToSetting name game
486 | putStrLn "Setting:"
487 | displayBlock' (Text.pack " ") setting
488 | putStrLn "Loaded. Executing..."
489 | putStrLn ""
490 | mapM_ (executeAction setting) (actions game)
491 |
492 | playFile :: Bool -> FilePath -> IO ()
493 | playFile useUtf8 path = parseFile useUtf8 path >>= playGame path
494 |
495 | playGame' :: Name -> Setting -> [GameObject] -> IO ()
496 | playGame' name base game = do
497 | putStrLn "Setting loaded from other files:"
498 | displayBlock' (Text.pack " ") base
499 | local <- run $ gameToSetting name game
500 | putStrLn "Setting from this file:"
501 | displayBlock' (Text.pack " ") local
502 | setting <- run $ mergeSettingsR base local
503 | putStrLn "Merged. Executing..."
504 | putStrLn ""
505 | mapM_ (executeAction setting) (actions game)
506 |
507 | playFile' :: Bool -> FilePath -> Setting -> IO ()
508 | playFile' useUtf8 path setting = parseFile useUtf8 path >>= playGame' path setting
509 |
--------------------------------------------------------------------------------