packages feed

DBlimited 0.1 → 0.1.1

raw patch · 4 files changed

+246/−2 lines, 4 files

Files

DBlimited.cabal view
@@ -7,7 +7,7 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented.-Version:             0.1+Version:             0.1.1  -- A short (one-line) description of the package. Synopsis:            A command-line SQL interface for flat files (tdf,csv,etc.)@@ -54,7 +54,7 @@   Build-depends:     containers, base >= 2 && < 4, parsec      -- Modules not exported by this package.-  -- Other-modules:       +  Other-modules:     Utils, Schema, Query      -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.   -- Build-tools:         
+ Query.hs view
@@ -0,0 +1,89 @@+{-+  Query+-}++module Query where++-- DBlimited imports+import Schema+import qualified Utils as U++-- Package imports+import Data.Char+import System.IO+import qualified Text.ParserCombinators.Parsec as P+import qualified Data.Map as M++data Clause = All |+              And Clause Clause |+              Or Clause Clause |+              Equals {getTarget :: String, getTest :: String}+              deriving (Show)++type Row = [String]++data Query = InvalidQuery |+             Query {getSelect :: [Column], getFrom :: Table, getWhere :: Clause}+             deriving (Show)++-- build a query+getQuery :: Schema -> String -> IO Query+getQuery schema queryString = case (P.parse (parseQuery schema) "" queryString) of+                                Left err       -> return InvalidQuery+                                Right (Just q) -> return q++parseQuery :: Schema -> P.Parser (Maybe Query)+parseQuery schema = do+  parseChars "SELECT "+  cols <- U.csv -- re-used from Schema+  parseChars " FROM "+  maybeTable <- (parseTable schema)+  P.char ';'+  return $ case maybeTable of+             Nothing      -> Nothing+             (Just table) -> Just (Query cols table All)+  P.<?> "SELECT a,b,c FROM tablename;"++parseTable :: Schema -> P.Parser (Maybe Table)+parseTable schema = do+  tablename <- U.object+  return (M.lookup tablename schema)++parseChars :: [Char] -> P.Parser String+parseChars [] = return ""+parseChars (c:cs) = do+  pc <- P.char (toUpper c) P.<|> P.char (toLower c)+  pcs <- parseChars cs+  return (toLower pc : pcs)+  P.<?> (c:cs)++-- use the schema to look up and load the table+loadTable :: Table -> IO [Row]+loadTable table = do+  handle <- openFile (getPath table) ReadMode+  raw <- hGetContents handle+  return . (map.(U.split).getDlmt$table) . lines $ raw++-- given a initial list of columns and a subset, return the corresponding subset from a given row+evalSelect :: [Column] -> [Column] -> (Row -> Row)+evalSelect sourceCols pickedCols = +    (\inputrow -> [ inputrow !! i | i <- indices ])+    where +      indices = U.selector sourceCols pickedCols++-- given an initial list of columns and a test, determine if a given line passes the test+evalWhere :: [Column] -> Clause -> (Row -> Bool)+evalWhere _cols All              = (\r -> True)+evalWhere cols (And c1 c2)       = (\r -> (evalWhere cols c1 r && evalWhere cols c2 r))+evalWhere cols (Or  c1 c2)       = (\r -> (evalWhere cols c1 r || evalWhere cols c2 r))+evalWhere cols (Equals target f) = undefined++-- given a query and some rows, return the resulting rows+evalQuery :: Query -> IO ()+evalQuery InvalidQuery              = putStrLn "Error in query"+evalQuery (Query cols table clause) = do+  inputRows <- loadTable table+  putStrLn . show . +               (map $ evalSelect (getCols table) cols) . +               (filter $ evalWhere (getCols table) clause) $ +               inputRows
+ Schema.hs view
@@ -0,0 +1,126 @@+{-+  Schema+-}++module Schema where++--import Char+import Control.Monad+import qualified Data.Map as M+import qualified Text.ParserCombinators.Parsec as P+--import Data.Either+import Data.Maybe+import Data.Monoid++import qualified Utils as U++type Column = String +type Delimiter = Char++data Table  = Table {getName :: String, getPath :: FilePath, getDlmt :: Delimiter, getCols :: [Column]}+              deriving (Show)++{-+delimiterMap :: Map Char Char+delimiterMap = fromList +               [('c',','),+                ('t','\t'),+                ('s',' '),+                ('l','~')]+-}++prettyPrintTables :: [Maybe Table] -> IO ()+prettyPrintTables [] = return ()+prettyPrintTables (Nothing :ts) = putStrLn "Not a table."+prettyPrintTables ((Just t):ts) = do +  putStrLn $ (getName t) ++ " : " ++ (show.getCols$t)+  prettyPrintTables ts++type Schema = M.Map String Table++prettyPrintSchema :: Schema -> IO ()+prettyPrintSchema schema = prettyPrintTables $ map Just (M.elems schema)++{-+Schema file format:++<tablename>;<absolutefilepath>;<delimitercharacter>;<column_0>,<column_1>,...,<column_n-1>++tablename can be alphanumeric, but can't start with a digit+aboslutefilepath is self-explanitory+delimitercharacter is coded, t for tab, c for comma, s for space, l for tilde+finally, comma separated columns, with same rules as tablename+-}+++tablename :: P.Parser String+tablename = U.object++abspath :: P.Parser FilePath+abspath = do+  root <- P.char '/'+  rest <- P.many (P.letter +            P.<|> P.digit +            P.<|> P.char '/' +            P.<|> P.char '-' +            P.<|> P.char '_' +            P.<|> P.char '.')+  return (root:rest)+  P.<?> "absoulute file path"++delimiter :: P.Parser Delimiter+delimiter = P.anyChar++comment :: P.Parser ()+comment = do+  P.char '#'+  P.skipMany (P.noneOf "\n")+  P.<?> "comment"++eol :: P.Parser ()+eol = do+  P.oneOf "\n"+  return ()+  P.<?> "end of line"++item :: P.Parser (String,Table)+item = do+  tn <- tablename+  P.char ';'+  path <- abspath+  P.char ';'+  dlmt <- delimiter+  P.char ';'+  cols <- U.csv+  P.manyTill P.anyChar ( P.try eol P.<|> P.try comment P.<|> P.eof)+  return (tn,Table tn path dlmt cols)+  P.<?> "item"++line :: P.Parser (Maybe (String,Table))+line = do+  P.skipMany P.space+  P.try (comment >> return Nothing) P.<|> (item >>= return . Just)++fileParser :: P.Parser [(String,Table)]+fileParser = do+  lines <- P.many line+  return (catMaybes lines)++{-+readSchema1 :: FilePath -> IO (Either P.ParseError Schema)+readSchema1 schemaPath = +    P.parseFromFile fileParser schemaPath >>= +    return . fmap (foldr (uncurry M.insert) M.empty)+-}++readSchema :: FilePath -> IO Schema+readSchema schemaPath = do+  result <- P.parseFromFile fileParser schemaPath+  return $ case result of+             Left err -> M.empty+             Right ts -> foldr (uncurry M.insert) M.empty ts++{- test instances -}++sampleSchema = readSchema "/DBlimited/sampleschema.txt"+sampleTable = Table "person" "/DBlimited/sample/person.csv" ',' ["id","name","address","telephone","dob"]
+ Utils.hs view
@@ -0,0 +1,29 @@+{-+  Utils+-}++module Utils where++import Data.List+import Data.Function+import Data.Maybe+import qualified Text.ParserCombinators.Parsec as P++split :: Char -> String -> [String]+split dlmt = filter ( not . any ((==) dlmt)). groupBy (on (==) ((==) dlmt))++selector :: Eq a => [a] -> [a] -> [Int]+selector source picks = (catMaybes [elemIndex x source | x <- picks])++object :: P.Parser String+object = do+  c <- P.letter+  cs <- P.many (P.letter P.<|> P.digit)+  return (c:cs)+  P.<?> "object"++csv :: P.Parser [String]+csv = do+  csv <- P.many (P.letter P.<|> P.digit P.<|> P.char ',')+  return (split ',' csv)+  P.<?> "columns"