Added a few syntaxes for arbitrary-base number literals
Getty Ritter
8 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 '+') |