futhark-0.18.6: src/Futhark/Test/Values/Parser.hs
{-# LANGUAGE OverloadedStrings #-}
-- | Megaparsec-based parser for primitive 'Value's. The difference
-- between this and the parser defined in "Futhark.Test.Values" is
-- that we don't try to handle both the textual and binary format -
-- only the former. On the other hand, this parser has (much) better
-- error messages and can be easily used by other parsers (like the
-- ones for FutharkScript or test blocks.
module Futhark.Test.Values.Parser
( parsePrimType,
parseType,
parsePrimValue,
parseValue,
)
where
import Control.Monad.Except
import Data.Functor
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Vector.Storable as SVec
import Data.Void
import Futhark.Test.Values
import qualified Language.Futhark.Syntax as F
import Text.Megaparsec
import Text.Megaparsec.Char.Lexer
( binary,
decimal,
float,
hexadecimal,
signed,
)
type Parser = Parsec Void T.Text
-- | Parse the name of a primitive type. Does *not* consume any
-- trailing whitespace, nor does it permit any internal whitespace.
parsePrimType :: Parser F.PrimType
parsePrimType =
choice
[ "i8" $> F.Signed F.Int8,
"i16" $> F.Signed F.Int16,
"i32" $> F.Signed F.Int32,
"i64" $> F.Signed F.Int64,
"u8" $> F.Unsigned F.Int8,
"u16" $> F.Unsigned F.Int16,
"u32" $> F.Unsigned F.Int32,
"u64" $> F.Unsigned F.Int64,
"f32" $> F.FloatType F.Float32,
"f64" $> F.FloatType F.Float64,
"bool" $> F.Bool
]
parseInteger :: Parser Integer
parseInteger =
signed (pure ()) $
choice
[ "0b" *> binary,
"0x" *> hexadecimal,
decimal
]
parseIntConst :: Parser F.PrimValue
parseIntConst = do
x <- parseInteger
notFollowedBy $ "f32" <|> "f64" <|> "."
choice
[ signedV F.Int8Value x "i8",
signedV F.Int16Value x "i16",
signedV F.Int32Value x "i32",
signedV F.Int64Value x "i64",
unsignedV F.Int8Value x "u8",
unsignedV F.Int16Value x "u16",
unsignedV F.Int32Value x "u32",
unsignedV F.Int64Value x "u64",
signedV F.Int32Value x ""
]
where
signedV mk x suffix =
suffix $> F.SignedValue (mk (fromInteger x))
unsignedV mk x suffix =
suffix $> F.UnsignedValue (mk (fromInteger x))
parseFloatConst :: Parser F.PrimValue
parseFloatConst =
choice
[ "f32.nan" $> F.FloatValue (F.Float32Value (0 / 0)),
"f64.nan" $> F.FloatValue (F.Float64Value (0 / 0)),
"f32.inf" $> F.FloatValue (F.Float32Value (1 / 0)),
"f64.inf" $> F.FloatValue (F.Float64Value (1 / 0)),
"-f32.inf" $> F.FloatValue (F.Float32Value (-1 / 0)),
"-f64.inf" $> F.FloatValue (F.Float64Value (-1 / 0)),
numeric
]
where
numeric = do
x <-
signed (pure ()) $ choice [try float, fromInteger <$> decimal]
choice
[ floatV F.Float32Value x "f32",
floatV F.Float64Value x "f64",
floatV F.Float64Value x ""
]
floatV mk x suffix =
suffix $> F.FloatValue (mk (realToFrac (x :: Double)))
-- | Parse a primitive value. Does *not* consume any trailing
-- whitespace, nor does it permit any internal whitespace.
parsePrimValue :: Parser F.PrimValue
parsePrimValue =
choice
[ try parseIntConst,
parseFloatConst,
"true" $> F.BoolValue True,
"false" $> F.BoolValue False
]
lexeme :: Parser () -> Parser a -> Parser a
lexeme sep p = p <* sep
inBrackets :: Parser () -> Parser a -> Parser a
inBrackets sep = between (lexeme sep "[") (lexeme sep "]")
-- | Parse a type. Does *not* consume any trailing whitespace, nor
-- does it permit any internal whitespace.
parseType :: Parser ValueType
parseType = ValueType <$> many parseDim <*> parsePrimType
where
parseDim = fromInteger <$> ("[" *> parseInteger <* "]")
parseEmpty :: Parser Value
parseEmpty = do
ValueType dims t <- parseType
unless (product dims == 0) $ fail "Expected at least one empty dimension"
pure $ case t of
F.Signed F.Int8 -> Int8Value (SVec.fromList dims) mempty
F.Signed F.Int16 -> Int16Value (SVec.fromList dims) mempty
F.Signed F.Int32 -> Int32Value (SVec.fromList dims) mempty
F.Signed F.Int64 -> Int64Value (SVec.fromList dims) mempty
F.Unsigned F.Int8 -> Word8Value (SVec.fromList dims) mempty
F.Unsigned F.Int16 -> Word16Value (SVec.fromList dims) mempty
F.Unsigned F.Int32 -> Word32Value (SVec.fromList dims) mempty
F.Unsigned F.Int64 -> Word64Value (SVec.fromList dims) mempty
F.FloatType F.Float32 -> Float32Value (SVec.fromList dims) mempty
F.FloatType F.Float64 -> Float64Value (SVec.fromList dims) mempty
F.Bool -> BoolValue (SVec.fromList dims) mempty
-- | Parse a value, given a post-lexeme parser for whitespace.
parseValue :: Parser () -> Parser Value
parseValue sep =
choice
[ putValue' $ lexeme sep parsePrimValue,
putValue' $ inBrackets sep (parseValue sep `sepBy` lexeme sep ","),
lexeme sep $ "empty(" *> parseEmpty <* ")"
]
where
putValue' :: PutValue v => Parser v -> Parser Value
putValue' p = do
o <- getOffset
x <- p
case putValue x of
Nothing ->
parseError . FancyError o . S.singleton $
ErrorFail "array is irregular or has elements of multiple types."
Just v ->
pure v