module SyntaxChecker (isCoreModule, isCoreExpression, getModule, getNames) where
import Language.Haskell.Exts
-- Disable module Name, default is Main even if you omit it
isCoreModuleName :: ModuleName -> Bool
isCoreModuleName (ModuleName mn)
| mn == "Main" = True
| otherwise = False
-- Disable all module pragma
isCoreModulePragma :: [ModulePragma] -> Bool
isCoreModulePragma [] = True
isCoreModulePragma _ = error "You can't use module pragma."
-- Disable all warning text
isCoreWarningText :: Maybe WarningText -> Bool
isCoreWarningText Nothing = True
isCoreWarningText _ = error "You can't use warning text."
-- Disable all export specification
isCoreExportSpec :: Maybe [ExportSpec] -> Bool
isCoreExportSpec Nothing = True
isCoreExportSpec (Just [EVar (UnQual (Ident n))]) | n == "main" = True
isCoreExportSpec _ = error "You can't use export specification."
-- Disable all import declaration
isCoreImportDecl :: [ImportDecl] -> Bool
isCoreImportDecl [] = True
isCoreImportDecl _ = error "You can't use import declaration."
-- Check top-level declaration
-- FunBind example: f n = n + 1
-- d: the declaration, ns: the names of top-level definition
isCoreDecl :: [String] -> [Decl] -> Bool
isCoreDecl ns = all (isCoreDecl' ns) where
isCoreDecl' ns' (PatBind _ p t r b) =
isCorePat ns' p && isCoreType t &&
isCoreRhs ns' r && isCoreBinds b
isCoreDecl' _ _ = error "You can only use pattern binding."
isCorePat :: [String] -> Pat -> Bool
isCorePat ns (PVar n) = isCoreName ns n
isCorePat _ _ = error "You can't use pattern matching."
isCoreType :: Maybe Type -> Bool
isCoreType Nothing = True
isCoreType _ = error "You can't use type signature in pattern matching."
isCoreRhs :: [String] -> Rhs -> Bool
isCoreRhs ns (UnGuardedRhs e)= isCoreExp ns e
isCoreRhs _ (GuardedRhss _) = error "You can't use guard."
isCoreExp :: [String] -> Exp -> Bool
isCoreExp ns (Var qn) = isCoreQName ns qn
isCoreExp ns (Con qn) = isCoreQName ns qn
isCoreExp _ (Lit l) = isCoreLiterial l
isCoreExp ns (InfixApp expr1 qop expr2) = isCoreExp ns expr1 && isCoreExp ns expr2 && isCoreQOp qop
isCoreExp ns (App expr1 expr2) = isCoreExp ns expr1 && isCoreExp ns expr2
isCoreExp ns (NegApp e) = isCoreExp ns e
isCoreExp ns (If expr1 expr2 e3) = isCoreExp ns expr1 && isCoreExp ns expr2 && isCoreExp ns e3
-- ns: names, vs: variables
isCoreExp ns (Lambda _ ps expr) = all (isCorePat (ns ++ vs)) ps && isCoreExp (ns ++ vs) expr
where vs = map getNameFromPat ps
isCoreExp ns (List exprs) = all (isCoreExp ns) exprs
-- LeftSection: (1+)
isCoreExp ns (LeftSection expr qop) = isCoreExp ns expr && isCoreQOp qop
-- RightSection (+1)
isCoreExp ns (RightSection qop expr) = isCoreExp ns expr && isCoreQOp qop
isCoreExp ns (Paren e) = isCoreExp ns e
isCoreExp _ _ = error (
"You can only use lambda expression," ++
"parenthesis, list, if and operator defined in core-haskell." )
isCoreQOp :: QOp -> Bool
isCoreQOp (QVarOp qn) = isCoreQName [] qn
isCoreQOp (QConOp qn) = isCoreQName [] qn
isCoreBinds :: Binds -> Bool
isCoreBinds (BDecls []) = True
isCoreBinds (IPBinds []) = True
isCoreBinds _ = error "You can't use let or where clause"
{-
core function
-}
isCoreQName :: [String] -> QName -> Bool
isCoreQName _ (Qual _ _) = error "You can't import module"
isCoreQName qns (UnQual n) = isCoreName qns n
isCoreQName _ (Special s) = isCoreSpecialCon s
isCoreName :: [String] -> Name -> Bool
isCoreName ns (Ident s) = s `elem`
["div", "mod", "not", "head", "tail", "False", "True"] ++ ns
-- H1-simple.pdf
++ ["null", "length", "elem"]
-- H2-map_filter.pdf
++ ["map"]
-- H3-foldr.pdf
++ ["foldr", "foldl", "and", "all", "any", "elem", "sum", "filter"]
-- H4-ListUtils
++ ["drop", "take", "dropWhile", "zip", "zipWith"]
-- H6-Accumulators
++ ["max", "min"]
|| error ("You can't use " ++ show s)
isCoreName ns (Symbol s) = s `elem`
["+", "-", "*", "&&", "||", "==", "/=", "<=", ">=", "<", ">"] ++ ns
|| error ("You can't use " ++ show s)
-- Get name from top level definitions
getNames :: Module -> [String]
getNames (Module _ _ _ _ _ _ ds) = map getName ds
where
getName (PatBind _ p _ _ _) = getNameFromPat p
getName _ = error "You can only use pattern binding"
getNameFromPat :: Pat -> String
getNameFromPat (PVar (Ident n)) = n
getNameFromPat (PVar (Symbol n)) = n
getNameFromPat _ = error "You can't use pattern matching."
isCoreSpecialCon :: SpecialCon -> Bool
isCoreSpecialCon (Cons) = True
isCoreSpecialCon _ = error "You can't use () [] -> , #,# # #"
isCoreLiterial :: Literal -> Bool
isCoreLiterial (Char _) = True
isCoreLiterial (String _) = True
isCoreLiterial (Int _) = True
isCoreLiterial _ = error "You can only use Char String Int"
-- Check the whole module/file
isCoreModule :: [String] -> Module -> Bool
isCoreModule ns (Module _ mn mp wt es im ds) =
isCoreModuleName mn && isCoreModulePragma mp &&
isCoreWarningText wt && isCoreExportSpec es &&
isCoreImportDecl im && isCoreDecl ns ds
-- Check single expression whithin interpreter
-- Can't do any definiton by default: let where a=x <- etc
isCoreExpression :: [String] -> String -> Bool
isCoreExpression ns expr = isCoreExp ns (fromParseResult (parseExp expr))
-- isCoreDecl [fromParseResult (parseDecl expr)]
getModule :: String -> IO Module
getModule mPath = do
src <- readFile mPath
--src <- readFile "./hello_world.hs"
return . fromParseResult $ parseModule src
--below just used for test the parse result of haskell-src-ext
--printAlldecl :: Module -> IO()
--printAlldecl (Module _ _ _ _ _ _ ds) = mapM_ print ds
--main :: IO ()
--main = do
-- m <- getModule "Hello.hs"
-- print m
--printAlldecl m
--print (isCoreModule m)