diff --git a/System/DotFS/Core/BodyParser.hs b/System/DotFS/Core/BodyParser.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/BodyParser.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.BodyParser where
+
+import Prelude hiding (lex,lookup)
+
+import System.DotFS.Core.Datatypes
+import System.DotFS.Core.Lexers
+import System.DotFS.Core.ExpressionParsers
+import System.DotFS.Core.HelperParsers
+import System.DotFS.Core.HeaderParser
+
+import Data.Map
+
+import Control.Applicative ((<$>), (<*), (*>), (<$), (<*>))
+import Text.Parsec
+import Text.Parsec.Token as P
+
+
+bodyP :: VarParser (Header,Body)
+bodyP = do { b <- id <$ headerP *> blocksP <* eof <?> "body with annotations"
+           ; h <- getState
+           ; return (h,b)
+           }
+
+blocksP :: VarParser Body
+blocksP = many blockP
+
+blockP :: VarParser BodyElem
+blockP =  try conditionalBlockP
+      <|> try exprBlockP
+      <|> verbBlockP <?> "body element (if, reference or verbatim)"
+
+
+conditionalBlockP :: VarParser BodyElem
+conditionalBlockP = do{ state <- getState
+                      ; _ <- symbol lex (extractTagStart state)
+                      ; _ <- symbol lex "if"
+                      ; cond <- exprP
+                      ; _ <- string (extractTagStop state)
+                      ; content <- blocksP
+                      ; _ <- symbol lex (extractTagStart state)
+                      ; _ <- symbol lex "endif" <|> symbol lex "/if" <|> symbol lex "fi"
+                      ; _ <- string (extractTagStop state)
+                      ; return $ Cond cond content
+                      }
+
+exprBlockP :: VarParser BodyElem
+exprBlockP = do{ state <- getState
+               ; _ <- symbol lex (extractTagStart state)
+               ; _ <- symbol lex "var"
+               ; var <- exprP
+               ; _ <- string (extractTagStop state)
+               ; return $ Ref var
+               }
+
+
+verbBlockP :: VarParser BodyElem
+verbBlockP = do{ state <- getState
+               ; let opentag = const () <$> try (string (extractTagStart state))
+                     endofVerb = lookAhead (eof <|> opentag)
+                 in Verb <$> many1Till anyChar endofVerb
+               }
+
+
+-- helpers to retrieve start and stop tags as string from the state map:
+extractTagStart,extractTagStop :: Map String DFSExpr -> String
+extractTagStart m = case lookup "tagstart" m of
+                       Just (Prim (VString s)) -> s
+                       _                -> "<<"
+
+extractTagStop m = case lookup "tagstop" m of
+                       Just (Prim (VString s)) -> s
+                       _                -> ">>"
diff --git a/System/DotFS/Core/Constants.hs b/System/DotFS/Core/Constants.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/Constants.hs
@@ -0,0 +1,43 @@
+{-
+ - Everything here is evil by definition.
+ -}
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.Constants where
+
+import System.DotFS.Core.Datatypes
+
+import System.Fuse
+import System.Posix.Files
+
+-- there is a system call for this.
+-- but never mind, since we have a read-only file
+-- system, we don't need that.
+dotfsGetFileSystemStats :: Conf -> String -> IO (Either Errno FileSystemStats)
+dotfsGetFileSystemStats dp str = -- use stats from home dp
+  return $ Right FileSystemStats
+    { fsStatBlockSize  = 512
+    , fsStatBlockCount = 1000
+    , fsStatBlocksFree = 0
+    , fsStatBlocksAvailable = 0
+    , fsStatFileCount  = 5      -- IS THIS CORRECT?
+    , fsStatFilesFree  = 10     -- WHAT IS THIS?
+    , fsStatMaxNameLength = 255 -- SEEMS SMALL?
+    }
+
+-- this is dummy data, and turns out to never be
+-- shown, even when doing `ls -la`
+dirStat = FileStat {
+    statEntryType = Directory
+  , statFileMode  = ownerReadMode
+  , statLinkCount = 5
+  , statFileOwner = 666
+  , statFileGroup = 666
+  , statSpecialDeviceID = 0
+  , statFileSize  = 4096
+  , statBlocks    = 1
+  , statAccessTime= 0
+  , statModificationTime = 0
+  , statStatusChangeTime = 0
+  }
+
+
diff --git a/System/DotFS/Core/Datatypes.hs b/System/DotFS/Core/Datatypes.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/Datatypes.hs
@@ -0,0 +1,93 @@
+{-# LANGUAGE GADTs, ExistentialQuantification, FlexibleInstances #-}
+module System.DotFS.Core.Datatypes where
+
+import Data.Maybe
+import Text.ParserCombinators.Parsec.Prim
+import Data.Map (lookup, fromList, Map)
+import Data.Char (toLower)
+
+import Prelude hiding (lookup)
+
+data Conf = C FilePath deriving Show
+
+-- our threaded parser type
+type VarParser a = GenParser Char DFSState a
+
+-- and the results
+type VarName = String
+
+type DFSState = Map VarName DFSExpr
+
+type Mountpoint = FilePath
+
+-- | primitives
+data Value = VInt Integer
+           | VBool Bool
+           | VString String
+           deriving Eq
+
+instance Show Value where
+  show (VInt    i) = show i
+  show (VBool   b) = map toLower $ show b
+  show (VString s) = s
+
+
+-- | a config file is either just a normal file, to be passed
+-- through unchanged, or else it's an annotated file with a header
+-- and a body.
+-- a proper AST for our config files, in other words.
+data Config = Vanilla
+            | Annotated Header Body
+            deriving Show
+
+type Header = DFSState
+
+type Body = [BodyElem]
+
+-- | the body is either a conditional block (shown depending on some
+-- boolean value) or a literal expression (usually a variable inserted somewhere)
+-- or, of course, verbatim content. These components can be chained.
+data BodyElem = Cond DFSExpr Body
+              | Ref  DFSExpr
+              | Verb String
+              deriving Show
+
+-- | an expression
+data DFSExpr   = Var  VarName
+               | Prim Value
+               | Sys  String
+               | If DFSExpr DFSExpr DFSExpr
+               | UniOp Op DFSExpr
+               | BiOp  Op DFSExpr DFSExpr
+               deriving Eq
+
+instance Show DFSExpr where
+  show (Var  n) = n
+  show (Prim v) = show v
+  show (Sys  s) = s
+  show (If c e1 e2) = "if("++show c++"){"++show e1++"}else{" ++ show e2 ++ "}"
+  show (UniOp o e) = "(" ++ show o ++ show e ++ ")"
+  show (BiOp  o e1 e2) = "("++show e1++ show o ++ show e2++")"
+
+instance Show Op where
+  show op = (\o -> " "++o++" ") $
+            fromMaybe "?" (lookup op (fromList
+            [ (Add, "+")
+            , (Sub, "-")
+            , (Div, "/")
+            , (Mul, "*")
+            , (Eq,  "==")
+            , (LTOp, "<")
+            , (GTOp, ">")
+            , (LEQ, "<=")
+            , (GEQ, ">=")
+            , (NEQ, "!=")
+            , (And, "&&")
+            , (Or, "||")
+            , (Not, "!")
+            ]))
+
+data Op     = Add | Sub | Mul | Div
+            | Eq  | LTOp| GTOp| LEQ | GEQ | NEQ
+            | And | Or  | Not
+            deriving (Eq,Ord)
diff --git a/System/DotFS/Core/ExpressionEvaluator.hs b/System/DotFS/Core/ExpressionEvaluator.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/ExpressionEvaluator.hs
@@ -0,0 +1,108 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.ExpressionEvaluator where
+
+import Prelude hiding (lookup)
+import System.IO
+import Control.Applicative
+import System.Process
+import System.IO.Unsafe
+
+import System.DotFS.Core.Datatypes
+import Data.Map
+
+eval :: DFSState -> DFSExpr -> Value
+eval s (Prim p) = p
+eval s (Var v)  = case lookup v s of
+                    Nothing -> VBool False -- default...
+                    Just e  -> eval s e
+eval s (Sys c)  = VString $ execSystem c
+eval s (If c t e) = case eval s c of
+                        VBool True -> eval s t
+                        _          -> eval s e
+eval s o@(UniOp _ e) = evalUni s o
+eval s o@(BiOp  _ e1 e2) = evalBi s o
+
+
+execSystem :: String -> String
+execSystem c = unsafePerformIO $ do (inn,out,err,pid) <- runInteractiveCommand c
+                                    mapM_ (`hSetBinaryMode` False) [inn, out]
+                                    hSetBuffering out NoBuffering
+                                    parsedIntro <- parseUntilPrompt out
+                                    return (concat parsedIntro)
+
+parseUntilPrompt :: Handle -> IO [String]
+parseUntilPrompt out = do
+  h <- hIsEOF out
+  if h
+      then
+        return []
+      else do
+        latest <- hGetLine out
+        (:) <$> return latest <*> parseUntilPrompt out
+
+evalUni :: DFSState -> DFSExpr -> Value
+evalUni s (UniOp Not b) = case eval s b of
+                            VBool b -> VBool $ not b
+                            _       -> VBool False   -- default value?? some way of error reporting here please
+evalUni _ _ = VBool False -- no other uni-operators for now.
+
+evalBi :: DFSState -> DFSExpr -> Value
+evalBi s (BiOp Add e1 e2)   = doAdd s e1 e2
+evalBi s (BiOp Sub e1 e2)   = doInt s (-) e1 e2
+evalBi s (BiOp Mul e1 e2)   = doInt s (*) e1 e2
+evalBi s (BiOp Div e1 e2)   = doInt s div e1 e2
+evalBi s (BiOp Eq  e1 e2)   = let e1' = eval s e1
+                                  e2' = eval s e2
+                              in VBool $ e1' == e2'
+evalBi s (BiOp LTOp e1 e2)  = doIntBool s (<) e1 e2
+evalBi s (BiOp GTOp e1 e2)  = doIntBool s (>) e1 e2
+evalBi s (BiOp LEQ  e1 e2)  = doIntBool s (<=) e1 e2
+evalBi s (BiOp GEQ  e1 e2)  = doIntBool s (>=) e1 e2
+evalBi s (BiOp NEQ  e1 e2)  = let e1' = eval s e1
+                                  e2' = eval s e2
+                              in VBool $ e1' /= e2'
+evalBi s (BiOp And  e1 e2)  = doBool s (&&) e1 e2
+evalBi s (BiOp Or   e1 e2)  = doBool s (||) e1 e2
+evalBi s _                  = VBool False
+
+doAdd :: DFSState -> DFSExpr -> DFSExpr -> Value
+doAdd s a b   = let e1 = eval s a
+                    e2 = eval s b
+                in  f e1 e2
+                where f (VString s1) (VString s2) = VString $ s1 ++ s2
+                      f (VInt    i1) (VInt    i2) = VInt $ i1 +  i2
+                      f _            _            = VInt 0
+
+doInt :: DFSState
+      -> (Integer -> Integer -> Integer)
+      -> DFSExpr
+      -> DFSExpr
+      -> Value
+doInt s f a b = let e1' = eval s a
+                    e2' = eval s b
+                in  e1' `handle` e2'
+                where handle (VInt a) (VInt b) = VInt $ f a b
+                      handle _ _ = VInt 0
+
+doIntBool :: DFSState
+                            -> (Integer -> Integer -> Bool)
+                            -> DFSExpr
+                            -> DFSExpr
+                            -> Value
+doIntBool s f a b = let e1' = eval s a
+                        e2' = eval s b
+                    in  e1' `handle` e2'
+                    where handle (VInt a) (VInt b) = VBool $ f a b
+                          handle _ _ = VBool False
+
+doBool :: DFSState
+                         -> (Bool -> Bool -> Bool)
+                         -> DFSExpr
+                         -> DFSExpr
+                         -> Value
+doBool s f a b = let e1' = eval s a
+                     e2' = eval s b
+                 in  e1' `handle` e2'
+                 where handle (VBool a) (VBool b) = VBool $ f a b
+                       handle _ _ = VBool False
diff --git a/System/DotFS/Core/ExpressionParsers.hs b/System/DotFS/Core/ExpressionParsers.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/ExpressionParsers.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE NoImplicitPrelude, GADTs, ExistentialQuantification #-}
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.ExpressionParsers where
+
+import Prelude hiding (lex,lookup)
+import Control.Applicative ((<$>))
+import System.DotFS.Core.Datatypes
+import System.DotFS.Core.Lexers
+import Data.Functor.Identity
+import Text.Parsec
+import Text.Parsec.Token as P
+import Text.Parsec.Expr
+
+exprP :: VarParser DFSExpr
+exprP = buildExpressionParser table factor <?> "expression"
+
+table :: [[ Operator String st Identity DFSExpr ]]
+table = [
+    [pre "!" (UniOp Not)],
+    [ op "&&" (BiOp And) AssocNone ],
+    [ op "||" (BiOp Or) AssocNone ],
+    [ op "*" (BiOp Mul) AssocLeft, op "/" (BiOp Div) AssocLeft ],
+    [ op "+" (BiOp Add) AssocLeft, op "-" (BiOp Sub) AssocLeft ],
+    [ op "==" (BiOp Eq) AssocNone
+        , op ">" (BiOp GTOp) AssocNone
+        , op "<" (BiOp LTOp) AssocNone
+        , op "<=" (BiOp LEQ) AssocNone
+        , op ">=" (BiOp GEQ) AssocNone
+        , op "!=" (BiOp NEQ) AssocNone
+    ]
+    ]
+  where
+    op s f       = Infix   (do { reservedOp lex s; return f } <?> "operator")
+    pre s f      = Prefix  (do { reservedOp lex s; return f })
+
+factor :: ParsecT String DFSState Identity DFSExpr
+factor =  parens lex exprP
+      <|> ((Prim . VInt) <$> integer lex)
+      <|> ((Prim . VBool) <$> boolTerm)
+      <|> ((Prim . VString) <$> stringLiteral lex)
+      <|> Var <$> identifier lex
+      <|> ifTerm
+      <?> "simple expression or variable"
+
+boolTerm :: forall u. ParsecT String u Identity Bool
+boolTerm =  do { _ <- symbol lex "true"
+               ; return True
+               }
+        <|> do { _ <- symbol lex "false"
+               ; return False
+               }
+
+ifTerm :: ParsecT String DFSState Identity DFSExpr
+ifTerm = do { reservedOp lex "if"
+            ; condition <- parens lex exprP
+            ; _ <- symbol lex "{"
+            ; thenbody <- exprP
+            ; _ <- symbol lex "}"
+            ; reservedOp lex "else"
+            ; _ <- symbol lex "{"
+            ; elsebody <- exprP
+            ; _ <- symbol lex "}"
+            ; return (If condition thenbody elsebody)
+            }
diff --git a/System/DotFS/Core/FSActions.hs b/System/DotFS/Core/FSActions.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/FSActions.hs
@@ -0,0 +1,203 @@
+module System.DotFS.Core.FSActions where
+
+import System.DotFS.Core.Datatypes
+import System.DotFS.Core.FuseTypes
+import System.DotFS.Util.Debug
+import System.DotFS.Util.Options
+import System.DotFS.Core.Constants
+import System.DotFS.Core.Parsers
+
+import qualified Data.ByteString.Char8 as B
+import System.Posix.Types
+import System.Posix.Files
+import System.FilePath.Posix
+import System.Posix.IO
+import System.Directory
+import System.Fuse
+import Control.Monad
+
+import Data.Bits
+
+import Data.List (intersperse, intercalate)
+
+import Prelude hiding (readFile, length)
+import Data.ByteString.Char8 hiding (notElem, map, filter, drop, take, intersperse, concat, intercalate)
+
+dirContents :: FilePath -> IO [FilePath]
+dirContents fp = do
+                contents <- getDirectoryContents fp
+                debug (intercalate "," contents)
+                return $ filter (`notElem` [".",".."]) contents
+
+fileExists, dirExists :: FilePath -> FilePath -> IO Bool
+fileExists path name = doesFileExist $ path </> name
+dirExists  path name = doesDirectoryExist $ path </> name
+
+
+getGenStats :: FilePath -> FilePath -> IO DotFS
+getGenStats path name = do p' <- canonicalizePath $ path </> name
+                           let p = path </> name
+                           st <- getSymbolicLinkStatus p
+                           if isDirectory st then
+                             getStats Directory p
+                             else
+                               if isSymbolicLink st then
+                                   getStats SymbolicLink p
+                               else --  isRegularFile st then
+                                   getStats RegularFile p
+
+getStats :: EntryType -> FilePath -> IO DotFS
+getStats entrytype uri = do
+  status <- getSymbolicLinkStatus uri
+  children <- case entrytype of
+    Directory -> do contents <- dirContents uri
+                    -- list of files
+                    files    <- filterM (fileExists uri) contents
+                    fileList <- mapM (getGenStats uri) files
+                    -- list of directories
+                    dirs     <- filterM (dirExists uri) contents
+                    dirList  <- mapM (getGenStats uri) dirs
+                    return $ dirList ++ fileList
+    RegularFile -> return []
+    _           -> return [] -- symlinks etc etc
+  sz <- case entrytype of
+    Directory -> return $ fileSize status
+    RegularFile -> do
+      fd <- readFile uri
+      let parsed = process uri fd
+      return $ fromIntegral (length parsed)
+    SymbolicLink -> return 42 -- arbitrary size for symlinks
+    _ -> return 0
+  return DotFS {
+      dotfsEntryName   = takeFileName uri
+    , dotfsActualPath  = uri
+    , dotfsVirtualPath = "" -- uri -- "test"
+    , dotfsFileStat    = FileStat
+        { statEntryType = entrytype
+        , statFileMode  = fileMode status
+        , statLinkCount = linkCount status
+        , statFileOwner = fileOwner status
+        , statFileGroup = fileGroup status
+        , statSpecialDeviceID = specialDeviceID status
+        , statFileSize  = sz
+        , statBlocks    = fromIntegral $ sz `div` 1024
+        , statAccessTime= accessTime status
+        , statModificationTime = modificationTime status
+        , statStatusChangeTime = statusChangeTime status
+        }
+     , dotfsContents = children
+  }
+
+
+statIfExists :: FilePath -> FilePath -> IO (Maybe DotFS)
+statIfExists dir file = do
+                          existsAsDir <- dir `dirExists` file
+                          if existsAsDir then
+                                      do debug $ file ++ " is a dir in "++dir
+                                         stats <- dir `getGenStats` file
+                                         return $ Just stats -- this already contains the children
+                             else
+                                      do existsAsFile <- dir `fileExists` file
+                                         if existsAsFile then do
+                                                       debug $ file ++ " is a file in " ++ dir
+                                                       stats <- dir `getGenStats` file
+                                                       return $ Just stats
+                                            else return Nothing
+
+dotFSOps :: Options -> Mountpoint -> Conf -> FuseOperations String
+dotFSOps os mp dir =
+  defaultFuseOps {
+                   fuseGetFileStat        = dotfsGetFileStat dir
+                 , fuseGetFileSystemStats = dotfsGetFileSystemStats dir
+                 , fuseOpenDirectory      = dotfsOpenDirectory dir
+                 , fuseReadDirectory      = dotfsReadDirectory dir
+                 , fuseRead               = dotfsRead dir
+                 , fuseOpen               = dotfsOpen dir
+                 , fuseReadSymbolicLink   = dotfsReadSymbolicLink dir mp
+                 }
+
+{- | here's quite a bit of logic: we want symlinks to be presented as
+ - such, but we would also like links pointing outside the repo to work.
+ -}
+dotfsReadSymbolicLink :: Conf -> Mountpoint -> FilePath -> IO (Either Errno FilePath)
+dotfsReadSymbolicLink (C confdir) mp path = do
+                                              let absPathToLink = normalise $ confdir ++ "/" ++ path
+                                              linkDestination <- readSymbolicLink absPathToLink
+                                              let mountedDestination = mp </> linkDestination
+                                              let finalDestination = normalise mountedDestination
+                                              let answer = makeRelative mp finalDestination
+                                              return $ Right answer
+
+{- possible FUSE operations:
+ --
+ -- | Check file access permissions; this will be called for the
+ --   access() system call.  If the @default_permissions@ mount option
+ --   is given, this method is not called.  This method is also not
+ --   called under Linux kernel versions 2.4.x
+ fuseAccess :: FilePath -> Int -> IO Errno, -- FIXME present a nicer type to Haskell
+ -}
+
+{-
+ - this is a rather important function for the system.
+ - it finds a file by name. the idea is, if a file or directory
+ - exists in the conf tree, return that.
+ -}
+dotfsLookUp :: Conf -> FilePath -> IO (Maybe DotFS)
+dotfsLookUp (C confdir) path = do
+    confVersion <- statIfExists confdir path
+    case confVersion of
+        Just stats ->  do let oldFileStat = dotfsFileStat stats
+                              -- Prevent other users accessing the files,
+                              -- and prevent writing permission.
+                              newFileStat = oldFileStat {statFileMode = 0o500 .&. statFileMode oldFileStat}
+                              stats'      = stats {dotfsFileStat = newFileStat}
+                          return $ Just stats'
+        Nothing -> return Nothing
+
+
+
+dotfsGetFileStat :: Conf -> FilePath -> IO (Either Errno FileStat)
+dotfsGetFileStat dp (_:dir) = do
+  lkup <- dotfsLookUp dp dir
+  case lkup of
+    Just file -> return $ Right $ dotfsFileStat file
+    Nothing   -> return $ Left eNOENT
+
+dotfsOpen :: Conf -> FilePath -> OpenMode -> OpenFileFlags -> IO (Either Errno String)
+dotfsOpen dirs (_:path) ReadOnly flags = do
+  file <- dotfsLookUp dirs path
+  case file of
+    Just f -> do
+      -- at this point load and parse the file.
+      fd <- readFile (dotfsActualPath f)
+
+      let parsed = process path fd
+      return (Right $ unpack parsed)
+    Nothing -> return (Left eNOENT)
+dotfsOpen dirs (_:path) mode flags = return (Left eACCES)
+
+-- TODO: check permissions with `getPermissions`
+dotfsOpenDirectory :: Conf -> FilePath -> IO Errno
+dotfsOpenDirectory (C confdir) (_:path) = do
+  extantDirs <- confdir `dirExists` path
+  return $ if extantDirs then eOK else eNOENT
+
+dotfsReadDirectory :: Conf -> FilePath -> IO (Either Errno [(FilePath, FileStat)])
+dotfsReadDirectory dirs@(C confdir) (_:dir) = do
+  entry <- dotfsLookUp dirs dir
+  case entry of
+    Nothing -> return $ Left eNOENT
+    Just e -> do
+        let contents = dotfsContents e
+        let dirContents = map (\x -> (dotfsEntryName x :: String , dotfsFileStat x)) contents
+        dotstats <- confdir `getGenStats` dir
+        return $ Right $ [ (".", dotfsFileStat dotstats), ("..", dirStat)] ++ dirContents
+
+dotfsRead  :: Conf -> FilePath -> String -> ByteCount -> FileOffset -> IO (Either Errno B.ByteString)
+dotfsRead dirsToUnion (_:path) fd byteCount offset = do
+  --this is unused, so it's probably for error checking. should die gracefully
+  --if something goes wrong, not throw unmatched pattern...
+  --(Just file) <- dotfsLookUp dirsToUnion path
+  let a = drop (fromIntegral offset) fd
+      b = take (fromIntegral byteCount) a
+  return $ Right $ pack b
diff --git a/System/DotFS/Core/FuseTypes.hs b/System/DotFS/Core/FuseTypes.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/FuseTypes.hs
@@ -0,0 +1,16 @@
+module System.DotFS.Core.FuseTypes where
+
+import System.Fuse
+
+data DotFS = DotFS {
+    dotfsEntryName     :: FilePath
+  , dotfsActualPath    :: FilePath
+  , dotfsVirtualPath   :: FilePath
+  , dotfsFileStat      :: FileStat
+  , dotfsContents      :: [DotFS]
+  }
+ deriving Show
+
+instance Eq DotFS where
+  (==) x y = dotfsEntryName x == dotfsEntryName y
+
diff --git a/System/DotFS/Core/HeaderParser.hs b/System/DotFS/Core/HeaderParser.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/HeaderParser.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE NoImplicitPrelude, GADTs, ExistentialQuantification #-}
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.HeaderParser where
+
+import Prelude hiding (lex)
+
+import System.DotFS.Core.Datatypes
+import System.DotFS.Core.Lexers
+import System.DotFS.Core.ExpressionParsers
+import System.DotFS.Core.ExpressionEvaluator
+import System.DotFS.Core.HelperParsers (eatEverything)
+
+import Control.Applicative ((<*))
+import Text.Parsec hiding (parseTest)
+import Text.Parsec.Token as P
+import Data.Map
+
+headerRecogniseP = do { _ <- symbol lex "<<dotfs"
+                      ; return ()
+                      }
+
+-- parse the header, no whitespace around it is eaten
+headerP :: VarParser DFSState
+headerP = do { _ <- symbol lex "<<dotfs"
+             ; whiteSpace lex
+             ; _ <- many assignmentP
+             ; _ <- string ">>\n"
+             ; getState -- returns the state
+             }
+
+-- parse an assignment
+assignmentP :: VarParser ()
+assignmentP = (try tagstyleP
+           <|> try commentstyleP
+           <|> try shellCommandP
+           <|> assignState
+            ) <* ( semi lex <* whiteSpace lex) <?> "assignment"
+
+
+-- we must prevent comment tags from being ignored by the lexer,
+-- so use the alternative lexer with great care
+tagstyleP,commentstyleP :: VarParser ()
+tagstyleP = do{ _ <- symbol lex "tagstyle"
+              ; _ <- symbol styleLex "="
+              ; s1 <- operator styleLex
+              ; _ <- symbol styleLex "tag"
+              ; s2 <- operator lex
+              ; updateState (insert "tagstart" (Prim(VString s1)))
+              ; updateState (insert "tagstop"  (Prim(VString s2)))
+              }
+
+commentstyleP = do{ _ <- symbol lex "commentstyle"
+                  ; _ <- symbol styleLex "="
+                  ; s1 <- operator styleLex
+                  ; updateState (insert "commentstart" (Prim(VString s1)))
+                  ; _ <- symbol styleLex "comment"
+                  ; optional (do s2 <- operator lex
+                                 updateState (insert "commentstop"  (Prim(VString s2))))
+                  }
+
+-- | this parses a shell command. These are denoted by using := instead
+-- of = for assignment. This is because backticks are a pain to parse, and
+-- we prefer the built-in stringLiteral parser.
+shellCommandP :: VarParser ()
+shellCommandP = do { name <- identifier lex
+                   ; whiteSpace lex
+                   ; _ <- symbol lex ":="
+                   ; whiteSpace lex
+                   ; command <- exprP
+                   ; s <- getState
+                   ; let finalCommand = eval s command
+                   ; let e = eval s (Sys (show finalCommand))
+                   ; updateState (insert name (Prim e))
+}
+
+-- | assignState parses an assignment. That is, an identifier, an equals (=)
+-- symbol, and then an expression.
+assignState :: VarParser ()
+assignState = do{ name <- identifier lex
+                ; whiteSpace lex
+                ; _ <- symbol lex "="
+                ; whiteSpace lex
+                ; val <- exprP
+                ; s   <- getState
+                ; let e = eval s val
+                ; updateState (insert name (Prim e))
+                }
diff --git a/System/DotFS/Core/HelperParsers.hs b/System/DotFS/Core/HelperParsers.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/HelperParsers.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.HelperParsers where
+
+import System.DotFS.Core.Datatypes
+
+import Control.Monad (join)
+import Text.Parsec
+import Text.Parsec.String
+
+
+eatEverything :: VarParser String
+eatEverything = many anyChar
+
+
+
+-- new combinator: (source: http://www.haskell.org/pipermail/beginners/2010-January/003123.html)
+many1Till :: Show end => VarParser a -> VarParser end -> VarParser [a]
+many1Till p end = do notFollowedBy' end
+                     p1 <- p
+                     ps <- manyTill p end
+                     return (p1:ps) where
+                       notFollowedBy' :: Show a => GenParser tok st a -> GenParser tok st ()
+                       notFollowedBy' p = try $ join $ do a <- try p
+                                                          return (unexpected (show a))
+                                                       <|> return (return ())
+
+
+
+
+-- combinator that outputs the state tupled with the parse result
+includeState :: GenParser s st a -> GenParser s st (a,st)
+includeState p = do{ res <- p
+                   ; state <- getState
+                   ; return (res,state)
+                   }
+
+-- parseTest adepted to accept an initial state
+parseTest p st inp = case runParser (includeState p) st "" inp of
+                          (Left err) -> do{ putStr "parse error at "
+                                          ; print err
+                                          }
+                          (Right (x,state))  -> case x of
+                                                 Vanilla       -> putStrLn "Vanilla"
+                                                 Annotated h b -> putStrLn "Annotated"
diff --git a/System/DotFS/Core/Lexers.hs b/System/DotFS/Core/Lexers.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/Lexers.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE Haskell98 #-}
+{-# LANGUAGE RankNTypes #-}
+module System.DotFS.Core.Lexers where
+
+import Prelude hiding (lex)
+
+import Text.Parsec
+import Text.Parsec.Language
+import Text.Parsec.Token as P
+import Data.Functor.Identity
+
+
+-- stuff about the language and the default lexer
+tagletter :: forall u. ParsecT [Char] u Data.Functor.Identity.Identity Char
+tagletter = oneOf "~!@#$%^&*_+|`-=\\:<>?[]',./"
+
+lang :: LanguageDef st
+lang = javaStyle
+     { reservedNames = ["commentstyle","tagstyle","if","else","true","false"]
+     , caseSensitive = True
+     , opStart  = tagletter
+     , opLetter = tagletter
+     }
+
+lex :: forall u. GenTokenParser String u Identity
+lex = P.makeTokenParser lang
+
+-- alternative lexer for style definitions
+styleLang :: LanguageDef st
+styleLang = emptyDef
+          { opStart  = tagletter
+          , opLetter = tagletter }
+
+styleLex :: forall u. GenTokenParser String u Identity
+styleLex = P.makeTokenParser styleLang
diff --git a/System/DotFS/Core/Parsers.hs b/System/DotFS/Core/Parsers.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Core/Parsers.hs
@@ -0,0 +1,60 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Core.Parsers where
+
+import Prelude hiding (lex, lookup, readFile, putStrLn)
+
+import System.DotFS.Core.Datatypes
+import System.DotFS.Core.HeaderParser (headerP, headerRecogniseP)
+import System.DotFS.Core.HelperParsers
+import System.DotFS.Core.Lexers
+import System.DotFS.Core.ExpressionEvaluator
+import System.DotFS.Core.BodyParser
+
+import Control.Applicative ((<$>))
+import Text.Parsec hiding (parseTest)
+import Text.Parsec.Token
+
+import Data.Map
+
+import Data.ByteString.Char8 (unpack, pack, ByteString, putStrLn)
+import Data.ByteString (readFile)
+
+-- | Test-process a given file, and show the result.
+-- Especially useful for testing in combination with GHCi.
+testfile :: FilePath -> IO ()
+testfile name = do { fc <- readFile name
+                   ; let output = process name fc
+                   ; putStrLn output
+                   ; return ()
+                   }
+
+
+-- run the header parser and evaluator, and then the body parser on the result
+process :: FilePath -> ByteString -> ByteString
+process file contents =
+                       let inp = unpack contents in
+                         case runParser headerRecogniseP empty file inp of
+                             Left err  -> contents
+                             Right _   -> case runParser bodyP empty file inp of
+                                Left err     -> pack $ "\n" ++ "error = \n" ++ show err ++ "\n"
+                                Right (h,bs) -> pack $ present h bs
+
+present :: Header -> Body -> String
+present _ []     = ""
+present h (Cond c b:bs) = case eval h c of
+                                VBool True -> outputComment h c "if:" ++ present h b ++ outputComment h c "endif:"
+                                _          -> outputComment h c "if-hiding; false == "
+                            ++ present h bs
+present h (Ref r:bs)    = outputComment h r "ref:" ++ show (eval h r) ++ present h bs
+present h (Verb v:bs)   = v ++ present h bs
+
+outputComment :: Header -> DFSExpr -> String -> String
+outputComment h e note   = case lookup "commentstart" h of
+                           Nothing -> ""
+                           Just (Prim (VString start)) ->
+                             case lookup "commentstop" h of
+                             Nothing -> concat               ("\n":[start,note,show e]++["\n"])
+                             Just (Prim (VString stop)) -> unwords ([start,note,show e]++[stop])
+                             _ -> ""
+                           _ -> ""
diff --git a/System/DotFS/Test/Tests.hs b/System/DotFS/Test/Tests.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Test/Tests.hs
@@ -0,0 +1,34 @@
+module System.DotFS.Test.Tests where
+
+import System.DotFS.Core.ExpressionParsers
+import Text.Parsec hiding (parseTest)
+import Control.Monad
+import System.DotFS.Core.Datatypes
+import Test.QuickCheck.Gen
+import Data.Char
+import Data.Map
+
+-- import the instances of Arbitrary ... from Test.Unit
+-- this uses a trick to prevent circular dependencies
+import {-# SOURCE #-} System.DotFS.Test.Unit()
+
+prop_parseExpr :: DFSExpr -> Bool
+prop_parseExpr xs = xs == testExprP (show xs)
+
+--prop_parseHeader :: Header -> Bool
+--prop_parseHeader xs = xs == testHeaderP (show xs)
+
+testExprP :: String -> DFSExpr
+testExprP inp = case runParser exprP empty "expr" inp of
+              Left err -> Prim . VString $ "error = \n" ++ show (errorPos err) ++ "\n"
+              Right s  -> s
+
+-- | unfortunately we need to override the "instance Arbitrary [a]" for strings
+arbitraryStr :: Gen String
+arbitraryStr = sized $ \n ->
+    do k <- choose (0,n)
+       sequence [ arbitraryChar | _ <- [1..k] ]
+
+-- | we don't want newlines in our strings...
+arbitraryChar :: Gen Char
+arbitraryChar = chr `fmap` oneof [choose (65,90), choose (97,122)]
diff --git a/System/DotFS/Test/Unit.hs b/System/DotFS/Test/Unit.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Test/Unit.hs
@@ -0,0 +1,41 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports -fforce-recomp -fth #-}
+module System.DotFS.Test.Unit where
+
+import System.DotFS.Test.Utility -- our TH functions
+import System.DotFS.Test.Tests   -- our test cases
+import Control.Monad
+import Test.QuickCheck
+import System.DotFS.Core.Datatypes
+import Test.QuickCheck.Test
+import System.Exit
+
+runTests :: IO ()
+runTests = $(mkChecks tests)
+
+-- Arbitrary instances:
+
+-- there's actually a reason these instances are here, even if
+-- they seem like orphans. The TH above requires them, but ghc doesn't
+-- realise this at compile time.
+instance Arbitrary DFSExpr where
+    arbitrary  =  sized expr
+        where
+          expr n | n <= 0     =  atom
+                 | otherwise  =  oneof [ atom
+                                       , liftM2 UniOp unioperator subform
+                                       , liftM3 BiOp bioperator subform subform'
+                                       ]
+                 where
+                   atom = oneof [ liftM Var (elements ["P", "Q", "R", "S"])
+                                , liftM Prim arbitrary
+                                ]
+                   subform  = expr (n `div` 2)
+                   subform' = expr (n `div` 4)
+                   unioperator = elements [Not]
+                   bioperator  = elements [Add, Sub, Mul, Div, And, Or, Eq, LTOp, GTOp, GEQ, LEQ, NEQ]
+
+instance Arbitrary Value where
+    arbitrary = oneof [ liftM VBool   arbitrary
+                      , liftM VInt    arbitrary
+                      , liftM VString arbitraryStr
+                      ]
diff --git a/System/DotFS/Test/Unit.hs-boot b/System/DotFS/Test/Unit.hs-boot
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Test/Unit.hs-boot
@@ -0,0 +1,1 @@
+module System.DotFS.Test.Unit where
diff --git a/System/DotFS/Test/Utility.hs b/System/DotFS/Test/Utility.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Test/Utility.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module System.DotFS.Test.Utility where
+
+import Language.Haskell.Syntax
+import Language.Haskell.TH
+import Data.List
+import Test.QuickCheck
+import Test.QuickCheck.Test
+import Control.Monad (unless)
+import Language.Haskell.Parser
+import System.IO.Unsafe
+import System.IO
+import System.Exit
+
+{- | looks in Tests.hs for functions like prop_foo and returns
+  the list.  Requires that Tests.hs be valid Haskell98. -}
+tests :: [String]
+tests = unsafePerformIO $
+  do h <- openFile "System/DotFS/Test/Tests.hs" ReadMode
+     s <- hGetContents h
+     case parseModule s of
+       (ParseOk (HsModule _ _ _ _ ds)) -> return (map declName (filter isProp ds))
+       (ParseFailed loc s')            -> error (s' ++ " " ++ show loc)
+
+{- | checks if function binding name starts with @prop_@ indicating
+ that it is a quickcheck property -}
+isProp :: HsDecl -> Bool
+isProp d@(HsFunBind _) = "prop_" `isPrefixOf` declName d
+isProp _ = False
+
+{- | takes an HsDecl and returns the name of the declaration -}
+declName :: HsDecl -> String
+declName (HsFunBind (HsMatch _ (HsIdent name) _ _ _:_)) = name
+declName _                                              = undefined
+
+mkCheck :: String -> Q Exp
+mkCheck name = [| putStr (name ++ ": ")
+               >> do res <- quickCheckResult $(varE (mkName name))
+                     unless (isSuccess res) exitFailure
+               |]
+
+mkChecks :: [String] -> Q Exp
+mkChecks []        = undefined -- if we don't have any tests, then the test suite is undefined right?
+mkChecks [name]    = mkCheck name
+mkChecks (name:ns) = [| $(mkCheck name) >> $(mkChecks ns) |]
diff --git a/System/DotFS/Util/Debug.hs b/System/DotFS/Util/Debug.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Util/Debug.hs
@@ -0,0 +1,6 @@
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Util.Debug where
+
+-- TODO: nicer logging, to a variable filename (State / Reader monad?)
+debug :: String -> IO ()
+debug str = appendFile "/tmp/foo" (str ++ "\n")
diff --git a/System/DotFS/Util/Options.hs b/System/DotFS/Util/Options.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Util/Options.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Util.Options where
+
+import System.DotFS.Util.Version
+import System.Exit
+import System.Environment
+import System.Console.GetOpt
+import System.IO
+
+data Options = Options {
+    optLog :: String,
+    script :: Bool
+    }
+
+
+defaultOptions :: Options
+defaultOptions = Options { optLog = undefined, script = False }
+
+
+options :: [OptDescr (Options -> IO Options)]
+options =
+  [ Option "V?" ["version"] (NoArg printVersion) "show version number"
+  , Option "l"  ["log"] (ReqArg (\ arg opt -> return opt {optLog = arg})
+                          "FILE") "write log to FILE"
+  , Option "h"  ["help"] (NoArg printHelp) "show this help message"
+  , Option "g"  ["gen","gen-symlinks"] (NoArg printSyms) "generate a script to set symlinks"
+  ]
+
+printSyms :: Options -> IO Options
+printSyms os = do
+    hPutStrLn stderr "Printing script to stdout."
+    return $ os {script = True}
+
+printHelp :: Options -> IO Options
+printHelp _ = do
+  prg <- getProgName
+  hPutStrLn stderr "Usage:"
+  hPutStrLn stderr $ "\t"++prg++" [options] confdir mountpoint"
+  hPutStrLn stderr (usageInfo prg options)
+  exitSuccess
+
+
+printVersion :: Options -> IO Options
+printVersion _ = do
+  hPutStrLn stderr $ "DotFS v" ++ version ++ "\n\nCopyright 2012 Sjoerd Timmer, Paul van der Walt"
+  exitSuccess
+
+
diff --git a/System/DotFS/Util/Sanity.hs b/System/DotFS/Util/Sanity.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Util/Sanity.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Util.Sanity where
+
+import System.DotFS.Core.Datatypes
+import System.DotFS.Util.Options
+
+import System.IO
+import System.Directory
+import Control.Monad
+import System.Exit
+
+validateDirs :: [String] -> IO (String, Conf)
+validateDirs dirs =
+                   do
+                      existingDirs <- filterM doesDirectoryExist dirs
+                      canonicalDirs <- mapM canonicalizePath existingDirs
+                      if length canonicalDirs == 2 then do
+                          let (confdir : mountpoint : []) = canonicalDirs
+                          return (mountpoint, C confdir)
+                         else do
+                           hPutStrLn stderr "Invalid director(y|ies)"
+                           _ <- printHelp defaultOptions
+                           exitWith $ ExitFailure 1
+
+
diff --git a/System/DotFS/Util/Version.hs b/System/DotFS/Util/Version.hs
new file mode 100644
--- /dev/null
+++ b/System/DotFS/Util/Version.hs
@@ -0,0 +1,5 @@
+{-# LANGUAGE Haskell98 #-}
+module System.DotFS.Util.Version where
+
+version :: String
+version = "0.1"
diff --git a/dotfs.cabal b/dotfs.cabal
--- a/dotfs.cabal
+++ b/dotfs.cabal
@@ -1,5 +1,5 @@
 name:                dotfs
-version:             0.1.1.2
+version:             0.1.1.3
 synopsis:            Filesystem to manage and parse dotfiles
 description:         A system which, when pointed to a folder full of specially
                      annotated config files, will present these files tailored to
@@ -37,6 +37,46 @@
   ghc-options:      -Wall -rtsopts
   default-language: Haskell98
   main-is:          System/DotFS/Test/Main.hs
+
+Library
+  other-modules:      System.DotFS.Core.BodyParser,
+                      System.DotFS.Core.ExpressionParsers,
+                      System.DotFS.Core.Constants,
+                      System.DotFS.Core.Datatypes,
+                      System.DotFS.Core.ExpressionEvaluator,
+                      System.DotFS.Core.FSActions,
+                      System.DotFS.Core.FuseTypes,
+                      System.DotFS.Core.HeaderParser,
+                      System.DotFS.Core.HelperParsers,
+                      System.DotFS.Core.Lexers,
+                      System.DotFS.Core.Parsers,
+                      System.DotFS.Test.Tests,
+                      System.DotFS.Test.Unit,
+                      System.DotFS.Test.Utility,
+                      System.DotFS.Util.Debug,
+                      System.DotFS.Util.Options,
+                      System.DotFS.Util.Sanity,
+                      System.DotFS.Util.Version
+
+  default-language:   Haskell98
+  build-depends:      bytestring >=0.9,
+                      base >= 4 && < 5,
+                      HFuse > 0.2.4,
+                      directory>=1,
+                      unix >= 2.3,
+                      filepath >=1.1,
+                      parsec,
+                      containers,
+                      transformers,
+                      process,
+                      HUnit >= 1.2 && < 2,
+                      QuickCheck >= 2.4,
+                      test-framework >= 0.4.1,
+                      test-framework-quickcheck2,
+                      test-framework-hunit,
+                      parsec >= 3,
+                      haskell-src,
+                      template-haskell
 
 Executable            dotfs
   default-language:   Haskell98
