hdiff-0.0.1: executables/Languages/While.hs
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
{-# OPTIONS_GHC -Wno-missing-pattern-synonym-signatures #-}
module Languages.While where
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import Text.ParserCombinators.Parsec.Language
import qualified Text.ParserCombinators.Parsec.Token as Token
import Control.Monad
import Data.Type.Equality
import Data.Text.Prettyprint.Doc (pretty)
import Generics.MRSOP.Base hiding (Infix)
import Generics.MRSOP.TH
import Generics.MRSOP.HDiff.Renderer
import Generics.MRSOP.HDiff.Digest
import System.IO
import System.Exit
-----------------------
-- * Parser
data BExpr = BoolConst Bool
| Not BExpr
| BBinary BBinOp BExpr BExpr
| RBinary RBinOp AExpr AExpr
deriving (Show , Eq)
data BBinOp = And | Or deriving (Show , Eq)
data RBinOp = Greater | Less | Equal deriving (Show , Eq)
data AExpr = Var String
| IntConst Integer
| Neg AExpr
| ABinary ABinOp AExpr AExpr
| ARange AExpr AExpr
deriving (Show , Eq)
data ABinOp = Add
| Subtract
| Multiply
| Reminder
| Divide
| Power
deriving (Show , Eq)
data Stmt = Seq [Stmt]
| Assign String AExpr
| If BExpr Stmt Stmt
| While BExpr Stmt
| Skip
deriving (Show , Eq)
-- |Custom Opaque type
data WKon = WInt | WString | WBool
-- |And their singletons.
--
-- Note we need instances of EqHO, ShowHO and DigestibleHO
data W :: WKon -> * where
W_Integer :: Integer -> W 'WInt
W_String :: String -> W 'WString
W_Bool :: Bool -> W 'WBool
deriving instance Eq (W x)
deriving instance Show (W x)
instance DigestibleHO W where
digestHO (W_Integer i) = hashStr (show i)
digestHO (W_String s) = hashStr s
digestHO (W_Bool b) = hashStr (show b)
-- Now we derive the 'Family' instance
-- using 'W' for the constants.
deriveFamilyWithTy [t| W |] [t| Stmt |]
instance RendererHO W where
renderHO (W_Integer i) = pretty i
renderHO (W_String s) = pretty s
renderHO (W_Bool b) = pretty b
instance TestEquality W where
testEquality (W_Integer _) (W_Integer _) = Just Refl
testEquality (W_String _) (W_String _) = Just Refl
testEquality (W_Bool _) (W_Bool _) = Just Refl
testEquality _ _ = Nothing
-- ** Parser definition
languageDef =
emptyDef { Token.commentStart = "/*"
, Token.commentEnd = "*/"
, Token.commentLine = "//"
, Token.identStart = letter
, Token.identLetter = alphaNum
, Token.reservedNames = [ "if"
, "then"
, "else"
, "while"
, "do"
, "skip"
, "true"
, "false"
, "not"
, "and"
, "or"
, "range"
]
, Token.reservedOpNames = ["+", "-", "*", "^", "/", ":=" , "%"
, "<", ">", "and", "or", "not" , "=="
]
}
lexer = Token.makeTokenParser languageDef
identifier = Token.identifier lexer -- parses an identifier
reserved = Token.reserved lexer -- parses a reserved name
reservedOp = Token.reservedOp lexer -- parses an operator
parseBraces = Token.braces lexer
parseParens = Token.parens lexer -- parses surrounding parenthesis:
-- parseParens p
-- takes care of the parenthesis and
-- uses p to parse what's inside them
integer = Token.integer lexer -- parses an integer
parseSemi = Token.semi lexer -- parses a parseSemicolon
whiteSpace = Token.whiteSpace lexer -- parses whitespace
whileParser :: Parser Stmt
whileParser = whiteSpace >> sequenceOfStmt
statement :: Parser Stmt
statement = statement'
<|> parseBraces sequenceOfStmt
sequenceOfStmt =
do list <- (many statement')
-- If there's only one statement return it without using Seq.
return $ if length list == 1 then head list else Seq list
statement' :: Parser Stmt
statement' = ifStmt
<|> whileStmt
<|> (skipStmt <* parseSemi)
<|> (assignStmt <* parseSemi)
ifStmt :: Parser Stmt
ifStmt =
do reserved "if"
cond <- bExpression
reserved "then"
stmt1 <- statement
reserved "else"
stmt2 <- statement
return $ If cond stmt1 stmt2
whileStmt :: Parser Stmt
whileStmt =
do reserved "while"
cond <- bExpression
reserved "do"
stmt <- statement
return $ While cond stmt
assignStmt :: Parser Stmt
assignStmt =
do var <- identifier
reservedOp ":="
expr <- aExpression
return $ Assign var expr
skipStmt :: Parser Stmt
skipStmt = reserved "skip" >> return Skip
aExpression :: Parser AExpr
aExpression = buildExpressionParser aOperators aTerm
bExpression :: Parser BExpr
bExpression = buildExpressionParser bOperators bTerm
aOperators = [ [Prefix (reservedOp "-" >> return (Neg )) ]
, [Infix (reservedOp "^" >> return (ABinary Power )) AssocLeft]
, [Infix (reservedOp "*" >> return (ABinary Multiply)) AssocLeft,
Infix (reservedOp "/" >> return (ABinary Divide )) AssocLeft,
Infix (reservedOp "%" >> return (ABinary Reminder)) AssocLeft]
, [Infix (reservedOp "+" >> return (ABinary Add )) AssocLeft,
Infix (reservedOp "-" >> return (ABinary Subtract)) AssocLeft]
]
bOperators = [ [Prefix (reservedOp "not" >> return (Not )) ]
, [Infix (reservedOp "and" >> return (BBinary And )) AssocLeft,
Infix (reservedOp "or" >> return (BBinary Or )) AssocLeft]
]
aTerm = parseParens aExpression
<|> liftM Var identifier
<|> liftM IntConst integer
<|> (reserved "range" >> liftM2 ARange aExpression aExpression)
bTerm = parseParens bExpression
<|> (reserved "true" >> return (BoolConst True ))
<|> (reserved "false" >> return (BoolConst False))
<|> rExpression
rExpression =
do a1 <- aExpression
op <- relation
a2 <- aExpression
return $ RBinary op a1 a2
relation = (reservedOp ">" >> return Greater)
<|> (reservedOp "<" >> return Less)
<|> (reservedOp "==" >> return Equal)
parseString :: String -> Stmt
parseString str =
case parse whileParser "" str of
Left e -> error $ show e
Right r -> r
testString :: String -> IO ()
testString str
= do let stmt = parseString str
putStrLn $ show $ renderEl renderHO (into @FamStmt stmt)
parseFile :: String -> IO Stmt
parseFile file =
do program <- readFile file
case parse whileParser "" program of
Left e -> hPutStrLn stderr (show e) >> exitWith (ExitFailure 10)
Right r -> return r
type Block = Stmt