syntax-0.3.0.0: Data/Syntax/Char.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{- |
Module : Data.Syntax.Char
Description : Char specific combinators.
Copyright : (c) Paweł Nowak
License : MIT
Maintainer : Paweł Nowak <pawel834@gmail.com>
Stability : experimental
Common combinators that work with sequences of chars.
-}
module Data.Syntax.Char (
SyntaxChar(..),
SyntaxText,
signed,
spaces,
spaces_,
spaces1,
endOfLine,
digitDec,
digitOct,
digitHex
) where
import Control.Lens.SemiIso
import Data.Bits
import Data.Char
import Data.MonoTraversable
import Data.Scientific (Scientific)
import Data.SemiIsoFunctor
import Data.Syntax
import Data.Syntax.Combinator
import Data.Text (Text)
-- | Syntax constrainted to sequences of chars.
--
-- Note: methods of this class do not have default implementations (for now),
-- because their code is quite ugly and already written in most parser libraries.
class (Syntax syn seq, Element seq ~ Char) => SyntaxChar syn seq where
-- | An unsigned decimal number.
decimal :: Integral a => syn a
-- | An unsigned hexadecimal number.
hexadecimal :: (Integral a, Bits a) => syn a
-- | A signed real number.
realFloat :: RealFloat a => syn a
-- | A scientific number.
scientific :: syn Scientific
{-# MINIMAL decimal, hexadecimal, realFloat, scientific #-}
-- | An useful synonym for SyntaxChars with Text sequences.
type SyntaxText syn = SyntaxChar syn Text
-- | A number with an optional leading '+' or '-' sign character.
signed :: (Real a, SyntaxChar syn seq) => syn a -> syn a
signed n = _Negative /$/ char '-' */ n
/|/ opt_ (char '+') */ n
-- | Accepts zero or more spaces. Generates a single space.
spaces :: SyntaxChar syn seq => syn ()
spaces = opt spaces1
-- | Accepts zero or more spaces. Generates no output.
spaces_ :: SyntaxChar syn seq => syn ()
spaces_ = opt_ spaces1
-- | Accepts one or more spaces. Generates a single space.
spaces1 :: SyntaxChar syn seq => syn ()
spaces1 = constant (opoint ' ') /$/ takeWhile1 isSpace
-- | Accepts a single newline. Generates a newline.
endOfLine :: SyntaxChar syn seq => syn ()
endOfLine = char '\n'
-- | A decimal digit.
digitDec :: SyntaxChar syn seq => syn Int
digitDec = semiIso toChar toInt /$/ anyChar
where toInt c | isDigit c = Right (digitToInt c)
| otherwise = Left ("Expected a decimal digit, got " ++ [c])
toChar i | i >= 0 && i <= 9 = Right (intToDigit i)
| otherwise = Left ("Expected a decimal digit, got number " ++ show i)
-- | An octal digit.
digitOct :: SyntaxChar syn seq => syn Int
digitOct = semiIso toChar toInt /$/ anyChar
where toInt c | isOctDigit c = Right (digitToInt c)
| otherwise = Left ("Expected an octal digit, got " ++ [c])
toChar i | i >= 0 && i <= 7 = Right (intToDigit i)
| otherwise = Left ("Expected an octal digit, got number " ++ show i)
-- | A hex digit.
digitHex :: SyntaxChar syn seq => syn Int
digitHex = semiIso toChar toInt /$/ anyChar
where toInt c | isHexDigit c = Right (digitToInt c)
| otherwise = Left ("Expected a hex digit, got " ++ [c])
toChar i | i >= 0 && i <= 15 = Right (intToDigit i)
| otherwise = Left ("Expected a hex digit, got number " ++ show i)