packages feed

spreadsheet (empty) → 0.1

raw patch · 6 files changed

+366/−0 lines, 6 filesdep +basedep +explicit-exceptiondep +transformerssetup-changed

Dependencies added: base, explicit-exception, transformers, utility-ht

Files

+ LICENSE view
@@ -0,0 +1,31 @@+Copyright (c) 2009, Henning Thielemann++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * The names of contributors may not be used to endorse or promote+      products derived from this software without specific prior+      written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ spreadsheet.cabal view
@@ -0,0 +1,53 @@+Name:             spreadsheet+Version:          0.1+License:          BSD3+License-File:     LICENSE+Author:           Henning Thielemann <haskell@henning-thielemann.de>+Maintainer:       Henning Thielemann <haskell@henning-thielemann.de>+Homepage:         http://www.haskell.org/haskellwiki/Spreadsheet+Category:         Data, Text+Synopsis:         Read and write spreadsheets from and to CSV files in a lazy way+Description:+  Read and write spreadsheets from and to CSV files in a lazy way.+  See also the csv package on Hackage and+     <http://www.xoltar.org/languages/haskell.html>,+     <http://www.xoltar.org/languages/haskell/CSV.hs>.+  Both do not parse lazy.+  Reading from other source than plain 'String's could be easily added.+Tested-With:       GHC==6.8.2+Cabal-Version:     >=1.6+Build-Type:        Simple+Source-Repository head+  type:     darcs+  location: http://code.haskell.org/~thielema/spreadsheet/++Source-Repository this+  type:     darcs+  location: http://code.haskell.org/~thielema/spreadsheet/+  tag:      0.1++Flag splitBase+  description: Choose the new smaller, split-up base package.++Flag buildTests+  description: Build test executables+  default:     False++Library+  Build-Depends:+    utility-ht >=0.0.2 && <0.1,+    transformers >=0.0 && <0.1,+    explicit-exception >=0.1 && <0.2+  If flag(splitBase)+    Build-Depends: base >= 2+  Else+    Build-Depends: base >= 1.0 && < 2++  GHC-Options:      -Wall+  Extensions:       GeneralizedNewtypeDeriving+  Hs-Source-Dirs:   src+  Exposed-Modules:+    Data.Spreadsheet+  Other-Modules:+    Data.Spreadsheet.Parser+    Data.Spreadsheet.CharSource
+ src/Data/Spreadsheet.hs view
@@ -0,0 +1,129 @@+module Data.Spreadsheet (+   T,+   -- * parsing+   fromString,+   fromStringSimple,+   -- * formatting+   toString,+   toStringSimple,+   ) where++import Data.List.HT  (chop, switchR, )+import Data.List     (intersperse, )+import Data.Maybe.HT (toMaybe, )++import qualified Data.Spreadsheet.Parser as Parser+import Control.Monad.Trans.State (runState, )+import Control.Monad (liftM, mplus, )++import qualified Control.Monad.Exception.Asynchronous as Async+import qualified Data.Spreadsheet.CharSource as CharSource+++{- |+A spreadsheet is a list of lines,+each line consists of cells,+and each cell is a string.+Ideally, spreadsheets read from a CSV file+have lines with the same number of cells per line.+However, we cannot assert this,+and thus we parse the lines as they come in.+-}+type T = [[String]]++parseChar :: CharSource.C source =>+   Char -> Parser.Fallible source Char+parseChar qm =+   Parser.eitherOr+      (Parser.satisfy (qm/=))+      (Parser.string [qm,qm] >> return qm)++parseQuoted :: CharSource.C source =>+   Char -> Parser.PartialFallible source String+parseQuoted qm =+   Parser.between "missing closing quote"+      (Parser.char qm) (Parser.char qm)+      (liftM Async.pure $ Parser.many (parseChar qm))++parseUnquoted :: CharSource.C source =>+   Char -> Char -> Parser.Straight source String+parseUnquoted qm sep =+   Parser.many+      (Parser.satisfy (not . flip elem [qm,sep,'\r','\n']))++parseCell :: CharSource.C source =>+   Char -> Char -> Parser.Partial source String+parseCell qm sep =+   Parser.deflt (liftM Async.pure $ parseUnquoted qm sep) (parseQuoted qm)++parseLine :: CharSource.C source =>+   Char -> Char -> Parser.Partial source [String]+parseLine qm sep =+   Parser.sepByIncomplete (Parser.char sep) (CharSource.fallible $ parseCell qm sep)++parseLineEnd :: CharSource.C source =>+   Parser.Fallible source ()+parseLineEnd =+   (Parser.char '\r' >> (Parser.char '\n' `Parser.eitherOr` return ()))+   `Parser.eitherOr`+   Parser.char '\n'++parseLineWithEnd :: CharSource.C source =>+   Char -> Char -> Parser.Partial source [String]+parseLineWithEnd qm sep =+   Parser.terminated "line end expected" parseLineEnd $+   parseLine qm sep+++parseTable :: CharSource.C source =>+   Char -> Char -> Parser.Partial source [[String]]+parseTable qm sep =+   Parser.manyIncomplete $+{-+   CharSource.fallible $ parseLineWithEnd qm sep+-}+   CharSource.fallible CharSource.isEnd >>= \b ->+   if b then CharSource.stop else CharSource.fallible $ parseLineWithEnd qm sep++{- |+@fromString qm sep text@ parses @text@ into a spreadsheet,+using the quotation character @qm@ and the separator character @sep@.+-}+fromString :: Char -> Char -> String -> Async.Exceptional Parser.UserMessage T+fromString qm sep str =+   let (~(Async.Exceptional e table), rest) =+          runState (CharSource.runString (parseTable qm sep)) str+   in  Async.Exceptional+          (mplus e (toMaybe (not (null rest)) "junk after table")) table+++toString :: Char -> Char -> T -> String+toString qm sep =+   unlines . map (concat . intersperse [sep] . map (quote qm))++quote :: Char -> String -> String+quote qm s = qm : foldr (\c cs -> c : if c==qm then qm:cs else cs) [qm] s+-- quote qm s = [qm] ++ replace [qm] [qm,qm] s ++ [qm]+++fromStringSimple :: Char -> Char -> String -> T+fromStringSimple qm sep =+   map (map (dequoteSimple qm) . chop (sep==)) . lines++toStringSimple :: Char -> Char -> T -> String+toStringSimple qm sep =+   unlines . map (concat . intersperse [sep] . map (\s -> [qm]++s++[qm]))++dequoteSimple :: Eq a => a -> [a] -> [a]+dequoteSimple _ [] = error "dequoteSimple: string is empty"+dequoteSimple qm (x:xs) =+   if x == qm+     then error "dequoteSimple: quotation mark missing at beginning"+     else+       switchR+         (error "dequoteSimple: string consists only of a single quotation mark")+         (\ys y ->+            if y == qm+              then ys+              else (error "dequoteSimple: string does not end with a quotation mark"))+         xs
+ src/Data/Spreadsheet/CharSource.hs view
@@ -0,0 +1,40 @@+module Data.Spreadsheet.CharSource where++import qualified Control.Monad.Trans.State as State+import Control.Monad.Trans.State (StateT(StateT), gets, runStateT, mapStateT, )+import Control.Monad.Identity (Identity(Identity), runIdentity, )++import Data.List.HT (viewL, )+import Data.Tuple.HT (forcePair, )++import qualified Prelude as P+import Prelude hiding (String)+++class (Monad (m Maybe), Monad (m Identity)) => C m where+   get   :: m Maybe Char+   isEnd :: m Identity Bool+   stop  :: m Maybe a+   fallible :: m Identity a -> m Maybe a+   {- |+   Try to run a parser.+   If it succeeds, return (Just value) and advance input position.+   If it fails, return Nothing and keep the input position.+   -}+   try  :: m Maybe a -> m Identity (Maybe a)+++newtype String fail a = String {runString :: StateT P.String fail a}+   deriving (Monad)+++instance C String where+   get   = String $ StateT viewL+   isEnd = String $ gets null+   stop  = String $ StateT $ const Nothing+   fallible x = String $ mapStateT (Just . runIdentity) $ runString x+   try x = String $ StateT $ \s0 ->+      Identity $ forcePair $+      case runStateT (runString x) s0 of+         Nothing -> (Nothing, s0)+         Just (a,s1) -> (Just a, s1)
+ src/Data/Spreadsheet/Parser.hs view
@@ -0,0 +1,110 @@+module Data.Spreadsheet.Parser where++import qualified Data.Spreadsheet.CharSource as CharSource++import qualified Control.Monad.Exception.Asynchronous as Async++import Control.Monad.Identity (Identity, )+import Control.Monad (liftM, liftM2, )++import Data.Maybe (fromMaybe, )+++type T source a = source a++type Straight        source a = source Identity a+type Fallible        source a = source Maybe    a+type Partial         source a = source Identity (PossiblyIncomplete a)+type PartialFallible source a = source Maybe    (PossiblyIncomplete a)++type PossiblyIncomplete a = Async.Exceptional UserMessage a++type UserMessage = String+++satisfy ::+   (CharSource.C source) =>+   (Char -> Bool) -> Fallible source Char+satisfy p =+   do c <- CharSource.get+      if p c+        then return c+        else CharSource.stop++char :: (CharSource.C source) =>+   Char -> Fallible source ()+char c = satisfy (c==) >> return ()++string :: (CharSource.C source) =>+   String -> Fallible source ()+string s = mapM_ char s++many :: (CharSource.C source) =>+   Fallible source a -> Straight source [a]+many p =+   let go =+         liftM (fromMaybe []) $+         CharSource.try+            (liftM2 (:) p (CharSource.fallible go))+   in  go++appendIncomplete ::+   CharSource.C source =>+   PartialFallible source a ->+   Partial source [a] ->+   PartialFallible source [a]+appendIncomplete p ps =+   do ~(Async.Exceptional me x) <- p+      CharSource.fallible $ liftM (fmap (x:)) $+         maybe ps (\_ -> return (Async.Exceptional me [])) me++absorbException ::+   (CharSource.C source) =>+   PartialFallible source [a] ->+   Partial source [a]+absorbException =+   liftM (fromMaybe (Async.pure [])) .+   CharSource.try++manyIncomplete :: CharSource.C source =>+   PartialFallible source a -> Partial source [a]+manyIncomplete p =+   let go = absorbException (appendIncomplete p go)+   in  go++sepByIncomplete :: CharSource.C source =>+   Fallible source sep -> PartialFallible source a -> Partial source [a]+sepByIncomplete sep p =+   absorbException $+   appendIncomplete p $+   manyIncomplete (sep >> p)++between :: (CharSource.C source) =>+   UserMessage ->+   Fallible source open -> Fallible source close ->+   Partial source a -> PartialFallible source a+between msg open close p =+   open >>+   CharSource.fallible (terminated msg close p)++terminated :: (CharSource.C source) =>+   UserMessage ->+   Fallible source close ->+   Partial source a -> Partial source a+terminated msg close p =+   do enclosed <- p+      term <- CharSource.try close+      return (enclosed `Async.maybeAbort`+              maybe (Just msg) (const Nothing) term)+++-- mplus+eitherOr :: (CharSource.C source) =>+   Fallible source a -> Fallible source a -> Fallible source a+eitherOr x y =+   CharSource.fallible (CharSource.try x) >>= maybe y return++deflt :: (CharSource.C source) =>+   Straight source a -> Fallible source a -> Straight source a+deflt x y =+   maybe x return =<< CharSource.try y