pass assert_malformed tests

This commit is contained in:
Ilya Rezvov
2018-04-18 22:42:47 -07:00
parent 78e59dea8f
commit 1720730df5
2 changed files with 115 additions and 49 deletions
+97 -43
View File
@@ -5,7 +5,9 @@ module Language.Wasm.Lexer (
Lexeme(..),
Token(..),
AlexPosn(..),
scanner
scanner,
asFloat,
asDouble
) where
import qualified Data.ByteString.Lazy as LBS
@@ -14,8 +16,10 @@ import qualified Data.ByteString.Lazy.UTF8 as LBSUtf8
import Control.Applicative ((<$>))
import Control.Monad (when)
import Numeric.IEEE (infinity, nan)
import Language.Wasm.FloatUtils (makeNaN)
import Language.Wasm.FloatUtils (makeNaN, doubleToFloat)
import Data.Word (Word8)
import Data.List (isPrefixOf)
import Text.Read (readEither)
import qualified Debug.Trace as Debug
@@ -58,13 +62,13 @@ $doublequote = \"
tokens :-
<0> $space ;
<0> "nan" { constToken $ TFloatLit (abs nan) }
<0> "+nan" { constToken $ TFloatLit (abs nan) }
<0> "-nan" { constToken $ TFloatLit nan }
<0> "nan" { constToken $ TFloatLit $ BinRep (abs nan) }
<0> "+nan" { constToken $ TFloatLit $ BinRep (abs nan) }
<0> "-nan" { constToken $ TFloatLit $ BinRep nan }
<0> $sign? @nanhex { parseNanSigned }
<0> "inf" { constToken $ TFloatLit inf }
<0> "+inf" { constToken $ TFloatLit inf }
<0> "-inf" { constToken $ TFloatLit minusInf }
<0> "inf" { constToken $ TFloatLit $ BinRep inf }
<0> "+inf" { constToken $ TFloatLit $ BinRep inf }
<0> "-inf" { constToken $ TFloatLit $ BinRep minusInf }
<0> @keyword { tokenStr TKeyword }
<0> @linecomment ;
<0> @id { tokenStr TId }
@@ -130,7 +134,7 @@ parseNanSigned :: AlexAction Lexeme
parseNanSigned = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in
let num = readHexFromPrefix (len - 6 - slen) $ LBSUtf8.drop (6 + slen) s in
Lexeme (Just pos) $ TFloatLit $ sign $ makeNaN $ fromIntegral num
Lexeme (Just pos) $ TFloatLit $ BinRep $ sign $ makeNaN $ fromIntegral num
parseDecimalSignedInt :: AlexAction Lexeme
parseDecimalSignedInt = token $ \(pos, _, s, _) len ->
@@ -140,15 +144,86 @@ parseDecimalSignedInt = token $ \(pos, _, s, _) len ->
parseDecFloat :: AlexAction Lexeme
parseDecFloat = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in
let str = filter (/= '_') $ takeChars (len - slen) $ LBSUtf8.drop slen s in
Lexeme (Just pos) $ TFloatLit $ sign $ readDecFloat str
Lexeme (Just pos) $ TFloatLit $ DecRep $ filter (/= '_') $ takeChars len s
expAsInt :: String -> Int
expAsInt [] = 0
expAsInt ('+' : rest) = expAsInt rest
expAsInt ('-' : rest) = negate $ expAsInt rest
expAsInt str = read str
readDecFloat :: String -> Either String Float
readDecFloat str =
let (sign, rest) = case str of
('+':rest) -> (abs, rest)
('-':rest) -> (negate, rest)
rest -> (abs, rest)
in
let (val, exp) = splitBy (\c -> c == 'E' || c == 'e') rest in
let (int, frac) = splitBy (== '.') val in
let nullIfEmpty str = if null str then "0" else str in
let expInt = expAsInt $ nullIfEmpty exp in
if expInt > 38
then Left $ "constant out of range"
else fmap sign $ readEither $ nullIfEmpty int ++ "." ++ nullIfEmpty frac ++ "e" ++ nullIfEmpty exp
readDecDouble :: String -> Either String Double
readDecDouble str =
let (sign, rest) = case str of
('+':rest) -> (abs, rest)
('-':rest) -> (negate, rest)
rest -> (abs, rest)
in
let (val, exp) = splitBy (\c -> c == 'E' || c == 'e') rest in
let (int, frac) = splitBy (== '.') val in
let nullIfEmpty str = if null str then "0" else str in
let expInt = expAsInt $ nullIfEmpty exp in
if expInt > 308
then Left $ "constant out of range"
else fmap sign $ readEither $ nullIfEmpty int ++ "." ++ nullIfEmpty frac ++ "e" ++ nullIfEmpty exp
parseHexFloat :: AlexAction Lexeme
parseHexFloat = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in
let ('0' : 'x' : str) = filter (/= '_') $ takeChars (len - slen) $ LBS.drop slen s in
Lexeme (Just pos) $ TFloatLit $ sign $ readHexFloat str
Lexeme (Just pos) $ TFloatLit $ HexRep $ filter (/= '_') $ takeChars len s
readHexFloat :: Int -> String -> String -> Either String Double
readHexFloat expLimit restrictedPrefix str =
let (sign, '0':'x':rest) = case str of
('+':rest) -> (abs, rest)
('-':rest) -> (negate, rest)
rest -> (abs, rest)
in
let (val, exp) = splitBy (\c -> c == 'P' || c == 'p') rest in
let (int, frac) = splitBy (== '.') val in
let intLen = length int in
let expInt = expAsInt exp in
if int == "1" && ((restrictedPrefix `isPrefixOf` frac && expInt == expLimit - 1) || expInt >= expLimit)
then Left $ "constant out of range"
else
let intVal = sum $ zipWith (\i c -> readHexFromChar c * (16 ^ (intLen - i))) [1..] int in
Right $ sign $ (intVal + readHexFrac frac) * readHexExp exp
where
readHexExp :: String -> Double
readHexExp [] = 1
readHexExp ('+' : rest) = readHexExp rest
readHexExp ('-' : rest) = 1 / readHexExp rest
readHexExp expStr = 2 ^ read expStr
readHexFrac :: String -> Double
readHexFrac [] = 0
readHexFrac val =
let len = length val in
sum $ zipWith (\i c -> readHexFromChar c / (16 ^ i)) [1..] val
asFloat :: FloatRep -> Either String Float
asFloat (BinRep d) = Right $ doubleToFloat d
asFloat (HexRep s) = doubleToFloat <$> readHexFloat 128 "ffffff" s
asFloat (DecRep s) = readDecFloat s
asDouble :: FloatRep -> Either String Double
asDouble (BinRep d) = Right d
asDouble (HexRep s) = readHexFloat 1024 "fffffffffffff8" s
asDouble (DecRep s) = readDecDouble s
startBlockComment :: AlexAction Lexeme
startBlockComment _inp _len = do
@@ -222,9 +297,15 @@ constToken tok = token $ \(pos, _, _, _) _len -> (Lexeme (Just pos) tok)
{- End Lexem Helpers -}
data FloatRep
= BinRep Double
| DecRep String
| HexRep String
deriving (Show, Eq)
data Token = TKeyword LBS.ByteString
| TIntLit Integer
| TFloatLit Double
| TFloatLit FloatRep
| TStringLit LBS.ByteString
| TId LBS.ByteString
| TOpenBracket
@@ -341,33 +422,6 @@ splitBy pred str =
(left, (_ : rest)) -> (left, rest)
res -> res
readHexFloat :: String -> Double
readHexFloat str =
let (val, exp) = splitBy (\c -> c == 'P' || c == 'p') str in
let (int, frac) = splitBy (== '.') val in
let intLen = length int in
let intVal = sum $ zipWith (\i c -> readHexFromChar c * (16 ^ (intLen - i))) [1..] int in
(intVal + readHexFrac frac) * readHexExp exp
where
readHexExp :: String -> Double
readHexExp [] = 1
readHexExp ('+' : rest) = readHexExp rest
readHexExp ('-' : rest) = 1 / readHexExp rest
readHexExp expStr = 2 ^ read expStr
readHexFrac :: String -> Double
readHexFrac [] = 0
readHexFrac val =
let len = length val in
sum $ zipWith (\i c -> readHexFromChar c / (16 ^ i)) [1..] val
readDecFloat :: String -> Double
readDecFloat str =
let (val, exp) = splitBy (\c -> c == 'E' || c == 'e') str in
let (int, frac) = splitBy (== '.') val in
let nullIfEmpty str = if null str then "0" else str in
read $ nullIfEmpty int ++ "." ++ nullIfEmpty frac ++ "e" ++ nullIfEmpty exp
scanner :: LBS.ByteString -> Either String [Lexeme]
scanner str = runAlex str loop
where
+18 -6
View File
@@ -73,7 +73,7 @@ import Control.Monad (guard, foldM)
import Numeric.Natural (Natural)
import Data.Word (Word32, Word64)
import Data.Bits ((.|.))
import Numeric.IEEE (infinity, nan)
import Numeric.IEEE (infinity, nan, maxFinite)
import Language.Wasm.FloatUtils (doubleToFloat)
import Control.DeepSeq (NFData)
import GHC.Generics (Generic)
@@ -91,7 +91,9 @@ import Language.Wasm.Lexer (
EOF
),
Lexeme(..),
AlexPosn(..)
AlexPosn(..),
asFloat,
asDouble
)
import Debug.Trace as Debug
@@ -401,12 +403,22 @@ int64 :: { Integer }
}
float32 :: { Float }
: int { fromIntegral $1 }
| f64 { asFloat32 $1 }
: int {%
let maxInt = 340282356779733623858607532500980858880 in
if $1 <= maxInt && $1 >= -maxInt
then return $ fromIntegral $1
else Left "constant out of range"
}
| f64 {% asFloat $1 }
float64 :: { Double }
: int { fromIntegral $1 }
| f64 { $1 }
: int {%
let maxInt = round (maxFinite :: Double) in
if $1 <= maxInt && $1 >= -maxInt
then return $ fromIntegral $1
else Left "constant out of range"
}
| f64 {% asDouble $1 }
plaininstr :: { PlainInstr }
-- control instructions