packages feed

futhark-0.12.2: unittests/Futhark/Analysis/ScalExpTests.hs

{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Futhark.Analysis.ScalExpTests
  ( tests
  , parseScalExp
  )
where

import Test.Tasty

import Control.Applicative
import Control.Monad.State
import qualified Data.Map as M
import Data.Void
import Text.Megaparsec hiding (token, (<|>), many, State)
import Control.Monad.Combinators.Expr
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L

import Futhark.Analysis.ScalExp
import Futhark.Representation.AST hiding (constant, SDiv)

tests :: TestTree
tests = testGroup "ScalExpTests" []

parseScalExp :: M.Map String (Int, Type) -> String -> ScalExp
parseScalExp m s = case evalState (runParserT expr ("string: " ++ s) s) (0, m) of
  Left err -> error $ show err
  Right e  -> e

type ParserState = (Int, M.Map String (Int, Type))
type Parser = ParsecT Void String (State ParserState)

newVar :: String -> Type -> Parser Ident
newVar s t = do (x, m) <- lift get
                case M.lookup s m of
                  Just _ -> fail $ "Variable " ++ s ++ " double-declared."
                  Nothing -> do lift $ put (x+1, M.insert s (x,t) m)
                                return $ Ident (VName (nameFromString s) x) t

knownVar :: String -> Parser Ident
knownVar s = do (_, m) <- lift get
                case M.lookup s m of
                  Just (y,t) -> return $ Ident (VName (nameFromString s) y) t
                  Nothing -> fail $ "Undeclared variable " ++ s

token :: String -> Parser ()
token = L.lexeme space . void . string

parens :: Parser a -> Parser a
parens = between (token "(") (token ")")

identifier :: Parser Ident
identifier = do s <- (:) <$> letterChar <*> many alphaNumChar
                varDecl s <|> knownVar s
  where varDecl s = do
          t <- parens $
               (token "int" >> pure (Prim $ IntType Int32)) <|>
               (token "float32" >> pure (Prim $ FloatType Float32)) <|>
               (token "float64" >> pure (Prim $ FloatType Float64)) <|>
               (token "bool" >> pure (Prim Bool))
          newVar s t

constant :: Parser ScalExp
constant = (token "True" >> pure (Val $ BoolValue True)) <|>
           (token "False" >> pure (Val $ BoolValue True)) <|>
           (Val . IntValue . Int32Value <$> integer)
  where integer = L.lexeme space (L.signed space L.decimal)

expr :: Parser ScalExp
expr = makeExprParser prim operators

prim :: Parser ScalExp
prim = parens expr <|>
       constant <|>
       maxapp <|>
       minapp <|>
       (scalExpId <$> identifier)
  where maxapp = token "max" >> MaxMin False <$> parens (expr `sepBy` comma)
        minapp = token "min" >> MaxMin True <$> parens (expr `sepBy` comma)
        comma = token ","
        scalExpId (Ident name (Prim t)) = Id name t
        scalExpId (Ident name t) = error $
                                   pretty name ++ " is of type " ++ pretty t ++
                                   " but supposed to be a ScalExp."

operators :: [[Operator Parser ScalExp]]
operators = [ [Prefix (token "-"   >> return SNeg)]
            , [InfixL (token "*"   >> return STimes)]
            , [InfixL (token "pow" >> return SPow)]
            , [InfixL (token "/"   >> return SDiv)]
            , [InfixL (token "+"   >> return SPlus)]
            , [InfixL (token "-"   >> return SMinus)]
            , [InfixL (token "<="  >> return leq)]
            , [InfixL (token "<"   >> return lth)]
            , [InfixL (token ">="  >> return (flip leq))]
            , [InfixL (token ">"   >> return (flip lth))]
            , [InfixL (token "&&"  >> return SLogAnd)]
            , [InfixL (token "||"  >> return SLogOr)]
            ]
  where leq x y =
          RelExp LEQ0 $ x `SMinus` y
        lth x y =
          RelExp LTH0 $ x `SMinus` y