add float literal parsers

This commit is contained in:
Ilya Rezvov
2018-01-19 10:08:43 -08:00
parent 07c55cea4d
commit 4853e1bdf5
2 changed files with 2583 additions and 3 deletions
+65 -3
View File
@@ -34,6 +34,16 @@ $doublequote = \"
@num = $digit (\_? $digit*)
@hexnum = $hexdigit (\_? $hexdigit*)
@id = "$" $idchar+
@floatfrac = @num "." (@num)?
@exp = [Ee] $sign? @num
@scientificint = @num @exp
@scientificfloat = @floatfrac @exp
@float = @floatfrac | @scientificint | @scientificfloat
@hexfloatfrac = "0x" @hexnum "." (@hexnum)?
@hexexp = [Pp] $sign? @num
@hexscientificint = "0x" @hexnum @exp
@hexscientificfloat = @hexfloatfrac @hexexp
@hexfloat = @hexfloatfrac | @hexscientificint | @hexscientificfloat
tokens :-
@@ -45,6 +55,8 @@ tokens :-
<0> ")" { constToken TCloseBracket }
<0> $sign? @num { parseDecimalSignedInt }
<0> $sign? "0x" @hexnum { parseHexalSignedInt }
<0> $sign? @float { parseDecFloat }
<0> $sign? @hexfloat { parseHexFloat }
<0, blockComment> @startblockcomment { startBlockComment }
<blockComment> [.\n] ;
<blockComment> @endblockcomment { endBlockComment }
@@ -75,7 +87,7 @@ isAllowedStringChar _userState (_pos, _rest, inp, _) _len _nextInp =
let code = Char.ord char in
code >= 0x20 && code /= 0x7f && char /= '"' && char /= '\\'
parseSign :: LBS.ByteString -> ((Integer -> Integer), Int64)
parseSign :: (Num a) => LBS.ByteString -> ((a -> a), Int64)
parseSign str =
let Just (ch, _) = LBSUtf8.decode str in
case ch of
@@ -83,6 +95,9 @@ parseSign str =
'+' -> (abs, 1)
otherwise -> (abs, 0)
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Integer -> Integer), Int64) #-}
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Double -> Double), Int64) #-}
parseHexalSignedInt :: AlexAction Lexeme
parseHexalSignedInt = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in
@@ -95,6 +110,17 @@ parseDecimalSignedInt = token $ \(pos, _, s, _) len ->
let num = readDecFromPrefix (len - slen) $ LBSUtf8.drop slen s in
Lexeme pos $ TIntLit $ sign num
parseDecFloat :: AlexAction Lexeme
parseDecFloat = token $ \(pos, _, s, _) len ->
let str = filter (/= '_') $ takeChars len s in
Lexeme pos $ TFloatLit $ read str
parseHexFloat :: AlexAction Lexeme
parseHexFloat = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in
let ('0' : 'x' : str) = filter (/= '_') $ takeChars len $ LBS.drop slen s in
Lexeme pos $ TFloatLit $ readHexFloat str
startBlockComment :: AlexAction Lexeme
startBlockComment _inp _len = do
depth <- getLexerCommentDepth
@@ -222,7 +248,16 @@ addCharToLexerStringValue c = Alex $ \s ->
alexEOF = return $ Lexeme (error "Trying to read EOF position") EOF
readHexFromChar :: Char -> Integer
takeChars :: Int64 -> LBS.ByteString -> String
takeChars n str = reverse $ go n str []
where
go :: Int64 -> LBS.ByteString -> String -> String
go 0 _ acc = acc
go n str acc = case LBSUtf8.uncons str of
Just (c, rest) -> go (n - 1) rest (c : acc)
Nothing -> acc
readHexFromChar :: (Num a) => Char -> a
readHexFromChar chr =
case chr of
'0' -> 0
@@ -249,10 +284,13 @@ readHexFromChar chr =
'f' -> 15
otherwise -> 0
{-# SPECIALIZE readHexFromChar :: Char -> Integer #-}
{-# SPECIALIZE readHexFromChar :: Char -> Double #-}
readFromPrefix :: Int -> Int64 -> LBS.ByteString -> Integer
readFromPrefix base n bstr
| base <= 16 =
let str = filter (/= '_') $ LBSUtf8.toString $ LBSUtf8.take n bstr in
let str = filter (/= '_') $ takeChars n bstr in
let len = length str in
sum $ zipWith (\i c -> readHexFromChar c * (fromIntegral base ^ fromIntegral (len - i))) [1..] str
| otherwise = error "base has to be less than or equal 16"
@@ -263,6 +301,30 @@ readHexFromPrefix = readFromPrefix 16
readDecFromPrefix :: Int64 -> LBS.ByteString -> Integer
readDecFromPrefix = readFromPrefix 10
splitBy :: (Char -> Bool) -> String -> (String, String)
splitBy pred str =
case break pred str of
(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 expStr = 2 * read expStr
readHexFrac :: String -> Double
readHexFrac [] = 0
readHexFrac val =
let len = length val in
sum $ zipWith (\i c -> readHexFromChar c / (16 ^ len - i)) [1..] val
scanner :: LBS.ByteString -> Either String [Lexeme]
scanner str = runAlex str loop
where
File diff suppressed because it is too large Load Diff