diff --git a/argo.cabal b/argo.cabal
--- a/argo.cabal
+++ b/argo.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.2
 
 name: argo
-version: 0.2021.11.2
+version: 0.2021.11.7
 
 build-type: Simple
 category: JSON
@@ -86,6 +86,8 @@
         Argo.Literal
         Argo.Main
         Argo.Pattern
+        Argo.Pointer.Pointer
+        Argo.Pointer.Token
         Argo.QuasiQuoter
         Argo.Result
     hs-source-dirs: source/library
diff --git a/source/benchmark/Main.hs b/source/benchmark/Main.hs
--- a/source/benchmark/Main.hs
+++ b/source/benchmark/Main.hs
@@ -44,7 +44,7 @@
             , Tasty.bench "10000 elements" . Tasty.nf encode . Argo.Object . replicate 10000 $ Argo.Member (Argo.Name "") Argo.Null
             ]
         ]
-    , Tasty.bgroup "decode" $ let decode = resultToMaybe . Argo.decode :: ByteString.ByteString -> Maybe Argo.Value in
+    , Tasty.bgroup "decode" $ let decode = Argo.decode :: ByteString.ByteString -> Argo.Result Argo.Value in
         [ Tasty.bgroup "Null"
             [ Tasty.bench "null" $ Tasty.nf decode "null"
             ]
@@ -86,9 +86,8 @@
             , Tasty.bench "10000 elements" . Tasty.nf decode $ "{\"\":null" <> ByteString.pack (take (5 * 9999) $ cycle [0x2c, 0x22, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c]) <> "}"
             ]
         ]
+    , Tasty.bgroup "Pointer"
+        [ Tasty.bench "decode" $ Tasty.nf Argo.decodePointer ""
+        , Tasty.bench "encode" . Tasty.nf Builder.toLazyByteString . Argo.encodePointer $ Argo.Pointer []
+        ]
     ]
-
-resultToMaybe :: Argo.Result a -> Maybe a
-resultToMaybe r = case r of
-    Argo.Failure _ -> Nothing
-    Argo.Success x -> Just x
diff --git a/source/library/Argo.hs b/source/library/Argo.hs
--- a/source/library/Argo.hs
+++ b/source/library/Argo.hs
@@ -22,7 +22,13 @@
     , FromValue.FromValue(fromValue)
     , ToValue.ToValue(toValue)
     , QuasiQuoter.value
+    , QuasiQuoter.pointer
     , Result.Result(Failure, Success)
+    , Pointer.Pointer(Pointer.Pointer)
+    , Token.Token(Token.Token)
+    , Pointer.evaluate
+    , Encode.encodePointer
+    , Decode.decodePointer
     ) where
 
 import qualified Argo.Class.FromValue as FromValue
@@ -32,6 +38,8 @@
 import qualified Argo.Encoder as Encoder
 import qualified Argo.Json.Name as Name
 import qualified Argo.Json.Value as Value
+import qualified Argo.Pointer.Pointer as Pointer
+import qualified Argo.Pointer.Token as Token
 import qualified Argo.Pattern as Pattern
 import qualified Argo.QuasiQuoter as QuasiQuoter
 import qualified Argo.Result as Result
diff --git a/source/library/Argo/Class/FromValue.hs b/source/library/Argo/Class/FromValue.hs
--- a/source/library/Argo/Class/FromValue.hs
+++ b/source/library/Argo/Class/FromValue.hs
@@ -2,9 +2,11 @@
 
 module Argo.Class.FromValue where
 
+import qualified Argo.Decoder as Decoder
 import qualified Argo.Json.Number as Number
 import qualified Argo.Json.Value as Value
 import qualified Argo.Pattern as Pattern
+import qualified Argo.Pointer.Pointer as Pointer
 import qualified Argo.Result as Result
 import qualified Argo.Vendor.Text as Text
 import qualified Data.Bits as Bits
@@ -109,6 +111,12 @@
     fromValue = withObject "Map"
         $ fmap Map.fromList
         . traverse (\ (Pattern.Member (Pattern.Name k) v) -> (,) k <$> fromValue v)
+
+instance FromValue Pointer.Pointer where
+    fromValue = withString "Pointer"
+        $ fmap snd
+        . Decoder.run (Pointer.decode <* Decoder.eof)
+        . Text.encodeUtf8
 
 withBoolean :: String -> (Bool -> Result.Result a) -> Value.Value -> Result.Result a
 withBoolean s f x = case x of
diff --git a/source/library/Argo/Class/ToValue.hs b/source/library/Argo/Class/ToValue.hs
--- a/source/library/Argo/Class/ToValue.hs
+++ b/source/library/Argo/Class/ToValue.hs
@@ -2,9 +2,13 @@
 
 module Argo.Class.ToValue where
 
+import qualified Argo.Encoder as Encoder
 import qualified Argo.Json.Number as Number
 import qualified Argo.Json.Value as Value
 import qualified Argo.Pattern as Pattern
+import qualified Argo.Pointer.Pointer as Pointer
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.ByteString as ByteString
 import qualified Argo.Vendor.Text as Text
 import qualified Data.Int as Int
 import qualified Data.List as List
@@ -92,6 +96,15 @@
     toValue x = Pattern.Object
         . fmap (\ (k, v) -> Pattern.Member (Pattern.Name k) (toValue v))
         $ Map.toAscList x
+
+instance ToValue Pointer.Pointer where
+    toValue = either (error . mappend "Pointer.toValue: " . show) toValue
+        . Text.decodeUtf8'
+        . ByteString.toStrict
+        . Builder.toLazyByteString
+        . snd
+        . Encoder.run Encoder.defaultConfig
+        . Pointer.encode
 
 realFloatToValue :: RealFloat a => a -> Value.Value
 realFloatToValue x
diff --git a/source/library/Argo/Codec.hs b/source/library/Argo/Codec.hs
--- a/source/library/Argo/Codec.hs
+++ b/source/library/Argo/Codec.hs
@@ -242,7 +242,7 @@
         l <- Trans.get
         case l of
             [] -> Trans.lift $ Trans.throwE "unexpected empty list"
-            h : t ->  case decodeWith c h of
+            h : t -> case decodeWith c h of
                 Result.Failure y -> Trans.lift $ Trans.throwE y
                 Result.Success y -> do
                     Trans.put t
diff --git a/source/library/Argo/Decode.hs b/source/library/Argo/Decode.hs
--- a/source/library/Argo/Decode.hs
+++ b/source/library/Argo/Decode.hs
@@ -3,11 +3,16 @@
 import qualified Argo.Class.FromValue as FromValue
 import qualified Argo.Decoder as Decoder
 import qualified Argo.Json.Value as Value
+import qualified Argo.Pointer.Pointer as Pointer
 import qualified Argo.Result as Result
 import qualified Argo.Vendor.ByteString as ByteString
 
 decode :: FromValue.FromValue a => ByteString.ByteString -> Result.Result a
-decode x =
-    case Decoder.run (Decoder.spaces *> Value.decode <* Decoder.eof) x of
-        Result.Failure e -> Result.Failure e
-        Result.Success (_, y) -> FromValue.fromValue y
+decode x = do
+    (_, y) <- Decoder.run (Decoder.spaces *> Value.decode <* Decoder.eof) x
+    FromValue.fromValue y
+
+decodePointer :: ByteString.ByteString -> Result.Result Pointer.Pointer
+decodePointer =
+    let d = Pointer.decode <* Decoder.eof
+    in fmap snd . Decoder.run d
diff --git a/source/library/Argo/Encode.hs b/source/library/Argo/Encode.hs
--- a/source/library/Argo/Encode.hs
+++ b/source/library/Argo/Encode.hs
@@ -4,24 +4,25 @@
 import qualified Argo.Encoder as Encoder
 import qualified Argo.Json.Value as Value
 import qualified Argo.Literal as Literal
+import qualified Argo.Pointer.Pointer as Pointer
 import qualified Argo.Vendor.Builder as Builder
 import qualified Argo.Vendor.Transformers as Trans
 import qualified Control.Monad as Monad
-import qualified Data.Functor.Identity as Identity
 
 encode :: ToValue.ToValue a => a -> Builder.Builder
 encode = encodeWith $ Encoder.Spaces 0
 
 encodeWith :: ToValue.ToValue a => Encoder.Indent -> a -> Builder.Builder
 encodeWith i x =
-    let c = Encoder.Config { Encoder.indent = i, Encoder.level = 0 }
-    in snd
-    . Identity.runIdentity
-    . Trans.runWriterT
-    . flip Trans.runReaderT c
-    $ do
+    let c = Encoder.defaultConfig { Encoder.indent = i }
+    in snd . Encoder.run c $ do
         Value.encode $ ToValue.toValue x
         Monad.when (Encoder.hasIndent c)
             . Trans.lift
             . Trans.tell
             $ Builder.word8 Literal.newLine
+
+encodePointer :: Pointer.Pointer -> Builder.Builder
+encodePointer =
+    let c = Encoder.defaultConfig
+    in snd . Encoder.run c . Pointer.encode
diff --git a/source/library/Argo/Encoder.hs b/source/library/Argo/Encoder.hs
--- a/source/library/Argo/Encoder.hs
+++ b/source/library/Argo/Encoder.hs
@@ -9,10 +9,19 @@
 
 type Encoder = Trans.ReaderT Config (Trans.WriterT Builder.Builder Identity.Identity)
 
+run :: Config -> Encoder a -> (a, Builder.Builder)
+run c = Identity.runIdentity . Trans.runWriterT . flip Trans.runReaderT c
+
 data Config = Config
     { indent :: Indent
     , level :: Int
     } deriving (Eq, Show)
+
+defaultConfig :: Config
+defaultConfig = Config
+    { indent = Spaces 0
+    , level = 0
+    }
 
 data Indent
     = Spaces Int
diff --git a/source/library/Argo/Json/Value.hs b/source/library/Argo/Json/Value.hs
--- a/source/library/Argo/Json/Value.hs
+++ b/source/library/Argo/Json/Value.hs
@@ -18,6 +18,8 @@
 import qualified Argo.Vendor.TemplateHaskell as TH
 import qualified GHC.Generics as Generics
 
+-- | A JSON (JavaScript Object Notation) value, as described by RFC 8259.
+-- <https://datatracker.ietf.org/doc/html/rfc8259>
 data Value
     = Null Null.Null
     | Boolean Boolean.Boolean
diff --git a/source/library/Argo/Literal.hs b/source/library/Argo/Literal.hs
--- a/source/library/Argo/Literal.hs
+++ b/source/library/Argo/Literal.hs
@@ -18,6 +18,9 @@
 digitNine :: Word.Word8
 digitNine = 0x39
 
+digitOne :: Word.Word8
+digitOne = 0x31
+
 digitZero :: Word.Word8
 digitZero = 0x30
 
@@ -87,8 +90,14 @@
 rightSquareBracket :: Word.Word8
 rightSquareBracket = 0x5d
 
+solidus :: Word.Word8
+solidus = 0x2f
+
 space :: Word.Word8
 space = 0x20
+
+tilde :: Word.Word8
+tilde = 0x7e
 
 true :: ByteString.ByteString
 true = ByteString.pack [0x74, 0x72, 0x75, 0x65]
diff --git a/source/library/Argo/Pointer/Pointer.hs b/source/library/Argo/Pointer/Pointer.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Pointer/Pointer.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Pointer.Pointer where
+
+import qualified Argo.Decoder as Decoder
+import qualified Argo.Encoder as Encoder
+import qualified Argo.Json.Value as Value
+import qualified Argo.Json.Object as Object
+import qualified Argo.Json.Array as Array
+import qualified Argo.Json.Member as Member
+import qualified Argo.Json.Name as Name
+import qualified Argo.Json.String as String
+import qualified Argo.Literal as Literal
+import qualified Argo.Pointer.Token as Token
+import qualified Argo.Result as Result
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Argo.Vendor.Text as Text
+import qualified Control.Applicative as Applicative
+import qualified Data.List as List
+import qualified GHC.Generics as Generics
+import qualified Text.Read as Read
+
+-- | A JSON pointer, as described by RFC 6901.
+-- <https://datatracker.ietf.org/doc/html/rfc6901>
+newtype Pointer
+    = Pointer [Token.Token]
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromList :: [Token.Token] -> Pointer
+fromList = Pointer
+
+toList :: Pointer -> [Token.Token]
+toList (Pointer x) = x
+
+decode :: Decoder.Decoder Pointer
+decode = fromList <$> Applicative.many decodeToken
+
+decodeToken :: Decoder.Decoder Token.Token
+decodeToken = do
+    Decoder.word8 Literal.solidus
+    Token.decode
+
+encode :: Pointer -> Encoder.Encoder ()
+encode = mapM_ encodeToken . toList
+
+encodeToken :: Token.Token -> Encoder.Encoder ()
+encodeToken x = do
+    Trans.lift . Trans.tell $ Builder.word8 Literal.solidus
+    Token.encode x
+
+evaluate :: Pointer -> Value.Value -> Result.Result Value.Value
+evaluate p v = case toList p of
+    [] -> pure v
+    t : ts -> do
+        w <- case v of
+            Value.Array a -> atIndex t a
+            Value.Object o -> atKey t o
+            _ -> fail "not indexable"
+        evaluate (fromList ts) w
+
+atIndex :: Token.Token -> Array.ArrayOf value -> Result.Result value
+atIndex t a = do
+    i <- tokenToIndex t
+    case drop i $ Array.toList a of
+        [] -> fail $ "missing index: " <> show t
+        e : _ -> pure e
+
+tokenToIndex :: Token.Token -> Result.Result Int
+tokenToIndex token = do
+    let
+        text = Token.toText token
+        invalid = "invalid index: " <> show token
+    case Text.uncons text of
+        Just ('0', rest) -> if Text.null rest then pure 0 else fail invalid
+        _ -> maybe (fail invalid) pure . Read.readMaybe $ Text.unpack text
+
+atKey :: Token.Token -> Object.ObjectOf value -> Result.Result value
+atKey t = maybe (fail $ "missing key: " <> show t) (\ (Member.Member _ v) -> pure v)
+    . List.find (\ (Member.Member k _) -> String.toText (Name.toString k) == Token.toText t)
+    . Object.toList
diff --git a/source/library/Argo/Pointer/Token.hs b/source/library/Argo/Pointer/Token.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Pointer/Token.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Pointer.Token where
+
+import qualified Argo.Decoder as Decoder
+import qualified Argo.Encoder as Encoder
+import qualified Argo.Literal as Literal
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Data.Word as Word
+import qualified GHC.Generics as Generics
+
+newtype Token
+    = Token Text.Text
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromText :: Text.Text -> Token
+fromText = Token
+
+toText :: Token -> Text.Text
+toText (Token x) = x
+
+decode :: Decoder.Decoder Token
+decode = do
+    x <- Decoder.takeWhile $ (/=) Literal.solidus
+    y <- case Text.decodeUtf8' x of
+        Left e -> fail $ show e
+        Right y -> pure y
+    case unescapeText y of
+        Nothing -> fail "invalid escape"
+        Just z -> pure $ fromText z
+
+unescapeText :: Text.Text -> Maybe Text.Text
+unescapeText = fmap Text.pack . unescapeString . Text.unpack
+
+unescapeString :: String -> Maybe String
+unescapeString xs = case xs of
+    "" -> pure xs
+    x : ys -> case x of
+        '~' -> case ys of
+            '0' : zs -> ('~' :) <$> unescapeString zs
+            '1' : zs -> ('/' :) <$> unescapeString zs
+            _ -> fail "invalid escape"
+        _ -> (x :) <$> unescapeString ys
+
+encode :: Token -> Encoder.Encoder ()
+encode = Trans.lift
+    . Trans.tell
+    . Text.encodeUtf8BuilderEscaped encodeChar
+    . toText
+
+encodeChar :: Builder.BoundedPrim Word.Word8
+encodeChar =
+    Builder.condB (== Literal.tilde) (encodeEscape Literal.digitZero)
+    . Builder.condB (== Literal.solidus) (encodeEscape Literal.digitOne)
+    $ Builder.liftFixedToBounded Builder.word8F
+
+encodeEscape :: Word.Word8 -> Builder.BoundedPrim a
+encodeEscape x = Builder.liftFixedToBounded
+    $ const (Literal.tilde, x)
+    Builder.>$< Builder.word8F
+    Builder.>*< Builder.word8F
diff --git a/source/library/Argo/QuasiQuoter.hs b/source/library/Argo/QuasiQuoter.hs
--- a/source/library/Argo/QuasiQuoter.hs
+++ b/source/library/Argo/QuasiQuoter.hs
@@ -6,15 +6,21 @@
 import qualified Argo.Vendor.TemplateHaskell as TH
 import qualified Argo.Vendor.Text as Text
 
+pointer :: TH.QuasiQuoter
+pointer = TH.QuasiQuoter
+    { TH.quoteDec = const $ fail "cannot be used as a declaration"
+    , TH.quoteExp = Result.result fail TH.lift . Decode.decodePointer . Text.encodeUtf8 . Text.pack
+    , TH.quotePat = const $ fail "cannot be used as a pattern"
+    , TH.quoteType = const $ fail "cannot be used as a type"
+    }
+
 value :: TH.QuasiQuoter
 value = TH.QuasiQuoter
     { TH.quoteDec = const $ fail "cannot be used as a declaration"
-    , TH.quoteExp = quoteExp
+    , TH.quoteExp = Result.result fail TH.lift . fmap asValue . Decode.decode . Text.encodeUtf8 . Text.pack
     , TH.quotePat = const $ fail "cannot be used as a pattern"
     , TH.quoteType = const $ fail "cannot be used as a type"
     }
 
-quoteExp :: String -> TH.Q TH.Exp
-quoteExp x = case Decode.decode . Text.encodeUtf8 $ Text.pack x of
-    Result.Failure e -> fail e
-    Result.Success y -> TH.lift (y :: Value.Value)
+asValue :: Value.Value -> Value.Value
+asValue = id
diff --git a/source/library/Argo/Result.hs b/source/library/Argo/Result.hs
--- a/source/library/Argo/Result.hs
+++ b/source/library/Argo/Result.hs
@@ -1,11 +1,16 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+
 module Argo.Result where
 
+import qualified Argo.Vendor.DeepSeq as DeepSeq
 import qualified Control.Applicative as Applicative
+import qualified GHC.Generics as Generics
 
 data Result a
     = Failure String
     | Success a
-    deriving (Eq, Show)
+    deriving (Eq, Generics.Generic, DeepSeq.NFData, Show)
 
 instance Functor Result where
     fmap f r = case r of
@@ -32,3 +37,8 @@
     rx <|> ry = case rx of
         Failure _ -> ry
         Success _ -> rx
+
+result :: (String -> b) -> (a -> b) -> Result a -> b
+result f g r = case r of
+    Failure e -> f e
+    Success x -> g x
diff --git a/source/library/Argo/Vendor/Builder.hs b/source/library/Argo/Vendor/Builder.hs
--- a/source/library/Argo/Vendor/Builder.hs
+++ b/source/library/Argo/Vendor/Builder.hs
@@ -5,6 +5,7 @@
     , Prim.condB
     , Builder.integerDec
     , Prim.liftFixedToBounded
+    , Builder.toLazyByteString
     , Builder.word8
     , word8F
     , Prim.word16HexFixed
diff --git a/source/library/Argo/Vendor/ByteString.hs b/source/library/Argo/Vendor/ByteString.hs
--- a/source/library/Argo/Vendor/ByteString.hs
+++ b/source/library/Argo/Vendor/ByteString.hs
@@ -14,7 +14,9 @@
     , ByteString.stripPrefix
     , ByteString.take
     , ByteString.takeWhileEnd
+    , LazyByteString.toStrict
     , ByteString.uncons
     ) where
 
 import qualified Data.ByteString as ByteString
+import qualified Data.ByteString.Lazy as LazyByteString
diff --git a/source/test-suite/Main.hs b/source/test-suite/Main.hs
--- a/source/test-suite/Main.hs
+++ b/source/test-suite/Main.hs
@@ -347,6 +347,8 @@
             fromValue (Argo.Array [Argo.Boolean False]) @?= Just (False :| [])
         , Tasty.testCase "Map Text a" $ do
             fromValue (Argo.Object [Argo.Member (Argo.Name "a") $ Argo.Boolean False]) @?= Just (Map.fromList [("a" :: Text.Text, False)])
+        , Tasty.testCase "Pointer" $ do
+            fromValue (Argo.String "") @?= Just (Argo.Pointer [])
         ]
     , Tasty.testGroup "toValue"
         [ Tasty.testCase "Value" $ do
@@ -399,6 +401,8 @@
             Argo.toValue (False :| []) @?= Argo.Array [Argo.Boolean False]
         , Tasty.testCase "Map Text a" $ do
             Argo.toValue (Map.fromList [("a" :: Text.Text, False)]) @?= Argo.Object [Argo.Member (Argo.Name "a") $ Argo.Boolean False]
+        , Tasty.testCase "Pointer" $ do
+            Argo.toValue (Argo.Pointer []) @?= Argo.String ""
         ]
     , Tasty.testGroup "quasi quoter"
         [ Tasty.testCase "Null" $ do
@@ -416,12 +420,12 @@
         ]
     , Tasty.testGroup "property"
         [ property "decode . encode" $ \ x ->
-            (resultToMaybe . Argo.decode . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encode x) === Just (x :: Argo.Value)
+            (Argo.decode . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encode x) === Argo.Success (x :: Argo.Value)
         , property "decode . encodeWith" $ \ x ->
             (Argo.decode . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encodeWith Argo.Tab x) === Argo.Success (x :: Argo.Value)
         , Tasty.testGroup "fromValue . toValue"
             [ property "Value" $ \ x ->
-                (resultToMaybe . Argo.fromValue $ Argo.toValue x) === Just (x :: Argo.Value)
+                Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Argo.Value)
             , property "Bool" $ \ x ->
                 Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Bool)
             , property "Char" $ \ x ->
@@ -476,6 +480,8 @@
                 Argo.fromValue (Argo.toValue x) === Argo.Success (x :: NonEmpty Bool)
             , property "Map Text a" $ \ x ->
                 Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Map.Map Text.Text Bool)
+            , property "Pointer" $ \ x ->
+                Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Argo.Pointer)
             ]
         ]
     , Tasty.testGroup "Codec"
@@ -510,6 +516,52 @@
             Codec.decodeWith recordCodec (Argo.Object [Argo.Member (Argo.Name "bool") $ Argo.Boolean False]) @?= Argo.Success (Record False Nothing)
             Codec.decodeWith recordCodec (Argo.Object [Argo.Member (Argo.Name "bool") $ Argo.Boolean False, Argo.Member (Argo.Name "text") $ Argo.String ""]) @?= Argo.Success (Record False $ Just "")
         ]
+    , Tasty.testGroup "Pointer"
+        $ let pointer = Argo.Pointer . fmap Argo.Token in
+        [ Tasty.testCase "decode" $ do
+            let decode = resultToMaybe . Argo.decodePointer
+            decode "" @?= Just (pointer [])
+            decode "/" @?= Just (pointer [""])
+            decode "/a" @?= Just (pointer ["a"])
+            decode "/a/b" @?= Just (pointer ["a", "b"])
+            decode "/ab" @?= Just (pointer ["ab"])
+            decode "/~0" @?= Just (pointer ["~"])
+            decode "/~1" @?= Just (pointer ["/"])
+            decode "/~01" @?= Just (pointer ["~1"])
+            decode "a" @?= Nothing
+            decode "/~2" @?= Nothing
+        , Tasty.testCase "encode" $ do
+            let encode = Builder.toLazyByteString . Argo.encodePointer
+            encode (pointer []) @?= ""
+            encode (pointer [""]) @?= "/"
+            encode (pointer ["a"]) @?= "/a"
+            encode (pointer ["a", "b"]) @?= "/a/b"
+            encode (pointer ["ab"]) @?= "/ab"
+            encode (pointer ["~"]) @?= "/~0"
+            encode (pointer ["/"]) @?= "/~1"
+            encode (pointer ["~1"]) @?= "/~01"
+        , property "decode . encode" $ \ x ->
+            (Argo.decodePointer . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encodePointer x) === Argo.Success (x :: Argo.Pointer)
+        , Tasty.testCase "quasi-quoter" $ do
+            [Argo.pointer||] @?= pointer []
+            [Argo.pointer|/|] @?= pointer [""]
+            [Argo.pointer|/a|] @?= pointer ["a"]
+            [Argo.pointer|/a/b|] @?= pointer ["a", "b"]
+        , Tasty.testCase "evaluate" $ do
+            let evaluate p = resultToMaybe . Argo.evaluate p
+            evaluate (pointer []) Argo.Null @?= Just Argo.Null
+            evaluate (pointer ["a"]) (Argo.Object [Argo.Member (Argo.Name "a") Argo.Null]) @?= Just Argo.Null
+            evaluate (pointer ["a", "b"]) (Argo.Object [Argo.Member (Argo.Name "a") $ Argo.Object [Argo.Member (Argo.Name "b") Argo.Null]]) @?= Just Argo.Null
+            evaluate (pointer ["0"]) (Argo.Object [Argo.Member (Argo.Name "0") Argo.Null]) @?= Just Argo.Null
+            evaluate (pointer ["0"]) (Argo.Array [Argo.Null]) @?= Just Argo.Null
+            evaluate (pointer ["0", "1"]) (Argo.Array [Argo.Array [Argo.Boolean False, Argo.Null]]) @?= Just Argo.Null
+            evaluate (pointer ["a"]) Argo.Null @?= Nothing
+            evaluate (pointer ["a"]) (Argo.Object []) @?= Nothing
+            evaluate (pointer ["a"]) (Argo.Array [Argo.Null]) @?= Nothing
+            evaluate (pointer ["0"]) (Argo.Array []) @?= Nothing
+            evaluate (pointer ["-"]) (Argo.Array []) @?= Nothing
+            evaluate (pointer ["00"]) (Argo.Array [Argo.Null]) @?= Nothing
+        ]
     ]
 
 data Record = Record
@@ -584,3 +636,11 @@
 instance Tasty.Arbitrary a => Tasty.Arbitrary (NonEmpty a) where
     arbitrary = (:|) <$> Tasty.arbitrary <*> Tasty.arbitrary
     shrink (x :| xs) = uncurry (:|) <$> Tasty.shrink (x, xs)
+
+instance Tasty.Arbitrary Argo.Pointer where
+    arbitrary = Argo.Pointer <$> Tasty.arbitrary
+    shrink (Argo.Pointer x) = Argo.Pointer <$> Tasty.shrink x
+
+instance Tasty.Arbitrary Argo.Token where
+    arbitrary = Argo.Token <$> Tasty.arbitrary
+    shrink (Argo.Token x) = Argo.Token <$> Tasty.shrink x
