penrose-0.1.0.1: src/StyAst.hs
module StyAst where
-- module Main (main) where
import Control.Monad (void)
import Text.Megaparsec
import Text.Megaparsec.Expr
import Text.Megaparsec.String -- input stream is of the type ‘String’
import System.Environment
import qualified Data.Map.Strict as M
import qualified Text.Megaparsec.Lexer as L
-- Style grammar (relies on the Substance grammar, specifically SubObj)
data SubType = Set | Pt | Map | Intersect | NoIntersect | NoSubset | Subset | PointIn | PointNotIn | AllTypes deriving (Show, Eq)
data StyObj = Circle | Box | Dot | Arrow | NoShape | Color | Text | Auto
deriving (Show)
type StyObjInfo
= (StyObj, M.Map String Expr)
data StySpec = StySpec {
spType :: SubType,
spId :: String,
spArgs :: [String],
spShape :: StyObjInfo,
spShpMap :: M.Map String StyObjInfo
} deriving (Show)
type StyProg = [Block]
type Block = ([Selector], [Stmt])
data Selector = Selector
{ selTyp :: SubType
, selPatterns :: [Pattern]
-- , selIds :: [String]
}
deriving (Show)
data Pattern
= RawID String
| WildCard String
deriving (Show)
data Stmt
= Assign String Expr
| ObjFn String [Expr]
| ConstrFn String [Expr]
| Avoid String [Expr]
deriving (Show)
data Expr
= IntLit Integer
| FloatLit Float
| Id String
| BinOp BinaryOp Expr Expr
| Cons StyObj [Stmt] -- Constructors for objects
deriving (Show)
data BinaryOp = Access deriving (Show)
data Color = RndColor | Colo
{ r :: Float
, g :: Float
, b :: Float
, a :: Float }
deriving (Show)
--
-- data Shape
-- = Circle [(String, Expr)]
-- | Box [(String, Expr)]
-- | Dot [(String, Expr)]
-- | Arrow [(String, Expr)]
-- | NoShape
-- deriving (Show)
---- Style program
styleParser :: Parser [Block]
styleParser = between sc eof styProg
-- TODO: required global and/or style block or not?
-- TODO: How can I write something like noop???
-- NOTE: sequence matters here
styProg :: Parser [Block]
styProg = some block
-- globalBlock <|> typeBlock <|> objBlock
-- <|> emptyProg
---- Style blocks
block :: Parser Block
block = do
sel <- selector `sepBy1` comma
void (symbol "{")
try newline'
sc
stmts <- try stmtSeq
-- res <- try stmtSeq
-- let stmts = case res of
-- Just a -> a
-- Nothing -> []
void (symbol "}")
newline'
return (sel, stmts)
-- return (sel, [])
globalSelect :: Parser Selector
globalSelect = do
i <- WildCard <$> identifier
-- return $ Selector AllTypes [] []
return $ Selector AllTypes [i]
constructorSelect :: Parser Selector
constructorSelect = do
typ <- subtype
-- sc
pat <- patterns
-- void sc <|> void (symbol ":")
-- ids <- many identifier
-- res <- optional $ rword "as" *> some identifier
-- let ids = case res of
-- Just a -> a
-- Nothing -> []
-- return $ Selector typ pat ids
return $ Selector typ pat
selector :: Parser Selector
selector = constructorSelect <|> globalSelect
subtype :: Parser SubType
subtype = do
str <- symbol "Set"
<|> symbol "Point"
<|> symbol "Map"
<|> symbol "Subset"
<|> symbol "NoSubset"
<|> symbol "Intersect"
<|> symbol "NoIntersect"
<|> symbol "PointIn"
<|> symbol "PointNotIn"
return (convert str)
where convert s
| s == "Set" = Set
| s == "Point" = Pt
| s == "Map" = Map
| s == "Subset" = Subset
| s == "NoSubset" = NoSubset
| s == "Intersect" = Intersect
| s == "NoIntersect" = NoIntersect
| s == "PointIn" = PointIn
| s == "PointNotIn" = PointNotIn
styObj :: Parser StyObj
styObj = do
str <- symbol "Color"
<|> symbol "None"
<|> symbol "Arrow"
<|> symbol "Text"
<|> symbol "Circle"
<|> symbol "Box"
<|> symbol "Dot"
return (convert str)
where convert s
| s == "Color" = Color
| s == "Arrow" = Arrow
| s == "None" = NoShape
| s == "Text" = Text
| s == "Circle" = Circle
| s == "Box" = Box
| s == "Dot" = Dot
patterns :: Parser [Pattern]
patterns = many pattern
where pattern = (WildCard <$> identifier <|> RawID <$> backticks identifier)
-- manyTill anyChar (symbol "{")
---- Statements
stmtSeq :: Parser [Stmt]
stmtSeq = endBy stmt newline'
stmt :: Parser Stmt
stmt =
try objFn
<|> try assignStmt
<|> try avoidObjFn
<|> try constrFn
--
assignStmt :: Parser Stmt
assignStmt = do
var <- attribute
sc
void (symbol "=")
e <- expr
return (Assign var e)
--
objFn :: Parser Stmt
objFn = do
rword "objective"
fname <- identifier
void (symbol "(")
params <- expr `sepBy` comma
void (symbol ")")
-- params <- sepBy expr comma
return (ObjFn fname params)
avoidObjFn :: Parser Stmt
avoidObjFn = do
rword "avoid"
fname <- identifier
lbrac
params <- expr `sepBy` comma
rbrac
return (Avoid fname params)
constrFn :: Parser Stmt
constrFn = do
rword "constraint"
fname <- identifier
void (symbol "(")
params <- expr `sepBy` comma
void (symbol ")")
-- params <- sepBy expr comma
return (ConstrFn fname params)
expr :: Parser Expr
expr = try objConstructor
<|> makeExprParser term operators
<|> none
<|> auto
<|> number
term :: Parser Expr
term = Id <$> identifier
operators :: [[Operator Parser Expr]]
operators = [ [ InfixL (BinOp Access <$ symbol ".")] ]
none :: Parser Expr
none = do
rword "None"
return $ Cons NoShape []
auto :: Parser Expr
auto = do
rword "Auto"
return $ Cons Auto []
objConstructor :: Parser Expr
objConstructor = do
typ <- styObj
lbrac >> newline'
stmts <- stmtSeq
rbrac -- NOTE: not consuming the space because stmt already does
return $ Cons typ stmts
number :: Parser Expr
number = FloatLit <$> try float <|> IntLit <$> integer
attribute :: Parser String
attribute = many alphaNumChar
--
--
--
-- typeBlock :: Parser Block
-- typeBlock = do
-- t <- typ
-- stmts <- braces stmtSeq
-- return $ TypeBlock t stmts
--
-- objBlock :: Parser Block
-- objBlock = do
-- i <- identifier
-- stmts <- braces stmtSeq
-- return $ ObjBlock i stmts
---- Lexer functions
sc :: Parser ()
sc = L.space (void separatorChar) lineCmnt blockCmnt
where lineCmnt = L.skipLineComment "--" >> newline'
blockCmnt = L.skipBlockComment "/*" "*/" >> newline'
newline' :: Parser ()
newline' = void sc >> void (many newline) >> void sc
backticks :: Parser a -> Parser a
backticks = between (symbol "`") (symbol "`")
braces :: Parser a -> Parser a
braces = between (symbol "{") (symbol "}")
lparen, rparen, lbrac, rbrac :: Parser ()
lbrac = void (symbol "{")
rbrac = void (symbol "}")
lparen = void (symbol "(")
rparen = void (symbol ")")
parens :: Parser a -> Parser a
parens = between (symbol "(") (symbol ")")
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
comma :: Parser String
comma = symbol ","
symbol :: String -> Parser String
symbol = L.symbol sc
integer :: Parser Integer
integer = lexeme L.integer
float :: Parser Float
float = realToFrac <$> lexeme L.float -- TODO: parsing without sign?
identifier :: Parser String
identifier = (lexeme . try) (p >>= check)
where
p = (:) <$> letterChar <*> many alphaNumChar
check x = if x `elem` rws
then fail $ "keyword " ++ show x ++ " cannot be an identifier"
else return x
-- Reserved words
rword :: String -> Parser ()
rword w = string w *> notFollowedBy alphaNumChar *> sc
rws, attribs, attribVs, shapes, types :: [String] -- list of reserved words
rws = ["avoid", "global", "as"] ++ types ++ shapes
-- ++ types ++ attribs ++ shapes ++ colors
types = ["Set", "Map", "Point"]
attribs = ["shape", "color", "label", "scale", "position"]
attribVs = shapes ++ colors
shapes = ["Auto", "None", "Circle", "Box", "SolidArrow", "SolidDot", "HollowDot", "Cross"]
colors = ["Random", "Black", "Red", "Blue", "Yellow"]
--
-- getType :: String -> SubType
-- getType str = case str of
-- "Set" -> Set
-- "Map" -> Map
-- "Point" -> Pt
-- "Subset" -> Subset
--
--
-- getAttribute :: String -> Attribute
-- getAttribute str = case str of
-- "shape" -> Shape
-- "color" -> Color
-- "bordor" -> LineType
--
--
-- getAttributeV :: String -> AttributeV
-- getAttributeV str = case str of
-- "None" -> ShapeV NoShape
-- "Circle" -> ShapeV $ SS SetCircle
-- "Box" -> ShapeV $ SS Box
-- "SolidArrow" -> ShapeV $ SM SolidArrow
-- "SolidDot" -> ShapeV $ SP SolidDot
-- "HollowDot" -> ShapeV $ SP HollowDot
-- "Cross" -> ShapeV $ SP Cross
-- "Red" -> ColorV Red
-- "Yellow" -> ColorV Yellow
-- "Blue" -> ColorV Blue
-- "Black" -> ColorV Black
-- "Random" -> ColorV Random
--
-- attribute :: Parser Attribute
-- attribute = (lexeme . try) (p >>= check)
-- where
-- p = many letterChar
-- check x = if x `elem` attribs
-- then return (getAttribute x)
-- else fail $ "keyword " ++ show x ++ " cannot be an attribute"
--
-- attributeValue :: Parser AttributeV
-- attributeValue = (lexeme . try) (p >>= check)
-- where
-- p = many letterChar
-- check x = if x `elem` attribVs
-- then return (getAttributeV x)
-- else fail $ "keyword " ++ show x ++ " cannot be an attribute value"
--
-- typ :: Parser SubType
-- typ = (lexeme . try) (p >>= check)
-- where
-- p = some letterChar
-- check x = if x `elem` types
-- then return (getType x)
-- else fail $ "Type " ++ show x ++ " is not a valid type"
--
-- -- access :: Parser Expr
-- -- access = do
-- -- i <- identifier
-- -- void (symbol ".")
-- -- a <- attributeName
-- -- return (Access i a)
--
--
--
--
-- -- TODO
-- styPrettyPrint :: Block -> String
-- styPrettyPrint b = "HAHAHAHA"
parseFromFile p file = runParser p file <$> readFile file
main :: IO ()
main = do
args <- getArgs
let styFile = head args
styIn <- readFile styFile
-- putStrLn styIn
-- parseTest styleParser styIn
case runParser styleParser styFile styIn of
Left err -> putStr (parseErrorPretty err)
Right xs -> mapM_ print xs
return ()