packages feed

attoparsec-arff (empty) → 0.0

raw patch · 4 files changed

+198/−0 lines, 4 filesdep +attoparsecdep +basedep +bytestringsetup-changed

Dependencies added: attoparsec, base, bytestring

Files

+ LICENSE view
@@ -0,0 +1,17 @@+attoparsec-arff: an ARFF file parser using attoparsec+Copyright (C) 2012 statusfailed.com++This program is free software; you can redistribute it and/or+modify it under the terms of the GNU General Public License+as published by the Free Software Foundation; either version 2+of the License, or (at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program; if not, write to the Free Software+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.+
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain
+ attoparsec-arff.cabal view
@@ -0,0 +1,20 @@+Name:                attoparsec-arff+Version:             0.0+Synopsis:            An ARFF file parser using Attoparsec+Description:         Parse ARFF files+Category:            Data, Text, AI+License:             GPL-2+License-file:        LICENSE+Build-Type:          Simple+Cabal-version:       >= 1.6+Author:              Paul Wilson+Maintainer:          Paul Wilson <paul@statusfailed.com>++Library+  Build-Depends:     base >= 4 && < 5, attoparsec >= 0.10.1.0, bytestring >= 0.9.1.10+  Hs-Source-Dirs:    src+  Exposed-modules:   Text.ARFF++source-repository head+  type:              git+  location:          git://github.com/statusfailed/attoparsec-arff.git
+ src/Text/ARFF.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE OverloadedStrings #-}+module Text.ARFF where+{-+  ( identifier+  , comment+  , relation+  , attributeType+  , attribute+  , header+  ) where+-}++import Prelude hiding (takeWhile)+import Control.Monad+import Control.Applicative+import Data.Maybe+import Data.List hiding(takeWhile)++-- ByteStrings up in hurr+import qualified Data.ByteString as BS+import Data.Char as Char++-- Attoparsec+import Data.Attoparsec.Combinator+import Data.Attoparsec.ByteString.Char8+import qualified Data.Attoparsec.Text as Text++data AttributeType = Numeric | Nominal [BS.ByteString] | String deriving(Show)++-- Represents metadata for a single attribute+data Attribute = Attribute+  { name :: BS.ByteString+  , dataType :: AttributeType+  } deriving (Show)++-- | Represents the entire header+data Header = Header+  { title :: BS.ByteString+  -- ^ Name of the relation (\@RELATION foo)+  , attributes :: [Attribute]+  -- ^ Mapping of indexes to values+  } deriving (Show)++-- | Value of a single attribute in a single row+data AttributeValue = NumericValue Double |+                 NominalValue BS.ByteString |+                 StringValue BS.ByteString++showAttributeValue :: AttributeValue -> String+showAttributeValue (NumericValue x) = show x+showAttributeValue (NominalValue x) = show x+showAttributeValue (StringValue x) = show x++instance Show AttributeValue where+  show = showAttributeValue++-- | Parse two expressions sequentially, returning the result of the first.+before :: Parser p1 -> Parser p2 -> Parser p1+before p1 p2 = do+  p <- p1+  p2 >> return p++-- | matches non-newline space characters+isInlineSpace :: Char -> Bool+isInlineSpace c = Char.isSpace c && c /= '\n' && c /= '\r'++-- | Parses a sequence of non-newline space characters+lineSpace :: Parser ()+lineSpace = skipWhile isInlineSpace++-- Matches ignored data, e.g. comments up to end of line+comment :: Parser ()+comment = char '%' >> skipWhile (not . Text.isEndOfLine)++-- | Matches what should be the end of the line- optional comment then newline.+lineEnd :: Parser()+lineEnd = lineSpace >> option () comment >> (endOfInput <|> endOfLine <?> "lineEnd")++-- | @identifier@ parses arguments to '\@' directives, e.g. "\@RELATION foo" +-- TODO: Check these rules against the spec!+-- TODO: Allow quoted identifiers with spaces inside.+identifier :: Parser BS.ByteString+identifier = takeWhile (\x -> Char.isAlphaNum x || x == '-')++-- | Parse the title of the relation+relation :: Parser BS.ByteString+relation = char '@' >> stringCI "relation" >> lineSpace >> identifier++-- | Parse the attribute type: \@ATTRIBUTE <name> <type>+attributeType :: Parser AttributeType+attributeType = (stringCI "numeric" >> return Numeric)+                <|> (stringCI "real" >> return Numeric)+                <|> (nominal >>= return . Nominal)+                <?> "Attribute Type"+  where nominal = do+          char '{' >> lineSpace+          xs <- identifier `sepBy` (lineSpace >> char ',' >> lineSpace)+          lineSpace >> char '}'+          return xs++-- | Parse an attribute: \@ATTRIBUTE <Name> <Type>+attribute :: Parser Attribute+attribute = do char '@' >> stringCI "attribute" >> lineSpace+               i <- identifier `before` lineSpace+               t <- attributeType+               return $ Attribute i t+            <?> "Attribute"++-- | Parses the next expected line+line :: Parser p -> Parser p+line p = skipMany lineEnd >> lineSpace >> p `before` lineEnd+--line p' = skipMany lineEnd >> lineSpace >> p' `before` (lineEnd <|> endOfInput)+--line p = do +--  takeTill (not . Char.isSpace) >> (comment >> line p `before` lineEnd)++-- | Parse an ARFF header.+header :: Parser Header+header = do+  t <- line relation+  as <- manyTill (line attribute) (line $ stringCI "@data")+  return $ Header t as++-- | Parse a value of the expected type (not handling missings)+value' :: AttributeType -> Parser AttributeValue+value' (Nominal xs) = ((choice $ map string xs) >>= return . NominalValue)+                     <?> "Expected one of " ++ tail (xs >>= (',':) . show)+                     -- This last line displays a comma separated list of the+                     -- possible values of the nominal.+value' Numeric = liftM NumericValue double+value' String = error "Not implemented yet... sorry!"++-- | Parse a value of the expected type, returning Nothing for missing values+value :: AttributeType -> Parser (Maybe AttributeValue)+value a = (char '?' >> return Nothing) <|> liftM Just (value' a)++-- | Create a parser which parses a single row of AttributeValues, expecting+-- each to be in order of the Attributes supplied.+row :: [AttributeType] -> Parser [Maybe AttributeValue]+row [] = error "Can't parse empty list" -- no attributes is an error.+row (a:as) = sequence . (value a:) . map (sep >>) $ (map value as)+  where sep = lineSpace >> char ',' >> lineSpace++-- | Parse all data rows in the file.+rows :: Header -> Parser [[Maybe AttributeValue]]+rows header = do+  let as = map dataType $ attributes header+  let errMsg = "expected row of types: " ++ (intercalate ", " $ map show as)+  xs <- manyTill (line (row as) <?> errMsg) (manyTill lineEnd endOfInput)+  return xs++-- | Parse a tuple of Header data and a list of rows, composed of values or+-- "Nothing" (for missing- ?- values).+arff :: Parser (Header, [[Maybe AttributeValue]])+arff = do+  h <- header+  rs <- rows h+  return (h, rs)