Added a few syntaxes for arbitrary-base number literals
Getty Ritter
9 years ago
| 22 | 22 | , signedDozNumber |
| 23 | 23 | , hexNumber |
| 24 | 24 | , signedHexNumber |
| 25 | -- ** Numeric Literals for Arbitrary Bases | |
| 26 | , commonLispNumberAnyBase | |
| 27 | , gnuM4NumberAnyBase | |
| 25 | 28 | ) where |
| 26 | 29 | |
| 27 | 30 | #if !MIN_VERSION_base(4,8,0) |
| 28 | 31 | import Control.Applicative hiding ((<|>), many) |
| 29 | 32 | #endif |
| 33 | import Control.Monad (guard) | |
| 30 | 34 | import Data.Char |
| 31 | 35 | import Data.Text (Text) |
| 32 | 36 | import qualified Data.Text as T |
| 215 | 219 | number base digits = foldl go 0 <$> many1 digits |
| 216 | 220 | where go x d = base * x + toInteger (value d) |
| 217 | 221 | 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') | |
| 224 | 224 | | c >= '0' && c <= '9' = fromEnum c - fromEnum '0' |
| 225 | 225 | | c == '\x218a' = 0xa |
| 226 | 226 | | c == '\x218b' = 0xb |
| 227 | 227 | | 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) | |
| 228 | 260 | |
| 229 | 261 | sign :: Num a => Parser (a -> a) |
| 230 | 262 | sign = (pure id <* char '+') |