penrose-0.1.1.1: src/Penrose/Element.hs
-- | "Element" contains the grammers and parser for the
-- Element language
{-# OPTIONS_HADDOCK prune #-}
module Penrose.Element
( parseElement
) where
import Control.Arrow ((>>>))
import Control.Monad (void)
import Control.Monad.Combinators.Expr
import Control.Monad.State.Lazy (evalStateT)
import Data.Functor.Classes
import Data.List
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)
import Data.Tuple
import Data.Typeable
import Data.Void
import Debug.Trace
import Penrose.Env
import qualified Penrose.Tokenizer as T
import Penrose.Util
import System.Environment
import System.IO
import System.Process
import System.Random
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import Text.Show.Pretty
--------------------------------------- Element AST -------------------------------
type ElementProg = [ElementStmt]
data ElementStmt
= CdStmt Cd
| VdStmt Vd
| SubtypeDeclStmt SubtypeDecl
| OdStmt Od
| PdStmt Pd
| SnStmt Sn -- Statement notation
| PreludeDeclStmt Var
T
deriving (Show, Eq, Typeable)
-- | tconstructor
data Cd = Cd
{ nameCd :: String
, inputCd :: [(Y, K)]
} deriving (Eq, Typeable)
instance Show Cd where
show (Cd nameCd inputCd) =
"(TCon, " ++ nString ++ ", ValOfType " ++ iString ++ ")"
where
nString = show nameCd
iString = show inputCd
-- | vconstructor
data Vd = Vd
{ nameVd :: String
, varsVd :: [(Y, K)]
, typesVd :: [(Var, T)]
, toVd :: T
} deriving (Eq, Typeable)
instance Show Vd where
show (Vd nameVd varsVd typesVd toVd) =
"(VCon, " ++
aString ++
", forvars " ++
bString ++ ", fortypes " ++ cString ++ ", outputT " ++ dString ++ ")"
where
aString = show nameVd
bString = show varsVd
cString = show typesVd
dString = show toVd
-- | Subtype declarations
data SubtypeDecl = SubtypeDecl
{ subType :: T
, superType :: T
} deriving (Eq, Typeable)
instance Show SubtypeDecl where
show (SubtypeDecl subType superType) = aString ++ "Subtype of" ++ bString
where
aString = show subType
bString = show superType
-- | predicates
data Od = Od
{ nameOd :: String
, varsOd :: [(Y, K)]
, typesOd :: [(Var, T)]
, toOd :: T
} deriving (Eq, Typeable)
instance Show Od where
show (Od nameOd varsOd typesOd toOd) =
"(Op, " ++
aString ++
", forvars " ++
bString ++ ", fortypes " ++ cString ++ ", outputT " ++ dString ++ ")"
where
aString = show nameOd
bString = show varsOd
cString = show typesOd
dString = show toOd
-- | predicates
data Pd
= Pd1Const Pd1
| Pd2Const Pd2
deriving (Show, Eq, Typeable)
data Pd1 = Pd1
{ namePd1 :: String
, varsPd1 :: [(Y, K)]
, typesPd1 :: [(Var, T)]
} deriving (Eq, Typeable)
instance Show Pd1 where
show (Pd1 namePd1 varsPd1 typesPd1) =
"(Pred, " ++
aString ++
", forvars " ++ bString ++ ", fortypes " ++ cString ++ ", outputT " ++ ")"
where
aString = show namePd1
bString = show varsPd1
cString = show typesPd1
data Pd2 = Pd2
{ namePd2 :: String
, propsPd2 :: [(Var, Prop)]
} deriving (Eq, Typeable)
instance Show Pd2 where
show (Pd2 namePd2 propsPd2) =
"(Pred, " ++ aString ++ ", forProps " ++ bString ++ ", outputT " ++ ")"
where
aString = show namePd2
bString = show propsPd2
-- | Statement notation (for syntactic sugar)
data Sn = Sn
{ fromSn :: String
, toSn :: String
} deriving (Eq, Typeable)
instance Show Sn where
show (Sn fromSn toSn) = "(notation: from: " ++ a ++ " to: " ++ b
where
a = show fromSn
b = show toSn
----------------------------------------- Element Parser --------------------------
-- | 'ElementParser' is the top-level parser function. The parser contains a list
-- of functions that parse small parts of the language. When parsing a source
-- program, these functions are invoked in a top-down manner.
-- Parse all the statemnts between the spaces to the end of the input file
elementParser :: BaseParser [ElementStmt]
elementParser = evalStateT elementParser' Nothing
elementParser' :: Parser [ElementStmt]
elementParser' = between scn eof elementProgParser
-- |'elementProg' parses the entire actual Element program which is a collection of
-- constructors followed by a collection of operations followed by a collection
-- of predicates
elementProgParser :: Parser [ElementStmt]
elementProgParser = elementStmt `sepEndBy` newline'
elementStmt :: Parser ElementStmt
elementStmt =
try snParser <|> try cdParser <|> try vdParser <|> try odParser <|>
try subtypeDeclParser <|>
try pdParser <|>
try preludeParser
-- | type constructor parser
cdParser, cd1, cd2 :: Parser ElementStmt
cdParser = try cd1 <|> cd2
cd1 = do
rword "type"
name <- identifier
(y, k) <- parens ykParser
pos <- getSourcePos
return (CdStmt Cd {nameCd = name, inputCd = zip y k})
cd2 = do
rword "type"
name <- identifier
return (CdStmt Cd {nameCd = name, inputCd = []})
-- | sub type declarations parser
subtypeDeclParser :: Parser ElementStmt
subtypeDeclParser = do
subtype <- tParser
rword "<:"
supertype <- tParser
return $
SubtypeDeclStmt $ SubtypeDecl {subType = subtype, superType = supertype}
-- | prelude declarations parser
preludeParser :: Parser ElementStmt
preludeParser = do
rword "value"
pvar <- varParser
rword ":"
ptype <- tParser
return $ PreludeDeclStmt pvar ptype
-- | parser for the (y,k) list
ykParser :: Parser ([Y], [K])
ykParser = unzip <$> (yWithKind `sepBy1` comma)
where
yWithKind = (,) <$> (yParser <* colon) <*> kParser
varWithTypeParser :: Parser (Var, T)
varWithTypeParser = do
t <- tParser
v <- option (VarConst "") varParser
return (v, t)
varWithTypeParserNonOptional :: Parser (Var, T)
varWithTypeParserNonOptional = do
t <- tParser
v <- varParser
return (v, t)
-- | parser for the (b,t) list with optional var names
xtParser :: Parser ([Var], [T])
xtParser = unzip <$> (varWithTypeParser `sepBy1` star)
-- | parser for the (b,t) list with mandatory var names
xtParserNonOptional :: Parser ([Var], [T])
xtParserNonOptional = unzip <$> (varWithTypeParserNonOptional `sepBy1` star)
vpPairParser :: Parser (Var, Prop)
vpPairParser = do
p <- propParser
v <- varParser
return (v, p)
-- | parser for the (x, Prop) list
xPropParser :: Parser ([Var], [Prop])
xPropParser = unzip <$> (vpPairParser `sepBy1` star)
-- | var constructor parser
vdParser :: Parser ElementStmt
vdParser = do
rword "constructor"
name <- identifier
colon
(y', k') <- option ([], []) $ brackets ykParser
(b', t') <- option ([], []) $ xtParserNonOptional
arrow
t'' <- tParser
v <- option (VarConst "") varParser
return
(VdStmt
Vd {nameVd = name, varsVd = zip y' k', typesVd = zip b' t', toVd = t''})
-- | operation parser
odParser :: Parser ElementStmt
odParser = do
rword "function"
name <- identifier
colon
(y', k') <- option ([], []) $ brackets ykParser
(b', t') <- option ([], []) xtParser
arrow
t'' <- tParser
v <- option (VarConst "") varParser
return
(OdStmt
Od {nameOd = name, varsOd = zip y' k', typesOd = zip b' t', toOd = t''})
-- | predicate parser
pdParser, pd1, pd2 :: Parser ElementStmt
pdParser = try pd2 <|> pd1
pd1 = do
rword "predicate"
name <- identifier
colon
(y', k') <- option ([], []) $ brackets ykParser
(b', t') <- option ([], []) xtParser
return
(PdStmt
(Pd1Const
(Pd1 {namePd1 = name, varsPd1 = zip y' k', typesPd1 = zip b' t'})))
pd2 = do
rword "predicate"
name <- identifier
colon
(b', prop') <- xPropParser
return (PdStmt (Pd2Const (Pd2 {namePd2 = name, propsPd2 = zip b' prop'})))
snParser :: Parser ElementStmt
snParser = do
rword "notation"
quote
toSn' <- manyTill anySingle quote
tilde
quote
fromSn' <- manyTill anySingle quote
return (SnStmt (Sn {fromSn = fromSn', toSn = toSn'}))
-------------------------- Element Semantic Checker -------------------------------
-- | 'check' is the top-level semantic checking function. It takes a Element
-- program as the input, checks the validity of the program acoording to the
-- typechecking rules, and outputs a collection of information.
check :: ElementProg -> VarEnv
check p =
let env = foldl checkElementStmt initE p
in if null (errors env)
then env
else error $
"Element type checking failed with the following problems: \n" ++
(ppShow $ errors env) ++ ppShow env
where
initE =
VarEnv
{ typeConstructors = M.empty
, valConstructors = M.empty
, operators = M.empty
, predicates = M.empty
, typeVarMap = M.empty
, typeValConstructor = M.empty
, varMap = M.empty
, subTypes = []
, stmtNotations = []
, typeCtorNames = []
, preludes = []
, declaredNames = []
, errors = ""
}
checkElementStmt :: VarEnv -> ElementStmt -> VarEnv
checkElementStmt e (CdStmt c) =
let kinds = seconds (inputCd c)
env1 = foldl checkK e kinds
tc = TypeConstructor {nametc = nameCd c, kindstc = kinds}
ef = addName (nameCd c) env1
in ef {typeConstructors = M.insert (nameCd c) tc $ typeConstructors ef}
checkElementStmt e (SubtypeDeclStmt s) =
let env1 = checkDeclaredType e (subType s)
env2 = checkDeclaredType env1 (superType s)
env3 = env2 {subTypes = (subType s, superType s) : subTypes env2}
in env3
checkElementStmt e (PreludeDeclStmt (VarConst pvar) ptype) =
let env = checkT e ptype
in env {preludes = ((VarConst pvar), ptype) : preludes env}
checkElementStmt e (VdStmt v) =
let kinds = seconds (varsVd v)
env1 = foldl checkK e kinds
localEnv = foldl updateEnv env1 (varsVd v)
args = seconds (typesVd v)
res = toVd v
env2 = foldl checkT localEnv args
env3 = checkT env2 res
vc =
ValConstructor
{ namevc = nameVd v
, ylsvc = firsts (varsVd v)
, kindsvc = seconds (varsVd v)
, nsvc = firsts (typesVd v)
, tlsvc = seconds (typesVd v)
, tvc = toVd v
}
e1 = addName (nameVd v) env3
ef = addValConstructor vc e1
in if env2 == e || env2 /= e && env3 == e || env3 /= e && e1 == e || e1 /= e
then ef {valConstructors = M.insert (nameVd v) vc $ valConstructors ef}
else error "Error!" -- Does not suppose to reach here
checkElementStmt e (OdStmt v) =
let kinds = seconds (varsOd v)
env1 = foldl checkK e kinds
localEnv = foldl updateEnv env1 (varsOd v)
args = seconds (typesOd v)
res = toOd v
env2 = foldl checkT localEnv args
env3 = checkT env2 res
op =
Operator
{ nameop = nameOd v
, ylsop = firsts (varsOd v)
, kindsop = seconds (varsOd v)
, tlsop = seconds (typesOd v)
, top = toOd v
}
ef = addName (nameOd v) env3
in if env2 == e || env2 /= e && env3 == e || env3 /= e
then ef {operators = M.insert (nameOd v) op $ operators ef}
else error "Error!" -- Does not suppose to reach here
checkElementStmt e (PdStmt (Pd1Const v)) =
let kinds = seconds (varsPd1 v)
env1 = foldl checkK e kinds
localEnv = foldl updateEnv env1 (varsPd1 v)
args = seconds (typesPd1 v)
env2 = foldl checkT localEnv args
pd1 =
Pred1 $
Prd1
{ namepred1 = namePd1 v
, ylspred1 = firsts (varsPd1 v)
, kindspred1 = seconds (varsPd1 v)
, tlspred1 = seconds (typesPd1 v)
}
ef = addName (namePd1 v) e
in if env2 == e || env2 /= e
then ef {predicates = M.insert (namePd1 v) pd1 $ predicates ef}
else error "Error!" -- Does not suppose to reach here
checkElementStmt e (PdStmt (Pd2Const v)) =
let pd = Pred2 $ Prd2 {namepred2 = namePd2 v, plspred2 = seconds (propsPd2 v)}
ef = addName (namePd2 v) e
in ef {predicates = M.insert (namePd2 v) pd $ predicates ef}
checkElementStmt e (SnStmt s) =
let (from, to, patterns, entities) = T.translatePatterns (fromSn s, toSn s) e
newSnr =
StmtNotationRule
{ fromSnr = from
, toSnr = to
, patternsSnr = patterns
, entitiesSnr = entities
}
in e {stmtNotations = newSnr : stmtNotations e}
computeSubTypes :: VarEnv -> VarEnv
computeSubTypes e =
let env1 = e {subTypes = transitiveClosure (subTypes e)}
in if isClosureNotCyclic (subTypes env1)
then env1
else env1 {errors = errors env1 ++ "Cyclic Subtyping Relation! \n"}
-- | 'parseElement' runs the actual parser function: 'elementParser', taking in a
-- program String, parses it and semantically checks it. It outputs the
-- environement as returned from the element typechecker
parseElement :: String -> String -> Either CompilerError VarEnv
parseElement elementFile elementIn =
case runParser elementParser elementFile elementIn of
Left err -> Left $ ElementParse $ (errorBundlePretty err)
Right prog ->
let env = check prog
env1 = computeSubTypes env
in Right env1
----------------------------- Test Driver --------------------------------------
-- | For testing: first uncomment the module definition to make this module the
-- Main module. Usage: ghc Element.hs; ./Element <element-file> <output-file>
main :: IO ()
main = do
[elementFile, outputFile] <- getArgs
elementIn <- readFile elementFile
case parse elementParser elementFile elementIn of
Left err -> putStr (errorBundlePretty err)
Right xs -> do
writeFile outputFile (show xs)
let o = check xs
print o
putStrLn "Parsing Done!"
return ()