mima-tools/src/Mima/Parse/Common.hs

182 lines
4.7 KiB
Haskell

module Mima.Parse.Common
( Parser
-- * Character specifications
, isConnecting
, isWhitespace
-- * Basic parsers
, whitespace
, labelName
-- ** Number literals
, binDigit
, decDigit
, octDigit
, hexDigit
, binNumber
, decNumber
, octNumber
, hexNumber
, fixedWidthBin
, fixedWidthDec
, fixedWidthOct
, fixedWidthHex
-- ** MiMa types
, asWord
, asLargeValue
, asSmallValue
, fixedWidthHexAddress
) where
import Data.Char
import qualified Data.Set as Set
import qualified Data.Text as T
import Data.Void
import Text.Megaparsec
import Mima.Word
type Parser = Parsec Void T.Text
{- Character specifications -}
isConnecting :: Char -> Bool
isConnecting '_' = True
isConnecting '-' = True
isConnecting _ = False
isWhitespace :: Char -> Bool
isWhitespace '\n' = False
isWhitespace c = isSpace c
{- Basic parsers -}
whitespace :: Parser Char
whitespace = label "whitespace" $ satisfy isWhitespace
labelName :: Parser T.Text
labelName = label "label" $ do
t <- satisfy isAlpha
ts <- takeWhileP Nothing (\c -> isAlphaNum c || isConnecting c)
pure $ T.singleton t <> ts
binDigit :: (Num a) => Parser a
binDigit = label "binary digit" $ token helper Set.empty
where
helper '0' = Just 0
helper '1' = Just 1
helper _ = Nothing
octDigit :: (Num a) => Parser a
octDigit = label "octal digit" $ token helper Set.empty
where
helper '0' = Just 0
helper '1' = Just 1
helper '2' = Just 2
helper '3' = Just 3
helper '4' = Just 4
helper '5' = Just 5
helper '6' = Just 6
helper '7' = Just 7
helper _ = Nothing
decDigit :: (Num a) => Parser a
decDigit = label "decimal digit" $ token helper Set.empty
where
helper '0' = Just 0
helper '1' = Just 1
helper '2' = Just 2
helper '3' = Just 3
helper '4' = Just 4
helper '5' = Just 5
helper '6' = Just 6
helper '7' = Just 7
helper '8' = Just 8
helper '9' = Just 9
helper _ = Nothing
hexDigit :: (Num a) => Parser a
hexDigit = label "hexadecimal digit" $ token (helper . toLower) Set.empty
where
helper '0' = Just 0
helper '1' = Just 1
helper '2' = Just 2
helper '3' = Just 3
helper '4' = Just 4
helper '5' = Just 5
helper '6' = Just 6
helper '7' = Just 7
helper '8' = Just 8
helper '9' = Just 9
helper 'a' = Just 10
helper 'b' = Just 11
helper 'c' = Just 12
helper 'd' = Just 13
helper 'e' = Just 14
helper 'f' = Just 15
helper _ = Nothing
accumulateToBase :: (Integral a) => a -> [a] -> a
accumulateToBase base = helper
where
helper [] = 0
helper [d] = d
helper (d:ds) = d + base * helper ds
binNumber :: (Integral a) => Parser a
binNumber = label "binary number" $ accumulateToBase 2 <$> some binDigit
octNumber :: (Integral a) => Parser a
octNumber = label "octal number" $ accumulateToBase 8 <$> some octDigit
decNumber :: (Integral a) => Parser a
decNumber = label "decimal number" $ accumulateToBase 10 <$> some decDigit
hexNumber :: (Integral a) => Parser a
hexNumber = label "hexadecimal number" $ accumulateToBase 16 <$> some hexDigit
fixedWidthWithExponent :: (Num a) => a -> Parser a -> Int -> Parser a
fixedWidthWithExponent e digit width = do
digits <- count width digit
pure $ helper $ reverse digits
where
helper [] = 0
helper (x:xs) = x + e * helper xs
fixedWidthBin :: (Num a) => Int -> Parser a
fixedWidthBin = fixedWidthWithExponent 2 binDigit
fixedWidthOct :: (Num a) => Int -> Parser a
fixedWidthOct = fixedWidthWithExponent 8 octDigit
fixedWidthDec :: (Num a) => Int -> Parser a
fixedWidthDec = fixedWidthWithExponent 10 decDigit
fixedWidthHex :: (Num a) => Int -> Parser a
fixedWidthHex = fixedWidthWithExponent 16 hexDigit
asBoundedValue :: (Show a, Ord a) => a -> a -> Parser a -> Parser a
asBoundedValue lower upper parser =
label ("value within bounds " ++ show (lower, upper)) $ do
value <- parser
if lower <= value && value <= upper
then pure value
else empty
asWord :: Parser Integer -> Parser MimaWord
asWord parser =
let bound = fromIntegral (maxBound :: MimaWord)
in fromIntegral <$> asBoundedValue (-bound) bound parser
asLargeValue :: Parser Integer -> Parser LargeValue
asLargeValue parser =
let bound = fromIntegral (maxBound :: LargeValue)
in fromIntegral <$> asBoundedValue (-bound) bound parser
asSmallValue :: Parser Integer -> Parser SmallValue
asSmallValue parser =
let bound = fromIntegral (maxBound :: SmallValue)
in fromIntegral <$> asBoundedValue (-bound) bound parser
fixedWidthHexAddress :: Parser MimaAddress
fixedWidthHexAddress = label "fixed-width hexadecimal address"
$ asLargeValue
$ fixedWidthHex 5