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 | ||||||
| @ -527,53 +526,65 @@ fixedlotpricep = | |||||||
| -- | -- | ||||||
| numberp :: TextParser m (Quantity, Int, Maybe Char, Maybe DigitGroupStyle) | numberp :: TextParser m (Quantity, Int, Maybe Char, Maybe DigitGroupStyle) | ||||||
| numberp = do | numberp = do | ||||||
|   -- a number is an optional sign followed by a sequence of digits possibly |     -- a number is an optional sign followed by a sequence of digits possibly | ||||||
|   -- 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 |  | ||||||
|               let (s:ss, d) = (init puncparts, last puncparts)  -- the leftmost is a separator and the rightmost may be a decimal point |  | ||||||
|               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 |                 (firstSep, digitGroups, Nothing) -> (firstSep, digitGroups, Nothing, []) | ||||||
|   let (intparts',fracparts') = span ((/= mdecimalpoint) . Just . head) parts |                 (firstSep, digitGroups, Just (d, frac)) -> (firstSep, digitGroups, Just d, frac) | ||||||
|       (intparts, fracpart) = (filter numeric intparts', filter numeric fracparts') | 
 | ||||||
|       groupsizes = reverse $ case map length intparts of |     -- get the digit group sizes and digit group style if any | ||||||
|  |     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 | rawnumberp :: TextParser m (Maybe Char, [String], Maybe (Char, String)) | ||||||
|       quantity = read $ sign++int'++"."++frac' -- this read should never fail | rawnumberp = do | ||||||
|  |     let sepChars = ['.', ','] -- all allowed punctuation characters | ||||||
|  | 
 | ||||||
|  |     (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" | ||||||
| 
 | 
 | ||||||
|   return $ dbg8 "numberp quantity,precision,mdecimalpoint,mgrps" (quantity,precision,mdecimalpoint,mgrps) |  | ||||||
|   <?> "numberp" |  | ||||||
|   where |  | ||||||
|     numeric = isNumber . headDef '_' |  | ||||||
| 
 | 
 | ||||||
| -- 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