diff --git a/DBlimited.cabal b/DBlimited.cabal
--- a/DBlimited.cabal
+++ b/DBlimited.cabal
@@ -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:         
diff --git a/Query.hs b/Query.hs
new file mode 100644
--- /dev/null
+++ b/Query.hs
@@ -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
diff --git a/Schema.hs b/Schema.hs
new file mode 100644
--- /dev/null
+++ b/Schema.hs
@@ -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"]
diff --git a/Utils.hs b/Utils.hs
new file mode 100644
--- /dev/null
+++ b/Utils.hs
@@ -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"
