packages feed

scanf (empty) → 0.1.0.0

raw patch · 8 files changed

+393/−0 lines, 8 filesdep +basedep +scanfdep +template-haskellsetup-changed

Dependencies added: base, scanf, template-haskell

Files

+ LICENSE view
@@ -0,0 +1,19 @@+Copyright Li-yao Xia (c) 2018++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the “Software”), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,42 @@+# Haskell scanf [![Build Status](https://travis-ci.org/Lysxia/scanf.svg?branch=master)](https://travis-ci.org/Lysxia/scanf)++A lightweight library for one-off parsing and printing.++```haskell+scanf [fmt|%d lazy %s and %d strict %s|]+      "3 lazy functions and 2 strict fields"+  = Just (3 :+ "functions" :+ 2 :+ "fields" :+ ())+```++Quasiquotes are optional. Format strings can also be defined with pure+Haskell combinators.++```haskell+printf (fmt_ (int . " lazy " % string . " and " % int . " strict " % string)+       (8 :+ "dogs" :+ 9 :+ "cats" :+ ())+  = "8 lazy dogs and 9 strict cats"+```++With quasiquotes, the following conversion strings are supported:++- `%d`: signed integer (`Int`)+- `%l`: signed integer (`Integer`, unbounded)+- `%f`: floating point (`Double`)+- `%s`: string of non-space characters (`String`)+- `%c`: single character (`Char`)+- `%%`: parse/print a literal percent character++N.B.: in `scanf`, spaces in the format string match any number of whitespace+character until the next nonspace character.++---++Though it also offers a `printf` function, *scanf* is thus named as+there are only few other preexisting Haskell implementations,+whereas many Haskell *printf* libraries can already be found on Hackage.++Similar to:++- [xformat](https://hackage.haskell.org/package/xformat) (scanf and printf)+- [fmt](https://hackage.haskell.org/package/fmt) (printf)+- [formatting](https://hackage.haskell.org/package/formatting) (printf)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ scanf.cabal view
@@ -0,0 +1,46 @@+name:                scanf+version:             0.1.0.0+synopsis:            Easy and type-safe format strings for parsing and printing+description:++  A lightweight library for one-off parsing and printing.+  .+  See README.++homepage:            https://github.com/Lysxia/scanf#readme+license:             MIT+license-file:        LICENSE+author:              Li-yao Xia+maintainer:          lysxia@gmail.com+copyright:           2018 Li-yao Xia+category:            Text+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10+tested-with:         GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1++library+  hs-source-dirs:      src+  exposed-modules:+    Text.Scanf+    Text.Scanf.Internal+    Text.Scanf.TH+  build-depends:+    template-haskell,+    base >= 4.7 && < 5+  ghc-options:         -Wall+  default-language:    Haskell2010++test-suite example-test+  hs-source-dirs: test+  main-is: example.hs+  build-depends:+    scanf,+    base+  ghc-options: -Wall+  default-language: Haskell2010+  type: exitcode-stdio-1.0++source-repository head+  type:     git+  location: https://github.com/Lysxia/scanf
+ src/Text/Scanf.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE TypeOperators #-}++-- | Dead simple and type-safe @'scanf'@/@'printf'@.++module Text.Scanf+  ( -- * Main definitions+    scanf+  , printf+  , (:+)(..)+  , fmt+  , fmt_+  , Format++    -- * Format constructors+  , (%)+  , integer+  , int+  , double+  , string+  , char+  ) where++import Text.Scanf.Internal+import Text.Scanf.TH
+ src/Text/Scanf/Internal.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeOperators #-}++module Text.Scanf.Internal where++import Data.Char (isSpace)+import Data.List (stripPrefix)++-- | A pretty pair type to build lists with values of different types. +-- Remember to close lists with @()@.+--+-- @+-- 3 ':+' "14" ':+' () :: 'Int' ':+' 'String' ':+' ()+-- @+data a :+ b = a :+ b+  deriving (Eq, Ord, Show)++infixr 1 :+++-- | Typed @'scanf'@/@'printf'@ format strings.+-- They can be built using the 'Text.Scanf.fmt' quasiquote or+-- with the 'fmt_' function and format combinators.+data Format t where+  Empty :: Format ()+  Constant :: String -> Format t -> Format t+  Whitespace :: String -> Format t -> Format t+  Readable :: (Read a, Show a) => Format t -> Format (a :+ t)+  String :: Format t -> Format (String :+ t)  -- whitespace delimited+  Char :: Format t -> Format (Char :+ t)++deriving instance Show (Format t)++emptyFmt :: Format ()+emptyFmt = Empty++-- | Construct a format string. This is an alternative to 'Text.Scanf.fmt'+-- that doesn't rely on Template Haskell.+--+-- The components of a format string are composed using @('.')@ (function+-- composition) and @('%')@ (wrapper for constant strings).+--+-- @+-- 'fmt_' ('int' '.' \" lazy \" '%' 'string' '.' \" and \" '%' 'int' '.' \" strict \" '%' 'string')+--   :: 'Format' ('Int' ':+' 'String' ':+' 'Int' ':+' 'String' ':+' ())+-- @+fmt_ :: (Format () -> Format t) -> Format t+fmt_ f = f emptyFmt++-- | Append a constant string to a format string component.+--+-- N.B.: in 'scanf', spaces in the format string match any number of whitespace+-- character until the next nonspace character.+(%) :: String -> (Format t -> Format q) -> Format t -> Format q+(%) s f = constant s . f++infixr 9 %++-- | Append a constant string to a format string.+constant :: String -> Format t -> Format t+constant "" f = f+constant s@(c : _) f | isSpace c =+  let (s0, s') = span isSpace s+  in whitespace s0 (constant s' f)+constant s f =+  let (s0, s') = break isSpace s+  in constant' s0 (constant s' f)++-- | Append a constant string with no whitespace to a format string.+constant' :: String -> Format t -> Format t+constant' s (Constant s' f) = Constant (s ++ s') f+constant' "" f = f+constant' s f = Constant s f++-- | Append a constant whitespace string to a format string.+whitespace :: String -> Format t -> Format t+whitespace s (Whitespace s' f) = Whitespace (s ++ s') f+whitespace s f = Whitespace s f++readable :: (Read a, Show a) => Format t -> Format (a :+ t)+readable = Readable++-- | Format an 'Integer'.+integer :: Format t -> Format (Integer :+ t)+integer = Readable++-- | Format an 'Int'.+int :: Format t -> Format (Int :+ t)+int = Readable++-- | Format a 'Double'.+double :: Format t -> Format (Double :+ t)+double = Readable++-- | Format a 'String'.+string :: Format t -> Format (String :+ t)+string = String++-- | Format a 'Char'.+char :: Format t -> Format (Char :+ t)+char = Char++readmap :: (a -> b) -> ReadS a -> ReadS b+readmap f = (fmap . fmap) (\(r, s) -> (f r, s))++readsFormat :: Format t -> ReadS t+readsFormat (Constant z f) s = do+  Just s' <- pure (stripPrefix z s)+  readsFormat f s'+readsFormat (Readable f) s = do+  (a, s') <- readsPrec 0 s+  readmap (a :+) (readsFormat f) s'+readsFormat (String f) s = do+  let (s0, s') = break isSpace s+  readmap (s0 :+) (readsFormat f) s'+readsFormat (Char f) s = do+  c : s' <- pure s+  readmap (c :+) (readsFormat f) s'+readsFormat (Whitespace _ f) s = do+  let s' = dropWhile isSpace s+  readsFormat f s'+readsFormat Empty s = pure ((), s)++-- | Parse a string according to a format string.+--+-- @+-- 'scanf' ['Text.Scanf.fmt'|Hello %s|] \"Hello world!\" :: 'Maybe' ('String' ':+' ())+--   = (\"world!\" ':+' ())+-- @+scanf :: Format t -> String -> Maybe t+scanf f s = do+  (r, _) : _ <- pure (readsFormat f s)+  pure r++-- | Print a string according to a format string.+--+-- @+-- 'printf' ['Text.Scanf.fmt'|Hello %s|] (\"everyone!\" ':+' ())+--   = \"Hello everyone!\" +-- @+printf :: Format t -> t -> String+printf (Constant z f) t = z ++ printf f t+printf (Readable f) (a :+ t) = show a ++ printf f t+printf (String f) (s :+ t) = s ++ printf f t+printf (Char f) (c :+ t) = c : printf f t+printf (Whitespace s f) t = s ++ printf f t+printf Empty _ = ""
+ src/Text/Scanf/TH.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE TemplateHaskell #-}++module Text.Scanf.TH where++import Data.Char (isSpace)++import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Language.Haskell.TH.Quote++import Text.Scanf.Internal++formatString :: String -> Q Exp+formatString "" = [|Empty|]+formatString ('%' : s1) =+  case s1 of+    'c' : s' -> [|char $(formatString s')|]+    'd' : s' -> [|int $(formatString s')|]+    'f' : s' -> [|double $(formatString s')|]+    'l' : s' -> [|integer $(formatString s')|]+    's' : s' -> [|string $(formatString s')|]+    '%' : s' -> [|constant "%" $(formatString s')|]+    _ -> error "Invalid format string"+formatString s@(c : _) | isSpace c =+  let (s0, s') = span isSpace s+  in [|whitespace $(lift s0) $(formatString s')|]+formatString s =+  let (s0, s') = break (\c -> isSpace c || c == '%') s+  in [|constant $(lift s0) $(formatString s')|]++-- | Parse a typed 'Format' string.+--+-- The following conversion strings are supported:+--+-- - @%d@: signed integer ('Int')+-- - @%l@: signed integer ('Integer', unbounded)+-- - @%f@: floating point ('Double')+-- - @%s@: string of non-space characters ('String')+-- - @%c@: single character ('Char')+-- - @%%@: parse/print a literal percent character+--+-- N.B.: in 'scanf', spaces in the format string match any number of whitespace+-- character until the next nonspace character.+--+-- @+-- ['fmt'|%d lazy %s and %d strict %s|]+--   :: 'Format' ('Int' ':+' 'String' ':+' 'Int' ':+' 'String' ':+' ())+-- @+fmt :: QuasiQuoter+fmt = QuasiQuoter+  { quoteExp = formatString+  , quotePat = undefined+  , quoteType = undefined+  , quoteDec = undefined+  }
+ test/example.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE QuasiQuotes #-}++import Text.Scanf++main :: IO ()+main = do+  scanf [fmt|%c|] "34"+    =? Just ('3' :+ ())++  scanf [fmt|%d|] "10"+    =? Just (10 :+ ())++  scanf [fmt|%f|] "4.3"+    =? Just (4.3 :+ ())++  scanf [fmt| %l|]+        "    1000000000000000000000000000000000"+    =? Just (1000000000000000000000000000000000 :+ ())++  scanf [fmt|%s %s|] " a b"+    =? Just ("" :+ "a" :+ ())++  scanf [fmt|%%|] "%"+    =? Just ()++  scanf [fmt|a a|] "a  a b"+    =? Just ()++  let s0 = "10-11-12   Fri  4.3C"+      f0 = [fmt|%d-%d-%l %s %f %c|]+      f0' = fmt_ (int . "-" % int . "-" % integer . " " % string . " " % double . " " % char)+      r0 = Just+        ((10 :: Int) :+ (11 :: Int) :+ (12 :: Integer)+          :+ "Fri" :+ (4.3 :: Double) :+ 'C' :+ ())+  scanf f0 s0 =? r0+  scanf f0' s0 =? r0++  let s1 = "3 lazy functions and 4 strict fields"+      f1 = [fmt|%d lazy %s and %d strict %s|]+      f1' = fmt_ (int . " lazy " % string . " and " % int . " strict " % string)+      r1 = Just ((3 :: Int) :+ "functions" :+ (4 :: Int) :+ "fields" :+ ())+      p1 = (2 :: Int) :+ "dogs" :+ (3 :: Int) :+ "cats" :+ ()+      q1 = "2 lazy dogs and 3 strict cats"+  scanf f1  s1 =? r1+  scanf f1' s1 =? r1++  printf f1  p1 =? q1+  printf f1' p1 =? q1++assertEq, (=?) :: (Eq a, Show a) => a -> a -> IO ()+assertEq a b = do+  if a == b then+    return ()+  else+    fail $ show a ++ " /= " ++ show b++(=?) = assertEq+