diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for chr-parse
+
+## 0.1.0.0  -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, Atze Dijkstra
+
+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.
+
+    * Neither the name of Atze Dijkstra nor the names of other
+      contributors may 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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/chr-parse.cabal b/chr-parse.cabal
new file mode 100644
--- /dev/null
+++ b/chr-parse.cabal
@@ -0,0 +1,38 @@
+-- Initial chr-parse.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                chr-parse
+version:             0.1.0.0
+synopsis:            Parsing for chr library
+description:         Parsing for chr library, currently wrapping around the parsing lib from uulib and adding some utilities
+homepage:            https://github.com/atzedijkstra/chr
+Bug-Reports:         https://github.com/atzedijkstra/chr/issues
+license:             BSD3
+license-file:        LICENSE
+author:              Atze Dijkstra
+maintainer:          atzedijkstra@gmail.com
+-- copyright:           
+category:            Development
+build-type:          Simple
+extra-source-files:  ChangeLog.md
+cabal-version:       >=1.10
+
+source-repository head
+  type:     git
+  location: git@github.com:atzedijkstra/chr.git
+
+library
+  exposed-modules:
+    CHR.Parse,
+    CHR.Scan
+  -- other-modules:       
+  other-extensions:
+    RankNTypes,
+    CPP,
+    FlexibleContexts
+  build-depends:
+    base >=4.9 && < 5,
+    uulib >=0.9 && <0.10,
+    containers >=0.5 && <0.6
+  hs-source-dirs:      src
+  default-language:    Haskell2010
diff --git a/src/CHR/Parse.hs b/src/CHR/Parse.hs
new file mode 100644
--- /dev/null
+++ b/src/CHR/Parse.hs
@@ -0,0 +1,208 @@
+{-# LANGUAGE RankNTypes, FlexibleContexts, CPP #-}
+
+module CHR.Parse
+  ( module UU.Parsing
+
+    -- * Specific parser types
+  , PlainParser
+  , LayoutParser, LayoutParser2
+  
+  -- * Top level wrappers/invocations
+  , parsePlain
+  , parseOffsideToResMsgs
+  , parseToResMsgs
+  , parseToResWith
+  
+  , parseOffsideToResMsgsStopAtErr
+  
+  -- * Additional parser combinators
+  , pAnyFromMap, pAnyKey
+  , pMaybe, pMb
+  
+  , pDo
+  
+  -- * Re-exports
+  , position
+  
+  -- * Dealing with Message
+  , fromMessage
+  )
+  where
+
+#if __GLASGOW_HASKELL__ >= 710
+import Prelude hiding ( (<*>), (<*), (*>), (<$>), (<$) )
+#else
+#endif
+
+import           UU.Parsing
+import           UU.Parsing.Machine
+import           UU.Parsing.Offside
+import           UU.Scanner.Position( Position(..) )
+import           UU.Scanner.GenToken
+
+import qualified Data.Map as Map
+import           Data.Maybe
+
+-------------------------------------------------------------------------
+-- Type(s) of parsers
+-------------------------------------------------------------------------
+
+type LayoutParser tok ep
+  = forall i o p .
+    (IsParser (OffsideParser i o tok p) tok,InputState i tok p, OutputState o, Position p)
+       => OffsideParser i o tok p ep
+
+type LayoutParser2 tok ep
+  = forall i o p .
+    (IsParser (OffsideParser i o tok p) tok,InputState i tok p, OutputState o, Position p)
+       => OffsideParser i o tok p ep -> OffsideParser i o tok p ep
+
+type PlainParser tok gp = forall p . IsParser p tok => p gp
+
+-------------------------------------------------------------------------
+-- Parsing utils
+-------------------------------------------------------------------------
+
+valFromPair :: Steps (Pair a (Pair a1 r)) s p -> Steps (a, a1) s p
+valFromPair p
+  = val fromPair p
+  where fromPair (Pair x (Pair y _)) = (x,y)
+
+toResMsgs :: Steps (Pair a r) s pos -> (a, [Message s pos])
+toResMsgs steps
+  = (r,getMsgs steps)
+  where (Pair r _) = evalSteps steps
+
+toOffsideResMsgs :: Steps (a,b) s pos -> (a, [Message s pos])
+toOffsideResMsgs steps
+  = r `seq` (r,getMsgs steps)
+  where (r,_) = evalSteps steps
+
+parsePlain :: (Symbol s, InputState inp s pos) 
+      => AnaParser inp Pair s pos a 
+      -> inp 
+      -> Steps (a, inp) s pos
+parsePlain p inp
+  = valFromPair (parse p inp)
+
+-- | Invoke parser, yielding result + errors
+parseToResMsgs :: (Symbol s, InputState inp s pos) => AnaParser inp Pair s pos a -> inp -> (a,[Message s pos])
+parseToResMsgs p inp
+  = toResMsgs (parse p inp)
+
+-- | Invoke parser, yielding result + errors processed with a function
+parseToResWith :: (Symbol s, Show s, Eq s, InputState inp s pos) => (pos -> String -> String -> e) -> AnaParser inp Pair s pos a -> inp -> (a,[e])
+parseToResWith f p inp
+  = (r, map (fromMessage f) e)
+  where (r,e) = toResMsgs (parse p inp)
+
+parseOffsideToResMsgs
+  :: (Symbol s, InputState i s p, Position p)
+       => OffsideParser i Pair s p a -> OffsideInput i s p -> (a,[Message (OffsideSymbol s) p])
+parseOffsideToResMsgs p inp
+  = toOffsideResMsgs (parseOffside p inp)
+
+-------------------------------------------------------------------------
+-- Parsing, stopping at first error
+-------------------------------------------------------------------------
+
+handleEofStopAtErr input
+  = case splitStateE input
+       of Left'  s  ss  ->  NoMoreSteps (Pair ss ())
+          Right' ss     ->  NoMoreSteps (Pair ss ())
+
+parseStopAtErr
+  :: (Symbol s, InputState inp s pos) 
+      => AnaParser inp Pair s pos a 
+      -> inp 
+      -> Steps (Pair a (Pair inp ())) s pos
+parseStopAtErr
+  = parsebasic handleEofStopAtErr
+
+parseOffsideStopAtErr
+  :: (Symbol s, InputState i s p, Position p) 
+     => OffsideParser i Pair s p a 
+     -> OffsideInput i s p
+     -> Steps (a, OffsideInput i s p) (OffsideSymbol s) p
+parseOffsideStopAtErr (OP p) inp
+  = valFromPair (parseStopAtErr p inp)
+
+parseOffsideToResMsgsStopAtErr
+  :: (Symbol s, InputState i s p, Position p) =>
+     OffsideParser i Pair s p a
+     -> OffsideInput i s p
+     -> (a, [Message (OffsideSymbol s) p])
+parseOffsideToResMsgsStopAtErr p inp
+  = toOffsideResMsgs (parseOffsideStopAtErr p inp)
+
+-------------------------------------------------------------------------
+-- Offside for 'do' notation.
+-- Problem tackled here is that both do statements and the last expr may start with 'let x=e',
+-- and the presence of 'in e' following 'let x=e' indicates that it is the last statement.
+-- This is a variation of pBlock1.
+-------------------------------------------------------------------------
+
+pDo :: (InputState i s p, OutputState o, Position p, Symbol s, Ord s) 
+       => OffsideParser i o s p x 
+       -> OffsideParser i o s p y 
+       -> OffsideParser i o s p z 
+       -> OffsideParser i o s p a 
+       -> OffsideParser i o s p (Maybe last -> a)
+       -> OffsideParser i o s p last 
+       -> OffsideParser i o s p [a]
+pDo open sep close pPlain pLastPrefix pLastRest
+  = pOffside open close explicit implicit
+  where sep'    = () <$ sep
+        elems s = sep0 *> es <* sep0
+                where es =   (:) <$> pPlain <*> esTail
+                         <|> (pLastPrefix
+                              <**> (   (\r pre -> [pre (Just r)]) <$> pLastRest
+                                   <|> (\tl pre -> pre Nothing : tl) <$> esTail
+                                   )
+                             )
+                      esTail = pList1 s *> es <|> pSucceed []
+                      sep0 = pList s
+        explicit = elems sep'
+        implicit = elems (sep' <|> pSeparator)
+
+{-
+pBlock1 :: (InputState i s p, OutputState o, Position p, Symbol s, Ord s) 
+       => OffsideParser i o s p x 
+       -> OffsideParser i o s p y 
+       -> OffsideParser i o s p z 
+       -> OffsideParser i o s p a 
+       -> OffsideParser i o s p [a]
+pBlock1 open sep close p =  pOffside open close explicit implicit
+ where sep'    = () <$ sep
+       elems s = pList s *> pList1Sep (pList1 s) p <* pList s
+       explicit = elems sep'
+       implicit = elems (sep' <|> pSeparator)
+
+-}
+
+-------------------------------------------------------------------------
+-- Misc combinators
+-------------------------------------------------------------------------
+
+-- parse possibly present p
+pMaybe :: (IsParser p s) => a1 -> (a -> a1) -> p a -> p a1
+pMaybe n j p = j <$> p <|> pSucceed n
+
+pAnyKey :: (IsParser p s) => (a1 -> p a) -> [a1] -> p a
+pAnyKey pKey = foldr1 (<|>) . map pKey
+
+pMb :: (IsParser p s) => p a -> p (Maybe a)
+pMb = pMaybe Nothing Just
+
+-- given (non-empty) key->value map, return parser for all keys returning corresponding value
+pAnyFromMap :: (IsParser p s) => (k -> p a1) -> Map.Map k v -> p v
+pAnyFromMap pKey m = foldr1 (<|>) [ v <$ pKey k | (k,v) <- Map.toList m ]
+
+-------------------------------------------------------------------------
+-- Dealing with error Message
+-------------------------------------------------------------------------
+
+-- | Convert from Message to anything using a function taking as String position, expected symbol and action taken respectively
+fromMessage :: (Show s, Eq s) => (p -> String -> String -> x) -> Message s p -> x
+fromMessage f (Msg e p a) = f p (show e) (show a)
+
diff --git a/src/CHR/Scan.hs b/src/CHR/Scan.hs
new file mode 100644
--- /dev/null
+++ b/src/CHR/Scan.hs
@@ -0,0 +1,195 @@
+module CHR.Scan
+  ( module UU.Scanner
+  
+  , ScanOpts(..), defaultScanOpts
+
+  , isNoPos, posIs1stColumn
+
+  , InFilePos(..), infpStart, infpNone
+  , infpAdvCol, infpAdvLine, infpAdv1Line, infpAdvStr
+
+  , genTokVal, genTokTp, genTokMap
+
+  , isLF, isStr, isStrQuote
+  , isWhite, isBlack
+  , isVarStart, isVarRest
+
+  )
+  where
+
+import           System.IO
+import           Data.Char
+import           Data.List
+import qualified Data.Set as Set
+import qualified Data.Map as Map
+
+import           CHR.Parse
+
+import           UU.Parsing
+import           UU.Scanner
+import           UU.Scanner.Position( noPos, Pos(..), Position(..) )
+import           UU.Scanner.GenToken
+
+-------------------------------------------------------------------------
+-- Utils for GenToken
+-------------------------------------------------------------------------
+
+genTokVal :: GenToken v t v -> v
+genTokVal (ValToken _ v _) = v
+genTokVal (Reserved   v _) = v
+
+genTokTp :: GenToken k t v -> Maybe t
+genTokTp (ValToken t _ _) = Just t
+genTokTp _                = Nothing
+
+genTokMap :: (a->b) -> GenToken a t a -> GenToken b t b
+genTokMap f (ValToken t v p) = ValToken t (f v) p
+genTokMap f (Reserved   k p) = Reserved   (f k) p
+
+-------------------------------------------------------------------------
+-- Utils for Pos
+-------------------------------------------------------------------------
+
+isNoPos :: Pos -> Bool
+isNoPos (Pos l c f) = l < 0 || c < 0
+
+posIs1stColumn :: Pos -> Bool
+posIs1stColumn p = column p == 1
+
+-------------------------------------------------------------------------
+-- InFilePos: Simplified Pos for inside a file only
+-------------------------------------------------------------------------
+
+data InFilePos
+  = InFilePos { infpLine, infpColumn :: Int }
+  deriving (Eq,Ord)
+
+instance Show InFilePos where
+  show (InFilePos l c) = if l < 0 || c < 0 then "" else "(" ++ show l ++ ":" ++ show c ++ ")"
+
+infpStart :: InFilePos
+infpStart = InFilePos 1 1
+
+infpNone :: InFilePos
+infpNone = InFilePos (-1) (-1)
+
+infpAdvCol :: Int -> InFilePos -> InFilePos
+infpAdvCol i p = p {infpColumn = i + infpColumn p}
+
+infpAdvStr :: String -> InFilePos -> InFilePos
+infpAdvStr s p = infpAdvCol (length s) p
+
+infpAdvLine :: Int -> InFilePos -> InFilePos
+infpAdvLine i p = p {infpLine = i + infpLine p, infpColumn = 1}
+
+infpAdv1Line :: InFilePos -> InFilePos
+infpAdv1Line = infpAdvLine 1
+
+-------------------------------------------------------------------------
+-- PP of parse errors
+-------------------------------------------------------------------------
+
+instance Position p => Position (Maybe p) where
+  line   = maybe (line   noPos) line
+  column = maybe (column noPos) column
+  file   = maybe (file   noPos) file
+
+instance Position (GenToken k t v) where
+  line   = line   . position
+  column = column . position
+  file   = file   . position
+
+{-
+instance Show Pos where
+  show (Pos l c f) = "(" ++ (if null f then "" else f ++ ":" ) ++ show l ++ "," ++ show c ++ ")"
+
+instance PP Pos where
+  pp (Pos l c f) = ppParens $ (if null f then empty else pp f >|< ":" ) >|< l >|< "," >|< c
+-}
+
+-------------------------------------------------------------------------
+-- ScanOpts
+-------------------------------------------------------------------------
+
+{-
+ScanOpts encode all possible options we ever might want to pass to a scanner used inside the EHC project.
+Hence not all options are used by all scanners.
+-}
+
+data ScanOpts
+  =  ScanOpts
+        {   scoKeywordsTxt      ::  !(Set.Set String)       -- identifiers which are keywords
+        ,   scoPragmasTxt       ::  !(Map.Map String Bool)  -- identifiers which are pragmas, associated with yes/no parse remainder as string literal
+        ,   scoCommandsTxt      ::  !(Set.Set String)       -- identifiers which are commands
+        ,   scoKeywordsOps      ::  !(Set.Set String)       -- operators which are keywords
+        ,   scoKeywExtraChars   ::  !(Set.Set Char)         -- extra chars to be used by identifiers
+        ,   scoSpecChars        ::  !(Set.Set Char)         -- 1 char keywords
+        ,   scoStringDelims     ::  !String                 -- allowed delimiter for string
+        ,   scoOpChars          ::  !(Set.Set Char)         -- chars used for operators
+        ,   scoSpecPairs        ::  !(Set.Set String)       -- pairs of chars which form keywords
+        ,   scoDollarIdent      ::  !Bool                   -- allow $ encoded identifiers
+        ,   scoOffsideTrigs     ::  ![String]               -- offside triggers
+        ,   scoOffsideTrigsGE   ::  ![String]               -- offside triggers, but allowing equal indentation (for HS 'do' notation, as per Haskell2010)
+        ,   scoOffsideModule    ::  !String                 -- offside start of module
+        ,   scoOffsideOpen      ::  !String                 -- offside open symbol
+        ,   scoOffsideClose     ::  !String                 -- offside close symbol
+        ,   scoLitmode          ::  !Bool                   -- do literal scanning
+        ,   scoVerbOpenClose    ::  ![(String,String)]      -- open/close pairs used for verbatim text
+        ,   scoAllowQualified   ::  !Bool                   -- allow qualified variations, i.e. prefixing with "XXX."
+        ,   scoAllowFloat       ::  !Bool                   -- allow float notation, i.e. numbers with dots inside
+        }
+
+defaultScanOpts :: ScanOpts
+defaultScanOpts
+  =  ScanOpts
+        {   scoKeywordsTxt      =   Set.empty
+        ,   scoPragmasTxt       =   Map.empty
+        ,   scoCommandsTxt      =   Set.empty
+        ,   scoKeywordsOps      =   Set.empty
+        ,   scoKeywExtraChars   =   Set.empty
+        ,   scoSpecChars        =   Set.empty
+        ,   scoStringDelims     =   "\""
+        ,   scoOpChars          =   Set.empty
+        ,   scoSpecPairs        =   Set.empty
+        ,   scoDollarIdent      =   False
+        ,   scoOffsideTrigs     =   []
+        ,   scoOffsideTrigsGE   =   []
+        ,   scoOffsideModule    =   ""
+        ,   scoOffsideOpen      =   ""
+        ,   scoOffsideClose     =   ""
+        ,   scoLitmode          =   False
+        ,   scoVerbOpenClose    =   []
+        ,   scoAllowQualified   =   True
+        ,   scoAllowFloat       =   True
+        }
+
+-------------------------------------------------------------------------
+-- Char predicates
+-------------------------------------------------------------------------
+
+isLF :: Char -> Bool
+isLF = (`elem` "\n\r")
+
+isStrQuote :: Char -> Bool
+isStrQuote c = c == '"'
+
+isStr :: Char -> Bool
+isStr c = not (isStrQuote c || isLF c)
+
+isVarStart :: Char -> Bool
+isVarStart c = c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
+
+isVarRest :: Char -> Bool
+isVarRest c = isVarStart c || isDigit c || c `elem` "'_"
+
+isWhite :: Char -> Bool
+isWhite = (`elem` " \t")
+
+{-
+isDig :: Char -> Bool
+isDig c = c >= '0' && c <= '9'
+-}
+
+isBlack :: Char -> Bool
+isBlack c = not (isWhite c || isLF c)
+
