diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for yasi
+
+## 0.0.0.0 -- 2021-02-13
+
+* First version. Released on an uNsUsPeCtInG world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# yasi
+[![CI](https://github.com/amesgen/yasi/workflows/CI/badge.svg)](https://github.com/amesgen/yasi/actions)
+[![Hackage](https://img.shields.io/hackage/v/yasi)](https://hackage.haskell.org/package/yasi)
+
+Yet another string interpolator for Haskell.
+
+See [the haddocks](https://hackage.haskell.org/package/yasi/docs/Yasi.html) for documentation.
diff --git a/src/Yasi.hs b/src/Yasi.hs
new file mode 100644
--- /dev/null
+++ b/src/Yasi.hs
@@ -0,0 +1,39 @@
+-- | Yet another string interpolator
+--
+--  * Dead simple
+--  * No dependency on [haskell-src-meta](https://hackage.haskell.org/package/haskell-src-meta).
+--    It is not actively developed, has long compile times and several bugs, some of which are
+--    by design (e.g. operator fixities).
+--  * Supports to interpolate 'String', 'Data.Text.Text', 'Data.Text.Lazy.Text',
+--    'Data.ByteString.ByteString' and 'Data.ByteString.Lazy.ByteString' (UTF8).
+module Yasi (i) where
+
+import qualified Language.Haskell.TH.Quote as TH
+import Yasi.Internal
+
+-- $setup
+-- >>> import Yasi
+-- >>> import Data.Text (Text)
+-- >>> import Data.ByteString (ByteString)
+
+-- | The interpolator, intended to be used with
+-- [@QuasiQuotes@](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/exts/template_haskell.html#extension-QuasiQuotes).
+--
+-- >>> :set -XQuasiQuotes
+--
+-- >>> (foo, bar) = ("yet another ", "interpolator")
+-- >>> [i|${foo}string $bar|] :: String
+-- "yet another string interpolator"
+--
+-- You can also use @${}@ to create a function interpolator (this "abstraction" feature is inspired by
+-- [interpolate](https://hackage.haskell.org/package/interpolate)):
+--
+-- >>> [i|more ${}${} code|] "point" "free" :: Text
+-- "more pointfree code"
+--
+-- To use 'show' to interpolate a value:
+--
+-- >>> let x = 1 + 1 in [i|1 + 1 = ${show x}|] :: ByteString
+-- "1 + 1 = 2"
+i :: TH.QuasiQuoter
+i = interpolator '$' pure pure
diff --git a/src/Yasi/Internal.hs b/src/Yasi/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Yasi/Internal.hs
@@ -0,0 +1,150 @@
+{-# LANGUAGE CPP #-}
+
+-- | Internal module, no stability guarantees
+module Yasi.Internal
+  ( Segment (..),
+    parseSegments,
+    interpolator,
+    Stringy (..),
+  )
+where
+
+import Control.Monad ((>=>))
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.Char as C
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.Encoding as TL
+import GHC.Generics (Generic)
+import qualified Language.Haskell.TH.Quote as TH
+import qualified Language.Haskell.TH.Syntax as TH
+
+#if !(MIN_VERSION_base(4,13,0))
+import Control.Monad.Fail (MonadFail)
+#endif
+
+data Segment
+  = Lit String
+  | Var String
+  | ShowVar String
+  | Abs -- idea due to interpolate
+  deriving (Show, Eq, Generic)
+
+parseSegments :: MonadFail m => Char -> String -> m [Segment]
+parseSegments c = fmap (group []) . go
+  where
+    go s
+      | let (lit, rest) = span (/= c) s, not (null lit) = (Lit lit :) <$> go rest
+      | s == "" = pure []
+      | s == [c] = fail $ "should not end with single " <> [c]
+      | _ : c' : rest <- s, c == c' = (Lit [c] :) <$> go rest
+      | _ : '{' : rest <- s = case span (/= '}') rest of
+        (var, '}' : rest) ->
+          let seg = case var of
+                "" -> Abs
+                's' : 'h' : 'o' : 'w' : ' ' : var -> ShowVar var
+                var -> Var var
+           in (seg :) <$> go rest
+        _ -> fail "missing closing bracket"
+      | _ : v : rest' <- s,
+        isVarStartChar v =
+        let (vs, rest) = span isVarChar rest'
+         in (Var (v : vs) :) <$> go rest
+      | otherwise = fail $ "invalid char after " <> [c]
+    isVarStartChar v = C.isAscii v && C.isAlpha v
+    isVarChar v = C.isAscii v && (C.isAlphaNum v || v == '_' || v == '\'')
+
+    group ls [] = lit ls
+    group ls (Lit l : ss) = group (l : ls) ss
+    group ls (s : ss) = lit ls ++ (s : group [] ss)
+    lit [] = []
+    lit ls = [Lit $ mconcat $ reverse ls]
+
+ipExpr :: TH.Exp -> TH.Exp -> [Segment] -> TH.Q TH.Exp
+ipExpr cast combine segs = do
+  (ls, lams) <- go segs
+  pure $ lams $ TH.AppE combine (TH.ListE ls)
+  where
+    go = \case
+      [] -> pure ([], id)
+      (s : ss) -> prep (go ss) $ case s of
+        Lit l -> pure (TH.SigE (TH.LitE (TH.StringL l)) (TH.ConT ''String), id)
+        Var v -> pure (TH.VarE (TH.mkName v), id)
+        ShowVar v -> pure (TH.AppE (TH.VarE 'show) $ TH.VarE (TH.mkName v), id)
+        Abs -> do
+          n <- TH.newName "int"
+          pure (TH.VarE n, TH.LamE [TH.VarP n])
+    prep asg af = do
+      (as, g) <- asg
+      (a, f) <- af
+      pure (TH.AppE cast a : as, f . g)
+
+interpolator ::
+  Char ->
+  -- | postprocess the 'Segment's
+  ([Segment] -> TH.Q [Segment]) ->
+  -- | postprocess the 'TH.Exp'
+  (TH.Exp -> TH.Q TH.Exp) ->
+  TH.QuasiQuoter
+interpolator c pp pp' = TH.QuasiQuoter {..}
+  where
+    quoteExp = parseSegments c >=> pp >=> ipExpr (TH.VarE 'stringy) (TH.VarE 'mconcat) >=> pp'
+    quotePat = const $ fail "pattern context not supported"
+    quoteType = const $ fail "type context not supported"
+    quoteDec = const $ fail "declaration context not supported"
+
+class Stringy a b where
+  stringy :: a -> b
+
+instance Stringy String String where
+  stringy = id
+
+instance Stringy String T.Text where
+  stringy = T.pack
+
+instance Stringy String TL.Text where
+  stringy = TL.pack
+
+instance Stringy String B.ByteString where
+  stringy = T.encodeUtf8 . T.pack
+
+instance Stringy String BL.ByteString where
+  stringy = TL.encodeUtf8 . TL.pack
+
+instance Stringy T.Text T.Text where
+  stringy = id
+
+instance Stringy T.Text String where
+  stringy = T.unpack
+
+instance Stringy T.Text TL.Text where
+  stringy = TL.fromStrict
+
+instance Stringy T.Text B.ByteString where
+  stringy = T.encodeUtf8
+
+instance Stringy T.Text BL.ByteString where
+  stringy = BL.fromStrict . T.encodeUtf8
+
+instance Stringy TL.Text TL.Text where
+  stringy = id
+
+instance Stringy TL.Text String where
+  stringy = TL.unpack
+
+instance Stringy TL.Text T.Text where
+  stringy = TL.toStrict
+
+instance Stringy TL.Text B.ByteString where
+  stringy = BL.toStrict . TL.encodeUtf8
+
+instance Stringy TL.Text BL.ByteString where
+  stringy = TL.encodeUtf8
+
+instance Stringy B.ByteString B.ByteString where
+  stringy = id
+
+instance Stringy BL.ByteString BL.ByteString where
+  stringy = id
diff --git a/test/Driver.hs b/test/Driver.hs
new file mode 100644
--- /dev/null
+++ b/test/Driver.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF tasty-discover #-}
diff --git a/test/Interpolations.hs b/test/Interpolations.hs
new file mode 100644
--- /dev/null
+++ b/test/Interpolations.hs
@@ -0,0 +1,34 @@
+module Interpolations where
+
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import Hedgehog
+import qualified Hedgehog.Gen as Gen
+import qualified Hedgehog.Range as Range
+import Test.Tasty
+import Test.Tasty.Hedgehog
+import Yasi
+
+test_interpolation :: TestTree
+test_interpolation =
+  testGroup
+    "interpolation"
+    [ testProperty "to String" . property $ do
+        (a, b, c) <- (,,) <$> forAll genString <*> forAll genText <*> forAll genString
+        [i|${a}++$b🙀🙀🙀🙀$c🙀|] === a <> "++" <> T.unpack b <> "🙀🙀🙀🙀" <> c <> "🙀",
+      testProperty "to Text" . property $ do
+        (a, b, c) <- (,,) <$> forAll genText <*> forAll genText <*> forAll genString
+        [i|😶😶$a😶😶$b😶😶$c😶😶|] === "😶😶" <> a <> "😶😶" <> b <> "😶😶" <> T.pack c <> "😶😶",
+      testProperty "to ByteString" . property $ do
+        (a, b, c) <- (,,) <$> forAll genBS <*> forAll genText <*> forAll genString
+        let p = T.encodeUtf8 "℘"
+        [i|℘$a℘$b℘$c℘|] === p <> a <> p <> T.encodeUtf8 b <> p <> T.encodeUtf8 (T.pack c) <> p,
+      testProperty "abstractions" . property $ do
+        (a, b) <- (,) <$> forAll genText <*> forAll genText
+        [i|xxx-${}${}-yyy|] a b === "xxx-" <> a <> b <> "-yyy"
+    ]
+  where
+    genString = Gen.string range Gen.unicodeAll
+    genText = Gen.text range Gen.unicodeAll
+    genBS = Gen.bytes range
+    range = Range.constant 0 20
diff --git a/test/Segments.hs b/test/Segments.hs
new file mode 100644
--- /dev/null
+++ b/test/Segments.hs
@@ -0,0 +1,25 @@
+module Segments where
+
+import Test.Tasty.HUnit
+import Yasi.Internal
+
+unit_segment_parsing :: IO ()
+unit_segment_parsing = do
+  ps "foo\\\"" @?= Just [Lit "foo\\\""]
+  ps "$a-$b" @?= Just [Var "a", Lit "-", Var "b"]
+  ps "asef$" @?= Nothing
+  ps "lol$${}" @?= Just [Lit "lol${}"]
+  ps "lol$$${}" @?= Just [Lit "lol$", Abs]
+  ps "${89+}${}" @?= Just [Var "89+", Abs]
+  ps "${}}" @?= Just [Abs, Lit "}"]
+  ps "asdf${show bar}$$" @?= Just [Lit "asdf", ShowVar "bar", Lit "$"]
+  ps "a$$b" @?= Just [Lit "a$b"]
+  ps "$a$b$c" @?= Just [Var "a", Var "b", Var "c"]
+  ps "" @?= Just []
+  ps " " @?= Just [Lit " "]
+  ps "$foobar:\n - $b" @?= Just [Var "foobar", Lit ":\n - ", Var "b"]
+  ps "${foobar]" @?= Nothing
+  ps "test-$0foo" @?= Nothing
+  ps "${show }" @?= Just [ShowVar ""]
+  where
+    ps = parseSegments '$'
diff --git a/yasi.cabal b/yasi.cabal
new file mode 100644
--- /dev/null
+++ b/yasi.cabal
@@ -0,0 +1,62 @@
+cabal-version: 2.4
+name: yasi
+version: 0.0.0.0
+
+synopsis: Yet another string interpolator
+description:
+  Yet another string interpolator,
+  with a different set of tradeoffs
+category: String, Text, QuasiQuotes
+
+homepage: https://github.com/amesgen/yasi
+bug-reports: https://github.com/amesgen/yasi/issues
+license: CC0-1.0
+license-file: LICENSE
+extra-source-files: README.md
+author: amesgen
+maintainer: amesgen@amesgen.de
+extra-source-files: CHANGELOG.md
+
+source-repository head
+  location: https://github.com/amesgen/yasi
+  type: git
+
+common commons
+  default-language: Haskell2010
+  ghc-options: -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -fno-warn-name-shadowing -fhide-source-paths
+  if impl(ghc >= 8.10)
+    ghc-options: -Wunused-packages
+  default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances LambdaCase MultiParamTypeClasses OverloadedStrings QuasiQuotes RecordWildCards TemplateHaskell
+
+library
+  import: commons
+  ghc-options: -Wmissing-export-lists
+  hs-source-dirs: src
+  exposed-modules:
+    Yasi
+    Yasi.Internal
+  build-depends:
+      base >= 4.11 && < 5
+    , template-haskell >= 2.13 && < 2.18
+    , text ^>= 1.2
+    , bytestring >= 0.10.8.2 && < 0.12
+
+test-suite tasty
+  import: commons
+  type: exitcode-stdio-1.0
+  main-is: Driver.hs
+  hs-source-dirs: test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-tool-depends:
+      tasty-discover:tasty-discover ^>= 4.2
+  build-depends:
+      base
+    , yasi
+    , text
+    , tasty >= 1.3 && <= 1.5
+    , tasty-hunit ^>= 0.10
+    , tasty-hedgehog ^>= 1.0
+    , hedgehog ^>= 1.0
+  other-modules:
+      Segments
+      Interpolations
