diff --git a/Language/Nock5K.hs b/Language/Nock5K.hs
new file mode 100644
--- /dev/null
+++ b/Language/Nock5K.hs
@@ -0,0 +1,5 @@
+module Language.Nock5K (Noun(Atom, (:-)), nock, noun, repl) where
+
+import Language.Nock5K.Parse
+import Language.Nock5K.Repl
+import Language.Nock5K.Spec
diff --git a/Language/Nock5K/Parse.hs b/Language/Nock5K/Parse.hs
new file mode 100644
--- /dev/null
+++ b/Language/Nock5K/Parse.hs
@@ -0,0 +1,23 @@
+module Language.Nock5K.Parse where
+
+import Control.Applicative ((<$>))
+import Language.Nock5K.Spec
+import Text.ParserCombinators.Parsec
+
+instance Show Noun where
+  show (Atom a) = show a
+  show x@(_ :- _) = "[" ++ showCell x ++ "]"
+    where showCell (a :- b) = show a ++ " " ++ showCell b
+          showCell a = show a
+
+noun :: Parser Noun
+noun = atom <|> cell
+
+atom :: Parser Noun
+atom = (Atom . read) <$> many1 digit
+
+cell :: Parser Noun
+cell = foldr1 (:-) <$> (noun `sepBy2` char ' ') `surroundBy` "[]"
+  where
+    p `sepBy2` sep = do { a <- p; sep; bs <- p `sepBy1` sep; return (a:bs) }
+    p `surroundBy` (s:e:_) = do { char s; a <- p; char e; return a }
diff --git a/Language/Nock5K/Repl.hs b/Language/Nock5K/Repl.hs
new file mode 100644
--- /dev/null
+++ b/Language/Nock5K/Repl.hs
@@ -0,0 +1,19 @@
+module Language.Nock5K.Repl where
+
+import Language.Nock5K.Parse
+import Language.Nock5K.Spec
+import qualified Control.Exception as C
+import System.Console.Readline
+import Text.ParserCombinators.Parsec
+
+repl = do ln <- readline "nock "
+          case ln of
+            Nothing     -> return ()
+            Just "exit" -> return ()
+            Just s -> do addHistory s
+                         case parse noun "" s of
+                           Left e -> print e
+                           Right n -> ep n
+                         repl
+  where
+    ep n = (print . nock) n `C.catch` (\e -> print (e :: C.SomeException))
diff --git a/Language/Nock5K/Spec.lhs b/Language/Nock5K/Spec.lhs
new file mode 100644
--- /dev/null
+++ b/Language/Nock5K/Spec.lhs
@@ -0,0 +1,144 @@
+
+> module Language.Nock5K.Spec where
+
+1 Structures
+
+  A noun is an atom or a cell.  An atom is any natural number.
+  A cell is an ordered pair of nouns.
+
+> data Noun = Atom Integer | Noun :- Noun deriving (Eq)
+
+
+2 Reductions
+
+  nock(a)           *a
+
+> nock :: Noun -> Noun
+> nock = tar
+
+  [a b c]           [a [b c]]
+
+> infixr 1 :-
+
+
+  ?[a b]            0
+
+> wut (a :- b) = Atom 0
+
+  ?a                1
+
+> wut a = Atom 1
+
+  +[a b]            +[a b]
+
+> lus (a :- b) = error "+[a b]"
+
+  +a                1 + a
+
+> lus (Atom a) = Atom (1 + a)
+
+  =[a a]            0
+
+> tis (a :- a') | a == a' = Atom 0
+
+  =[a b]            1
+
+> tis (a :- b) = Atom 1
+
+  =a                =a
+
+> tis a = error "=a"
+
+
+  /[1 a]            a
+
+> fas (Atom 1 :- a) = a
+
+  /[2 a b]          a
+
+> fas (Atom 2 :- a :- b) = a
+
+  /[3 a b]          b
+
+> fas (Atom 3 :- a :- b) = b
+
+  /[(a + a) b]      /[2 /[a b]]
+
+> fas (Atom a :- b) | a > 2 && a `mod` 2 == 0 =
+>   fas $ Atom 2 :- fas (Atom (a `div` 2) :- b)
+
+  /[(a + a + 1) b]  /[3 /[a b]]
+
+> fas (Atom a :- b) | a > 3 && a `mod` 2 == 1 =
+>   fas $ Atom 3 :- fas (Atom (a `div` 2) :- b)
+
+  /a                /a
+
+> fas a = error "/a"
+
+
+  *[a [b c] d]      [*[a b c] *[a d]]
+
+> tar (a :- (b :- c) :- d) = tar (a :- b :- c) :- tar (a :- d)
+
+
+  *[a 0 b]          /[b a]
+
+> tar (a :- (Atom 0 :- b)) = fas $ b :- a
+
+  *[a 1 b]          b
+
+> tar (a :- (Atom 1 :- b)) = b
+
+  *[a 2 b c]        *[*[a b] *[a c]]
+
+> tar (a :- Atom 2 :- b :- c) = tar $ tar (a :- b) :- tar (a :- c)
+
+  *[a 3 b]          ?*[a b]
+
+> tar (a :- Atom 3 :- b) = (wut.tar) (a :- b)
+
+  *[a 4 b]          +*[a b]
+
+> tar (a :- Atom 4 :- b) = (lus.tar) (a :- b)
+
+  *[a 5 b]          =*[a b]
+
+> tar (a :- Atom 5 :- b) = (tis.tar) (a :- b)
+
+
+  *[a 6 b c d]      *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
+
+> tar (a :- Atom 6 :- b :- c :- d) =
+>   tar (a :- Atom 2 :- (Atom 0 :- Atom 1) :- Atom 2 :- (Atom 1 :- c :- d) :-
+>        (Atom 1 :- Atom 0) :- Atom 2 :- (Atom 1 :- Atom 2 :- Atom 3) :-
+>        (Atom 1 :- Atom 0) :- Atom 4 :- Atom 4 :- b)
+
+  *[a 7 b c]        *[a 2 b 1 c]
+
+> tar (a :- Atom 7 :- b :- c) = tar $ a :- Atom 2 :- b :- Atom 1 :- c
+
+  *[a 8 b c]        *[a 7 [[7 [0 1] b] 0 1] c]
+
+> tar (a :- Atom 8 :- b :- c) =
+>   tar (a :- Atom 7 :- ((Atom 7 :- (Atom 0 :- Atom 1) :- b) :-
+>         Atom 0 :- Atom 1) :- c)
+
+  *[a 9 b c]        *[a 7 c 2 [0 1] 0 b]
+
+> tar (a :- Atom 9 :- b :- c) =
+>   tar (a :- Atom 7 :- c :- Atom 2 :- (Atom 0 :- Atom 1) :- Atom 0 :- b)
+
+  *[a 10 [b c] d]   *[a 8 c 7 [0 3] d]
+
+> tar (a :- Atom 10 :- (b :- c) :- d) =
+>   tar (a :- Atom 8 :- c :- Atom 7 :- (Atom 0 :- Atom 3) :- d)
+
+  *[a 10 b c]       *[a c]
+
+> tar (a :- Atom 10 :- b :- c) = tar $ a :- c
+
+
+  *a                *a
+
+> tar a = error "*a"
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,13 @@
+Usage:
+./hsnock.lhs
+
+Try forms like:
+[42 1 0]
+[42 0 1]
+[[1 3 2] 0 3]
+[42 [8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1]]
+[[42 43] [8 [1 0] [8 [1 [6 [5 [0 6] [0 14]] [1 0] [6 [5 [0 6] [0 15]] [1 1] [9 2 [[0 2] [4 0 6] [0 7]]]]]] [9 2 0 1]]]]
+[[43 42] [8 [1 0] [8 [1 [6 [5 [0 6] [0 14]] [1 0] [6 [5 [0 6] [0 15]] [1 1] [9 2 [[0 2] [4 0 6] [0 7]]]]]] [9 2 0 1]]]]
+
+See also:
+http://www.urbit.org/2013/08/22/Chapter-2-nock.html
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/hsnock.cabal b/hsnock.cabal
new file mode 100644
--- /dev/null
+++ b/hsnock.cabal
@@ -0,0 +1,38 @@
+name                 : hsnock
+version              : 0.1.3
+category             : Language
+license              : PublicDomain
+synopsis             : Nock 5K interpreter.
+author               : Steven Dee
+maintainer           : mrdomino@gmail.com
+homepage             : https://github.com/mrdomino/hsnock/
+
+build-type           : Simple
+cabal-version        : >=1.8
+data-files           : README
+
+source-repository      head
+  type               : git
+  location           : https://github.com/mrdomino/hsnock.git
+
+library
+  build-depends      : base >=4.5 && <5
+                     , parsec >=3.1
+                     , readline >=1.0
+  exposed-modules    : Language.Nock5K
+                     , Language.Nock5K.Parse
+                     , Language.Nock5K.Spec
+                     , Language.Nock5K.Repl
+
+executable             hsnock
+  main-is            : hsnock.lhs
+  build-depends      : base >=4.5 && <5
+                     , parsec >=3.1
+                     , readline >=1.0
+
+test-suite test
+  type               : exitcode-stdio-1.0
+  main-is            : test.hs
+  build-depends      : base >=4.5 && <5
+                     , parsec >=3.1
+                     , QuickCheck >=2.6
diff --git a/hsnock.lhs b/hsnock.lhs
new file mode 100644
--- /dev/null
+++ b/hsnock.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Language.Nock5K
+> main = repl
diff --git a/test.hs b/test.hs
new file mode 100644
--- /dev/null
+++ b/test.hs
@@ -0,0 +1,38 @@
+import Control.Applicative
+import Language.Nock5K
+import Test.QuickCheck
+import Text.ParserCombinators.Parsec (parse)
+import Text.Printf
+
+main = mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests
+
+instance Arbitrary Noun where
+  arbitrary = choose (0, 32) >>= arbD
+    where
+      arbD :: Int -> Gen Noun
+      arbD 0 = (Atom . abs) <$> arbitrary
+      arbD n = do coin <- arbitrary
+                  if coin
+                    then (Atom . abs) <$> arbitrary
+                    else (:-) <$> arbD (n - 1) <*> arbD (n - 1)
+
+parsenoun n = case parse noun "" n of
+  Left e -> error "parse"
+  Right n -> n
+
+prop_parse_show n = n == (parsenoun . show) n
+
+prop_dec a' = nock (Atom (a + 1) :- dec) == Atom a
+  where
+    ds = "[8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1]"
+    dec = parsenoun ds
+    a = abs a'
+
+prop_6_is_if a' b = nock (ifs $ Atom 0) == Atom (a + 1) && nock (ifs $ Atom 1) == b
+  where
+    ifs c = Atom a :- Atom 6 :- (Atom 1 :- c) :- (Atom 4 :- Atom 0 :- Atom 1) :- (Atom 1 :- b)
+    a = abs a'
+
+tests = [("parse_show", quickCheck prop_parse_show)
+        ,("decrement", quickCheck prop_dec)
+        ,("6_is_if", quickCheck prop_6_is_if)]
