gdritter repos s-cargot / 7bd9840
Added a few syntaxes for arbitrary-base number literals Getty Ritter 7 years ago
1 changed file(s) with 38 addition(s) and 6 deletion(s). Collapse all Expand all
2222 , signedDozNumber
2323 , hexNumber
2424 , signedHexNumber
25 -- ** Numeric Literals for Arbitrary Bases
26 , commonLispNumberAnyBase
27 , gnuM4NumberAnyBase
2528 ) where
2629
2730 #if !MIN_VERSION_base(4,8,0)
2831 import Control.Applicative hiding ((<|>), many)
2932 #endif
33 import Control.Monad (guard)
3034 import Data.Char
3135 import Data.Text (Text)
3236 import qualified Data.Text as T
215219 number base digits = foldl go 0 <$> many1 digits
216220 where go x d = base * x + toInteger (value d)
217221 value c
218 | c == 'a' || c == 'A' = 0xa
219 | c == 'b' || c == 'B' = 0xb
220 | c == 'c' || c == 'C' = 0xc
221 | c == 'd' || c == 'D' = 0xd
222 | c == 'e' || c == 'E' = 0xe
223 | c == 'f' || c == 'F' = 0xf
222 | c >= 'a' && c <= 'z' = 0xa + (fromEnum c - fromEnum 'a')
223 | c >= 'A' && c <= 'Z' = 0xa + (fromEnum c - fromEnum 'A')
224224 | c >= '0' && c <= '9' = fromEnum c - fromEnum '0'
225225 | c == '\x218a' = 0xa
226226 | c == '\x218b' = 0xb
227227 | otherwise = error ("Unknown letter in number: " ++ show c)
228
229 digitsFor :: Int -> [Char]
230 digitsFor n
231 | n <= 10 = take n ['0'..'9']
232 | n <= 36 = take (n-10) ['A'..'Z'] ++ take (n-10) ['a'..'z'] ++ ['0'..'9']
233 | otherwise = error ("Invalid base for parser: " ++ show n)
234
235 anyBase :: Integer -> Parser Integer
236 anyBase n = number n (oneOf (digitsFor (fromIntegral n)))
237
238 -- | A parser for Common Lisp's arbitrary-base number syntax, of
239 -- the form @#[base]r[number]@, where the base is given in
240 -- decimal. Note that this syntax begins with a @#@, which
241 -- means it might conflict with defined reader macros.
242 commonLispNumberAnyBase :: Parser Integer
243 commonLispNumberAnyBase = do
244 _ <- char '#'
245 n <- decNumber
246 guard (n >= 2 && n <= 36)
247 _ <- char 'r'
248 signed (anyBase n)
249
250 -- | A parser for GNU m4's arbitrary-base number syntax, of
251 -- the form @0r[base]:[number]@, where the base is given in
252 -- decimal.
253 gnuM4NumberAnyBase :: Parser Integer
254 gnuM4NumberAnyBase = do
255 _ <- string "0r"
256 n <- decNumber
257 guard (n >= 2 && n <= 36)
258 _ <- char ':'
259 signed (anyBase n)
228260
229261 sign :: Num a => Parser (a -> a)
230262 sign = (pure id <* char '+')