diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+# 1.1.0.0
+
+  * Add builtin for last field
+  * Performance improvements
+  * Fix bug in how fields were split
+  * `:` works on column literals
+  * Add `#*` (list length) builtin
+
 # 1.0.0.0
 
   * Generalize type of `\.` (prior)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -7,6 +7,8 @@
 
 There are binaries for some platforms on the [releases page](https://github.com/vmchale/jacinda/releases/).
 
+If you are on Mac, you will need to install `*-librure.dylib` as well.
+
 ## From Source
 
 First, install [Rust's regex library](https://github.com/rust-lang/regex/tree/master/regex-capi#c-api-for-rusts-regex-engine). You'll need to put `librure.so` or `librure.dylib` etc. in the appropriate place.
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -26,6 +26,7 @@
     <> metavar "REGEXP"
     <> help "Field separator")
 
+-- FIXME: this seems to mishandle iota on the command-line..
 jacExpr :: Parser BSL.ByteString
 jacExpr = argument str
     (metavar "EXPR"
diff --git a/doc/guide.pdf b/doc/guide.pdf
Binary files a/doc/guide.pdf and b/doc/guide.pdf differ
diff --git a/examples/avg.jac b/examples/avg.jac
--- a/examples/avg.jac
+++ b/examples/avg.jac
@@ -1,4 +1,4 @@
 let
-  val tot := (+)|0.0 $1:f
+  val tot := (+)|0.0 $1:
   val n := (+)|0.0 [:1.0"$0
 in tot%n end
diff --git a/examples/mtlCtx.jac b/examples/mtlCtx.jac
new file mode 100644
--- /dev/null
+++ b/examples/mtlCtx.jac
@@ -0,0 +1,15 @@
+{. finds any line matching /MonadState/ or /MonadError/ without
+{. an INLINABLE pragma on the preceding line
+
+@include'lib/maybe.jac'
+
+fn step(ctx, line) :=
+  let
+    val fpCtx ≔ line ~ /\{-#.*INLINABLE.*#-\}/
+    val mLine ≔
+      if (ctx->1) || line !~ /:.*Monad(State|Error)/
+        then None
+        else Some line
+  in (fpCtx.mLine) end;
+
+(->2):?step^(#f.None)$0
diff --git a/examples/nmCtx.jac b/examples/nmCtx.jac
--- a/examples/nmCtx.jac
+++ b/examples/nmCtx.jac
@@ -9,7 +9,7 @@
     else None;
 
 fn step(ctx, line) :=
-  let 
+  let
     val fpCtx := line ~* 1 /(.*\.o):$/
     val mSym := line ~* 1 /^[0-0a-f]{16}.{3}(.*)/
   in (alternative (ctx->1) fpCtx.mSym) end;
diff --git a/jacinda.cabal b/jacinda.cabal
--- a/jacinda.cabal
+++ b/jacinda.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.0
 name:               jacinda
-version:            1.0.0.0
+version:            1.1.0.0
 license:            AGPL-3
 license-file:       COPYING
 maintainer:         vamchale@gmail.com
diff --git a/man/ja.1 b/man/ja.1
--- a/man/ja.1
+++ b/man/ja.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pandoc 2.17.1.1
+.\" Automatically generated by Pandoc 2.18
 .\"
 .\" Define V font for inline verbatim, using C font in formats
 .\" that render this, and otherwise B font.
@@ -151,6 +151,8 @@
 .SS SYNTAX
 .PP
 \f[B]\[ga]n\f[R] nth field
+.PP
+\f[B]\[ga]*\f[R] last field
 .PP
 \f[B]$n\f[R] nth column
 .PP
diff --git a/src/Jacinda/AST.hs b/src/Jacinda/AST.hs
--- a/src/Jacinda/AST.hs
+++ b/src/Jacinda/AST.hs
@@ -122,6 +122,7 @@
          | Dedup
          | CatMaybes
          | Negate
+         | TallyList -- length of vector
          deriving (Eq)
 
 instance Pretty BUn where
@@ -139,6 +140,7 @@
     pretty Dedup      = "~."
     pretty CatMaybes  = ".?"
     pretty Negate     = "-."
+    pretty TallyList  = "#*"
 
 -- ternary
 data BTer = ZipW
@@ -232,7 +234,9 @@
 data E a = Column { eLoc :: a, col :: Int }
          | IParseCol { eLoc :: a, col :: Int } -- always a column
          | FParseCol { eLoc :: a, col :: Int }
+         | ParseCol { eLoc :: a, col :: Int }
          | Field { eLoc :: a, eField :: Int }
+         | LastField { eLoc :: a }
          | AllField { eLoc :: a } -- ^ Think @$0@ in awk.
          | AllColumn { eLoc :: a } -- ^ Think @$0@ in awk.
          | EApp { eLoc :: a, eApp0 :: E a, eApp1 :: E a }
@@ -241,11 +245,11 @@
          | Let { eLoc :: a, eBind :: (Name a, E a), eE :: E a }
          -- TODO: literals type (make pattern matching easier down the road)
          | Var { eLoc :: a, eVar :: Name a }
-         | IntLit { eLoc :: a, eInt :: Integer }
-         | BoolLit { eLoc :: a, eBool :: Bool }
+         | IntLit { eLoc :: a, eInt :: !Integer }
+         | BoolLit { eLoc :: a, eBool :: !Bool }
          | StrLit { eLoc :: a, eStr :: BS.ByteString }
          | RegexLit { eLoc :: a, eRr :: BS.ByteString }
-         | FloatLit { eLoc :: a, eFloat :: Double }
+         | FloatLit { eLoc :: a, eFloat :: !Double }
          | Lam { eLoc :: a, eBound :: Name a, lamE :: E a }
          | Dfn { eLoc :: a, eDfn :: E a } -- to be rewritten as a lambda...
          -- TODO: builtin sum type ? (makes pattern matching easier down the road)
@@ -272,7 +276,9 @@
 data EF a x = ColumnF a Int
             | IParseColF a Int
             | FParseColF a Int
+            | ParseColF a Int
             | FieldF a Int
+            | LastFieldF a
             | AllFieldF a
             | AllColumnF a
             | EAppF a x x
@@ -314,8 +320,10 @@
     pretty AllColumn{}                                                  = "$0"
     pretty (IParseCol _ i)                                              = "$" <> pretty i <> ":i"
     pretty (FParseCol _ i)                                              = "$" <> pretty i <> ":f"
+    pretty (ParseCol _ i)                                               = "$" <> pretty i <> ":"
     pretty AllField{}                                                   = "`0"
     pretty (Field _ i)                                                  = "`" <> pretty i
+    pretty LastField{}                                                  = "`*"
     pretty (EApp _ (EApp _ (BBuiltin _ Prior) e) e')                    = pretty e <> "\\." <+> pretty e'
     pretty (EApp _ (EApp _ (BBuiltin _ Max) e) e')                      = "max" <+> pretty e <+> pretty e'
     pretty (EApp _ (EApp _ (BBuiltin _ Min) e) e')                      = "min" <+> pretty e <+> pretty e'
@@ -356,7 +364,7 @@
     pretty (Guarded _ p e)                                              = braces (pretty p) <> braces (pretty e)
     pretty (Implicit _ e)                                               = braces ("|" <+> pretty e)
     pretty (NBuiltin _ n)                                               = pretty n
-    pretty RegexCompiled{}                                              = error "Nonsense."
+    pretty RegexCompiled{}                                              = "(compiled regex)"
     pretty (Let _ (n, b) e)                                             = "let" <+> "val" <+> pretty n <+> ":=" <+> pretty b <+> "in" <+> pretty e <+> "end"
     pretty (Paren _ e)                                                  = parens (pretty e)
     pretty (Arr _ es)                                                   = tupledByFunky "," (V.toList $ pretty <$> es)
@@ -374,6 +382,7 @@
     (==) (IParseCol _ i) (IParseCol _ j)        = i == j
     (==) (FParseCol _ i) (FParseCol _ j)        = i == j
     (==) (Field _ i) (Field _ j)                = i == j
+    (==) LastField{} LastField{}                = True
     (==) AllColumn{} AllColumn{}                = True
     (==) AllField{} AllField{}                  = True
     (==) (EApp _ e0 e1) (EApp _ e0' e1')        = e0 == e0' && e1 == e1'
diff --git a/src/Jacinda/Backend/Normalize.hs b/src/Jacinda/Backend/Normalize.hs
--- a/src/Jacinda/Backend/Normalize.hs
+++ b/src/Jacinda/Backend/Normalize.hs
@@ -1,8 +1,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 
-module Jacinda.Backend.Normalize ( compileR
-                                 , compileIn
-                                 , eClosed
+module Jacinda.Backend.Normalize ( eClosed
                                  , closedProgram
                                  , readDigits
                                  , readFloat
@@ -21,7 +19,6 @@
 
 import           Control.Exception          (Exception, throw)
 import           Control.Monad.State.Strict (State, evalState, gets, modify, runState)
-import           Control.Recursion          (cata, embed)
 import           Data.Bifunctor             (second)
 import qualified Data.ByteString            as BS
 import qualified Data.ByteString.Char8      as ASCII
@@ -83,22 +80,6 @@
 readFloat :: BS.ByteString -> Double
 readFloat = read . ASCII.unpack
 
--- TODO: do this on all expressions?
--- fill in regex with compiled.
-compileR :: E a
-         -> E a
-compileR = cata a where -- TODO: combine with eNorm pass?
-    a (RegexLitF _ rr) = RegexCompiled (compileDefault rr)
-    a x                = embed x
-    -- TODO: is cata performant enough?
-
-compileIn :: Program a -> Program a
-compileIn (Program ds e) = Program (compileD <$> ds) (compileR e)
-
-compileD :: D a -> D a
-compileD d@SetFS{}       = d
-compileD (FunDecl n l e) = FunDecl n l (compileR e)
-
 desugar :: a
 desugar = error "Should have been desugared by this stage."
 
@@ -184,7 +165,9 @@
 eNorm e@AllColumn{}   = pure e
 eNorm e@IParseCol{}   = pure e
 eNorm e@FParseCol{}   = pure e
+eNorm e@ParseCol{}    = pure e
 eNorm e@AllField{}    = pure e
+eNorm e@LastField{}   = pure e
 eNorm (Guarded ty pe e) = Guarded ty <$> eNorm pe <*> eNorm e
 eNorm (Implicit ty e) = Implicit ty <$> eNorm e
 eNorm (Lam ty n e)    = Lam ty n <$> eNorm e
@@ -289,6 +272,11 @@
     pure $ case eI of
         StrLit _ str -> IntLit tyI (fromIntegral $ BS.length str)
         _            -> EApp ty (UBuiltin ty' Tally) eI
+eNorm (EApp ty op@(UBuiltin _ TallyList) e) = do
+    eI <- eNorm e
+    pure $ case eI of
+        (Arr _ xs) -> mkI $ fromIntegral $ V.length xs
+        _          -> EApp ty op eI
 eNorm (EApp ty (EApp ty' op@(BBuiltin _ Lt) e) e') = do
     eI <- eNorm e
     eI' <- eNorm e'
@@ -511,3 +499,4 @@
         BoolLit _ True  -> eNorm e0
         BoolLit _ False -> eNorm e1
         _               -> Cond ty p' <$> eNorm e0 <*> eNorm e1 -- needed to perform substitutions
+eNorm e = error ("Internal error: " ++ show e)
diff --git a/src/Jacinda/Backend/TreeWalk.hs b/src/Jacinda/Backend/TreeWalk.hs
--- a/src/Jacinda/Backend/TreeWalk.hs
+++ b/src/Jacinda/Backend/TreeWalk.hs
@@ -35,11 +35,9 @@
                  | InternalError
                  deriving (Show)
 
-type FileBS = BS.ByteString
-
 instance Exception StreamError where
 
-(!) :: V.Vector a -> Int -> a
+(!) :: Show a => V.Vector a -> Int -> a
 v ! ix = case v V.!? ix of
     Just x  -> x
     Nothing -> throw $ IndexOutOfBounds ix
@@ -80,16 +78,11 @@
 
 -- TODO: do I want to interleave state w/ eNorm or w/e
 
-withFp :: FileBS -> E (T K) -> E (T K)
-withFp fp = cata a where
-    a (NBuiltinF _ Fp) = mkStr fp
-    a x                = embed x
-
 -- eval
-eEval :: (FileBS, Int, BS.ByteString, V.Vector BS.ByteString) -- ^ Field context (for that line)
+eEval :: (Int, BS.ByteString, V.Vector BS.ByteString) -- ^ Field context (for that line)
       -> E (T K)
       -> E (T K)
-eEval (fp, ix, line, ctx) = go where
+eEval (ix, line, ctx) = go where
     go b@BoolLit{} = b
     go i@IntLit{} = i
     go f@FloatLit{} = f
@@ -102,13 +95,13 @@
     go (NBuiltin _ Nf) = mkI (fromIntegral $ V.length ctx)
     go (EApp ty op@BBuiltin{} e) = EApp ty op (go e)
     go (NBuiltin _ Ix) = mkI (fromIntegral ix)
-    go (NBuiltin _ Fp) = mkStr fp
     go (NBuiltin _ None) = OptionVal undefined Nothing
     go (EApp ty (UBuiltin _ Some) e) =
         let eI = go e
             in OptionVal ty (Just eI)
     go AllField{} = StrLit tyStr line
     go (Field _ i) = StrLit tyStr (ctx ! (i-1)) -- cause vector indexing starts at 0
+    go LastField{} = StrLit tyStr (V.last ctx)
     go (EApp _ (UBuiltin _ IParse) e) =
         let eI = asStr (go e)
             in parseAsEInt eI
@@ -274,12 +267,12 @@
         let str = asStr (go e)
             re = asRegex (go e')
             bss = splitBy re str
-            in Arr undefined (StrLit undefined <$> bss)
+            in Arr undefined (mkStr <$> bss)
     go (EApp _ (EApp _ (BBuiltin _ Splitc) e) e') =
         let str = asStr (go e)
             c = the (asStr (go e'))
             bss = BS.split c str
-            in Arr undefined (StrLit undefined <$> V.fromList bss)
+            in Arr undefined (mkStr <$> V.fromList bss)
     go (EApp _ (EApp _ (EApp _ (TBuiltin _ Substr) e0) e1) e2) =
         let eI0 = asStr (go e0)
             eI1 = asInt (go e1)
@@ -359,6 +352,10 @@
     go (Cond _ p e0 e1) =
         let p' = asBool (go p)
             in if p' then go e0 else go e1
+    go (EApp _ (UBuiltin _ TallyList) e) =
+        let xs = asArr (go e)
+            in mkI $ fromIntegral $ V.length xs
+    go e = error ("Internal error: " ++ show e)
 
 -- just shove some big number into the renamer and hope it doesn't clash (bad,
 -- hack, this is why we got kicked out of the garden of Eden)
@@ -377,8 +374,8 @@
         -> BS.ByteString
 atField re i = (! (i-1)) . splitBy re
 
-mkCtx :: FileBS -> RurePtr -> Int -> BS.ByteString -> (FileBS, Int, BS.ByteString, V.Vector BS.ByteString)
-mkCtx fp re ix line = (fp, ix, line, splitBy re line)
+mkCtx :: RurePtr -> Int -> BS.ByteString -> (Int, BS.ByteString, V.Vector BS.ByteString)
+mkCtx re ix line = (ix, line, splitBy re line)
 
 applyUn :: E (T K)
         -> E (T K)
@@ -389,88 +386,80 @@
         _             -> error "Internal error?"
 
 -- | Turn an expression representing a stream into a stream of expressions (using line as context)
-ir :: FileBS
-   -> RurePtr
+ir :: RurePtr
    -> E (T K)
    -> [BS.ByteString]
    -> [E (T K)] -- TODO: include chunks/context too?
-ir _ _ AllColumn{} = fmap mkStr
-ir _ re (Column _ i) = fmap (mkStr . atField re i)
-ir _ re (IParseCol _ i) = fmap (parseAsEInt . atField re i)
-ir _ re (FParseCol _ i) = fmap (parseAsF . atField re i)
-ir fp re (Implicit _ e) =
-    let e' = compileR e
-        in imap (\ix line -> eEval (mkCtx fp re ix line) e')
-ir fp re (Guarded _ pe e) =
-    let pe' = compileR pe
-        e' = compileR e
+ir _ AllColumn{} = fmap mkStr
+ir re (Column _ i) = fmap (mkStr . atField re i)
+ir re (IParseCol _ i) = fmap (parseAsEInt . atField re i)
+ir re (FParseCol _ i) = fmap (parseAsF . atField re i)
+ir re (ParseCol ty@(TyApp _ _ (TyB _ TyFloat)) i) = ir re (FParseCol ty i)
+ir re (ParseCol ty@(TyApp _ _ (TyB _ TyInteger)) i) = ir re (IParseCol ty i)
+ir re (Implicit _ e) =
+    imap (\ix line -> eEval (mkCtx re ix line) e)
+ir re (Guarded _ pe e) =
     -- TODO: normalize before stream
-        in fmap (uncurry (\ix line -> eEval (mkCtx fp re ix line) e')) . ifilter' (\ix line -> asBool (eEval (mkCtx fp re ix line) pe'))
-ir fp re (EApp _ (EApp _ (BBuiltin _ Map) op) stream) = let op' = compileR (withFp fp op) in fmap (applyUn op') . ir fp re stream
-ir fp re (EApp _ (EApp _ (BBuiltin _ Filter) op) stream) =
-    let op' = compileR (withFp fp op)
-        in filter (asBool . applyUn op') . ir fp re stream
-ir fp re (EApp _ (EApp _ (BBuiltin _ MapMaybe) op) stream) =
-    let op' = compileR (withFp fp op)
-        in mapMaybe (asOpt . applyUn op') . ir fp re stream
-ir fp re (EApp _ (UBuiltin _ CatMaybes) stream) =
-    mapMaybe asOpt . ir fp re stream
-ir fp re (EApp _ (EApp _ (BBuiltin _ Prior) op) stream) = prior (applyOp (withFp fp (compileR op))) . ir fp re stream
-ir fp re (EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) op) streaml) streamr) = \lineStream ->
+    fmap (uncurry (\ix line -> eEval (mkCtx re ix line) e)) . ifilter' (\ix line -> asBool (eEval (mkCtx re ix line) pe))
+ir re (EApp _ (EApp _ (BBuiltin _ Map) op) stream) = fmap (applyUn op) . ir re stream
+ir re (EApp _ (EApp _ (BBuiltin _ Filter) op) stream) =
+    filter (asBool . applyUn op) . ir re stream
+ir re (EApp _ (EApp _ (BBuiltin _ MapMaybe) op) stream) =
+    mapMaybe (asOpt . applyUn op) . ir re stream
+ir re (EApp _ (UBuiltin _ CatMaybes) stream) =
+    mapMaybe asOpt . ir re stream
+ir re (EApp _ (EApp _ (BBuiltin _ Prior) op) stream) = prior (applyOp op) . ir re stream
+ir re (EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) op) streaml) streamr) = \lineStream ->
     let
-        irl = ir fp re streaml lineStream
-        irr = ir fp re streamr lineStream
-    in zipWith (applyOp (withFp fp (compileR op))) irl irr
-ir fp re (EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) op) seed) xs) =
-    scanl' (applyOp (withFp fp (compileR op))) seed . ir fp re xs
-ir fp re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyStr)) _) Dedup) e) =
-    nubOrdOn asStr . ir fp re e
-ir fp re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyInteger)) _) Dedup) e) =
-    nubIntOn (fromIntegral . asInt) . ir fp re e
-ir fp re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyFloat)) _) Dedup) e) =
-    nubIntOn (fromEnum . asFloat) . ir fp re e
-ir fp re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyBool)) _) Dedup) e) =
-    nubIntOn (fromEnum . asBool) . ir fp re e
+        irl = ir re streaml lineStream
+        irr = ir re streamr lineStream
+    in zipWith (applyOp op) irl irr
+ir re (EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) op) seed) xs) =
+    scanl' (applyOp op) seed . ir re xs
+ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyStr)) _) Dedup) e) =
+    nubOrdOn asStr . ir re e
+ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyInteger)) _) Dedup) e) =
+    nubIntOn (fromIntegral . asInt) . ir re e
+ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyFloat)) _) Dedup) e) =
+    nubIntOn (fromEnum . asFloat) . ir re e
+ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyBool)) _) Dedup) e) =
+    nubIntOn (fromEnum . asBool) . ir re e
 
 -- | Output stream that prints each entry (expression)
 printStream :: [E (T K)] -> IO ()
 printStream = traverse_ print
 
-foldWithCtx :: FileBS
-            -> RurePtr
+foldWithCtx :: RurePtr
             -> E (T K)
             -> E (T K)
             -> E (T K)
             -> [BS.ByteString]
             -> E (T K)
-foldWithCtx fp re op seed streamExpr = foldl' (applyOp $ withFp fp (compileR op)) seed . ir fp re streamExpr
+foldWithCtx re op seed streamExpr = foldl' (applyOp op) seed . ir re streamExpr
 
-fold1 :: FileBS
-      -> RurePtr
+fold1 :: RurePtr
       -> E (T K)
       -> E (T K)
       -> [BS.ByteString]
       -> E (T K)
-fold1 fp re op streamExpr bs =
-    case ir fp re streamExpr bs of
+fold1 re op streamExpr bs =
+    case ir re streamExpr bs of
         e:es -> foldl' (applyOp op) e es
         _    -> throw EmptyFold
 
-runJac :: FileBS
-       -> RurePtr -- ^ Record separator
+runJac :: RurePtr -- ^ Record separator
        -> Int
        -> Program (T K)
        -> Either StreamError ([BS.ByteString] -> IO ())
-runJac fp re i e = fileProcessor fp re (closedProgram i e)
+runJac re i e = fileProcessor re (closedProgram i e)
 
-foldAll :: FileBS
-        -> RurePtr
+foldAll :: RurePtr
         -> [(Int, E (T K), E (T K), E (T K))]
         -> [BS.ByteString]
         -> [(Int, E (T K))]
-foldAll fp re foldExprs bs = evalAll seeds (mkStreams streamExprs) where
+foldAll re foldExprs bs = evalAll seeds (mkStreams streamExprs) where
     (is, ops, seeds, streamExprs) = unzip4 foldExprs
-    mkStreams = fmap (\streamExpr -> ir fp re streamExpr bs)
+    mkStreams = fmap (\streamExpr -> ir re streamExpr bs)
 
     evalAll seedsϵ ess | not (any null ess) = let es' = zipWith3 applyOp' ops seedsϵ (headMaybe <$> ess) in es' `seqAll` evalAll es' (tail' <$> ess)
                        -- if I try to use the (all null ess) criterion it space
@@ -514,103 +503,92 @@
 mkFoldVar :: Int -> b -> E b
 mkFoldVar i l = Var l (Name "fold_placeholder" (Unique i) l)
 
-gatherFoldsM :: FileBS -> E (T K) -> State (Int, [(Int, E (T K), E (T K), E (T K))]) (E (T K))
-gatherFoldsM fp (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = do
+gatherFoldsM :: E (T K) -> State (Int, [(Int, E (T K), E (T K), E (T K))]) (E (T K))
+gatherFoldsM (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = do
     (i,_) <- get
-    modify (bimap (+1) ((i, withFp fp (compileR op), seed, stream) :))
+    modify (bimap (+1) ((i, op, seed, stream) :))
     pure $ mkFoldVar i undefined
-gatherFoldsM fp (EApp ty e0 e1) = EApp ty <$> gatherFoldsM fp e0 <*> gatherFoldsM fp e1
-gatherFoldsM fp (Tup ty es) = Tup ty <$> traverse (gatherFoldsM fp) es
-gatherFoldsM fp (Arr ty es) = Arr ty <$> traverse (gatherFoldsM fp) es
-gatherFoldsM fp (OptionVal ty e) = OptionVal ty <$> traverse (gatherFoldsM fp) e
-gatherFoldsM fp (Cond ty p e e') = Cond ty <$> gatherFoldsM fp p <*> gatherFoldsM fp e <*> gatherFoldsM fp e'
-gatherFoldsM fp (NBuiltin _ Fp) = pure $ mkStr fp
-gatherFoldsM _ (NBuiltin _ None) = pure $ OptionVal undefined Nothing
-gatherFoldsM _ e@BBuiltin{} = pure e
-gatherFoldsM _ e@TBuiltin{} = pure e
-gatherFoldsM _ e@UBuiltin{} = pure e
-gatherFoldsM _ e@NBuiltin{} = pure e
-gatherFoldsM _ e@StrLit{} = pure e
-gatherFoldsM _ e@FloatLit{} = pure e
-gatherFoldsM _ e@IntLit{} = pure e
-gatherFoldsM _ e@BoolLit{} = pure e
+gatherFoldsM (EApp ty e0 e1) = EApp ty <$> gatherFoldsM e0 <*> gatherFoldsM e1
+gatherFoldsM (Tup ty es) = Tup ty <$> traverse gatherFoldsM es
+gatherFoldsM (Arr ty es) = Arr ty <$> traverse gatherFoldsM es
+gatherFoldsM (OptionVal ty e) = OptionVal ty <$> traverse gatherFoldsM e
+gatherFoldsM (Cond ty p e e') = Cond ty <$> gatherFoldsM p <*> gatherFoldsM e <*> gatherFoldsM e'
+gatherFoldsM (NBuiltin _ None) = pure $ OptionVal undefined Nothing
+gatherFoldsM e@BBuiltin{} = pure e
+gatherFoldsM e@TBuiltin{} = pure e
+gatherFoldsM e@UBuiltin{} = pure e
+gatherFoldsM e@NBuiltin{} = pure e
+gatherFoldsM e@StrLit{} = pure e
+gatherFoldsM e@FloatLit{} = pure e
+gatherFoldsM e@IntLit{} = pure e
+gatherFoldsM e@BoolLit{} = pure e
 
--- evaluate something that has a fold nested in it
-eWith :: FileBS -> RurePtr -> E (T K) -> [BS.ByteString] -> E (T K)
-eWith fp re (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = foldWithCtx fp re op seed stream
-eWith fp re (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _)) Fold1) op) stream)                          = fold1 fp re (withFp fp $ compileR op) stream
-eWith _ _ e@BBuiltin{}                                                                                                                 = const e
-eWith _ _ e@UBuiltin{}                                                                                                                 = const e
-eWith _ _ e@TBuiltin{}                                                                                                                 = const e
-eWith _ _ e@StrLit{}                                                                                                                   = const e
-eWith _ _ e@FloatLit{}                                                                                                                 = const e
-eWith _ _ e@IntLit{}                                                                                                                   = const e
-eWith _ _ e@BoolLit{}                                                                                                                  = const e
-eWith fp _ (NBuiltin _ Fp)                                                                                                             = const (mkStr fp)
-eWith fp re e = \bs ->
-    let (eHoles, (_, folds)) = runState (gatherFoldsM fp e) (0, []) -- 0 state, should contain no vars by now
-        in eClosed undefined $ ungather (IM.fromList $ foldAll fp re folds bs) eHoles
-{-
-eWith fp re (EApp ty e0 e1)                                                                                                            = \bs -> eClosed undefined (EApp ty (eWith fp re e0 bs) (eWith fp re e1 bs)) -}
-{-
-eWith fp re (Tup ty es)                                                                                                                = \bs -> Tup ty ((\e -> eWith fp re e bs) <$> es)
-eWith fp re (OptionVal ty e)                                                                                                           = \bs -> OptionVal ty ((\eϵ -> eWith fp re eϵ bs) <$> e)
-eWith fp re (Cond ty p e e')                                                                                                           = \bs -> eClosed undefined (Cond ty (eWith fp re p bs) (eWith fp re e bs) (eWith fp re e' bs))
--}
+eWith :: RurePtr -> E (T K) -> [BS.ByteString] -> E (T K)
+eWith re (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = foldWithCtx re op seed stream
+eWith re (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _)) Fold1) op) stream)                          = fold1 re op stream
+eWith _ e@BBuiltin{}                                                                                                                 = const e
+eWith _ e@UBuiltin{}                                                                                                                 = const e
+eWith _ e@TBuiltin{}                                                                                                                 = const e
+eWith _ e@StrLit{}                                                                                                                   = const e
+eWith _ e@FloatLit{}                                                                                                                 = const e
+eWith _ e@IntLit{}                                                                                                                   = const e
+eWith _ e@BoolLit{}                                                                                                                  = const e
+eWith re e = \bs ->
+    let (eHoles, (_, folds)) = runState (gatherFoldsM e) (0, []) -- 0 state, should contain no vars by now
+        in eClosed undefined $ ungather (IM.fromList $ foldAll re folds bs) eHoles
 
 takeConcatMap :: (a -> [b]) -> [a] -> [b]
 takeConcatMap f = concat . transpose . fmap f
 
 -- | Given an expression, turn it into a function which will process the file.
-fileProcessor :: FileBS
-              -> RurePtr
+fileProcessor :: RurePtr
               -> E (T K)
               -> Either StreamError ([BS.ByteString] -> IO ())
-fileProcessor _ _ AllField{}    = Left NakedField
-fileProcessor _ _ Field{}       = Left NakedField
-fileProcessor _ _ (NBuiltin _ Ix) = Left NakedField
-fileProcessor _ _ AllColumn{} = Right $ \inp ->
-    printStream $ fmap mkStr inp
-fileProcessor _ re (Column _ i) = Right $ \inp -> do
-    printStream $ fmap (mkStr . atField re i) inp
-fileProcessor _ re (IParseCol _ i) = Right $ \inp -> do
-    printStream $ fmap (parseAsEInt . atField re i) inp
-fileProcessor _ re (FParseCol _ i) = Right $ \inp -> do
-    printStream $ fmap (parseAsF . atField re i) inp
-fileProcessor fp re e@Guarded{} = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@Implicit{} = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (EApp _ (BBuiltin _ Filter) _) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
+fileProcessor _ AllField{}    = Left NakedField
+fileProcessor _ Field{}       = Left NakedField
+fileProcessor _ (NBuiltin _ Ix) = Left NakedField
+fileProcessor re e@AllColumn{} = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@Column{} = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@IParseCol{} = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@FParseCol{} = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@ParseCol{} = Right $ \inp -> printStream $ ir re e inp
+fileProcessor re e@Guarded{} = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@Implicit{} = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (EApp _ (BBuiltin _ Filter) _) _) = Right $ \inp ->
+    printStream $ ir re e inp
 -- at the moment, catMaybes only works on streams
-fileProcessor fp re e@(EApp _ (UBuiltin _ CatMaybes) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyStream) _))) Map) _) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyStream) _))) MapMaybe) _) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (EApp _ (BBuiltin _ Prior) _) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) _) _) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) _) _) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re e@(EApp _ (UBuiltin _ Dedup) _) = Right $ \inp ->
-    printStream $ ir fp re e inp
-fileProcessor fp re (Anchor _ es) = Right $ \inp ->
-    printStream $ takeConcatMap (\e -> ir fp re e inp) es
-fileProcessor _ _ Var{} = error "Internal error?"
-fileProcessor _ _ e@IntLit{} = Right $ const (print e)
-fileProcessor _ _ e@BoolLit{} = Right $ const (print e)
-fileProcessor _ _ e@StrLit{} = Right $ const (print e)
-fileProcessor _ _ e@FloatLit{} = Right $ const (print e)
-fileProcessor _ _ e@RegexLit{} = Right $ const (print e)
-fileProcessor fp _ (NBuiltin _ Fp) = Right $ const (print fp)
-fileProcessor _ _ Lam{} = Left UnevalFun
-fileProcessor _ _ Dfn{} = badSugar
-fileProcessor _ _ ResVar{} = badSugar
-fileProcessor _ _ BBuiltin{} = Left UnevalFun
-fileProcessor _ _ UBuiltin{} = Left UnevalFun
-fileProcessor _ _ TBuiltin{} = Left UnevalFun
-fileProcessor fp re e = Right $ print . eWith fp re e
+fileProcessor re e@(EApp _ (UBuiltin _ CatMaybes) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyStream) _))) Map) _) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyStream) _))) MapMaybe) _) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (EApp _ (BBuiltin _ Prior) _) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) _) _) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) _) _) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re e@(EApp _ (UBuiltin _ Dedup) _) = Right $ \inp ->
+    printStream $ ir re e inp
+fileProcessor re (Anchor _ es) = Right $ \inp ->
+    printStream $ takeConcatMap (\e -> ir re e inp) es
+fileProcessor _ Var{} = error "Internal error?"
+fileProcessor _ e@IntLit{} = Right $ const (print e)
+fileProcessor _ e@BoolLit{} = Right $ const (print e)
+fileProcessor _ e@StrLit{} = Right $ const (print e)
+fileProcessor _ e@FloatLit{} = Right $ const (print e)
+fileProcessor _ e@RegexLit{} = Right $ const (print e)
+fileProcessor _ Lam{} = Left UnevalFun
+fileProcessor _ Dfn{} = badSugar
+fileProcessor _ ResVar{} = badSugar
+fileProcessor _ BBuiltin{} = Left UnevalFun
+fileProcessor _ UBuiltin{} = Left UnevalFun
+fileProcessor _ TBuiltin{} = Left UnevalFun
+fileProcessor re e = Right $ print . eWith re e
diff --git a/src/Jacinda/File.hs b/src/Jacinda/File.hs
--- a/src/Jacinda/File.hs
+++ b/src/Jacinda/File.hs
@@ -10,6 +10,7 @@
 import           Control.Monad              ((<=<))
 import           Control.Monad.IO.Class     (liftIO)
 import           Control.Monad.State.Strict (StateT, get, put, runStateT)
+import           Control.Recursion          (cata, embed)
 import           Data.Bifunctor             (second)
 import qualified Data.ByteString            as BS
 import qualified Data.ByteString.Char8      as ASCII
@@ -58,13 +59,32 @@
 parseWithMax' :: BSL.ByteString -> Either (ParseError AlexPosn) (Program AlexPosn, Int)
 parseWithMax' = fmap (uncurry renamePGlobal . second (rewriteProgram . snd)) . parseWithMax
 
+type FileBS = BS.ByteString
+
+-- fill in regex with compiled.
+compileR :: FileBS
+         -> E (T K)
+         -> E (T K)
+compileR fp = cata a where
+    a (RegexLitF _ rrϵ) = RegexCompiled (compileDefault rrϵ)
+    a (NBuiltinF _ Fp)  = mkStr fp
+    a x                 = embed x
+    -- TODO: is cata performant enough?
+
+compileIn :: FileBS -> Program (T K) -> Program (T K)
+compileIn fp (Program ds e) = Program (compileD fp <$> ds) (compileR fp e)
+
+compileD :: FileBS -> D (T K) -> D (T K)
+compileD _ d@SetFS{}        = d
+compileD fp (FunDecl n l e) = FunDecl n l (compileR fp e)
+
 exprEval :: BSL.ByteString -> E (T K)
 exprEval src =
     case parseWithMax' src of
         Left err -> throw err
         Right (ast, m) ->
             let (typed, i) = yeet $ runTypeM m (tyProgram ast)
-            in closedProgram i (compileIn typed)
+            in closedProgram i (compileIn undefined typed)
 
 compileFS :: Maybe BS.ByteString -> RurePtr
 compileFS (Just bs) = compileDefault bs
@@ -80,7 +100,7 @@
     incls' <- defaultIncludes <*> pure incls
     (ast, m) <- parseEWithMax incls' src
     (typed, i) <- yeetIO $ runTypeM m (tyProgram ast)
-    cont <- yeetIO $ runJac (ASCII.pack fp) (compileFS (cliFS <|> getFS ast)) i typed
+    cont <- yeetIO $ runJac (compileFS (cliFS <|> getFS ast)) i (compileIn (ASCII.pack fp) typed)
     cont $ fmap BSL.toStrict (ASCIIL.lines contents)
     -- see: BSL.split, BSL.splitWith
 
diff --git a/src/Jacinda/Lexer.x b/src/Jacinda/Lexer.x
--- a/src/Jacinda/Lexer.x
+++ b/src/Jacinda/Lexer.x
@@ -107,6 +107,7 @@
         ","                      { mkSym Comma }
         "."                      { mkSym Dot }
         "#"                      { mkSym TallyTok }
+        "#*"                     { mkSym LengthTok }
         "[:"                     { mkSym ConstTok }
         "!"                      { mkSym Exclamation }
         ":"                      { mkSym Colon }
@@ -121,6 +122,7 @@
         ":?"                     { mkSym MapMaybeTok }
         "~*"                     { mkSym CapTok }
         "-."                     { mkSym NegTok }
+        "`*"                     { mkSym LastFieldTok }
 
         in                       { mkKw KwIn }
         let                      { mkKw KwLet }
@@ -273,6 +275,7 @@
          | Comma
          | Dot
          | TallyTok
+         | LengthTok
          | ConstTok
          | LBracePercent
          | LBraceBar
@@ -288,6 +291,7 @@
          | MapMaybeTok
          | CapTok
          | NegTok
+         | LastFieldTok
 
 instance Pretty Sym where
     pretty PlusTok       = "+"
@@ -320,6 +324,7 @@
     pretty Comma         = ","
     pretty Dot           = "."
     pretty TallyTok      = "#"
+    pretty LengthTok     = "#*"
     pretty Quot          = "\""
     pretty Caret         = "^"
     pretty ConstTok      = "[:"
@@ -336,6 +341,7 @@
     pretty MapMaybeTok   = ":?"
     pretty CapTok        = "~*"
     pretty NegTok        = "-."
+    pretty LastFieldTok  = "`*"
 
 data Keyword = KwLet
              | KwIn
diff --git a/src/Jacinda/Parser.y b/src/Jacinda/Parser.y
--- a/src/Jacinda/Parser.y
+++ b/src/Jacinda/Parser.y
@@ -53,6 +53,7 @@
     lbracePercent { TokSym $$ LBracePercent }
     lbraceBar { TokSym $$ LBraceBar }
     tally { TokSym $$ TallyTok }
+    tallyL { TokSym $$ LengthTok }
     const { TokSym $$ ConstTok }
     filter { TokSym $$ FilterTok }
     exclamation { TokSym $$ Exclamation }
@@ -99,6 +100,7 @@
     allField { TokFieldLit $$ 0 }
     column { $$@(TokStreamLit _ _) }
     field { $$@(TokFieldLit _ _) }
+    lastField { TokSym $$ LastFieldTok } -- TokSym is maybe insensible but whatever
 
     let { TokKeyword $$ KwLet }
     in { TokKeyword $$ KwIn }
@@ -218,12 +220,16 @@
   | field { Field (loc $1) (ix $1) }
   | allColumn { AllColumn $1 }
   | allField { AllField $1 }
+  | lastField { LastField $1 }
   | field iParse { EApp (loc $1) (UBuiltin $2 IParse) (Field (loc $1) (ix $1)) }
   | field fParse { EApp (loc $1) (UBuiltin $2 FParse) (Field (loc $1) (ix $1)) }
   | name iParse { EApp (Name.loc $1) (UBuiltin $2 IParse) (Var (Name.loc $1) $1) }
   | name fParse { EApp (Name.loc $1) (UBuiltin $2 FParse) (Var (Name.loc $1) $1) }
   | field colon { EApp (loc $1) (UBuiltin $2 Parse) (Field (loc $1) (ix $1)) }
   | name colon { EApp (Name.loc $1) (UBuiltin $2 Parse) (Var (Name.loc $1) $1) }
+  | lastField iParse { EApp $1 (UBuiltin $2 IParse) (LastField $1) }
+  | lastField fParse { EApp $1 (UBuiltin $2 FParse) (LastField $1) }
+  | lastField colon { EApp $1 (UBuiltin $2 Parse) (LastField $1) }
   | x colon { EApp $1 (UBuiltin $2 Parse) (ResVar $1 X) }
   | y colon { EApp $1 (UBuiltin $2 Parse) (ResVar $1 Y) }
   | x iParse { EApp $1 (UBuiltin $2 IParse) (ResVar $1 X) }
@@ -232,6 +238,7 @@
   | y fParse { EApp $1 (UBuiltin $2 FParse) (ResVar $1 Y) }
   | column iParse { IParseCol (loc $1) (ix $1) }
   | column fParse { FParseCol (loc $1) (ix $1) }
+  | column colon { ParseCol (loc $1) (ix $1) }
   | parens(iParse) { UBuiltin $1 IParse }
   | parens(fParse) { UBuiltin $1 FParse }
   | parens(colon) { UBuiltin $1 Parse }
@@ -251,6 +258,7 @@
   | lanchor sepBy(E, dot) rparen { Anchor $1 (reverse $2) }
   | E E { EApp (eLoc $1) $1 $2 }
   | tally { UBuiltin $1 Tally }
+  | tallyL { UBuiltin $1 TallyList }
   | const { UBuiltin $1 Const }
   | exclamation { UBuiltin $1 Not }
   | lsqbracket E rsqbracket { Dfn $1 $2 }
diff --git a/src/Jacinda/Regex.hs b/src/Jacinda/Regex.hs
--- a/src/Jacinda/Regex.hs
+++ b/src/Jacinda/Regex.hs
@@ -58,12 +58,12 @@
 splitBy :: RurePtr
         -> BS.ByteString
         -> V.Vector BS.ByteString
+splitBy _ "" = mempty
 splitBy re haystack@(BS.BS fp l) =
     (\sp -> V.fromList [BS.BS (fp `plusForeignPtr` s) (e-s) | (s, e) <- sp]) slicePairs
     where ixes = unsafeDupablePerformIO $ matches' re haystack
           slicePairs = case ixes of
                 (RureMatch 0 i:rms) -> mkMiddle (fromIntegral i) rms
-                []                  -> []
                 rms                 -> mkMiddle 0 rms
           mkMiddle begin' []        = [(begin', l)]
           mkMiddle begin' (rm0:rms) = (begin', fromIntegral (start rm0)) : mkMiddle (fromIntegral $ end rm0) rms
diff --git a/src/Jacinda/Rename.hs b/src/Jacinda/Rename.hs
--- a/src/Jacinda/Rename.hs
+++ b/src/Jacinda/Rename.hs
@@ -132,6 +132,7 @@
 renameProgram :: Program a -> RenameM (Program a)
 renameProgram (Program ds e) = Program <$> traverse renameD ds <*> renameE e
 
+{-# INLINABLE renameE #-}
 renameE :: (HasRenames s, MonadState s m) => E a -> m (E a)
 renameE (EApp l e e')   = EApp l <$> renameE e <*> renameE e'
 renameE (Tup l es)      = Tup l <$> traverse renameE es
diff --git a/src/Jacinda/Ty.hs b/src/Jacinda/Ty.hs
--- a/src/Jacinda/Ty.hs
+++ b/src/Jacinda/Ty.hs
@@ -425,6 +425,7 @@
 tyE0 (IParseCol _ i)         = pure $ IParseCol (tyStream tyI) i
 tyE0 (FParseCol _ i)         = pure $ FParseCol (tyStream tyF) i
 tyE0 (Field _ i)             = pure $ Field tyStr i
+tyE0 (LastField _)           = pure $ LastField tyStr
 tyE0 AllField{}              = pure $ AllField tyStr
 tyE0 AllColumn{}             = pure $ AllColumn (tyStream tyStr)
 tyE0 (NBuiltin _ Ix)         = pure $ NBuiltin tyI Ix
@@ -456,6 +457,10 @@
 tyE0 (UBuiltin _ FParse)     = pure $ UBuiltin (tyArr tyStr tyF) FParse
 tyE0 (UBuiltin _ Floor)      = pure $ UBuiltin (tyArr tyF tyI) Floor
 tyE0 (UBuiltin _ Ceiling)    = pure $ UBuiltin (tyArr tyF tyI) Ceiling
+tyE0 (UBuiltin _ TallyList) = do
+    a <- dummyName "a"
+    let a' = var a
+    pure $ UBuiltin (tyArr a' tyI) TallyList
 tyE0 (UBuiltin l Negate) = do
     a <- dummyName "a"
     modify (mapClassVars (addC a (IsNum, l)))
@@ -468,6 +473,11 @@
 tyE0 (NBuiltin _ None) = do
     a <- dummyName "a"
     pure $ NBuiltin (tyOpt $ var a) None
+tyE0 (ParseCol l i) = do
+    a <- dummyName "a"
+    let a' = var a
+    modify (mapClassVars (addC a (IsParseable, l)))
+    pure $ ParseCol (tyStream a') i
 tyE0 (UBuiltin l Parse) = do
     a <- dummyName "a"
     let a' = var a
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -29,6 +29,7 @@
             ["drwxr-xr-x","12","vanessa","staff","384","Dec","26","19:43","_darcs"]
         , splitWhitespaceT "      55 ./src/Jacinda/File.hs" ["55", "./src/Jacinda/File.hs"]
         , splitWhitespaceT "" []
+        , splitWhitespaceT "5" ["5"]
         , testCase "type of" (tyOfT sumBytes (TyB Star TyInteger))
         , testCase "type of" (tyOfT krakRegex (TyApp Star (TyB (KArr Star Star) TyStream) (TyB Star TyStr))) -- stream of str
         , testCase "type of" (tyOfT krakCol (TyApp Star (TyB (KArr Star Star) TyStream) (TyB Star TyStr))) -- stream of str
@@ -41,7 +42,8 @@
         , testCase "typechecks dfn" (tyFile "test/examples/ab.jac")
         , testCase "parses parens" (tyFile "examples/lib.jac")
         , testCase "typechecks/parses correctly" (tyFile "test/examples/line.jac")
-        , testCase "split eval" (evalTo "[x+' '+y]|'' split '01-23-1987' /-/" " 01 23 1987")
+        , testCase "split eval" (evalTo "[x+' '+y]|> split '01-23-1987' /-/" "01 23 1987")
+        , testCase "length eval" (evalTo "#*split '01-23-1987' /-/" "3")
         , testCase "captureE" (evalTo "'01-23-1987' ~* 3 /(\\d{2})-(\\d{2})-(\\d{4})/" "Some 1987")
         , testCase "if...then...else" (evalTo "if #t then 0 else 1" "0")
         ]
diff --git a/test/examples/awkBook1.jac b/test/examples/awkBook1.jac
--- a/test/examples/awkBook1.jac
+++ b/test/examples/awkBook1.jac
@@ -1,2 +1,1 @@
-{. TODO: maybe need parseable class?
-{`3:i>0}{(`1.`2:f*`3:f)}
+{`3:i>0}{(`1.`2:f*`3:)}
