journal: factor out rawnumberp
This commit is contained in:
parent
bcf7a1add5
commit
dafdaec1ca
@ -23,7 +23,6 @@ import Prelude.Compat hiding (readFile)
|
|||||||
import Control.Monad.Compat
|
import Control.Monad.Compat
|
||||||
import Control.Monad.Except (ExceptT(..), runExceptT, throwError) --, catchError)
|
import Control.Monad.Except (ExceptT(..), runExceptT, throwError) --, catchError)
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
import Data.Char (isNumber)
|
|
||||||
import Data.Data
|
import Data.Data
|
||||||
import Data.Default
|
import Data.Default
|
||||||
import Data.Functor.Identity
|
import Data.Functor.Identity
|
||||||
@ -531,49 +530,61 @@ numberp = do
|
|||||||
-- interspersed with periods, commas, or both
|
-- interspersed with periods, commas, or both
|
||||||
-- ptrace "numberp"
|
-- ptrace "numberp"
|
||||||
sign <- signp
|
sign <- signp
|
||||||
parts <- some $ choice' [some digitChar, some $ oneOf ['.', ',']]
|
raw <- rawnumberp
|
||||||
dbg8 "numberp parsed" (sign,parts) `seq` return ()
|
dbg8 "numberp parsed" raw `seq` return ()
|
||||||
|
return $ dbg8 "numberp quantity,precision,mdecimalpoint,mgrps" (fromRawNumber (sign == "-") raw)
|
||||||
|
<?> "numberp"
|
||||||
|
|
||||||
-- check the number is well-formed and identify the decimal point and digit
|
fromRawNumber :: Bool -> (Maybe Char, [String], Maybe (Char, String)) -> (Quantity, Int, Maybe Char, Maybe DigitGroupStyle)
|
||||||
-- group separator characters used, if any
|
fromRawNumber negated raw = (quantity, precision, mdecimalpoint, mgrps) where
|
||||||
let (numparts, puncparts) = partition numeric parts
|
-- unpack with a hint if useful
|
||||||
(ok, mdecimalpoint, mseparator) =
|
(mseparator, intparts, mdecimalpoint, frac) =
|
||||||
case (numparts, puncparts) of
|
case raw of
|
||||||
([],_) -> (False, Nothing, Nothing) -- no digits, not ok
|
-- just a single punctuation between two digits groups, assume it's a decimal point
|
||||||
(_,[]) -> (True, Nothing, Nothing) -- digits with no punctuation, ok
|
(Just s, [firstGroup, lastGroup], Nothing)
|
||||||
(_,[[d]]) -> (True, Just d, Nothing) -- just a single punctuation of length 1, assume it's a decimal point
|
-> (Nothing, [firstGroup], Just s, lastGroup)
|
||||||
(_,[_]) -> (False, Nothing, Nothing) -- a single punctuation of some other length, not ok
|
|
||||||
(_,_:_:_) -> -- two or more punctuations
|
(firstSep, digitGroups, Nothing) -> (firstSep, digitGroups, Nothing, [])
|
||||||
let (s:ss, d) = (init puncparts, last puncparts) -- the leftmost is a separator and the rightmost may be a decimal point
|
(firstSep, digitGroups, Just (d, frac)) -> (firstSep, digitGroups, Just d, frac)
|
||||||
in if any ((/=1).length) puncparts -- adjacent punctuation chars, not ok
|
|
||||||
|| any (s/=) ss -- separator chars vary, not ok
|
|
||||||
|| head parts == s -- number begins with a separator char, not ok
|
|
||||||
then (False, Nothing, Nothing)
|
|
||||||
else if s == d
|
|
||||||
then (True, Nothing, Just $ head s) -- just one kind of punctuation - must be separators
|
|
||||||
else (True, Just $ head d, Just $ head s) -- separator(s) and a decimal point
|
|
||||||
unless ok $ fail $ "number seems ill-formed: "++concat parts
|
|
||||||
|
|
||||||
-- get the digit group sizes and digit group style if any
|
-- get the digit group sizes and digit group style if any
|
||||||
let (intparts',fracparts') = span ((/= mdecimalpoint) . Just . head) parts
|
|
||||||
(intparts, fracpart) = (filter numeric intparts', filter numeric fracparts')
|
|
||||||
groupsizes = reverse $ case map length intparts of
|
groupsizes = reverse $ case map length intparts of
|
||||||
(a:b:cs) | a < b -> b:cs
|
(a:b:cs) | a < b -> b:cs
|
||||||
gs -> gs
|
gs -> gs
|
||||||
mgrps = (`DigitGroups` groupsizes) <$> mseparator
|
mgrps = (`DigitGroups` groupsizes) <$> mseparator
|
||||||
|
|
||||||
-- put the parts back together without digit group separators, get the precision and parse the value
|
-- put the parts back together without digit group separators, get the precision and parse the value
|
||||||
let int = concat $ "":intparts
|
repr = (if negated then "-" else "") ++ "0" ++ concat intparts ++ (if null frac then "" else "." ++ frac)
|
||||||
frac = concat $ "":fracpart
|
quantity = read repr
|
||||||
precision = length frac
|
precision = length frac
|
||||||
int' = if null int then "0" else int
|
|
||||||
frac' = if null frac then "0" else frac
|
|
||||||
quantity = read $ sign++int'++"."++frac' -- this read should never fail
|
|
||||||
|
|
||||||
return $ dbg8 "numberp quantity,precision,mdecimalpoint,mgrps" (quantity,precision,mdecimalpoint,mgrps)
|
rawnumberp :: TextParser m (Maybe Char, [String], Maybe (Char, String))
|
||||||
<?> "numberp"
|
rawnumberp = do
|
||||||
where
|
let sepChars = ['.', ','] -- all allowed punctuation characters
|
||||||
numeric = isNumber . headDef '_'
|
|
||||||
|
(firstSep, groups) <- option (Nothing, []) $ do
|
||||||
|
leadingDigits <- some digitChar
|
||||||
|
option (Nothing, [leadingDigits]) . try $ do
|
||||||
|
firstSep <- oneOf sepChars
|
||||||
|
groups <- some digitChar `sepBy1` char firstSep
|
||||||
|
return (Just firstSep, leadingDigits : groups)
|
||||||
|
|
||||||
|
let remSepChars = maybe sepChars (`delete` sepChars) firstSep
|
||||||
|
modifier
|
||||||
|
| null groups = fmap Just -- if no digits so far, we require at least some decimals
|
||||||
|
| otherwise = optional
|
||||||
|
|
||||||
|
extraGroup <- modifier $ do
|
||||||
|
lastSep <- oneOf remSepChars
|
||||||
|
digits <- modifier $ some digitChar -- decimal separator allowed to be without digits if had some before
|
||||||
|
return (lastSep, fromMaybe [] digits)
|
||||||
|
|
||||||
|
-- make sure we didn't leading part of mistyped number
|
||||||
|
notFollowedBy $ oneOf sepChars
|
||||||
|
|
||||||
|
return $ dbg8 "rawnumberp" (firstSep, groups, extraGroup)
|
||||||
|
<?> "rawnumberp"
|
||||||
|
|
||||||
|
|
||||||
-- test_numberp = do
|
-- test_numberp = do
|
||||||
-- let s `is` n = assertParseEqual (parseWithState mempty numberp s) n
|
-- let s `is` n = assertParseEqual (parseWithState mempty numberp s) n
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user