diff --git a/bizzlelude.cabal b/bizzlelude.cabal
--- a/bizzlelude.cabal
+++ b/bizzlelude.cabal
@@ -1,6 +1,6 @@
 Cabal-Version:       3.8
 Name:                bizzlelude
-Version:             4.20.2.0.3
+Version:             4.20.2.0.4
 License:             BSD-3-Clause
 License-File:        LICENSE.txt
 Author:              Jason Bertsche
@@ -33,13 +33,13 @@
     -funbox-strict-fields
   Build-depends:
     , base == 4.20.2.0
-    , bytestring     >= 0.12.0 && < 0.13
-    , containers     >= 0.7    && < 0.8
-    , directory      >= 1.3.0  && < 1.4
-    , mtl            >= 2.3.0  && < 2.4
-    , pcre-light     >= 0.4.0  && < 0.5
-    , text           >= 2.1.0  && < 2.2
-    , validation     >= 1.1.0  && < 1.2
+    , bytestring         >= 0.12.0 && < 0.13
+    , containers         >= 0.7    && < 0.8
+    , directory          >= 1.3.0  && < 1.4
+    , mtl                >= 2.3.0  && < 2.4
+    , regex-pcre-builtin >= 0.95   && < 0.96
+    , text               >= 2.1.0  && < 2.2
+    , validation         >= 1.1.0  && < 1.2
 
   -- The below is copied from the `exposed-modules` in 'base' (modulo `prelude`)
   -- https://gitlab.haskell.org/ghc/ghc/-/blob/master/libraries/base/base.cabal.in
diff --git a/src/main/External.hs b/src/main/External.hs
--- a/src/main/External.hs
+++ b/src/main/External.hs
@@ -5,7 +5,7 @@
 import Control.Applicative((<**>), Alternative((<|>), many, some), Applicative((<*>), (<*), (*>), pure), liftA2, optional)
 import Control.Monad((=<<), filterM, foldM, foldM_, forM, forM_, guard, join, mapM, mapM_, Monad((>>), (>>=), return), MonadPlus(), sequence, sequence_, unless, when)
 import Control.Monad.IO.Class(liftIO)
-import Control.Monad.State(evalState, execState, mapState, runState, State, withState)
+import Control.Monad.State(evalState, execState, gets, mapState, modify, runState, State, withState)
 
 import Data.Bifoldable(Bifoldable, bifold, bifoldMap, bimapM_)
 import Data.Bifunctor(Bifunctor(bimap, first, second))
diff --git a/src/main/Misc.hs b/src/main/Misc.hs
--- a/src/main/Misc.hs
+++ b/src/main/Misc.hs
@@ -1,13 +1,13 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# OPTIONS_GHC -fno-warn-missing-import-lists #-}
-module Misc((|>), (&>), (&>=), (>>>), (>=>), (/#), aempty, asString, asPath, asText, showText, concat, error, failOrM, fromEither, map, pam, (<&>), cartProduct, regexMatch, groupOn, length, orElse, return', scalaGroupBy, putStrFlush, traceLabel, unsafeRead, listDirsRecursively, uncurry5, uncurry6) where
+module Misc((|>), (&>), (&>=), (>>>), (>=>), (/#), (|*>), (<*|), aempty, andIfValidMV, andIfValidV, asString, asPath, asText, showText, concat, error, fromEither, headMaybe, lastMaybe, map, pam, (<&>), cartProduct, regexMatch, groupOn, length, mapMSequA, orElse, return', scalaGroupBy, putStrFlush, traceLabel, unsafeRead, listDirsRecursively, uncurry5, uncurry6) where
 
 import External
 
-import Data.Text.Encoding(decodeUtf8, encodeUtf8)
-
 import GHC.Real(Fractional((/)))
 
+import Text.Regex.PCRE((=~))
+
 import qualified Control.Applicative   as Apply
 import qualified Control.Arrow         as CArrow
 import qualified Control.Monad         as CMonad
@@ -21,7 +21,6 @@
 import qualified GHC.Err               as Err
 import qualified System.Directory      as SD
 import qualified System.IO             as SIO
-import qualified Text.Regex.PCRE.Light as PCRE
 
 (/#) :: (Real a, Real b) => a -> b -> Double
 a /# b = (realToFrac a) / (realToFrac b)
@@ -40,6 +39,20 @@
 infixl 2 >>>, >=>
 infixl 1 |>
 
+(<*|) :: (Monad m, Applicative a) => m (a x) -> m (a x) -> m (a x)
+(<*|) a b =
+  do
+    va <- a
+    vb <- b
+    return $ va <* vb
+
+(|*>) :: (Monad m, Applicative a) => m (a x) -> m (a x) -> m (a x)
+(|*>) a b =
+  do
+    va <- a
+    vb <- b
+    return $ va *> vb
+
 aempty :: Apply.Alternative f => f a
 aempty = Apply.empty
 
@@ -68,12 +81,36 @@
 pam   = flip map
 (<&>) = flip map
 
-failOrM :: Monad m => Validation f s1 -> (s1 -> m (Validation f s2)) -> m (Validation f s2)
-v `failOrM` vm = validation (Failure &> return) vm v
+mapMSequA :: (Monad m, Applicative f, Traversable t) => (a -> m (f b)) -> t a -> m (f (t b))
+mapMSequA f xs =
+  do
+    resVs <- mapM f xs
+    return $ sequenceA resVs
 
+andIfValidV :: Monad m => Validation f s1 -> (s1 -> m (Validation f s2)) -> m (Validation f s2)
+v `andIfValidV` vm = validation (Failure &> return) vm v
+
+andIfValidMV :: Monad m => m (Validation f s1) -> (s1 -> m (Validation f s2)) -> m (Validation f s2)
+sVM `andIfValidMV` f =
+  do
+    sV <- sVM
+    sV `andIfValidV` f
+
 orElse :: Maybe a -> a -> a
 orElse m x = maybe x id m
 
+headMaybe :: Foldable t => t a -> Maybe a
+headMaybe = Foldable.toList &> helper
+  where
+    helper    [] = Nothing
+    helper (h:_) = Just h
+
+lastMaybe :: Foldable t => t a -> Maybe a
+lastMaybe = Foldable.toList &> helper
+  where
+    helper [] = Nothing
+    helper xs = Just $ List.last xs
+
 length :: Foldable t => t a -> Word
 length = Foldable.length &> fromIntegral
 
@@ -81,14 +118,13 @@
 cartProduct xs ys = [(x, y) | x <- xs, y <- ys]
 
 regexMatch :: Text -> Text -> Maybe [Text]
-regexMatch regex text = map (map decodeUtf8) matched
-  where
-    compiled = PCRE.compile (encodeUtf8 regex) [PCRE.dotall, PCRE.multiline]
-    matchedL = PCRE.match compiled (encodeUtf8 text) []
-    matched  = map (unsafeNonEmpty &> NE.tail) matchedL
+regexMatch regex text =
+  case (Text.unpack text) =~ ("(?sm)" <> Text.unpack regex) :: [[String]] of
+    []       -> Nothing
+    captures -> Just [Text.pack group | (_full : groups) <- captures, group <- groups]
 
 unsafeNonEmpty :: [a] -> NonEmpty a
-unsafeNonEmpty = NE.nonEmpty &> maybe (error "Impossible") id
+unsafeNonEmpty = NE.nonEmpty &> maybe (error "`unsafeNonEmpty` is unsafe") id
 
 fromEither :: Either a a -> a
 fromEither = either id id
