packages feed

kdl-hs-0.2.0: src/KDL/Parser/Hustle/Internal.hs

{- FOURMOLU_DISABLE -}
{-# LANGUAGE OverloadedStrings #-}

{- | Vendered from https://github.com/fuzzypixelz/hustle -}
module KDL.Parser.Hustle.Internal where

import           Control.Monad                  ( void )
import           Data.Char                      ( digitToInt
                                                , isDigit
                                                )
import           Data.Either                    ( isRight )
import           Data.Scientific                ( Scientific )
import qualified Data.Scientific               as Sci
import           Data.Text                      ( Text )
import qualified Data.Text                     as T
import           KDL.Parser.Hustle.Types   ( Parser )
import           Text.Megaparsec                ( (<|>)
                                                , MonadParsec
                                                  ( eof
                                                  , takeWhileP
                                                  , try
                                                  )
                                                , option
                                                , runParser
                                                , satisfy
                                                )
import           Text.Megaparsec.Char           ( char
                                                , char'
                                                , digitChar
                                                , newline
                                                )
import qualified Text.Megaparsec.Char.Lexer    as L

signed :: Num a => Parser a -> Parser a
signed p = option id sign <*> p
  where sign = (id <$ char '+') <|> (negate <$ char '-')

lineComment :: Parser ()
lineComment = L.skipLineComment "//" >> (void newline <|> eof)

blockComment :: Parser ()
blockComment = L.skipBlockCommentNested "/*" "*/"

isBinDigit :: Char -> Bool
isBinDigit c = c `elem` ['0', '1']

data SP = SP Integer Int

number :: Integer -> (Char -> Bool) -> Parser Integer
number b isNumDigit = mkNum . T.filter (/= '_') <$> digits
 where
  mkNum = T.foldl' step 0
  step a c = a * b + fromIntegral (digitToInt c)
  digits = T.cons <$> satisfy isNumDigit <*> takeWhileP
    (Just "digit")
    (\c -> isNumDigit c || c == '_')

decimal_ :: Parser Integer
decimal_ = number 10 isDigit

scientific_ :: Parser Scientific
scientific_ = do
  c'      <- decimal_
  SP c e' <- dotDecimal_ c'
  e       <- option e' (try $ exponent_ e')
  return (Sci.scientific c e)

dotDecimal_ :: Integer -> Parser SP
dotDecimal_ c' = do
  void (char '.')
  let digits = T.cons <$> digitChar <*> takeWhileP
        (Just "digit")
        (\c -> isDigit c || c == '_')
  let mkNum = T.foldl' step (SP c' 0)
      step (SP a e') c = SP (a * 10 + fromIntegral (digitToInt c)) (e' - 1)
  mkNum . T.filter (/= '_') <$> digits

exponent_ :: Int -> Parser Int
exponent_ e' = do
  void (char' 'e')
  (+ e') <$> L.signed (return ()) (fromIntegral <$> decimal_)

match :: Parser a -> Text -> Bool
match p t = isRight $ runParser (p >> eof) "" t

escChar :: Char -> Text
escChar c = case c of
  '\x08' -> "\\b"
  '\x09' -> "\\t"
  '\x0A' -> "\\n"
  '\x0C' -> "\\f"
  '\x0D' -> "\\r"
  '\x22' -> "\\\""
  '\x2F' -> "\\/"
  '\x5C' -> "\\\\"
  _      -> T.singleton c